You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@age.apache.org by jg...@apache.org on 2023/03/24 23:10:24 UTC
[age] branch PG12 updated (c042b320 -> 15ea6787)
This is an automated email from the ASF dual-hosted git repository.
jgemignani pushed a change to branch PG12
in repository https://gitbox.apache.org/repos/asf/age.git
from c042b320 Update README.md (#705)
new 888d0fa8 Updated Readme for drivers folder (#642)
new f6fccd8c Removed async from function definitions (#680)
new a9099c9b Barbell graph generation (#648)
new 30d20c4a UPDATED Python Driver ANTLR 4.9.3 -> 4.11.1 (#706)
new ceebd754 Fix WITH ignoring WHERE clause (#646)
new fb954724 Implement isEmpty() predicate function (#710)
new 0631b5a9 Barbell regress tests (#708)
new eda403d6 Fix cypher function input argument checks (issue #718)
new 0f9c8958 Fix Issue #693 - server termination from return exists(path) (#721)
new 1123878a Update regression tests for cypher_with (#725)
new 9fefd365 Fix issue #733 - create_complete_graph() terminates the server (#734)
new 8102847d Prevent MATCH from following OPTIONAL MATCH (#740)
new 87a7d109 Fix property constraints against resolved variables in concurrent MATCH clauses(#701) (#747)
new e7e8c98d Fix property constraints against resolved variables (#724) (#751)
new 15ea6787 Include invalid labels in reused variables. (#751) (#762)
The 15 revisions listed above as "new" are entirely new to this
repository and will be described in separate emails. The revisions
listed as "add" were already present in the repository and have only
been added to this reference.
Summary of changes:
.gitignore | 2 +
age--1.1.1.sql | 21 ++
drivers/README | 5 +
drivers/nodejs/README.md | 4 +
drivers/nodejs/test/Agtype.test.ts | 8 +-
drivers/python/age/gen/AgtypeLexer.py | 153 ++++----
drivers/python/age/gen/AgtypeListener.py | 3 +-
drivers/python/age/gen/AgtypeParser.py | 100 +++--
drivers/python/age/gen/AgtypeVisitor.py | 3 +-
drivers/python/antlr/README.md | 14 +-
drivers/python/requirements.txt | Bin 158 -> 160 bytes
drivers/python/setup.py | 2 +-
regress/expected/analyze.out | 5 +
regress/expected/catalog.out | 2 +-
regress/expected/cypher_match.out | 616 ++++++++++++++++++++++++++++++-
regress/expected/cypher_merge.out | 40 +-
regress/expected/cypher_with.out | 317 +++++++++++++++-
regress/expected/graph_generation.out | 119 ++++++
regress/sql/analyze.sql | 4 +
regress/sql/cypher_match.sql | 240 +++++++++++-
regress/sql/cypher_with.sql | 204 +++++++++-
regress/sql/graph_generation.sql | 35 ++
src/backend/catalog/ag_label.c | 15 +
src/backend/parser/cypher_analyze.c | 9 +
src/backend/parser/cypher_clause.c | 313 +++++++++++-----
src/backend/parser/cypher_expr.c | 16 +-
src/backend/utils/adt/agtype.c | 81 ++++
src/backend/utils/graph_generation.c | 184 ++++++++-
src/backend/utils/load/age_load.c | 15 +-
src/include/catalog/ag_label.h | 2 +
30 files changed, 2253 insertions(+), 279 deletions(-)
[age] 14/15: Fix property constraints against resolved variables (#724) (#751)
Posted by jg...@apache.org.
This is an automated email from the ASF dual-hosted git repository.
jgemignani pushed a commit to branch PG12
in repository https://gitbox.apache.org/repos/asf/age.git
commit e7e8c98d33c8d23d841ee2f09d437c439dd99071
Author: John Gemignani <jr...@gmail.com>
AuthorDate: Wed Mar 22 10:08:57 2023 -0700
Fix property constraints against resolved variables (#724) (#751)
Fixed an issue where an already resolved variable, when used for
a property constraint, errored out. See #724 for more details.
This is the second part of the fix for the match property constraint
bug regarding variable reuse. The prior fix addressed clause-to-clause,
this fix addresses within a clause.
Adjusted and added additional regression tests.
Co-authored-by: Dehowe Feng <de...@gmail.com>
---
regress/expected/cypher_match.out | 118 +++++++++++++++++++++++++++++++++----
regress/sql/cypher_match.sql | 40 ++++++++++---
src/backend/parser/cypher_clause.c | 68 ++++++++++++++-------
3 files changed, 186 insertions(+), 40 deletions(-)
diff --git a/regress/expected/cypher_match.out b/regress/expected/cypher_match.out
index f3817a50..0e596406 100644
--- a/regress/expected/cypher_match.out
+++ b/regress/expected/cypher_match.out
@@ -502,25 +502,45 @@ SELECT * FROM cypher('cypher_match', $$MATCH (n:v)$$) AS (a agtype);
ERROR: syntax error at end of input
LINE 1: SELECT * FROM cypher('cypher_match', $$MATCH (n:v)$$) AS (a ...
^
---Invalid Variables
+--invalid variable reuse, these should fail
SELECT * FROM cypher('cypher_match', $$
MATCH (a)-[]-()-[]-(a:v1) RETURN a
$$) AS (a agtype);
-ERROR: variable a already exists
+ERROR: multiple labels for variable 'a' are not supported
LINE 2: MATCH (a)-[]-()-[]-(a:v1) RETURN a
^
+SELECT * FROM cypher('cypher_match', $$
+ MATCH (a)-[]-(a:v2)-[]-(a) RETURN a
+$$) AS (a agtype);
+ERROR: multiple labels for variable 'a' are not supported
+LINE 2: MATCH (a)-[]-(a:v2)-[]-(a) RETURN a
+ ^
+SELECT * FROM cypher('cypher_match', $$
+ MATCH (a)-[]-(a:v1) RETURN a
+$$) AS (a agtype);
+ERROR: multiple labels for variable 'a' are not supported
+LINE 2: MATCH (a)-[]-(a:v1) RETURN a
+ ^
+SELECT * FROM cypher('cypher_match', $$
+ MATCH (a)-[]-(a)-[]-(a:v1) RETURN a
+$$) AS (a agtype);
+ERROR: multiple labels for variable 'a' are not supported
+LINE 2: MATCH (a)-[]-(a)-[]-(a:v1) RETURN a
+ ^
+--Valid variable reuse, although why would you want to do it this way?
SELECT * FROM cypher('cypher_match', $$
MATCH (a:v1)-[]-()-[a]-() RETURN a
$$) AS (a agtype);
-ERROR: variable a already exists
+ERROR: variable 'a' is for a vertex
LINE 2: MATCH (a:v1)-[]-()-[a]-() RETURN a
^
SELECT * FROM cypher('cypher_match', $$
- MATCH (a:v1)-[]-()-[]-(a {id:'will_fail'}) RETURN a
+ MATCH (a:v1)-[]-()-[]-(a {id:'will_not_fail'}) RETURN a
$$) AS (a agtype);
-ERROR: variable a already exists
-LINE 2: MATCH (a:v1)-[]-()-[]-(a {id:'will_fail'}) RETURN a
- ^
+ a
+---
+(0 rows)
+
--Incorrect Labels
SELECT * FROM cypher('cypher_match', $$MATCH (n)-[:v]-() RETURN n$$) AS (n agtype);
n
@@ -1509,14 +1529,14 @@ SELECT * FROM cypher('cypher_match', $$
(1 row)
SELECT * FROM cypher('cypher_match', $$
- MATCH (a),(b) WHERE a.age = 4 AND a.name = "T" AND b.age = 6 CREATE
+ MATCH (a),(b) WHERE a.age = 4 AND a.name = "T" AND b.age = 6 CREATE
(a)-[:knows {relationship: "friends", years: 3}]->(b) $$) as (r agtype);
r
---
(0 rows)
SELECT * FROM cypher('cypher_match', $$
- MATCH (a),(b) WHERE a.age = 4 AND a.name = "orphan" AND b.age = 6 CREATE
+ MATCH (a),(b) WHERE a.age = 4 AND a.name = "orphan" AND b.age = 6 CREATE
(a)-[:knows {relationship: "enemies", years: 4}]->(b) $$) as (r agtype);
r
---
@@ -1534,7 +1554,7 @@ SELECT * FROM cypher('cypher_match', $$
{"id": 1407374883553283, "label": "e1", "end_id": 281474976710661, "start_id": 281474976710660, "properties": {}}::edge
(6 rows)
--- check reuse of 'a'
+-- check reuse of 'a' clause-to-clause - vertices
SELECT * FROM cypher('cypher_match', $$
MATCH (a {age:4}) RETURN a $$) as (a agtype);
a
@@ -1614,7 +1634,7 @@ SELECT * FROM cypher('cypher_match', $$
{"id": 281474976710666, "label": "", "properties": {"age": 6}}::vertex
(2 rows)
--- check reuse of 'r'
+-- check reuse of 'r' clause-to-clause - edges
SELECT * FROM cypher('cypher_match', $$
MATCH ()-[r]-() RETURN r $$) as (r agtype);
r
@@ -1672,6 +1692,82 @@ SELECT * FROM cypher('cypher_match', $$
---
(0 rows)
+-- check reuse within clause - vertices
+SELECT * FROM cypher('cypher_match', $$ CREATE (u {name: "Dave"})-[:knows]->({name: "John"})-[:knows]->(u) RETURN u $$) as (u agtype);
+ u
+------------------------------------------------------------------------------
+ {"id": 281474976710667, "label": "", "properties": {"name": "Dave"}}::vertex
+(1 row)
+
+SELECT * FROM cypher('cypher_match', $$ MATCH p=(u)-[]-()-[]-(u) RETURN p $$)as (p agtype);
+ p
+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
+ [{"id": 281474976710667, "label": "", "properties": {"name": "Dave"}}::vertex, {"id": 4785074604081155, "label": "knows", "end_id": 281474976710667, "start_id": 281474976710668, "properties": {}}::edge, {"id": 281474976710668, "label": "", "properties": {"name": "John"}}::vertex, {"id": 4785074604081156, "label": "knows", "end_id": 281474976710668, "start_id": 281474976710667, "properties": {}}::edge, {"id": 281474976710667, "label": "", "properties": {"name": "Dave"}}::vertex]::path
+ [{"id": 281474976710668, "label": "", "properties": {"name": "John"}}::vertex, {"id": 4785074604081155, "label": "knows", "end_id": 281474976710667, "start_id": 281474976710668, "properties": {}}::edge, {"id": 281474976710667, "label": "", "properties": {"name": "Dave"}}::vertex, {"id": 4785074604081156, "label": "knows", "end_id": 281474976710668, "start_id": 281474976710667, "properties": {}}::edge, {"id": 281474976710668, "label": "", "properties": {"name": "John"}}::vertex]::path
+ [{"id": 281474976710667, "label": "", "properties": {"name": "Dave"}}::vertex, {"id": 4785074604081156, "label": "knows", "end_id": 281474976710668, "start_id": 281474976710667, "properties": {}}::edge, {"id": 281474976710668, "label": "", "properties": {"name": "John"}}::vertex, {"id": 4785074604081155, "label": "knows", "end_id": 281474976710667, "start_id": 281474976710668, "properties": {}}::edge, {"id": 281474976710667, "label": "", "properties": {"name": "Dave"}}::vertex]::path
+ [{"id": 281474976710668, "label": "", "properties": {"name": "John"}}::vertex, {"id": 4785074604081156, "label": "knows", "end_id": 281474976710668, "start_id": 281474976710667, "properties": {}}::edge, {"id": 281474976710667, "label": "", "properties": {"name": "Dave"}}::vertex, {"id": 4785074604081155, "label": "knows", "end_id": 281474976710667, "start_id": 281474976710668, "properties": {}}::edge, {"id": 281474976710668, "label": "", "properties": {"name": "John"}}::vertex]::path
+(4 rows)
+
+SELECT * FROM cypher('cypher_match', $$ MATCH p=(u)-[]->()-[]->(u) RETURN p $$)as (p agtype);
+ p
+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
+ [{"id": 281474976710668, "label": "", "properties": {"name": "John"}}::vertex, {"id": 4785074604081155, "label": "knows", "end_id": 281474976710667, "start_id": 281474976710668, "properties": {}}::edge, {"id": 281474976710667, "label": "", "properties": {"name": "Dave"}}::vertex, {"id": 4785074604081156, "label": "knows", "end_id": 281474976710668, "start_id": 281474976710667, "properties": {}}::edge, {"id": 281474976710668, "label": "", "properties": {"name": "John"}}::vertex]::path
+ [{"id": 281474976710667, "label": "", "properties": {"name": "Dave"}}::vertex, {"id": 4785074604081156, "label": "knows", "end_id": 281474976710668, "start_id": 281474976710667, "properties": {}}::edge, {"id": 281474976710668, "label": "", "properties": {"name": "John"}}::vertex, {"id": 4785074604081155, "label": "knows", "end_id": 281474976710667, "start_id": 281474976710668, "properties": {}}::edge, {"id": 281474976710667, "label": "", "properties": {"name": "Dave"}}::vertex]::path
+(2 rows)
+
+SELECT * FROM cypher('cypher_match', $$ MATCH p=(a)-[]->()-[]->(a {name: "Dave"}) RETURN p $$)as (p agtype);
+ p
+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
+ [{"id": 281474976710667, "label": "", "properties": {"name": "Dave"}}::vertex, {"id": 4785074604081156, "label": "knows", "end_id": 281474976710668, "start_id": 281474976710667, "properties": {}}::edge, {"id": 281474976710668, "label": "", "properties": {"name": "John"}}::vertex, {"id": 4785074604081155, "label": "knows", "end_id": 281474976710667, "start_id": 281474976710668, "properties": {}}::edge, {"id": 281474976710667, "label": "", "properties": {"name": "Dave"}}::vertex]::path
+(1 row)
+
+SELECT * FROM cypher('cypher_match', $$ MATCH p=(a)-[]->()-[]->(a {name: "John"}) RETURN p $$)as (p agtype);
+ p
+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
+ [{"id": 281474976710668, "label": "", "properties": {"name": "John"}}::vertex, {"id": 4785074604081155, "label": "knows", "end_id": 281474976710667, "start_id": 281474976710668, "properties": {}}::edge, {"id": 281474976710667, "label": "", "properties": {"name": "Dave"}}::vertex, {"id": 4785074604081156, "label": "knows", "end_id": 281474976710668, "start_id": 281474976710667, "properties": {}}::edge, {"id": 281474976710668, "label": "", "properties": {"name": "John"}}::vertex]::path
+(1 row)
+
+SELECT * FROM cypher('cypher_match', $$ MATCH p=(a {name: "Dave"})-[]->()-[]->(a {name: "Dave"}) RETURN p $$)as (p agtype);
+ p
+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
+ [{"id": 281474976710667, "label": "", "properties": {"name": "Dave"}}::vertex, {"id": 4785074604081156, "label": "knows", "end_id": 281474976710668, "start_id": 281474976710667, "properties": {}}::edge, {"id": 281474976710668, "label": "", "properties": {"name": "John"}}::vertex, {"id": 4785074604081155, "label": "knows", "end_id": 281474976710667, "start_id": 281474976710668, "properties": {}}::edge, {"id": 281474976710667, "label": "", "properties": {"name": "Dave"}}::vertex]::path
+(1 row)
+
+SELECT * FROM cypher('cypher_match', $$ MATCH p=(a {name: "John"})-[]->()-[]->(a {name: "John"}) RETURN p $$)as (p agtype);
+ p
+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
+ [{"id": 281474976710668, "label": "", "properties": {"name": "John"}}::vertex, {"id": 4785074604081155, "label": "knows", "end_id": 281474976710667, "start_id": 281474976710668, "properties": {}}::edge, {"id": 281474976710667, "label": "", "properties": {"name": "Dave"}}::vertex, {"id": 4785074604081156, "label": "knows", "end_id": 281474976710668, "start_id": 281474976710667, "properties": {}}::edge, {"id": 281474976710668, "label": "", "properties": {"name": "John"}}::vertex]::path
+(1 row)
+
+SELECT * FROM cypher('cypher_match', $$ MATCH p=(a {name: "Dave"})-[]->()-[]->(a) RETURN p $$)as (p agtype);
+ p
+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
+ [{"id": 281474976710667, "label": "", "properties": {"name": "Dave"}}::vertex, {"id": 4785074604081156, "label": "knows", "end_id": 281474976710668, "start_id": 281474976710667, "properties": {}}::edge, {"id": 281474976710668, "label": "", "properties": {"name": "John"}}::vertex, {"id": 4785074604081155, "label": "knows", "end_id": 281474976710667, "start_id": 281474976710668, "properties": {}}::edge, {"id": 281474976710667, "label": "", "properties": {"name": "Dave"}}::vertex]::path
+(1 row)
+
+SELECT * FROM cypher('cypher_match', $$ MATCH p=(a {name: "John"})-[]->()-[]->(a) RETURN p $$)as (p agtype);
+ p
+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
+ [{"id": 281474976710668, "label": "", "properties": {"name": "John"}}::vertex, {"id": 4785074604081155, "label": "knows", "end_id": 281474976710667, "start_id": 281474976710668, "properties": {}}::edge, {"id": 281474976710667, "label": "", "properties": {"name": "Dave"}}::vertex, {"id": 4785074604081156, "label": "knows", "end_id": 281474976710668, "start_id": 281474976710667, "properties": {}}::edge, {"id": 281474976710668, "label": "", "properties": {"name": "John"}}::vertex]::path
+(1 row)
+
+-- these are illegal and should fail
+SELECT * FROM cypher('cypher_match', $$ MATCH p=(a)-[b]->()-[b]->(a) RETURN p $$)as (p agtype);
+ERROR: duplicate edge variable 'b' within a clause
+LINE 1: ...ROM cypher('cypher_match', $$ MATCH p=(a)-[b]->()-[b]->(a) R...
+ ^
+SELECT * FROM cypher('cypher_match', $$ MATCH p=(a)-[b]->()-[b:knows]->(a) RETURN p $$)as (p agtype);
+ERROR: duplicate edge variable 'b' within a clause
+LINE 1: ...ROM cypher('cypher_match', $$ MATCH p=(a)-[b]->()-[b:knows]-...
+ ^
+SELECT * FROM cypher('cypher_match', $$ MATCH p=(a)-[b:knows]->()-[b:knows]->(a) RETURN p $$)as (p agtype);
+ERROR: duplicate edge variable 'b' within a clause
+LINE 1: ...pher('cypher_match', $$ MATCH p=(a)-[b:knows]->()-[b:knows]-...
+ ^
+SELECT * FROM cypher('cypher_match', $$ MATCH p=(a)-[b:knows]->()-[b]->(a) RETURN p $$)as (p agtype);
+ERROR: duplicate edge variable 'b' within a clause
+LINE 1: ...pher('cypher_match', $$ MATCH p=(a)-[b:knows]->()-[b]->(a) R...
+ ^
--
-- Clean up
--
diff --git a/regress/sql/cypher_match.sql b/regress/sql/cypher_match.sql
index f08f8dfb..b05e79c5 100644
--- a/regress/sql/cypher_match.sql
+++ b/regress/sql/cypher_match.sql
@@ -271,17 +271,27 @@ EXECUTE property_ps(agtype_build_map('props',
-- need a following RETURN clause (should fail)
SELECT * FROM cypher('cypher_match', $$MATCH (n:v)$$) AS (a agtype);
---Invalid Variables
+--invalid variable reuse, these should fail
SELECT * FROM cypher('cypher_match', $$
MATCH (a)-[]-()-[]-(a:v1) RETURN a
$$) AS (a agtype);
+SELECT * FROM cypher('cypher_match', $$
+ MATCH (a)-[]-(a:v2)-[]-(a) RETURN a
+$$) AS (a agtype);
+SELECT * FROM cypher('cypher_match', $$
+ MATCH (a)-[]-(a:v1) RETURN a
+$$) AS (a agtype);
+SELECT * FROM cypher('cypher_match', $$
+ MATCH (a)-[]-(a)-[]-(a:v1) RETURN a
+$$) AS (a agtype);
+--Valid variable reuse, although why would you want to do it this way?
SELECT * FROM cypher('cypher_match', $$
MATCH (a:v1)-[]-()-[a]-() RETURN a
$$) AS (a agtype);
SELECT * FROM cypher('cypher_match', $$
- MATCH (a:v1)-[]-()-[]-(a {id:'will_fail'}) RETURN a
+ MATCH (a:v1)-[]-()-[]-(a {id:'will_not_fail'}) RETURN a
$$) AS (a agtype);
--Incorrect Labels
@@ -750,20 +760,19 @@ SELECT * FROM cypher('cypher_match', $$
SELECT * FROM cypher('cypher_match', $$
MATCH (a) WHERE exists(a.name) SET a.age = 4 RETURN a $$) as (a agtype);
-
SELECT * FROM cypher('cypher_match', $$
MATCH (a),(b) WHERE a.age = 4 AND a.name = "T" AND b.age = 6
RETURN a,b $$) as (a agtype, b agtype);
SELECT * FROM cypher('cypher_match', $$
- MATCH (a),(b) WHERE a.age = 4 AND a.name = "T" AND b.age = 6 CREATE
+ MATCH (a),(b) WHERE a.age = 4 AND a.name = "T" AND b.age = 6 CREATE
(a)-[:knows {relationship: "friends", years: 3}]->(b) $$) as (r agtype);
SELECT * FROM cypher('cypher_match', $$
- MATCH (a),(b) WHERE a.age = 4 AND a.name = "orphan" AND b.age = 6 CREATE
+ MATCH (a),(b) WHERE a.age = 4 AND a.name = "orphan" AND b.age = 6 CREATE
(a)-[:knows {relationship: "enemies", years: 4}]->(b) $$) as (r agtype);
SELECT * FROM cypher('cypher_match', $$
MATCH (a)-[r]-(b) RETURN r $$) as (r agtype);
--- check reuse of 'a'
+-- check reuse of 'a' clause-to-clause - vertices
SELECT * FROM cypher('cypher_match', $$
MATCH (a {age:4}) RETURN a $$) as (a agtype);
SELECT * FROM cypher('cypher_match', $$
@@ -790,7 +799,7 @@ SELECT * FROM cypher('cypher_match', $$
SELECT * FROM cypher('cypher_match', $$
MATCH (a) WHERE exists(a.age) AND NOT exists(a.name) RETURN a $$) as (a agtype);
--- check reuse of 'r'
+-- check reuse of 'r' clause-to-clause - edges
SELECT * FROM cypher('cypher_match', $$
MATCH ()-[r]-() RETURN r $$) as (r agtype);
SELECT * FROM cypher('cypher_match', $$
@@ -807,6 +816,23 @@ SELECT * FROM cypher('cypher_match', $$
SELECT * FROM cypher('cypher_match', $$
MATCH ()-[r {relationship:"enemies"}]-() MATCH ()-[r {relationship:"friends"}]-() RETURN r $$) as (r agtype);
+-- check reuse within clause - vertices
+SELECT * FROM cypher('cypher_match', $$ CREATE (u {name: "Dave"})-[:knows]->({name: "John"})-[:knows]->(u) RETURN u $$) as (u agtype);
+SELECT * FROM cypher('cypher_match', $$ MATCH p=(u)-[]-()-[]-(u) RETURN p $$)as (p agtype);
+SELECT * FROM cypher('cypher_match', $$ MATCH p=(u)-[]->()-[]->(u) RETURN p $$)as (p agtype);
+SELECT * FROM cypher('cypher_match', $$ MATCH p=(a)-[]->()-[]->(a {name: "Dave"}) RETURN p $$)as (p agtype);
+SELECT * FROM cypher('cypher_match', $$ MATCH p=(a)-[]->()-[]->(a {name: "John"}) RETURN p $$)as (p agtype);
+SELECT * FROM cypher('cypher_match', $$ MATCH p=(a {name: "Dave"})-[]->()-[]->(a {name: "Dave"}) RETURN p $$)as (p agtype);
+SELECT * FROM cypher('cypher_match', $$ MATCH p=(a {name: "John"})-[]->()-[]->(a {name: "John"}) RETURN p $$)as (p agtype);
+SELECT * FROM cypher('cypher_match', $$ MATCH p=(a {name: "Dave"})-[]->()-[]->(a) RETURN p $$)as (p agtype);
+SELECT * FROM cypher('cypher_match', $$ MATCH p=(a {name: "John"})-[]->()-[]->(a) RETURN p $$)as (p agtype);
+
+-- these are illegal and should fail
+SELECT * FROM cypher('cypher_match', $$ MATCH p=(a)-[b]->()-[b]->(a) RETURN p $$)as (p agtype);
+SELECT * FROM cypher('cypher_match', $$ MATCH p=(a)-[b]->()-[b:knows]->(a) RETURN p $$)as (p agtype);
+SELECT * FROM cypher('cypher_match', $$ MATCH p=(a)-[b:knows]->()-[b:knows]->(a) RETURN p $$)as (p agtype);
+SELECT * FROM cypher('cypher_match', $$ MATCH p=(a)-[b:knows]->()-[b]->(a) RETURN p $$)as (p agtype);
+
--
-- Clean up
--
diff --git a/src/backend/parser/cypher_clause.c b/src/backend/parser/cypher_clause.c
index 234e3aad..3bc39cce 100644
--- a/src/backend/parser/cypher_clause.c
+++ b/src/backend/parser/cypher_clause.c
@@ -4361,20 +4361,29 @@ static Expr *transform_cypher_edge(cypher_parsestate *cpstate,
transform_entity *entity = find_variable(cpstate, rel->name);
/*
- * TODO: openCypher allows a variable to be used before it
- * is properly declared. This logic is not satifactory
- * for that and must be better developed.
+ * If the variable already exists, verify that it is for an edge.
+ * You cannot have the same edge repeated in a path.
+ * You cannot have an variable that is for a vertex.
*/
- if (entity != NULL &&
- (entity->type != ENT_EDGE ||
- !IS_DEFAULT_LABEL_EDGE(rel->label) ||
- rel->props))
+ if (entity != NULL)
{
- ereport(ERROR,
- (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
- errmsg("variable %s already exists", rel->name),
- parser_errposition(pstate, rel->location)));
+ if (entity->type == ENT_EDGE)
+ {
+ ereport(ERROR,
+ (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("duplicate edge variable '%s' within a clause",
+ rel->name),
+ parser_errposition(pstate, rel->location)));
+ }
+ if (entity->type == ENT_VERTEX)
+ {
+ ereport(ERROR,
+ (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("variable '%s' is for a vertex", rel->name),
+ parser_errposition(pstate, rel->location)));
+ }
}
+
return te->expr;
}
}
@@ -4453,7 +4462,7 @@ static Expr *transform_cypher_node(cypher_parsestate *cpstate,
* segmentation faults, and other errors.
*
* Update: Nonexistent and mismatched labels now return a NULL value to
- * prevent segmentation faults, and other errors. We can also consider
+ * prevent segmentation faults, and other errors. We can also consider
* if an all-purpose label would be useful.
*/
node->label = NULL;
@@ -4510,22 +4519,37 @@ static Expr *transform_cypher_node(cypher_parsestate *cpstate,
{
transform_entity *entity = find_variable(cpstate, node->name);
- /*
- * TODO: openCypher allows a variable to be used before it
- * is properly declared. This logic is not satifactory
- * for that and must be better developed.
- */
- if (entity != NULL &&
- (entity->type != ENT_VERTEX ||
- !IS_DEFAULT_LABEL_VERTEX(node->label) ||
- node->props))
+ /* If the variable already exists, verify that it is for a vertex */
+ if (entity != NULL && (entity->type != ENT_VERTEX))
{
ereport(ERROR,
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
- errmsg("variable %s already exists", node->name),
+ errmsg("variable '%s' is for a edge", node->name),
parser_errposition(pstate, node->location)));
}
+ /*
+ * If the variable already exists, verify that any label specified
+ * is of the same name or scope. Reject those that aren't.
+ */
+ if (entity != NULL)
+ {
+ cypher_node *cnode = (cypher_node *)entity->entity.node;
+
+ if (cnode != NULL &&
+ node != NULL &&
+ /* allow node using a default label against resolved var */
+ pg_strcasecmp(node->label, AG_DEFAULT_LABEL_VERTEX) != 0 &&
+ /* allow labels with the same name */
+ pg_strcasecmp(cnode->label, node->label) != 0)
+ {
+ ereport(ERROR,
+ (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("multiple labels for variable '%s' are not supported", node->name),
+ parser_errposition(pstate, node->location)));
+ }
+ }
+
return te->expr;
}
}
[age] 13/15: Fix property constraints against resolved variables in concurrent MATCH clauses(#701) (#747)
Posted by jg...@apache.org.
This is an automated email from the ASF dual-hosted git repository.
jgemignani pushed a commit to branch PG12
in repository https://gitbox.apache.org/repos/asf/age.git
commit 87a7d1090fae7ea8cccf215c9fd5f87f51f2b1c8
Author: John Gemignani <jr...@gmail.com>
AuthorDate: Thu Mar 16 19:42:17 2023 -0700
Fix property constraints against resolved variables in concurrent MATCH clauses(#701) (#747)
Fixed an issue where an already resolved variable, when used for
a property constraint in a second MATCH clause, errored out.
See #701 and #724 for more details. This is part of a series of
PRs done to refactor MATCH clause behavior.
This also fixed a regression test that was erroring out, but was
overlooked.
Adjusted and added additional regression tests.
Co-authored-by: Dehowe Feng <de...@gmail.com>
---
regress/expected/cypher_match.out | 225 ++++++++++++++++++++++++++++++++++++-
regress/expected/cypher_merge.out | 40 +++----
regress/sql/cypher_match.sql | 73 ++++++++++++
src/backend/parser/cypher_clause.c | 167 +++++++++++++++++++++++----
4 files changed, 461 insertions(+), 44 deletions(-)
diff --git a/regress/expected/cypher_match.out b/regress/expected/cypher_match.out
index 4888556d..f3817a50 100644
--- a/regress/expected/cypher_match.out
+++ b/regress/expected/cypher_match.out
@@ -1450,11 +1450,233 @@ $$) as (n agtype);
{"id": 281474976710662, "label": "", "properties": {"i": 1, "j": 2, "k": 3}}::vertex
(1 row)
+--
+-- Regression tests to check previous clause variable refs
+--
+-- set up initial state and show what we're working with
+SELECT * FROM cypher('cypher_match', $$
+ CREATE (a {age: 4}) RETURN a $$) as (a agtype);
+ a
+------------------------------------------------------------------------
+ {"id": 281474976710665, "label": "", "properties": {"age": 4}}::vertex
+(1 row)
+
+SELECT * FROM cypher('cypher_match', $$
+ CREATE (b {age: 6}) RETURN b $$) as (b agtype);
+ b
+------------------------------------------------------------------------
+ {"id": 281474976710666, "label": "", "properties": {"age": 6}}::vertex
+(1 row)
+
+SELECT * FROM cypher('cypher_match', $$
+ MATCH (a) RETURN a $$) as (a agtype);
+ a
+--------------------------------------------------------------------------------------
+ {"id": 281474976710659, "label": "", "properties": {"name": "orphan"}}::vertex
+ {"id": 281474976710660, "label": "", "properties": {"name": "F"}}::vertex
+ {"id": 281474976710661, "label": "", "properties": {"name": "T"}}::vertex
+ {"id": 281474976710662, "label": "", "properties": {"i": 1, "j": 2, "k": 3}}::vertex
+ {"id": 281474976710663, "label": "", "properties": {"i": 1, "j": 3}}::vertex
+ {"id": 281474976710664, "label": "", "properties": {"i": 2, "k": 3}}::vertex
+ {"id": 281474976710665, "label": "", "properties": {"age": 4}}::vertex
+ {"id": 281474976710666, "label": "", "properties": {"age": 6}}::vertex
+(8 rows)
+
+SELECT * FROM cypher('cypher_match', $$
+ MATCH (a) WHERE exists(a.name) RETURN a $$) as (a agtype);
+ a
+--------------------------------------------------------------------------------
+ {"id": 281474976710659, "label": "", "properties": {"name": "orphan"}}::vertex
+ {"id": 281474976710660, "label": "", "properties": {"name": "F"}}::vertex
+ {"id": 281474976710661, "label": "", "properties": {"name": "T"}}::vertex
+(3 rows)
+
+SELECT * FROM cypher('cypher_match', $$
+ MATCH (a) WHERE exists(a.name) SET a.age = 4 RETURN a $$) as (a agtype);
+ a
+------------------------------------------------------------------------------------------
+ {"id": 281474976710659, "label": "", "properties": {"age": 4, "name": "orphan"}}::vertex
+ {"id": 281474976710660, "label": "", "properties": {"age": 4, "name": "F"}}::vertex
+ {"id": 281474976710661, "label": "", "properties": {"age": 4, "name": "T"}}::vertex
+(3 rows)
+
+SELECT * FROM cypher('cypher_match', $$
+ MATCH (a),(b) WHERE a.age = 4 AND a.name = "T" AND b.age = 6
+ RETURN a,b $$) as (a agtype, b agtype);
+ a | b
+-------------------------------------------------------------------------------------+------------------------------------------------------------------------
+ {"id": 281474976710661, "label": "", "properties": {"age": 4, "name": "T"}}::vertex | {"id": 281474976710666, "label": "", "properties": {"age": 6}}::vertex
+(1 row)
+
+SELECT * FROM cypher('cypher_match', $$
+ MATCH (a),(b) WHERE a.age = 4 AND a.name = "T" AND b.age = 6 CREATE
+ (a)-[:knows {relationship: "friends", years: 3}]->(b) $$) as (r agtype);
+ r
+---
+(0 rows)
+
+SELECT * FROM cypher('cypher_match', $$
+ MATCH (a),(b) WHERE a.age = 4 AND a.name = "orphan" AND b.age = 6 CREATE
+ (a)-[:knows {relationship: "enemies", years: 4}]->(b) $$) as (r agtype);
+ r
+---
+(0 rows)
+
+SELECT * FROM cypher('cypher_match', $$
+ MATCH (a)-[r]-(b) RETURN r $$) as (r agtype);
+ r
+-----------------------------------------------------------------------------------------------------------------------------------------------------------------
+ {"id": 4785074604081153, "label": "knows", "end_id": 281474976710666, "start_id": 281474976710661, "properties": {"years": 3, "relationship": "friends"}}::edge
+ {"id": 4785074604081153, "label": "knows", "end_id": 281474976710666, "start_id": 281474976710661, "properties": {"years": 3, "relationship": "friends"}}::edge
+ {"id": 4785074604081154, "label": "knows", "end_id": 281474976710666, "start_id": 281474976710659, "properties": {"years": 4, "relationship": "enemies"}}::edge
+ {"id": 4785074604081154, "label": "knows", "end_id": 281474976710666, "start_id": 281474976710659, "properties": {"years": 4, "relationship": "enemies"}}::edge
+ {"id": 1407374883553283, "label": "e1", "end_id": 281474976710661, "start_id": 281474976710660, "properties": {}}::edge
+ {"id": 1407374883553283, "label": "e1", "end_id": 281474976710661, "start_id": 281474976710660, "properties": {}}::edge
+(6 rows)
+
+-- check reuse of 'a'
+SELECT * FROM cypher('cypher_match', $$
+ MATCH (a {age:4}) RETURN a $$) as (a agtype);
+ a
+------------------------------------------------------------------------------------------
+ {"id": 281474976710665, "label": "", "properties": {"age": 4}}::vertex
+ {"id": 281474976710659, "label": "", "properties": {"age": 4, "name": "orphan"}}::vertex
+ {"id": 281474976710660, "label": "", "properties": {"age": 4, "name": "F"}}::vertex
+ {"id": 281474976710661, "label": "", "properties": {"age": 4, "name": "T"}}::vertex
+(4 rows)
+
+SELECT * FROM cypher('cypher_match', $$
+ MATCH (a) MATCH (a {age:4}) RETURN a $$) as (a agtype);
+ a
+------------------------------------------------------------------------------------------
+ {"id": 281474976710665, "label": "", "properties": {"age": 4}}::vertex
+ {"id": 281474976710659, "label": "", "properties": {"age": 4, "name": "orphan"}}::vertex
+ {"id": 281474976710660, "label": "", "properties": {"age": 4, "name": "F"}}::vertex
+ {"id": 281474976710661, "label": "", "properties": {"age": 4, "name": "T"}}::vertex
+(4 rows)
+
+SELECT * FROM cypher('cypher_match', $$
+ MATCH (a {age:4, name: "orphan"}) RETURN a $$) as (a agtype);
+ a
+------------------------------------------------------------------------------------------
+ {"id": 281474976710659, "label": "", "properties": {"age": 4, "name": "orphan"}}::vertex
+(1 row)
+
+SELECT * FROM cypher('cypher_match', $$
+ MATCH (a) MATCH (a {age:4}) MATCH (a {name: "orphan"}) RETURN a $$) as (a agtype);
+ a
+------------------------------------------------------------------------------------------
+ {"id": 281474976710659, "label": "", "properties": {"age": 4, "name": "orphan"}}::vertex
+(1 row)
+
+SELECT * FROM cypher('cypher_match', $$
+ MATCH (a {age:4}) MATCH (a {name: "orphan"}) RETURN a $$) as (a agtype);
+ a
+------------------------------------------------------------------------------------------
+ {"id": 281474976710659, "label": "", "properties": {"age": 4, "name": "orphan"}}::vertex
+(1 row)
+
+SELECT * FROM cypher('cypher_match', $$
+ MATCH (a) MATCH (a {age:4}) MATCH (a {name: "orphan"}) SET a.age = 3 RETURN a $$) as (a agtype);
+ a
+------------------------------------------------------------------------------------------
+ {"id": 281474976710659, "label": "", "properties": {"age": 3, "name": "orphan"}}::vertex
+(1 row)
+
+SELECT * FROM cypher('cypher_match', $$
+ MATCH (a) MATCH (a {age:3}) MATCH (a {name: "orphan"}) RETURN a $$) as (a agtype);
+ a
+------------------------------------------------------------------------------------------
+ {"id": 281474976710659, "label": "", "properties": {"age": 3, "name": "orphan"}}::vertex
+(1 row)
+
+SELECT * FROM cypher('cypher_match', $$
+ MATCH (a {name: "orphan"}) MATCH (a {age:3}) RETURN a $$) as (a agtype);
+ a
+------------------------------------------------------------------------------------------
+ {"id": 281474976710659, "label": "", "properties": {"age": 3, "name": "orphan"}}::vertex
+(1 row)
+
+SELECT * FROM cypher('cypher_match', $$
+ MATCH (a) WHERE exists(a.age) AND exists(a.name) RETURN a $$) as (a agtype);
+ a
+------------------------------------------------------------------------------------------
+ {"id": 281474976710660, "label": "", "properties": {"age": 4, "name": "F"}}::vertex
+ {"id": 281474976710661, "label": "", "properties": {"age": 4, "name": "T"}}::vertex
+ {"id": 281474976710659, "label": "", "properties": {"age": 3, "name": "orphan"}}::vertex
+(3 rows)
+
+SELECT * FROM cypher('cypher_match', $$
+ MATCH (a) WHERE exists(a.age) AND NOT exists(a.name) RETURN a $$) as (a agtype);
+ a
+------------------------------------------------------------------------
+ {"id": 281474976710665, "label": "", "properties": {"age": 4}}::vertex
+ {"id": 281474976710666, "label": "", "properties": {"age": 6}}::vertex
+(2 rows)
+
+-- check reuse of 'r'
+SELECT * FROM cypher('cypher_match', $$
+ MATCH ()-[r]-() RETURN r $$) as (r agtype);
+ r
+-----------------------------------------------------------------------------------------------------------------------------------------------------------------
+ {"id": 4785074604081153, "label": "knows", "end_id": 281474976710666, "start_id": 281474976710661, "properties": {"years": 3, "relationship": "friends"}}::edge
+ {"id": 4785074604081153, "label": "knows", "end_id": 281474976710666, "start_id": 281474976710661, "properties": {"years": 3, "relationship": "friends"}}::edge
+ {"id": 4785074604081154, "label": "knows", "end_id": 281474976710666, "start_id": 281474976710659, "properties": {"years": 4, "relationship": "enemies"}}::edge
+ {"id": 4785074604081154, "label": "knows", "end_id": 281474976710666, "start_id": 281474976710659, "properties": {"years": 4, "relationship": "enemies"}}::edge
+ {"id": 1407374883553283, "label": "e1", "end_id": 281474976710661, "start_id": 281474976710660, "properties": {}}::edge
+ {"id": 1407374883553283, "label": "e1", "end_id": 281474976710661, "start_id": 281474976710660, "properties": {}}::edge
+(6 rows)
+
+SELECT * FROM cypher('cypher_match', $$
+ MATCH ()-[r]-() MATCH ()-[r {relationship: "friends"}]-() RETURN r $$) as (r agtype);
+ r
+-----------------------------------------------------------------------------------------------------------------------------------------------------------------
+ {"id": 4785074604081153, "label": "knows", "end_id": 281474976710666, "start_id": 281474976710661, "properties": {"years": 3, "relationship": "friends"}}::edge
+ {"id": 4785074604081153, "label": "knows", "end_id": 281474976710666, "start_id": 281474976710661, "properties": {"years": 3, "relationship": "friends"}}::edge
+(2 rows)
+
+SELECT * FROM cypher('cypher_match', $$
+ MATCH ()-[r {years:3, relationship: "friends"}]-() RETURN r $$) as (r agtype);
+ r
+-----------------------------------------------------------------------------------------------------------------------------------------------------------------
+ {"id": 4785074604081153, "label": "knows", "end_id": 281474976710666, "start_id": 281474976710661, "properties": {"years": 3, "relationship": "friends"}}::edge
+ {"id": 4785074604081153, "label": "knows", "end_id": 281474976710666, "start_id": 281474976710661, "properties": {"years": 3, "relationship": "friends"}}::edge
+(2 rows)
+
+SELECT * FROM cypher('cypher_match', $$
+ MATCH ()-[r {years:3}]-() MATCH ()-[r {relationship: "friends"}]-() RETURN r $$) as (r agtype);
+ r
+-----------------------------------------------------------------------------------------------------------------------------------------------------------------
+ {"id": 4785074604081153, "label": "knows", "end_id": 281474976710666, "start_id": 281474976710661, "properties": {"years": 3, "relationship": "friends"}}::edge
+ {"id": 4785074604081153, "label": "knows", "end_id": 281474976710666, "start_id": 281474976710661, "properties": {"years": 3, "relationship": "friends"}}::edge
+(2 rows)
+
+--mismatch year #, should return nothing
+SELECT * FROM cypher('cypher_match', $$
+ MATCH ()-[r {years:2}]-() MATCH ()-[r {relationship: "friends"}]-() RETURN r $$) as (r agtype);
+ r
+---
+(0 rows)
+
+SELECT * FROM cypher('cypher_match', $$
+ MATCH ()-[r {relationship:"enemies"}]-() MATCH ()-[r {years:4}]-() RETURN r $$) as (r agtype);
+ r
+-----------------------------------------------------------------------------------------------------------------------------------------------------------------
+ {"id": 4785074604081154, "label": "knows", "end_id": 281474976710666, "start_id": 281474976710659, "properties": {"years": 4, "relationship": "enemies"}}::edge
+ {"id": 4785074604081154, "label": "knows", "end_id": 281474976710666, "start_id": 281474976710659, "properties": {"years": 4, "relationship": "enemies"}}::edge
+(2 rows)
+
+SELECT * FROM cypher('cypher_match', $$
+ MATCH ()-[r {relationship:"enemies"}]-() MATCH ()-[r {relationship:"friends"}]-() RETURN r $$) as (r agtype);
+ r
+---
+(0 rows)
+
--
-- Clean up
--
SELECT drop_graph('cypher_match', true);
-NOTICE: drop cascades to 16 other objects
+NOTICE: drop cascades to 17 other objects
DETAIL: drop cascades to table cypher_match._ag_label_vertex
drop cascades to table cypher_match._ag_label_edge
drop cascades to table cypher_match.v
@@ -1471,6 +1693,7 @@ drop cascades to table cypher_match.dup_edge
drop cascades to table cypher_match.other_v
drop cascades to table cypher_match.opt_match_v
drop cascades to table cypher_match.opt_match_e
+drop cascades to table cypher_match.knows
NOTICE: graph "cypher_match" has been dropped
drop_graph
------------
diff --git a/regress/expected/cypher_merge.out b/regress/expected/cypher_merge.out
index 389c34d1..988dff3c 100644
--- a/regress/expected/cypher_merge.out
+++ b/regress/expected/cypher_merge.out
@@ -513,15 +513,17 @@ SELECT * FROM cypher('cypher_merge', $$MATCH (n) DETACH DELETE n $$) AS (a agtyp
*/
--test query
SELECT * FROM cypher('cypher_merge', $$CREATE (n {i : 1}) SET n.i = 2 MERGE ({i: 2}) $$) AS (a agtype);
-ERROR: missing FROM-clause entry for table "_age_default_alias_0"
-LINE 5: SELECT * FROM cypher('cypher_merge', $$CREATE (n {i : 1}) SE...
- ^
---validate created correctly
-SELECT * FROM cypher('cypher_merge', $$MATCH (a) RETURN a$$) AS (a agtype);
a
---
(0 rows)
+--validate created correctly
+SELECT * FROM cypher('cypher_merge', $$MATCH (a) RETURN a$$) AS (a agtype);
+ a
+----------------------------------------------------------------------
+ {"id": 281474976710690, "label": "", "properties": {"i": 2}}::vertex
+(1 row)
+
--clean up
SELECT * FROM cypher('cypher_merge', $$MATCH (n) DETACH DELETE n $$) AS (a agtype);
a
@@ -541,7 +543,7 @@ SELECT * FROM cypher('cypher_merge', $$CREATE (n {i : 1}) SET n.i = 2 WITH n as
SELECT * FROM cypher('cypher_merge', $$MATCH (a) RETURN a$$) AS (a agtype);
a
----------------------------------------------------------------------
- {"id": 281474976710690, "label": "", "properties": {"i": 2}}::vertex
+ {"id": 281474976710691, "label": "", "properties": {"i": 2}}::vertex
(1 row)
--clean up
@@ -569,7 +571,7 @@ SELECT * FROM cypher('cypher_merge', $$MATCH (n {i : 1}) SET n.i = 2 WITH n as a
SELECT * FROM cypher('cypher_merge', $$MATCH (a) RETURN a$$) AS (a agtype);
a
----------------------------------------------------------------------
- {"id": 281474976710691, "label": "", "properties": {"i": 2}}::vertex
+ {"id": 281474976710692, "label": "", "properties": {"i": 2}}::vertex
(1 row)
--clean up
@@ -594,7 +596,7 @@ ERROR: vertex assigned to variable n was deleted
SELECT * FROM cypher('cypher_merge', $$MATCH (a) RETURN a$$) AS (a agtype);
a
----------------------------------------------------------------------
- {"id": 281474976710692, "label": "", "properties": {"i": 1}}::vertex
+ {"id": 281474976710693, "label": "", "properties": {"i": 1}}::vertex
(1 row)
--clean up
@@ -672,7 +674,7 @@ SELECT * FROM cypher('cypher_merge', $$MERGE ()-[:e]-()$$) AS (a agtype);
SELECT * FROM cypher('cypher_merge', $$MATCH p=()-[]->() RETURN p$$) AS (a agtype);
a
---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
- [{"id": 281474976710693, "label": "", "properties": {}}::vertex, {"id": 844424930131982, "label": "e", "end_id": 281474976710694, "start_id": 281474976710693, "properties": {}}::edge, {"id": 281474976710694, "label": "", "properties": {}}::vertex]::path
+ [{"id": 281474976710694, "label": "", "properties": {}}::vertex, {"id": 844424930131982, "label": "e", "end_id": 281474976710695, "start_id": 281474976710694, "properties": {}}::edge, {"id": 281474976710695, "label": "", "properties": {}}::vertex]::path
(1 row)
--clean up
@@ -687,14 +689,14 @@ SELECT * FROM cypher('cypher_merge', $$MATCH (n) DETACH DELETE n $$) AS (a agtyp
SELECT * FROM cypher('cypher_merge', $$MERGE (a) RETURN a$$) AS (a agtype);
a
----------------------------------------------------------------
- {"id": 281474976710695, "label": "", "properties": {}}::vertex
+ {"id": 281474976710696, "label": "", "properties": {}}::vertex
(1 row)
--validate
SELECT * FROM cypher('cypher_merge', $$MATCH (a) RETURN a$$) AS (a agtype);
a
----------------------------------------------------------------
- {"id": 281474976710695, "label": "", "properties": {}}::vertex
+ {"id": 281474976710696, "label": "", "properties": {}}::vertex
(1 row)
--clean up
@@ -709,14 +711,14 @@ SELECT * FROM cypher('cypher_merge', $$MATCH (n) DETACH DELETE n $$) AS (a agtyp
SELECT * FROM cypher('cypher_merge', $$MERGE p=()-[:e]-() RETURN p$$) AS (a agtype);
a
---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
- [{"id": 281474976710696, "label": "", "properties": {}}::vertex, {"id": 844424930131983, "label": "e", "end_id": 281474976710697, "start_id": 281474976710696, "properties": {}}::edge, {"id": 281474976710697, "label": "", "properties": {}}::vertex]::path
+ [{"id": 281474976710697, "label": "", "properties": {}}::vertex, {"id": 844424930131983, "label": "e", "end_id": 281474976710698, "start_id": 281474976710697, "properties": {}}::edge, {"id": 281474976710698, "label": "", "properties": {}}::vertex]::path
(1 row)
--validate
SELECT * FROM cypher('cypher_merge', $$MATCH p=()-[]->() RETURN p$$) AS (a agtype);
a
---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
- [{"id": 281474976710696, "label": "", "properties": {}}::vertex, {"id": 844424930131983, "label": "e", "end_id": 281474976710697, "start_id": 281474976710696, "properties": {}}::edge, {"id": 281474976710697, "label": "", "properties": {}}::vertex]::path
+ [{"id": 281474976710697, "label": "", "properties": {}}::vertex, {"id": 844424930131983, "label": "e", "end_id": 281474976710698, "start_id": 281474976710697, "properties": {}}::edge, {"id": 281474976710698, "label": "", "properties": {}}::vertex]::path
(1 row)
--clean up
@@ -731,14 +733,14 @@ SELECT * FROM cypher('cypher_merge', $$MATCH (n) DETACH DELETE n $$) AS (a agtyp
SELECT * FROM cypher('cypher_merge', $$MERGE (a)-[:e]-(b) RETURN a$$) AS (a agtype);
a
----------------------------------------------------------------
- {"id": 281474976710698, "label": "", "properties": {}}::vertex
+ {"id": 281474976710699, "label": "", "properties": {}}::vertex
(1 row)
--validate
SELECT * FROM cypher('cypher_merge', $$MATCH p=()-[]->() RETURN p$$) AS (a agtype);
a
---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
- [{"id": 281474976710698, "label": "", "properties": {}}::vertex, {"id": 844424930131984, "label": "e", "end_id": 281474976710699, "start_id": 281474976710698, "properties": {}}::edge, {"id": 281474976710699, "label": "", "properties": {}}::vertex]::path
+ [{"id": 281474976710699, "label": "", "properties": {}}::vertex, {"id": 844424930131984, "label": "e", "end_id": 281474976710700, "start_id": 281474976710699, "properties": {}}::edge, {"id": 281474976710700, "label": "", "properties": {}}::vertex]::path
(1 row)
--clean up
@@ -753,14 +755,14 @@ SELECT * FROM cypher('cypher_merge', $$MATCH (n) DETACH DELETE n $$) AS (a agtyp
SELECT * FROM cypher('cypher_merge', $$CREATE p=()-[:e]->() RETURN p$$) AS (a agtype);
a
---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
- [{"id": 281474976710700, "label": "", "properties": {}}::vertex, {"id": 844424930131985, "label": "e", "end_id": 281474976710701, "start_id": 281474976710700, "properties": {}}::edge, {"id": 281474976710701, "label": "", "properties": {}}::vertex]::path
+ [{"id": 281474976710701, "label": "", "properties": {}}::vertex, {"id": 844424930131985, "label": "e", "end_id": 281474976710702, "start_id": 281474976710701, "properties": {}}::edge, {"id": 281474976710702, "label": "", "properties": {}}::vertex]::path
(1 row)
SELECT * FROM cypher('cypher_merge', $$MERGE p=()-[:e]-() RETURN p$$) AS (a agtype);
a
---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
- [{"id": 281474976710700, "label": "", "properties": {}}::vertex, {"id": 844424930131985, "label": "e", "end_id": 281474976710701, "start_id": 281474976710700, "properties": {}}::edge, {"id": 281474976710701, "label": "", "properties": {}}::vertex]::path
- [{"id": 281474976710701, "label": "", "properties": {}}::vertex, {"id": 844424930131985, "label": "e", "end_id": 281474976710701, "start_id": 281474976710700, "properties": {}}::edge, {"id": 281474976710700, "label": "", "properties": {}}::vertex]::path
+ [{"id": 281474976710701, "label": "", "properties": {}}::vertex, {"id": 844424930131985, "label": "e", "end_id": 281474976710702, "start_id": 281474976710701, "properties": {}}::edge, {"id": 281474976710702, "label": "", "properties": {}}::vertex]::path
+ [{"id": 281474976710702, "label": "", "properties": {}}::vertex, {"id": 844424930131985, "label": "e", "end_id": 281474976710702, "start_id": 281474976710701, "properties": {}}::edge, {"id": 281474976710701, "label": "", "properties": {}}::vertex]::path
(2 rows)
--clean up
@@ -805,7 +807,7 @@ ERROR: Existing variable m cannot be NULL in MERGE clause
SELECT * FROM cypher('cypher_merge', $$MATCH (n) RETURN n$$) AS (a agtype);
a
----------------------------------------------------------------
- {"id": 281474976710702, "label": "", "properties": {}}::vertex
+ {"id": 281474976710703, "label": "", "properties": {}}::vertex
(1 row)
--
diff --git a/regress/sql/cypher_match.sql b/regress/sql/cypher_match.sql
index 344453e0..f08f8dfb 100644
--- a/regress/sql/cypher_match.sql
+++ b/regress/sql/cypher_match.sql
@@ -734,6 +734,79 @@ SELECT * FROM cypher('cypher_match', $$
RETURN n
$$) as (n agtype);
+--
+-- Regression tests to check previous clause variable refs
+--
+-- set up initial state and show what we're working with
+SELECT * FROM cypher('cypher_match', $$
+ CREATE (a {age: 4}) RETURN a $$) as (a agtype);
+SELECT * FROM cypher('cypher_match', $$
+ CREATE (b {age: 6}) RETURN b $$) as (b agtype);
+
+SELECT * FROM cypher('cypher_match', $$
+ MATCH (a) RETURN a $$) as (a agtype);
+SELECT * FROM cypher('cypher_match', $$
+ MATCH (a) WHERE exists(a.name) RETURN a $$) as (a agtype);
+SELECT * FROM cypher('cypher_match', $$
+ MATCH (a) WHERE exists(a.name) SET a.age = 4 RETURN a $$) as (a agtype);
+
+
+SELECT * FROM cypher('cypher_match', $$
+ MATCH (a),(b) WHERE a.age = 4 AND a.name = "T" AND b.age = 6
+ RETURN a,b $$) as (a agtype, b agtype);
+SELECT * FROM cypher('cypher_match', $$
+ MATCH (a),(b) WHERE a.age = 4 AND a.name = "T" AND b.age = 6 CREATE
+ (a)-[:knows {relationship: "friends", years: 3}]->(b) $$) as (r agtype);
+SELECT * FROM cypher('cypher_match', $$
+ MATCH (a),(b) WHERE a.age = 4 AND a.name = "orphan" AND b.age = 6 CREATE
+ (a)-[:knows {relationship: "enemies", years: 4}]->(b) $$) as (r agtype);
+SELECT * FROM cypher('cypher_match', $$
+ MATCH (a)-[r]-(b) RETURN r $$) as (r agtype);
+
+-- check reuse of 'a'
+SELECT * FROM cypher('cypher_match', $$
+ MATCH (a {age:4}) RETURN a $$) as (a agtype);
+SELECT * FROM cypher('cypher_match', $$
+ MATCH (a) MATCH (a {age:4}) RETURN a $$) as (a agtype);
+
+SELECT * FROM cypher('cypher_match', $$
+ MATCH (a {age:4, name: "orphan"}) RETURN a $$) as (a agtype);
+SELECT * FROM cypher('cypher_match', $$
+ MATCH (a) MATCH (a {age:4}) MATCH (a {name: "orphan"}) RETURN a $$) as (a agtype);
+SELECT * FROM cypher('cypher_match', $$
+ MATCH (a {age:4}) MATCH (a {name: "orphan"}) RETURN a $$) as (a agtype);
+
+SELECT * FROM cypher('cypher_match', $$
+ MATCH (a) MATCH (a {age:4}) MATCH (a {name: "orphan"}) SET a.age = 3 RETURN a $$) as (a agtype);
+
+SELECT * FROM cypher('cypher_match', $$
+ MATCH (a) MATCH (a {age:3}) MATCH (a {name: "orphan"}) RETURN a $$) as (a agtype);
+SELECT * FROM cypher('cypher_match', $$
+ MATCH (a {name: "orphan"}) MATCH (a {age:3}) RETURN a $$) as (a agtype);
+
+SELECT * FROM cypher('cypher_match', $$
+ MATCH (a) WHERE exists(a.age) AND exists(a.name) RETURN a $$) as (a agtype);
+
+SELECT * FROM cypher('cypher_match', $$
+ MATCH (a) WHERE exists(a.age) AND NOT exists(a.name) RETURN a $$) as (a agtype);
+
+-- check reuse of 'r'
+SELECT * FROM cypher('cypher_match', $$
+ MATCH ()-[r]-() RETURN r $$) as (r agtype);
+SELECT * FROM cypher('cypher_match', $$
+ MATCH ()-[r]-() MATCH ()-[r {relationship: "friends"}]-() RETURN r $$) as (r agtype);
+SELECT * FROM cypher('cypher_match', $$
+ MATCH ()-[r {years:3, relationship: "friends"}]-() RETURN r $$) as (r agtype);
+SELECT * FROM cypher('cypher_match', $$
+ MATCH ()-[r {years:3}]-() MATCH ()-[r {relationship: "friends"}]-() RETURN r $$) as (r agtype);
+--mismatch year #, should return nothing
+SELECT * FROM cypher('cypher_match', $$
+ MATCH ()-[r {years:2}]-() MATCH ()-[r {relationship: "friends"}]-() RETURN r $$) as (r agtype);
+SELECT * FROM cypher('cypher_match', $$
+ MATCH ()-[r {relationship:"enemies"}]-() MATCH ()-[r {years:4}]-() RETURN r $$) as (r agtype);
+SELECT * FROM cypher('cypher_match', $$
+ MATCH ()-[r {relationship:"enemies"}]-() MATCH ()-[r {relationship:"friends"}]-() RETURN r $$) as (r agtype);
+
--
-- Clean up
--
diff --git a/src/backend/parser/cypher_clause.c b/src/backend/parser/cypher_clause.c
index 8b6a4e3b..234e3aad 100644
--- a/src/backend/parser/cypher_clause.c
+++ b/src/backend/parser/cypher_clause.c
@@ -172,7 +172,8 @@ static A_Expr *filter_vertices_on_label_id(cypher_parsestate *cpstate,
Node *id_field, char *label);
static Node *create_property_constraints(cypher_parsestate *cpstate,
transform_entity *entity,
- Node *property_constraints);
+ Node *property_constraints,
+ Node *prop_expr);
static TargetEntry *findTarget(List *targetList, char *resname);
static transform_entity *transform_VLE_edge_entity(cypher_parsestate *cpstate,
cypher_relationship *rel,
@@ -3506,33 +3507,43 @@ static A_Expr *filter_vertices_on_label_id(cypher_parsestate *cpstate,
*/
static Node *create_property_constraints(cypher_parsestate *cpstate,
transform_entity *entity,
- Node *property_constraints)
+ Node *property_constraints,
+ Node *prop_expr)
{
ParseState *pstate = (ParseState *)cpstate;
char *entity_name;
- ColumnRef *cr;
- Node *prop_expr, *const_expr;
+ Node *const_expr;
RangeTblEntry *rte;
Node *last_srf = pstate->p_last_srf;
- cr = makeNode(ColumnRef);
-
- entity_name = get_entity_name(entity);
+ /*
+ * If the prop_expr node wasn't passed in, create it. Otherwise, skip
+ * the creation step.
+ */
+ if (prop_expr == NULL)
+ {
+ ColumnRef *cr = NULL;
- cr->fields = list_make2(makeString(entity_name), makeString("properties"));
+ cr = makeNode(ColumnRef);
+ entity_name = get_entity_name(entity);
+ cr->fields = list_make2(makeString(entity_name),
+ makeString("properties"));
- // use Postgres to get the properties' transform node
- if ((rte = find_rte(cpstate, entity_name)))
- {
- prop_expr = scanRTEForColumn(pstate, rte, AG_VERTEX_COLNAME_PROPERTIES,
- -1, 0, NULL);
- }
- else
- {
- prop_expr = transformExpr(pstate, (Node *)cr, EXPR_KIND_WHERE);
+ /* use Postgres to get the properties' transform node */
+ rte = find_rte(cpstate, entity_name);
+ if (rte != NULL)
+ {
+ prop_expr = scanRTEForColumn(pstate, rte,
+ AG_VERTEX_COLNAME_PROPERTIES, -1, 0,
+ NULL);
+ }
+ else
+ {
+ prop_expr = transformExpr(pstate, (Node *)cr, EXPR_KIND_WHERE);
+ }
}
- // use cypher to get the constraints' transform node
+ /* use cypher to get the constraints' transform node */
const_expr = transform_cypher_expr(cpstate, property_constraints,
EXPR_KIND_WHERE);
@@ -3817,13 +3828,68 @@ static List *transform_match_entities(cypher_parsestate *cpstate, Query *query,
entity = make_transform_entity(cpstate, ENT_VERTEX, (Node *)node,
expr);
- /* transform properties if they exist */
+ /* transform the properties if they exist */
if (node->props)
{
Node *n = NULL;
+ Node *prop_var = NULL;
+ Node *prop_expr = NULL;
+
+ /*
+ * We need to build a transformed properties(prop_var)
+ * expression IF the properties variable already exists from a
+ * previous clause. Please note that the "found" prop_var was
+ * previously transformed.
+ */
+
+ /* get the prop_var if it was previously resolved */
+ if (node->name != NULL)
+ {
+ prop_var = colNameToVar(pstate, node->name, false,
+ node->location);
+ }
+
+ /*
+ * If prop_var exists and is an alias, just pass it through by
+ * assigning the prop_expr the prop_var.
+ */
+ if (prop_var != NULL &&
+ pg_strncasecmp(node->name, AGE_DEFAULT_ALIAS_PREFIX,
+ strlen(AGE_DEFAULT_ALIAS_PREFIX)) == 0)
+ {
+ prop_expr = prop_var;
+ }
+ /*
+ * Else, if it exists and is not an alias, create the prop_expr
+ * as a transformed properties(prop_var) function node.
+ */
+ else if (prop_var != NULL)
+ {
+ /*
+ * Remember that prop_var is already transformed. We need
+ * to built the transform manually.
+ */
+ FuncCall *fc = NULL;
+ List *targs = NIL;
+ List *fname = NIL;
+
+ targs = lappend(targs, prop_var);
+ fname = list_make2(makeString("ag_catalog"),
+ makeString("age_properties"));
+ fc = makeFuncCall(fname, targs, -1);
+
+ /*
+ * Hand off to ParseFuncOrColumn to create the function
+ * expression for properties(prop_var)
+ */
+ prop_expr = ParseFuncOrColumn(pstate, fname, targs,
+ pstate->p_last_srf, fc, false,
+ -1);
+ }
((cypher_map*)node->props)->keep_null = true;
- n = create_property_constraints(cpstate, entity, node->props);
+ n = create_property_constraints(cpstate, entity, node->props,
+ prop_expr);
cpstate->property_constraint_quals =
lappend(cpstate->property_constraint_quals, n);
@@ -3882,13 +3948,66 @@ static List *transform_match_entities(cypher_parsestate *cpstate, Query *query,
if (rel->props)
{
- Node *n;
+ Node *r = NULL;
+ Node *prop_var = NULL;
+ Node *prop_expr = NULL;
+
+ /*
+ * We need to build a transformed properties(prop_var)
+ * expression IF the properties variable already exists from
+ * a previous clause. Please note that the "found" prop_var
+ * was previously transformed.
+ */
+
+ /* get the prop_var if it was previously resolved */
+ if (rel->name != NULL)
+ {
+ prop_var = colNameToVar(pstate, rel->name, false,
+ rel->location);
+ }
+
+ /*
+ * If prop_var exists and is an alias, just pass it through by
+ * assigning the prop_expr the prop_var.
+ */
+ if (prop_var != NULL &&
+ pg_strncasecmp(rel->name, AGE_DEFAULT_ALIAS_PREFIX,
+ strlen(AGE_DEFAULT_ALIAS_PREFIX)) == 0)
+ {
+ prop_expr = prop_var;
+ }
+ /*
+ * Else, if it exists and is not an alias, create the prop_expr
+ * as a transformed properties(prop_var) function node.
+ */
+ else if (prop_var != NULL)
+ {
+ /*
+ * Remember that prop_var is already transformed. We need
+ * to built the transform manually.
+ */
+ FuncCall *fc = NULL;
+ List *targs = NIL;
+ List *fname = NIL;
+
+ targs = lappend(targs, prop_var);
+ fname = list_make2(makeString("ag_catalog"),
+ makeString("age_properties"));
+ fc = makeFuncCall(fname, targs, -1);
+
+ /*
+ * Hand off to ParseFuncOrColumn to create the function
+ * expression for properties(prop_var)
+ */
+ prop_expr = ParseFuncOrColumn(pstate, fname, targs,
+ pstate->p_last_srf, fc,
+ false, -1);
+ }
((cypher_map*)rel->props)->keep_null = true;
- n = create_property_constraints(cpstate, entity,
- rel->props);
+ r = create_property_constraints(cpstate, entity, rel->props, prop_expr);
cpstate->property_constraint_quals =
- lappend(cpstate->property_constraint_quals, n);
+ lappend(cpstate->property_constraint_quals, r);
}
entities = lappend(entities, entity);
[age] 12/15: Prevent MATCH from following OPTIONAL MATCH (#740)
Posted by jg...@apache.org.
This is an automated email from the ASF dual-hosted git repository.
jgemignani pushed a commit to branch PG12
in repository https://gitbox.apache.org/repos/asf/age.git
commit 8102847da3213a221ea784d62fc3d1038a988a86
Author: Dehowe Feng <80...@users.noreply.github.com>
AuthorDate: Sat Mar 11 09:50:57 2023 +0900
Prevent MATCH from following OPTIONAL MATCH (#740)
Added logic to prevent match from following optional match. This
syntax is considered invalid, add logic to throw an error when
this happens.
Also added regression tests to test this logic.
---
regress/expected/cypher_match.out | 19 +++++++++++++++++++
regress/sql/cypher_match.sql | 14 ++++++++++++++
src/backend/parser/cypher_clause.c | 17 +++++++++++++++++
3 files changed, 50 insertions(+)
diff --git a/regress/expected/cypher_match.out b/regress/expected/cypher_match.out
index 8f325cbe..4888556d 100644
--- a/regress/expected/cypher_match.out
+++ b/regress/expected/cypher_match.out
@@ -1256,6 +1256,25 @@ SELECT * FROM cypher('cypher_match', $$
"nobody" | | | "anybody" | |
(12 rows)
+-- Tests to catch match following optional match logic
+-- this syntax is invalid in cypher
+SELECT * FROM cypher('cypher_match', $$
+ OPTIONAL MATCH (n)
+ MATCH (m)
+ RETURN n,m
+ $$) AS (n agtype, m agtype);
+ERROR: MATCH cannot follow OPTIONAL MATCH
+LINE 1: SELECT * FROM cypher('cypher_match', $$
+ ^
+SELECT * FROM cypher('cypher_match', $$
+ MATCH (n)
+ OPTIONAL MATCH (m)
+ MATCH (o)
+ RETURN n,m
+ $$) AS (n agtype, m agtype);
+ERROR: MATCH cannot follow OPTIONAL MATCH
+LINE 1: SELECT * FROM cypher('cypher_match', $$
+ ^
--
-- Tests retrieving Var from some parent's cpstate during transformation
--
diff --git a/regress/sql/cypher_match.sql b/regress/sql/cypher_match.sql
index 70f638df..344453e0 100644
--- a/regress/sql/cypher_match.sql
+++ b/regress/sql/cypher_match.sql
@@ -621,6 +621,20 @@ SELECT * FROM cypher('cypher_match', $$
ORDER BY n, p, m, q
$$) AS (n agtype, r agtype, p agtype, m agtype, s agtype, q agtype);
+-- Tests to catch match following optional match logic
+-- this syntax is invalid in cypher
+SELECT * FROM cypher('cypher_match', $$
+ OPTIONAL MATCH (n)
+ MATCH (m)
+ RETURN n,m
+ $$) AS (n agtype, m agtype);
+
+SELECT * FROM cypher('cypher_match', $$
+ MATCH (n)
+ OPTIONAL MATCH (m)
+ MATCH (o)
+ RETURN n,m
+ $$) AS (n agtype, m agtype);
--
-- Tests retrieving Var from some parent's cpstate during transformation
diff --git a/src/backend/parser/cypher_clause.c b/src/backend/parser/cypher_clause.c
index 5db7a3c8..8b6a4e3b 100644
--- a/src/backend/parser/cypher_clause.c
+++ b/src/backend/parser/cypher_clause.c
@@ -2517,6 +2517,23 @@ static Query *transform_cypher_match_pattern(cypher_parsestate *cpstate,
query = makeNode(Query);
query->commandType = CMD_SELECT;
+ if(self->optional == true && clause->next)
+ {
+ cypher_clause *next = clause->next;
+ if (is_ag_node(next->self, cypher_match))
+ {
+ cypher_match *next_self = (cypher_match *)next->self;
+ if (!next_self->optional)
+ {
+ ereport(ERROR,
+ (errcode(ERRCODE_SYNTAX_ERROR),
+ errmsg("MATCH cannot follow OPTIONAL MATCH"),
+ parser_errposition(pstate,
+ exprLocation((Node *) next_self))));
+ }
+ }
+ }
+
// If there is no previous clause, transform to a general MATCH clause.
if (self->optional == true && clause->prev != NULL)
{
[age] 15/15: Include invalid labels in reused variables. (#751) (#762)
Posted by jg...@apache.org.
This is an automated email from the ASF dual-hosted git repository.
jgemignani pushed a commit to branch PG12
in repository https://gitbox.apache.org/repos/asf/age.git
commit 15ea6787ef22fe7760514524fcac58ea4ae2df85
Author: Dehowe Feng <80...@users.noreply.github.com>
AuthorDate: Fri Mar 24 01:12:09 2023 +0900
Include invalid labels in reused variables. (#751) (#762)
Fixed a segmentation fault edge case regarding invalid labels
being used in a repeated variable.
---
regress/expected/cypher_match.out | 6 ++++++
regress/sql/cypher_match.sql | 3 +++
src/backend/parser/cypher_clause.c | 7 +++++--
3 files changed, 14 insertions(+), 2 deletions(-)
diff --git a/regress/expected/cypher_match.out b/regress/expected/cypher_match.out
index 0e596406..d1c58c1f 100644
--- a/regress/expected/cypher_match.out
+++ b/regress/expected/cypher_match.out
@@ -527,6 +527,12 @@ $$) AS (a agtype);
ERROR: multiple labels for variable 'a' are not supported
LINE 2: MATCH (a)-[]-(a)-[]-(a:v1) RETURN a
^
+SELECT * FROM cypher('cypher_match', $$
+ MATCH (a)-[]-(a)-[]-(a:invalid_label) RETURN a
+$$) AS (a agtype);
+ERROR: multiple labels for variable 'a' are not supported
+LINE 2: MATCH (a)-[]-(a)-[]-(a:invalid_label) RETURN a
+ ^
--Valid variable reuse, although why would you want to do it this way?
SELECT * FROM cypher('cypher_match', $$
MATCH (a:v1)-[]-()-[a]-() RETURN a
diff --git a/regress/sql/cypher_match.sql b/regress/sql/cypher_match.sql
index b05e79c5..2d9c5758 100644
--- a/regress/sql/cypher_match.sql
+++ b/regress/sql/cypher_match.sql
@@ -284,6 +284,9 @@ $$) AS (a agtype);
SELECT * FROM cypher('cypher_match', $$
MATCH (a)-[]-(a)-[]-(a:v1) RETURN a
$$) AS (a agtype);
+SELECT * FROM cypher('cypher_match', $$
+ MATCH (a)-[]-(a)-[]-(a:invalid_label) RETURN a
+$$) AS (a agtype);
--Valid variable reuse, although why would you want to do it this way?
SELECT * FROM cypher('cypher_match', $$
diff --git a/src/backend/parser/cypher_clause.c b/src/backend/parser/cypher_clause.c
index 3bc39cce..f0e6ba8c 100644
--- a/src/backend/parser/cypher_clause.c
+++ b/src/backend/parser/cypher_clause.c
@@ -4536,12 +4536,15 @@ static Expr *transform_cypher_node(cypher_parsestate *cpstate,
{
cypher_node *cnode = (cypher_node *)entity->entity.node;
- if (cnode != NULL &&
+
+
+ if (!node->label ||
+ (cnode != NULL &&
node != NULL &&
/* allow node using a default label against resolved var */
pg_strcasecmp(node->label, AG_DEFAULT_LABEL_VERTEX) != 0 &&
/* allow labels with the same name */
- pg_strcasecmp(cnode->label, node->label) != 0)
+ pg_strcasecmp(cnode->label, node->label) != 0))
{
ereport(ERROR,
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
[age] 04/15: UPDATED Python Driver ANTLR 4.9.3 -> 4.11.1 (#706)
Posted by jg...@apache.org.
This is an automated email from the ASF dual-hosted git repository.
jgemignani pushed a commit to branch PG12
in repository https://gitbox.apache.org/repos/asf/age.git
commit 30d20c4ac49ec7e4f858988a8a5f819be77f77ec
Author: Arunabh Das <pa...@hotmail.com>
AuthorDate: Thu Feb 23 17:54:29 2023 +0800
UPDATED Python Driver ANTLR 4.9.3 -> 4.11.1 (#706)
Conflicts:
drivers/python/setup.py
Co-authored-by: John Gemignani <jr...@gmail.com>
---
.gitignore | 2 +
drivers/python/age/gen/AgtypeLexer.py | 153 +++++++++++++++----------------
drivers/python/age/gen/AgtypeListener.py | 3 +-
drivers/python/age/gen/AgtypeParser.py | 100 ++++++++++----------
drivers/python/age/gen/AgtypeVisitor.py | 3 +-
drivers/python/antlr/README.md | 14 ++-
drivers/python/requirements.txt | Bin 158 -> 160 bytes
drivers/python/setup.py | 2 +-
8 files changed, 138 insertions(+), 139 deletions(-)
diff --git a/.gitignore b/.gitignore
index 46a93606..ac4fd622 100644
--- a/.gitignore
+++ b/.gitignore
@@ -4,3 +4,5 @@ build.sh
.idea
.deps
.DS_Store
+*.tokens
+*.interp
diff --git a/drivers/python/age/gen/AgtypeLexer.py b/drivers/python/age/gen/AgtypeLexer.py
index 543bee28..54f19c7d 100644
--- a/drivers/python/age/gen/AgtypeLexer.py
+++ b/drivers/python/age/gen/AgtypeLexer.py
@@ -12,7 +12,8 @@
# KIND, either express or implied. See the License for the
# specific language governing permissions and limitations
# under the License.
-# Generated from Agtype.g4 by ANTLR 4.9.2
+# Generated from ../Agtype.g4 by ANTLR 4.11.1
+
from antlr4 import *
from io import StringIO
import sys
@@ -22,86 +23,76 @@ else:
from typing.io import TextIO
-
def serializedATN():
- with StringIO() as buf:
- buf.write("\3\u608b\ua72a\u8133\ub9ed\u417c\u3be7\u7786\u5964\2\25")
- buf.write("\u00b9\b\1\4\2\t\2\4\3\t\3\4\4\t\4\4\5\t\5\4\6\t\6\4\7")
- buf.write("\t\7\4\b\t\b\4\t\t\t\4\n\t\n\4\13\t\13\4\f\t\f\4\r\t\r")
- buf.write("\4\16\t\16\4\17\t\17\4\20\t\20\4\21\t\21\4\22\t\22\4\23")
- buf.write("\t\23\4\24\t\24\4\25\t\25\4\26\t\26\4\27\t\27\4\30\t\30")
- buf.write("\4\31\t\31\4\32\t\32\4\33\t\33\3\2\3\2\3\2\3\2\3\2\3\3")
- buf.write("\3\3\3\3\3\3\3\3\3\3\3\4\3\4\3\4\3\4\3\4\3\5\3\5\3\6\3")
- buf.write("\6\3\7\3\7\3\b\3\b\3\t\3\t\3\n\3\n\3\13\3\13\3\13\3\f")
- buf.write("\3\f\3\r\3\r\3\r\3\r\3\r\3\r\3\r\3\r\3\r\3\16\3\16\3\16")
- buf.write("\3\16\3\17\3\17\7\17h\n\17\f\17\16\17k\13\17\3\20\3\20")
- buf.write("\3\20\7\20p\n\20\f\20\16\20s\13\20\3\20\3\20\3\21\3\21")
- buf.write("\3\21\5\21z\n\21\3\22\3\22\3\22\3\22\3\22\3\22\3\23\3")
- buf.write("\23\3\24\3\24\3\25\5\25\u0087\n\25\3\25\3\25\3\26\3\26")
- buf.write("\3\26\7\26\u008e\n\26\f\26\16\26\u0091\13\26\5\26\u0093")
- buf.write("\n\26\3\27\5\27\u0096\n\27\3\27\3\27\3\27\3\30\5\30\u009c")
- buf.write("\n\30\3\30\3\30\5\30\u00a0\n\30\3\30\3\30\3\31\3\31\6")
- buf.write("\31\u00a6\n\31\r\31\16\31\u00a7\3\32\3\32\5\32\u00ac\n")
- buf.write("\32\3\32\6\32\u00af\n\32\r\32\16\32\u00b0\3\33\6\33\u00b4")
- buf.write("\n\33\r\33\16\33\u00b5\3\33\3\33\2\2\34\3\3\5\4\7\5\t")
- buf.write("\6\13\7\r\b\17\t\21\n\23\13\25\f\27\r\31\16\33\17\35\20")
- buf.write("\37\21!\2#\2%\2\'\2)\22+\2-\23/\24\61\2\63\2\65\25\3\2")
- buf.write("\f\5\2C\\aac|\7\2&&\62;C\\aac|\n\2$$\61\61^^ddhhppttv")
- buf.write("v\5\2\62;CHch\5\2\2!$$^^\3\2\63;\3\2\62;\4\2GGgg\4\2-")
- buf.write("-//\5\2\13\f\17\17\"\"\2\u00bf\2\3\3\2\2\2\2\5\3\2\2\2")
- buf.write("\2\7\3\2\2\2\2\t\3\2\2\2\2\13\3\2\2\2\2\r\3\2\2\2\2\17")
- buf.write("\3\2\2\2\2\21\3\2\2\2\2\23\3\2\2\2\2\25\3\2\2\2\2\27\3")
- buf.write("\2\2\2\2\31\3\2\2\2\2\33\3\2\2\2\2\35\3\2\2\2\2\37\3\2")
- buf.write("\2\2\2)\3\2\2\2\2-\3\2\2\2\2/\3\2\2\2\2\65\3\2\2\2\3\67")
- buf.write("\3\2\2\2\5<\3\2\2\2\7B\3\2\2\2\tG\3\2\2\2\13I\3\2\2\2")
- buf.write("\rK\3\2\2\2\17M\3\2\2\2\21O\3\2\2\2\23Q\3\2\2\2\25S\3")
- buf.write("\2\2\2\27V\3\2\2\2\31X\3\2\2\2\33a\3\2\2\2\35e\3\2\2\2")
- buf.write("\37l\3\2\2\2!v\3\2\2\2#{\3\2\2\2%\u0081\3\2\2\2\'\u0083")
- buf.write("\3\2\2\2)\u0086\3\2\2\2+\u0092\3\2\2\2-\u0095\3\2\2\2")
- buf.write("/\u009b\3\2\2\2\61\u00a3\3\2\2\2\63\u00a9\3\2\2\2\65\u00b3")
- buf.write("\3\2\2\2\678\7v\2\289\7t\2\29:\7w\2\2:;\7g\2\2;\4\3\2")
- buf.write("\2\2<=\7h\2\2=>\7c\2\2>?\7n\2\2?@\7u\2\2@A\7g\2\2A\6\3")
- buf.write("\2\2\2BC\7p\2\2CD\7w\2\2DE\7n\2\2EF\7n\2\2F\b\3\2\2\2")
- buf.write("GH\7}\2\2H\n\3\2\2\2IJ\7.\2\2J\f\3\2\2\2KL\7\177\2\2L")
- buf.write("\16\3\2\2\2MN\7<\2\2N\20\3\2\2\2OP\7]\2\2P\22\3\2\2\2")
- buf.write("QR\7_\2\2R\24\3\2\2\2ST\7<\2\2TU\7<\2\2U\26\3\2\2\2VW")
- buf.write("\7/\2\2W\30\3\2\2\2XY\7K\2\2YZ\7p\2\2Z[\7h\2\2[\\\7k\2")
- buf.write("\2\\]\7p\2\2]^\7k\2\2^_\7v\2\2_`\7{\2\2`\32\3\2\2\2ab")
- buf.write("\7P\2\2bc\7c\2\2cd\7P\2\2d\34\3\2\2\2ei\t\2\2\2fh\t\3")
- buf.write("\2\2gf\3\2\2\2hk\3\2\2\2ig\3\2\2\2ij\3\2\2\2j\36\3\2\2")
- buf.write("\2ki\3\2\2\2lq\7$\2\2mp\5!\21\2np\5\'\24\2om\3\2\2\2o")
- buf.write("n\3\2\2\2ps\3\2\2\2qo\3\2\2\2qr\3\2\2\2rt\3\2\2\2sq\3")
- buf.write("\2\2\2tu\7$\2\2u \3\2\2\2vy\7^\2\2wz\t\4\2\2xz\5#\22\2")
- buf.write("yw\3\2\2\2yx\3\2\2\2z\"\3\2\2\2{|\7w\2\2|}\5%\23\2}~\5")
- buf.write("%\23\2~\177\5%\23\2\177\u0080\5%\23\2\u0080$\3\2\2\2\u0081")
- buf.write("\u0082\t\5\2\2\u0082&\3\2\2\2\u0083\u0084\n\6\2\2\u0084")
- buf.write("(\3\2\2\2\u0085\u0087\7/\2\2\u0086\u0085\3\2\2\2\u0086")
- buf.write("\u0087\3\2\2\2\u0087\u0088\3\2\2\2\u0088\u0089\5+\26\2")
- buf.write("\u0089*\3\2\2\2\u008a\u0093\7\62\2\2\u008b\u008f\t\7\2")
- buf.write("\2\u008c\u008e\t\b\2\2\u008d\u008c\3\2\2\2\u008e\u0091")
- buf.write("\3\2\2\2\u008f\u008d\3\2\2\2\u008f\u0090\3\2\2\2\u0090")
- buf.write("\u0093\3\2\2\2\u0091\u008f\3\2\2\2\u0092\u008a\3\2\2\2")
- buf.write("\u0092\u008b\3\2\2\2\u0093,\3\2\2\2\u0094\u0096\7/\2\2")
- buf.write("\u0095\u0094\3\2\2\2\u0095\u0096\3\2\2\2\u0096\u0097\3")
- buf.write("\2\2\2\u0097\u0098\5+\26\2\u0098\u0099\5\61\31\2\u0099")
- buf.write(".\3\2\2\2\u009a\u009c\7/\2\2\u009b\u009a\3\2\2\2\u009b")
- buf.write("\u009c\3\2\2\2\u009c\u009d\3\2\2\2\u009d\u009f\5+\26\2")
- buf.write("\u009e\u00a0\5\61\31\2\u009f\u009e\3\2\2\2\u009f\u00a0")
- buf.write("\3\2\2\2\u00a0\u00a1\3\2\2\2\u00a1\u00a2\5\63\32\2\u00a2")
- buf.write("\60\3\2\2\2\u00a3\u00a5\7\60\2\2\u00a4\u00a6\t\b\2\2\u00a5")
- buf.write("\u00a4\3\2\2\2\u00a6\u00a7\3\2\2\2\u00a7\u00a5\3\2\2\2")
- buf.write("\u00a7\u00a8\3\2\2\2\u00a8\62\3\2\2\2\u00a9\u00ab\t\t")
- buf.write("\2\2\u00aa\u00ac\t\n\2\2\u00ab\u00aa\3\2\2\2\u00ab\u00ac")
- buf.write("\3\2\2\2\u00ac\u00ae\3\2\2\2\u00ad\u00af\t\b\2\2\u00ae")
- buf.write("\u00ad\3\2\2\2\u00af\u00b0\3\2\2\2\u00b0\u00ae\3\2\2\2")
- buf.write("\u00b0\u00b1\3\2\2\2\u00b1\64\3\2\2\2\u00b2\u00b4\t\13")
- buf.write("\2\2\u00b3\u00b2\3\2\2\2\u00b4\u00b5\3\2\2\2\u00b5\u00b3")
- buf.write("\3\2\2\2\u00b5\u00b6\3\2\2\2\u00b6\u00b7\3\2\2\2\u00b7")
- buf.write("\u00b8\b\33\2\2\u00b8\66\3\2\2\2\21\2ioqy\u0086\u008f")
- buf.write("\u0092\u0095\u009b\u009f\u00a7\u00ab\u00b0\u00b5\3\b\2")
- buf.write("\2")
- return buf.getvalue()
-
+ return [
+ 4,0,19,183,6,-1,2,0,7,0,2,1,7,1,2,2,7,2,2,3,7,3,2,4,7,4,2,5,7,5,
+ 2,6,7,6,2,7,7,7,2,8,7,8,2,9,7,9,2,10,7,10,2,11,7,11,2,12,7,12,2,
+ 13,7,13,2,14,7,14,2,15,7,15,2,16,7,16,2,17,7,17,2,18,7,18,2,19,7,
+ 19,2,20,7,20,2,21,7,21,2,22,7,22,2,23,7,23,2,24,7,24,2,25,7,25,1,
+ 0,1,0,1,0,1,0,1,0,1,1,1,1,1,1,1,1,1,1,1,1,1,2,1,2,1,2,1,2,1,2,1,
+ 3,1,3,1,4,1,4,1,5,1,5,1,6,1,6,1,7,1,7,1,8,1,8,1,9,1,9,1,9,1,10,1,
+ 10,1,11,1,11,1,11,1,11,1,11,1,11,1,11,1,11,1,11,1,12,1,12,1,12,1,
+ 12,1,13,1,13,5,13,102,8,13,10,13,12,13,105,9,13,1,14,1,14,1,14,5,
+ 14,110,8,14,10,14,12,14,113,9,14,1,14,1,14,1,15,1,15,1,15,3,15,120,
+ 8,15,1,16,1,16,1,16,1,16,1,16,1,16,1,17,1,17,1,18,1,18,1,19,3,19,
+ 133,8,19,1,19,1,19,1,20,1,20,1,20,5,20,140,8,20,10,20,12,20,143,
+ 9,20,3,20,145,8,20,1,21,3,21,148,8,21,1,21,1,21,1,21,1,22,3,22,154,
+ 8,22,1,22,1,22,3,22,158,8,22,1,22,1,22,1,23,1,23,4,23,164,8,23,11,
+ 23,12,23,165,1,24,1,24,3,24,170,8,24,1,24,4,24,173,8,24,11,24,12,
+ 24,174,1,25,4,25,178,8,25,11,25,12,25,179,1,25,1,25,0,0,26,1,1,3,
+ 2,5,3,7,4,9,5,11,6,13,7,15,8,17,9,19,10,21,11,23,12,25,13,27,14,
+ 29,15,31,0,33,0,35,0,37,0,39,16,41,0,43,17,45,18,47,0,49,0,51,19,
+ 1,0,10,3,0,65,90,95,95,97,122,5,0,36,36,48,57,65,90,95,95,97,122,
+ 8,0,34,34,47,47,92,92,98,98,102,102,110,110,114,114,116,116,3,0,
+ 48,57,65,70,97,102,3,0,0,31,34,34,92,92,1,0,49,57,1,0,48,57,2,0,
+ 69,69,101,101,2,0,43,43,45,45,3,0,9,10,13,13,32,32,189,0,1,1,0,0,
+ 0,0,3,1,0,0,0,0,5,1,0,0,0,0,7,1,0,0,0,0,9,1,0,0,0,0,11,1,0,0,0,0,
+ 13,1,0,0,0,0,15,1,0,0,0,0,17,1,0,0,0,0,19,1,0,0,0,0,21,1,0,0,0,0,
+ 23,1,0,0,0,0,25,1,0,0,0,0,27,1,0,0,0,0,29,1,0,0,0,0,39,1,0,0,0,0,
+ 43,1,0,0,0,0,45,1,0,0,0,0,51,1,0,0,0,1,53,1,0,0,0,3,58,1,0,0,0,5,
+ 64,1,0,0,0,7,69,1,0,0,0,9,71,1,0,0,0,11,73,1,0,0,0,13,75,1,0,0,0,
+ 15,77,1,0,0,0,17,79,1,0,0,0,19,81,1,0,0,0,21,84,1,0,0,0,23,86,1,
+ 0,0,0,25,95,1,0,0,0,27,99,1,0,0,0,29,106,1,0,0,0,31,116,1,0,0,0,
+ 33,121,1,0,0,0,35,127,1,0,0,0,37,129,1,0,0,0,39,132,1,0,0,0,41,144,
+ 1,0,0,0,43,147,1,0,0,0,45,153,1,0,0,0,47,161,1,0,0,0,49,167,1,0,
+ 0,0,51,177,1,0,0,0,53,54,5,116,0,0,54,55,5,114,0,0,55,56,5,117,0,
+ 0,56,57,5,101,0,0,57,2,1,0,0,0,58,59,5,102,0,0,59,60,5,97,0,0,60,
+ 61,5,108,0,0,61,62,5,115,0,0,62,63,5,101,0,0,63,4,1,0,0,0,64,65,
+ 5,110,0,0,65,66,5,117,0,0,66,67,5,108,0,0,67,68,5,108,0,0,68,6,1,
+ 0,0,0,69,70,5,123,0,0,70,8,1,0,0,0,71,72,5,44,0,0,72,10,1,0,0,0,
+ 73,74,5,125,0,0,74,12,1,0,0,0,75,76,5,58,0,0,76,14,1,0,0,0,77,78,
+ 5,91,0,0,78,16,1,0,0,0,79,80,5,93,0,0,80,18,1,0,0,0,81,82,5,58,0,
+ 0,82,83,5,58,0,0,83,20,1,0,0,0,84,85,5,45,0,0,85,22,1,0,0,0,86,87,
+ 5,73,0,0,87,88,5,110,0,0,88,89,5,102,0,0,89,90,5,105,0,0,90,91,5,
+ 110,0,0,91,92,5,105,0,0,92,93,5,116,0,0,93,94,5,121,0,0,94,24,1,
+ 0,0,0,95,96,5,78,0,0,96,97,5,97,0,0,97,98,5,78,0,0,98,26,1,0,0,0,
+ 99,103,7,0,0,0,100,102,7,1,0,0,101,100,1,0,0,0,102,105,1,0,0,0,103,
+ 101,1,0,0,0,103,104,1,0,0,0,104,28,1,0,0,0,105,103,1,0,0,0,106,111,
+ 5,34,0,0,107,110,3,31,15,0,108,110,3,37,18,0,109,107,1,0,0,0,109,
+ 108,1,0,0,0,110,113,1,0,0,0,111,109,1,0,0,0,111,112,1,0,0,0,112,
+ 114,1,0,0,0,113,111,1,0,0,0,114,115,5,34,0,0,115,30,1,0,0,0,116,
+ 119,5,92,0,0,117,120,7,2,0,0,118,120,3,33,16,0,119,117,1,0,0,0,119,
+ 118,1,0,0,0,120,32,1,0,0,0,121,122,5,117,0,0,122,123,3,35,17,0,123,
+ 124,3,35,17,0,124,125,3,35,17,0,125,126,3,35,17,0,126,34,1,0,0,0,
+ 127,128,7,3,0,0,128,36,1,0,0,0,129,130,8,4,0,0,130,38,1,0,0,0,131,
+ 133,5,45,0,0,132,131,1,0,0,0,132,133,1,0,0,0,133,134,1,0,0,0,134,
+ 135,3,41,20,0,135,40,1,0,0,0,136,145,5,48,0,0,137,141,7,5,0,0,138,
+ 140,7,6,0,0,139,138,1,0,0,0,140,143,1,0,0,0,141,139,1,0,0,0,141,
+ 142,1,0,0,0,142,145,1,0,0,0,143,141,1,0,0,0,144,136,1,0,0,0,144,
+ 137,1,0,0,0,145,42,1,0,0,0,146,148,5,45,0,0,147,146,1,0,0,0,147,
+ 148,1,0,0,0,148,149,1,0,0,0,149,150,3,41,20,0,150,151,3,47,23,0,
+ 151,44,1,0,0,0,152,154,5,45,0,0,153,152,1,0,0,0,153,154,1,0,0,0,
+ 154,155,1,0,0,0,155,157,3,41,20,0,156,158,3,47,23,0,157,156,1,0,
+ 0,0,157,158,1,0,0,0,158,159,1,0,0,0,159,160,3,49,24,0,160,46,1,0,
+ 0,0,161,163,5,46,0,0,162,164,7,6,0,0,163,162,1,0,0,0,164,165,1,0,
+ 0,0,165,163,1,0,0,0,165,166,1,0,0,0,166,48,1,0,0,0,167,169,7,7,0,
+ 0,168,170,7,8,0,0,169,168,1,0,0,0,169,170,1,0,0,0,170,172,1,0,0,
+ 0,171,173,7,6,0,0,172,171,1,0,0,0,173,174,1,0,0,0,174,172,1,0,0,
+ 0,174,175,1,0,0,0,175,50,1,0,0,0,176,178,7,9,0,0,177,176,1,0,0,0,
+ 178,179,1,0,0,0,179,177,1,0,0,0,179,180,1,0,0,0,180,181,1,0,0,0,
+ 181,182,6,25,0,0,182,52,1,0,0,0,15,0,103,109,111,119,132,141,144,
+ 147,153,157,165,169,174,179,1,6,0,0
+ ]
class AgtypeLexer(Lexer):
@@ -151,7 +142,7 @@ class AgtypeLexer(Lexer):
def __init__(self, input=None, output:TextIO = sys.stdout):
super().__init__(input, output)
- self.checkVersion("4.9.2")
+ self.checkVersion("4.11.1")
self._interp = LexerATNSimulator(self, self.atn, self.decisionsToDFA, PredictionContextCache())
self._actions = None
self._predicates = None
diff --git a/drivers/python/age/gen/AgtypeListener.py b/drivers/python/age/gen/AgtypeListener.py
index 15016bf9..6e60da42 100644
--- a/drivers/python/age/gen/AgtypeListener.py
+++ b/drivers/python/age/gen/AgtypeListener.py
@@ -12,7 +12,8 @@
# KIND, either express or implied. See the License for the
# specific language governing permissions and limitations
# under the License.
-# Generated from Agtype.g4 by ANTLR 4.9.2
+# Generated from ../Agtype.g4 by ANTLR 4.11.1
+
from antlr4 import *
if __name__ is not None and "." in __name__:
from .AgtypeParser import AgtypeParser
diff --git a/drivers/python/age/gen/AgtypeParser.py b/drivers/python/age/gen/AgtypeParser.py
index ff98746a..daaf578e 100644
--- a/drivers/python/age/gen/AgtypeParser.py
+++ b/drivers/python/age/gen/AgtypeParser.py
@@ -12,8 +12,9 @@
# KIND, either express or implied. See the License for the
# specific language governing permissions and limitations
# under the License.
-# Generated from Agtype.g4 by ANTLR 4.9.2
+# Generated from ../Agtype.g4 by ANTLR 4.11.1
# encoding: utf-8
+
from antlr4 import *
from io import StringIO
import sys
@@ -22,39 +23,35 @@ if sys.version_info[1] > 5:
else:
from typing.io import TextIO
-
def serializedATN():
- with StringIO() as buf:
- buf.write("\3\u608b\ua72a\u8133\ub9ed\u417c\u3be7\u7786\u5964\3\25")
- buf.write("R\4\2\t\2\4\3\t\3\4\4\t\4\4\5\t\5\4\6\t\6\4\7\t\7\4\b")
- buf.write("\t\b\4\t\t\t\3\2\3\2\3\2\3\3\3\3\5\3\30\n\3\3\4\3\4\3")
- buf.write("\4\3\4\3\4\3\4\3\4\3\4\5\4\"\n\4\3\5\3\5\3\5\3\5\7\5(")
- buf.write("\n\5\f\5\16\5+\13\5\3\5\3\5\3\5\3\5\5\5\61\n\5\3\6\3\6")
- buf.write("\3\6\3\6\3\7\3\7\3\7\3\7\7\7;\n\7\f\7\16\7>\13\7\3\7\3")
- buf.write("\7\3\7\3\7\5\7D\n\7\3\b\3\b\3\b\3\t\3\t\3\t\5\tL\n\t\3")
- buf.write("\t\3\t\5\tP\n\t\3\t\2\2\n\2\4\6\b\n\f\16\20\2\2\2Y\2\22")
- buf.write("\3\2\2\2\4\25\3\2\2\2\6!\3\2\2\2\b\60\3\2\2\2\n\62\3\2")
- buf.write("\2\2\fC\3\2\2\2\16E\3\2\2\2\20O\3\2\2\2\22\23\5\4\3\2")
- buf.write("\23\24\7\2\2\3\24\3\3\2\2\2\25\27\5\6\4\2\26\30\5\16\b")
- buf.write("\2\27\26\3\2\2\2\27\30\3\2\2\2\30\5\3\2\2\2\31\"\7\21")
- buf.write("\2\2\32\"\7\22\2\2\33\"\5\20\t\2\34\"\7\3\2\2\35\"\7\4")
- buf.write("\2\2\36\"\7\5\2\2\37\"\5\b\5\2 \"\5\f\7\2!\31\3\2\2\2")
- buf.write("!\32\3\2\2\2!\33\3\2\2\2!\34\3\2\2\2!\35\3\2\2\2!\36\3")
- buf.write("\2\2\2!\37\3\2\2\2! \3\2\2\2\"\7\3\2\2\2#$\7\6\2\2$)\5")
- buf.write("\n\6\2%&\7\7\2\2&(\5\n\6\2\'%\3\2\2\2(+\3\2\2\2)\'\3\2")
- buf.write("\2\2)*\3\2\2\2*,\3\2\2\2+)\3\2\2\2,-\7\b\2\2-\61\3\2\2")
- buf.write("\2./\7\6\2\2/\61\7\b\2\2\60#\3\2\2\2\60.\3\2\2\2\61\t")
- buf.write("\3\2\2\2\62\63\7\21\2\2\63\64\7\t\2\2\64\65\5\4\3\2\65")
- buf.write("\13\3\2\2\2\66\67\7\n\2\2\67<\5\4\3\289\7\7\2\29;\5\4")
- buf.write("\3\2:8\3\2\2\2;>\3\2\2\2<:\3\2\2\2<=\3\2\2\2=?\3\2\2\2")
- buf.write("><\3\2\2\2?@\7\13\2\2@D\3\2\2\2AB\7\n\2\2BD\7\13\2\2C")
- buf.write("\66\3\2\2\2CA\3\2\2\2D\r\3\2\2\2EF\7\f\2\2FG\7\20\2\2")
- buf.write("G\17\3\2\2\2HP\7\23\2\2IP\7\24\2\2JL\7\r\2\2KJ\3\2\2\2")
- buf.write("KL\3\2\2\2LM\3\2\2\2MP\7\16\2\2NP\7\17\2\2OH\3\2\2\2O")
- buf.write("I\3\2\2\2OK\3\2\2\2ON\3\2\2\2P\21\3\2\2\2\n\27!)\60<C")
- buf.write("KO")
- return buf.getvalue()
-
+ return [
+ 4,1,19,80,2,0,7,0,2,1,7,1,2,2,7,2,2,3,7,3,2,4,7,4,2,5,7,5,2,6,7,
+ 6,2,7,7,7,1,0,1,0,1,0,1,1,1,1,3,1,22,8,1,1,2,1,2,1,2,1,2,1,2,1,2,
+ 1,2,1,2,3,2,32,8,2,1,3,1,3,1,3,1,3,5,3,38,8,3,10,3,12,3,41,9,3,1,
+ 3,1,3,1,3,1,3,3,3,47,8,3,1,4,1,4,1,4,1,4,1,5,1,5,1,5,1,5,5,5,57,
+ 8,5,10,5,12,5,60,9,5,1,5,1,5,1,5,1,5,3,5,66,8,5,1,6,1,6,1,6,1,7,
+ 1,7,1,7,3,7,74,8,7,1,7,1,7,3,7,78,8,7,1,7,0,0,8,0,2,4,6,8,10,12,
+ 14,0,0,87,0,16,1,0,0,0,2,19,1,0,0,0,4,31,1,0,0,0,6,46,1,0,0,0,8,
+ 48,1,0,0,0,10,65,1,0,0,0,12,67,1,0,0,0,14,77,1,0,0,0,16,17,3,2,1,
+ 0,17,18,5,0,0,1,18,1,1,0,0,0,19,21,3,4,2,0,20,22,3,12,6,0,21,20,
+ 1,0,0,0,21,22,1,0,0,0,22,3,1,0,0,0,23,32,5,15,0,0,24,32,5,16,0,0,
+ 25,32,3,14,7,0,26,32,5,1,0,0,27,32,5,2,0,0,28,32,5,3,0,0,29,32,3,
+ 6,3,0,30,32,3,10,5,0,31,23,1,0,0,0,31,24,1,0,0,0,31,25,1,0,0,0,31,
+ 26,1,0,0,0,31,27,1,0,0,0,31,28,1,0,0,0,31,29,1,0,0,0,31,30,1,0,0,
+ 0,32,5,1,0,0,0,33,34,5,4,0,0,34,39,3,8,4,0,35,36,5,5,0,0,36,38,3,
+ 8,4,0,37,35,1,0,0,0,38,41,1,0,0,0,39,37,1,0,0,0,39,40,1,0,0,0,40,
+ 42,1,0,0,0,41,39,1,0,0,0,42,43,5,6,0,0,43,47,1,0,0,0,44,45,5,4,0,
+ 0,45,47,5,6,0,0,46,33,1,0,0,0,46,44,1,0,0,0,47,7,1,0,0,0,48,49,5,
+ 15,0,0,49,50,5,7,0,0,50,51,3,2,1,0,51,9,1,0,0,0,52,53,5,8,0,0,53,
+ 58,3,2,1,0,54,55,5,5,0,0,55,57,3,2,1,0,56,54,1,0,0,0,57,60,1,0,0,
+ 0,58,56,1,0,0,0,58,59,1,0,0,0,59,61,1,0,0,0,60,58,1,0,0,0,61,62,
+ 5,9,0,0,62,66,1,0,0,0,63,64,5,8,0,0,64,66,5,9,0,0,65,52,1,0,0,0,
+ 65,63,1,0,0,0,66,11,1,0,0,0,67,68,5,10,0,0,68,69,5,14,0,0,69,13,
+ 1,0,0,0,70,78,5,17,0,0,71,78,5,18,0,0,72,74,5,11,0,0,73,72,1,0,0,
+ 0,73,74,1,0,0,0,74,75,1,0,0,0,75,78,5,12,0,0,76,78,5,13,0,0,77,70,
+ 1,0,0,0,77,71,1,0,0,0,77,73,1,0,0,0,77,76,1,0,0,0,78,15,1,0,0,0,
+ 8,21,31,39,46,58,65,73,77
+ ]
class AgtypeParser ( Parser ):
@@ -111,7 +108,7 @@ class AgtypeParser ( Parser ):
def __init__(self, input:TokenStream, output:TextIO = sys.stdout):
super().__init__(input, output)
- self.checkVersion("4.9.2")
+ self.checkVersion("4.11.1")
self._interp = ParserATNSimulator(self, self.atn, self.decisionsToDFA, self.sharedContextCache)
self._predicates = None
@@ -218,7 +215,7 @@ class AgtypeParser ( Parser ):
self.state = 21
self._errHandler.sync(self)
_la = self._input.LA(1)
- if _la==AgtypeParser.T__9:
+ if _la==10:
self.state = 20
self.typeAnnotation()
@@ -447,49 +444,49 @@ class AgtypeParser ( Parser ):
self.state = 31
self._errHandler.sync(self)
token = self._input.LA(1)
- if token in [AgtypeParser.STRING]:
+ if token in [15]:
localctx = AgtypeParser.StringValueContext(self, localctx)
self.enterOuterAlt(localctx, 1)
self.state = 23
self.match(AgtypeParser.STRING)
pass
- elif token in [AgtypeParser.INTEGER]:
+ elif token in [16]:
localctx = AgtypeParser.IntegerValueContext(self, localctx)
self.enterOuterAlt(localctx, 2)
self.state = 24
self.match(AgtypeParser.INTEGER)
pass
- elif token in [AgtypeParser.T__10, AgtypeParser.T__11, AgtypeParser.T__12, AgtypeParser.RegularFloat, AgtypeParser.ExponentFloat]:
+ elif token in [11, 12, 13, 17, 18]:
localctx = AgtypeParser.FloatValueContext(self, localctx)
self.enterOuterAlt(localctx, 3)
self.state = 25
self.floatLiteral()
pass
- elif token in [AgtypeParser.T__0]:
+ elif token in [1]:
localctx = AgtypeParser.TrueBooleanContext(self, localctx)
self.enterOuterAlt(localctx, 4)
self.state = 26
self.match(AgtypeParser.T__0)
pass
- elif token in [AgtypeParser.T__1]:
+ elif token in [2]:
localctx = AgtypeParser.FalseBooleanContext(self, localctx)
self.enterOuterAlt(localctx, 5)
self.state = 27
self.match(AgtypeParser.T__1)
pass
- elif token in [AgtypeParser.T__2]:
+ elif token in [3]:
localctx = AgtypeParser.NullValueContext(self, localctx)
self.enterOuterAlt(localctx, 6)
self.state = 28
self.match(AgtypeParser.T__2)
pass
- elif token in [AgtypeParser.T__3]:
+ elif token in [4]:
localctx = AgtypeParser.ObjectValueContext(self, localctx)
self.enterOuterAlt(localctx, 7)
self.state = 29
self.obj()
pass
- elif token in [AgtypeParser.T__7]:
+ elif token in [8]:
localctx = AgtypeParser.ArrayValueContext(self, localctx)
self.enterOuterAlt(localctx, 8)
self.state = 30
@@ -559,7 +556,7 @@ class AgtypeParser ( Parser ):
self.state = 39
self._errHandler.sync(self)
_la = self._input.LA(1)
- while _la==AgtypeParser.T__4:
+ while _la==5:
self.state = 35
self.match(AgtypeParser.T__4)
self.state = 36
@@ -697,7 +694,7 @@ class AgtypeParser ( Parser ):
self.state = 58
self._errHandler.sync(self)
_la = self._input.LA(1)
- while _la==AgtypeParser.T__4:
+ while _la==5:
self.state = 54
self.match(AgtypeParser.T__4)
self.state = 55
@@ -819,22 +816,22 @@ class AgtypeParser ( Parser ):
self.state = 77
self._errHandler.sync(self)
token = self._input.LA(1)
- if token in [AgtypeParser.RegularFloat]:
+ if token in [17]:
self.enterOuterAlt(localctx, 1)
self.state = 70
self.match(AgtypeParser.RegularFloat)
pass
- elif token in [AgtypeParser.ExponentFloat]:
+ elif token in [18]:
self.enterOuterAlt(localctx, 2)
self.state = 71
self.match(AgtypeParser.ExponentFloat)
pass
- elif token in [AgtypeParser.T__10, AgtypeParser.T__11]:
+ elif token in [11, 12]:
self.enterOuterAlt(localctx, 3)
self.state = 73
self._errHandler.sync(self)
_la = self._input.LA(1)
- if _la==AgtypeParser.T__10:
+ if _la==11:
self.state = 72
self.match(AgtypeParser.T__10)
@@ -842,7 +839,7 @@ class AgtypeParser ( Parser ):
self.state = 75
self.match(AgtypeParser.T__11)
pass
- elif token in [AgtypeParser.T__12]:
+ elif token in [13]:
self.enterOuterAlt(localctx, 4)
self.state = 76
self.match(AgtypeParser.T__12)
@@ -859,6 +856,3 @@ class AgtypeParser ( Parser ):
return localctx
-
-
-
diff --git a/drivers/python/age/gen/AgtypeVisitor.py b/drivers/python/age/gen/AgtypeVisitor.py
index 2f48c4ba..824eb512 100644
--- a/drivers/python/age/gen/AgtypeVisitor.py
+++ b/drivers/python/age/gen/AgtypeVisitor.py
@@ -12,7 +12,8 @@
# KIND, either express or implied. See the License for the
# specific language governing permissions and limitations
# under the License.
-# Generated from Agtype.g4 by ANTLR 4.9.2
+# Generated from ../Agtype.g4 by ANTLR 4.11.1
+
from antlr4 import *
if __name__ is not None and "." in __name__:
from .AgtypeParser import AgtypeParser
diff --git a/drivers/python/antlr/README.md b/drivers/python/antlr/README.md
index bd817adc..8b070e9a 100644
--- a/drivers/python/antlr/README.md
+++ b/drivers/python/antlr/README.md
@@ -7,7 +7,17 @@ Python driver for Apache AGE, graph extension for PostgreSQL.
```
# prerequisites :
# - java over 8
-# - download ANTLR4 from https://www.antlr.org/download/antlr-4.9.2-complete.jar
-# - java -cp antlr-4.9.2-complete.jar org.antlr.v4.Tool -Dlanguage=Python3 -visitor -o ../age/gen ../../Agtype.g4
+# - download ANTLR4 from https://www.antlr.org/download/antlr-4.11.1-complete.jar
+# - java -cp antlr-4.11.1-complete.jar org.antlr.v4.Tool -Dlanguage=Python3 -visitor -o ../age/gen ../../Agtype.g4
```
+
+#### 2) Remove the *.interp & *.tokens files
+
+#### 3) Proceed to unit testing
+```
+python -m unittest -v test_age_py.py
+```
+```
+python -m unittest -v test_agtypes.py
+`
diff --git a/drivers/python/requirements.txt b/drivers/python/requirements.txt
index 918763cb..3aff61d8 100644
Binary files a/drivers/python/requirements.txt and b/drivers/python/requirements.txt differ
diff --git a/drivers/python/setup.py b/drivers/python/setup.py
index e66e8951..4565dc5d 100644
--- a/drivers/python/setup.py
+++ b/drivers/python/setup.py
@@ -29,7 +29,7 @@ setup(
author_email = 'rhizome.ai@gmail.com',
url = 'https://github.com/apache/age',
license = 'Apache2.0',
- install_requires = [ 'psycopg2', 'antlr4-python3-runtime==4.9.3'],
+ install_requires = [ 'psycopg2', 'antlr4-python3-runtime==4.11.1'],
packages = ['age', 'age.gen'],
keywords = ['Graph Database', 'Apache AGE', 'PostgreSQL'],
python_requires = '>=3.9',
[age] 07/15: Barbell regress tests (#708)
Posted by jg...@apache.org.
This is an automated email from the ASF dual-hosted git repository.
jgemignani pushed a commit to branch PG12
in repository https://gitbox.apache.org/repos/asf/age.git
commit 0631b5a9622418b644e09a8a579d39f91e62ef18
Author: Marco Aurélio Silva de Souza Júnior <57...@users.noreply.github.com>
AuthorDate: Tue Feb 28 21:32:08 2023 -0300
Barbell regress tests (#708)
* Fixed bug where the system was crashing
when graph_name was passed as NULL.
* added checks for possible NULL arguments before
calling create_complete_graph.
* Added regress tests for Barbell graph generation
Conflicts:
src/backend/utils/graph_generation.c
Co-authored-by: John Gemignani <jr...@gmail.com>
---
regress/expected/graph_generation.out | 111 ++++++++++++++++++++++++++++++++++
regress/sql/graph_generation.sql | 27 +++++++++
src/backend/utils/graph_generation.c | 57 ++++++++++-------
3 files changed, 174 insertions(+), 21 deletions(-)
diff --git a/regress/expected/graph_generation.out b/regress/expected/graph_generation.out
index 118ab6e9..b9ed65b1 100644
--- a/regress/expected/graph_generation.out
+++ b/regress/expected/graph_generation.out
@@ -109,3 +109,114 @@ NOTICE: graph "gp2" has been dropped
(1 row)
+-- Tests for barbell graph generation
+SELECT * FROM age_create_barbell_graph('gp1',5,0,'vertices',NULL,'edges',NULL);
+NOTICE: graph "gp1" has been created
+NOTICE: VLabel "vertices" has been created
+NOTICE: ELabel "edges" has been created
+ age_create_barbell_graph
+--------------------------
+
+(1 row)
+
+SELECT COUNT(*) FROM gp1."edges";
+ count
+-------
+ 21
+(1 row)
+
+SELECT COUNT(*) FROM gp1."vertices";
+ count
+-------
+ 10
+(1 row)
+
+SELECT * FROM cypher('gp1', $$MATCH (a)-[e]->(b) RETURN e$$) as (n agtype);
+ n
+----------------------------------------------------------------------------------------------------------------------------
+ {"id": 1125899906842625, "label": "edges", "end_id": 844424930131970, "start_id": 844424930131969, "properties": {}}::edge
+ {"id": 1125899906842629, "label": "edges", "end_id": 844424930131971, "start_id": 844424930131970, "properties": {}}::edge
+ {"id": 1125899906842626, "label": "edges", "end_id": 844424930131971, "start_id": 844424930131969, "properties": {}}::edge
+ {"id": 1125899906842627, "label": "edges", "end_id": 844424930131972, "start_id": 844424930131969, "properties": {}}::edge
+ {"id": 1125899906842632, "label": "edges", "end_id": 844424930131972, "start_id": 844424930131971, "properties": {}}::edge
+ {"id": 1125899906842630, "label": "edges", "end_id": 844424930131972, "start_id": 844424930131970, "properties": {}}::edge
+ {"id": 1125899906842634, "label": "edges", "end_id": 844424930131973, "start_id": 844424930131972, "properties": {}}::edge
+ {"id": 1125899906842628, "label": "edges", "end_id": 844424930131973, "start_id": 844424930131969, "properties": {}}::edge
+ {"id": 1125899906842631, "label": "edges", "end_id": 844424930131973, "start_id": 844424930131970, "properties": {}}::edge
+ {"id": 1125899906842633, "label": "edges", "end_id": 844424930131973, "start_id": 844424930131971, "properties": {}}::edge
+ {"id": 1125899906842635, "label": "edges", "end_id": 844424930131975, "start_id": 844424930131974, "properties": {}}::edge
+ {"id": 1125899906842639, "label": "edges", "end_id": 844424930131976, "start_id": 844424930131975, "properties": {}}::edge
+ {"id": 1125899906842636, "label": "edges", "end_id": 844424930131976, "start_id": 844424930131974, "properties": {}}::edge
+ {"id": 1125899906842637, "label": "edges", "end_id": 844424930131977, "start_id": 844424930131974, "properties": {}}::edge
+ {"id": 1125899906842642, "label": "edges", "end_id": 844424930131977, "start_id": 844424930131976, "properties": {}}::edge
+ {"id": 1125899906842640, "label": "edges", "end_id": 844424930131977, "start_id": 844424930131975, "properties": {}}::edge
+ {"id": 1125899906842644, "label": "edges", "end_id": 844424930131978, "start_id": 844424930131977, "properties": {}}::edge
+ {"id": 1125899906842638, "label": "edges", "end_id": 844424930131978, "start_id": 844424930131974, "properties": {}}::edge
+ {"id": 1125899906842641, "label": "edges", "end_id": 844424930131978, "start_id": 844424930131975, "properties": {}}::edge
+ {"id": 1125899906842643, "label": "edges", "end_id": 844424930131978, "start_id": 844424930131976, "properties": {}}::edge
+ {"id": 1125899906842645, "label": "edges", "end_id": 844424930131978, "start_id": 844424930131969, "properties": {}}::edge
+(21 rows)
+
+SELECT * FROM age_create_barbell_graph('gp1',5,0,'vertices',NULL,'edges',NULL);
+ age_create_barbell_graph
+--------------------------
+
+(1 row)
+
+SELECT COUNT(*) FROM gp1."edges";
+ count
+-------
+ 42
+(1 row)
+
+SELECT COUNT(*) FROM gp1."vertices";
+ count
+-------
+ 20
+(1 row)
+
+SELECT * FROM age_create_barbell_graph('gp2',5,10,'vertices',NULL,'edges',NULL);
+NOTICE: graph "gp2" has been created
+NOTICE: VLabel "vertices" has been created
+NOTICE: ELabel "edges" has been created
+ age_create_barbell_graph
+--------------------------
+
+(1 row)
+
+-- SHOULD FAIL
+SELECT * FROM age_create_barbell_graph(NULL,NULL,NULL,NULL,NULL,NULL,NULL);
+ERROR: Graph name cannot be NULL
+SELECT * FROM age_create_barbell_graph('gp2',NULL,0,'vertices',NULL,'edges',NULL);
+ERROR: Graph size cannot be NULL or lower than 3
+SELECT * FROM age_create_barbell_graph('gp3',5,NULL,'vertices',NULL,'edges',NULL);
+ERROR: Bridge size cannot be NULL or lower than 0
+SELECT * FROM age_create_barbell_graph('gp4',NULL,0,'vertices',NULL,'edges',NULL);
+ERROR: Graph size cannot be NULL or lower than 3
+SELECT * FROM age_create_barbell_graph('gp5',5,0,'vertices',NULL,NULL,NULL);
+ERROR: edge label can not be NULL
+-- DROPPING GRAPHS
+SELECT drop_graph('gp1', true);
+NOTICE: drop cascades to 4 other objects
+DETAIL: drop cascades to table gp1._ag_label_vertex
+drop cascades to table gp1._ag_label_edge
+drop cascades to table gp1.vertices
+drop cascades to table gp1.edges
+NOTICE: graph "gp1" has been dropped
+ drop_graph
+------------
+
+(1 row)
+
+SELECT drop_graph('gp2', true);
+NOTICE: drop cascades to 4 other objects
+DETAIL: drop cascades to table gp2._ag_label_vertex
+drop cascades to table gp2._ag_label_edge
+drop cascades to table gp2.vertices
+drop cascades to table gp2.edges
+NOTICE: graph "gp2" has been dropped
+ drop_graph
+------------
+
+(1 row)
+
diff --git a/regress/sql/graph_generation.sql b/regress/sql/graph_generation.sql
index 1ad90f5a..32fa56dd 100644
--- a/regress/sql/graph_generation.sql
+++ b/regress/sql/graph_generation.sql
@@ -45,3 +45,30 @@ SELECT * FROM create_complete_graph(NULL,NULL,NULL);
SELECT drop_graph('gp1', true);
SELECT drop_graph('gp2', true);
+
+-- Tests for barbell graph generation
+SELECT * FROM age_create_barbell_graph('gp1',5,0,'vertices',NULL,'edges',NULL);
+
+SELECT COUNT(*) FROM gp1."edges";
+SELECT COUNT(*) FROM gp1."vertices";
+
+SELECT * FROM cypher('gp1', $$MATCH (a)-[e]->(b) RETURN e$$) as (n agtype);
+
+SELECT * FROM age_create_barbell_graph('gp1',5,0,'vertices',NULL,'edges',NULL);
+
+SELECT COUNT(*) FROM gp1."edges";
+SELECT COUNT(*) FROM gp1."vertices";
+
+SELECT * FROM age_create_barbell_graph('gp2',5,10,'vertices',NULL,'edges',NULL);
+
+-- SHOULD FAIL
+SELECT * FROM age_create_barbell_graph(NULL,NULL,NULL,NULL,NULL,NULL,NULL);
+SELECT * FROM age_create_barbell_graph('gp2',NULL,0,'vertices',NULL,'edges',NULL);
+SELECT * FROM age_create_barbell_graph('gp3',5,NULL,'vertices',NULL,'edges',NULL);
+SELECT * FROM age_create_barbell_graph('gp4',NULL,0,'vertices',NULL,'edges',NULL);
+SELECT * FROM age_create_barbell_graph('gp5',5,0,'vertices',NULL,NULL,NULL);
+
+-- DROPPING GRAPHS
+SELECT drop_graph('gp1', true);
+SELECT drop_graph('gp2', true);
+
diff --git a/src/backend/utils/graph_generation.c b/src/backend/utils/graph_generation.c
index f51f6271..e4672ea2 100644
--- a/src/backend/utils/graph_generation.c
+++ b/src/backend/utils/graph_generation.c
@@ -273,24 +273,23 @@ Datum age_create_barbell_graph(PG_FUNCTION_ARGS)
arguments = fcinfo;
- // create two separate complete graphs
- DirectFunctionCall4(create_complete_graph, arguments->args[0].value,
- arguments->args[1].value,
- arguments->args[5].value,
- arguments->args[3].value);
- DirectFunctionCall4(create_complete_graph, arguments->args[0].value,
- arguments->args[1].value,
- arguments->args[5].value,
- arguments->args[3].value);
+ // Checking for possible NULL arguments
+ // Name graph_name
+ if (PG_ARGISNULL(0))
+ {
+ ereport(ERROR, (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+ errmsg("Graph name cannot be NULL")));
+ }
- // Handling remaining arguments
- /*
- * graph_name: doesn't need to validate, since the create_complete_graph
- * function already does that.
- */
graph_name = PG_GETARG_NAME(0);
graph_name_str = NameStr(*graph_name);
- graph_oid = get_graph_oid(graph_name_str);
+
+ // int graph size (number of nodes in each complete graph)
+ if (PG_ARGISNULL(1) && PG_GETARG_INT32(1) < 3)
+ {
+ ereport(ERROR, (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+ errmsg("Graph size cannot be NULL or lower than 3")));
+ }
/*
* int64 bridge_size: currently only stays at zero.
@@ -299,7 +298,7 @@ Datum age_create_barbell_graph(PG_FUNCTION_ARGS)
if (PG_ARGISNULL(2) || PG_GETARG_INT32(2) < 0 )
{
ereport(ERROR, (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
- errmsg("Bridge size must not be NULL or lower than 0")));
+ errmsg("Bridge size cannot be NULL or lower than 0")));
}
// node label: if null, gets default label, which is "_ag_label_vertex"
@@ -312,14 +311,30 @@ Datum age_create_barbell_graph(PG_FUNCTION_ARGS)
node_label_name = PG_GETARG_NAME(3);
}
node_label_str = NameStr(*node_label_name);
- node_label_id = get_label_id(node_label_str, graph_oid);
- /*
- * edge_label: doesn't need to validate, since the create_complete_graph
- * function already does that.
- */
+ /* Name edge_label */
+ if (PG_ARGISNULL(5))
+ {
+ ereport(ERROR, (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+ errmsg("edge label can not be NULL")));
+ }
+
edge_label_name = PG_GETARG_NAME(5);
edge_label_str = NameStr(*edge_label_name);
+
+
+ // create two separate complete graphs
+ DirectFunctionCall4(create_complete_graph, arguments->args[0].value,
+ arguments->args[1].value,
+ arguments->args[5].value,
+ arguments->args[3].value);
+ DirectFunctionCall4(create_complete_graph, arguments->args[0].value,
+ arguments->args[1].value,
+ arguments->args[5].value,
+ arguments->args[3].value);
+
+ graph_oid = get_graph_oid(graph_name_str);
+ node_label_id = get_label_id(node_label_str, graph_oid);
edge_label_id = get_label_id(edge_label_str, graph_oid);
/*
[age] 10/15: Update regression tests for cypher_with (#725)
Posted by jg...@apache.org.
This is an automated email from the ASF dual-hosted git repository.
jgemignani pushed a commit to branch PG12
in repository https://gitbox.apache.org/repos/asf/age.git
commit 1123878a0c28bebe40455556c72ffbad1ca6f405
Author: Muhammad Taha Naveed <mn...@seecs.edu.pk>
AuthorDate: Fri Mar 3 23:41:55 2023 +0500
Update regression tests for cypher_with (#725)
- Used a small dataset for regression tests.
---
regress/expected/cypher_with.out | 408 ++++++++++++++++++---------------------
regress/sql/cypher_with.sql | 207 ++++++++++----------
2 files changed, 286 insertions(+), 329 deletions(-)
diff --git a/regress/expected/cypher_with.out b/regress/expected/cypher_with.out
index 1166fff3..e5f82aa2 100644
--- a/regress/expected/cypher_with.out
+++ b/regress/expected/cypher_with.out
@@ -16,7 +16,6 @@
* specific language governing permissions and limitations
* under the License.
*/
- \! cp -r regress/age_load/data regress/instance/data/age_load
LOAD 'age';
SET search_path TO ag_catalog;
--
@@ -29,82 +28,71 @@ NOTICE: graph "cypher_with" has been created
(1 row)
-SELECT create_vlabel('cypher_with','Country');
-NOTICE: VLabel "Country" has been created
- create_vlabel
----------------
-
-(1 row)
-
-SELECT load_labels_from_file('cypher_with', 'Country',
- 'age_load/countries.csv');
- load_labels_from_file
------------------------
-
-(1 row)
-
-SELECT create_vlabel('cypher_with','City');
-NOTICE: VLabel "City" has been created
- create_vlabel
----------------
-
-(1 row)
-
-SELECT load_labels_from_file('cypher_with', 'City',
- 'age_load/cities.csv');
- load_labels_from_file
------------------------
-
-(1 row)
-
-SELECT create_elabel('cypher_with','has_city');
-NOTICE: ELabel "has_city" has been created
- create_elabel
----------------
-
-(1 row)
-
-SELECT load_edges_from_file('cypher_with', 'has_city',
- 'age_load/edges.csv');
- load_edges_from_file
-----------------------
-
-(1 row)
+SELECT * FROM cypher('cypher_with', $$
+ CREATE (andres {name : 'Andres', age : 36}),
+ (caesar {name : 'Caesar', age : 25}),
+ (bossman {name : 'Bossman', age : 55}),
+ (david {name : 'David', age : 35}),
+ (george {name : 'George', age : 37}),
+ (andres)-[:BLOCKS]->(caesar),
+ (andres)-[:KNOWS]->(bossman),
+ (caesar)-[:KNOWS]->(george),
+ (bossman)-[:BLOCKS]->(david),
+ (bossman)-[:KNOWS]->(george),
+ (david)-[:KNOWS]->(andres)
+$$) as (a agtype);
+ a
+---
+(0 rows)
--
-- Test WITH clause
--
-SELECT count(*) FROM cypher('cypher_with', $$
- MATCH (m:City)-[:has_city]->(b:Country {iso2 : 'AT'})
- WITH m,b
- RETURN m,b
-$$) AS (City agtype, Country agtype);
- count
--------
- 2361
-(1 row)
+SELECT * FROM cypher('cypher_with', $$
+ MATCH (n)-[e]->(m)
+ WITH n,e,m
+ RETURN n,e,m
+$$) AS (N1 agtype, edge agtype, N2 agtype);
+ n1 | edge | n2
+--------------------------------------------------------------------------------------------+----------------------------------------------------------------------------------------------------------------------------+--------------------------------------------------------------------------------------------
+ {"id": 281474976710657, "label": "", "properties": {"age": 36, "name": "Andres"}}::vertex | {"id": 844424930131969, "label": "BLOCKS", "end_id": 281474976710658, "start_id": 281474976710657, "properties": {}}::edge | {"id": 281474976710658, "label": "", "properties": {"age": 25, "name": "Caesar"}}::vertex
+ {"id": 281474976710659, "label": "", "properties": {"age": 55, "name": "Bossman"}}::vertex | {"id": 844424930131970, "label": "BLOCKS", "end_id": 281474976710660, "start_id": 281474976710659, "properties": {}}::edge | {"id": 281474976710660, "label": "", "properties": {"age": 35, "name": "David"}}::vertex
+ {"id": 281474976710657, "label": "", "properties": {"age": 36, "name": "Andres"}}::vertex | {"id": 1125899906842625, "label": "KNOWS", "end_id": 281474976710659, "start_id": 281474976710657, "properties": {}}::edge | {"id": 281474976710659, "label": "", "properties": {"age": 55, "name": "Bossman"}}::vertex
+ {"id": 281474976710658, "label": "", "properties": {"age": 25, "name": "Caesar"}}::vertex | {"id": 1125899906842626, "label": "KNOWS", "end_id": 281474976710661, "start_id": 281474976710658, "properties": {}}::edge | {"id": 281474976710661, "label": "", "properties": {"age": 37, "name": "George"}}::vertex
+ {"id": 281474976710659, "label": "", "properties": {"age": 55, "name": "Bossman"}}::vertex | {"id": 1125899906842627, "label": "KNOWS", "end_id": 281474976710661, "start_id": 281474976710659, "properties": {}}::edge | {"id": 281474976710661, "label": "", "properties": {"age": 37, "name": "George"}}::vertex
+ {"id": 281474976710660, "label": "", "properties": {"age": 35, "name": "David"}}::vertex | {"id": 1125899906842628, "label": "KNOWS", "end_id": 281474976710657, "start_id": 281474976710660, "properties": {}}::edge | {"id": 281474976710657, "label": "", "properties": {"age": 36, "name": "Andres"}}::vertex
+(6 rows)
-- WITH/AS
SELECT * FROM cypher('cypher_with', $$
- MATCH (b:Country {iso2 : 'AT'})
- WITH b.name AS name, id(b) AS id
- RETURN name,id
-$$) AS (Country agtype, Country_id agtype);
- country | country_id
------------+-----------------
- "Austria" | 844424930131983
-(1 row)
+ MATCH (n)-[e]->(m)
+ WITH n.name AS n1, e as edge, m.name as n2
+ RETURN n1,label(edge),n2
+$$) AS (start_node agtype,edge agtype, end_node agtype);
+ start_node | edge | end_node
+------------+----------+-----------
+ "Andres" | "BLOCKS" | "Caesar"
+ "Bossman" | "BLOCKS" | "David"
+ "Andres" | "KNOWS" | "Bossman"
+ "Caesar" | "KNOWS" | "George"
+ "Bossman" | "KNOWS" | "George"
+ "David" | "KNOWS" | "Andres"
+(6 rows)
-SELECT * FROM cypher('cypher_with', $$
- MATCH (m:City {name: 'Zell'})-[]-(b:Country {iso2 : 'AT'})
- WITH b as country, count(*) AS foaf
- WHERE foaf > 1
- RETURN country.name, foaf
-$$) as (name agtype, foaf agtype);
- name | foaf
------------+------
- "Austria" | 2
-(1 row)
+SELECT * FROM cypher('cypher_with',$$
+ MATCH (person)-[r]->(otherPerson)
+ WITH *, type(r) AS connectionType
+ RETURN person.name, connectionType, otherPerson.name
+$$) AS (start_node agtype, connection agtype, end_node agtype);
+ start_node | connection | end_node
+------------+------------+-----------
+ "Andres" | "BLOCKS" | "Caesar"
+ "Bossman" | "BLOCKS" | "David"
+ "Andres" | "KNOWS" | "Bossman"
+ "Caesar" | "KNOWS" | "George"
+ "Bossman" | "KNOWS" | "George"
+ "David" | "KNOWS" | "Andres"
+(6 rows)
SELECT * FROM cypher('cypher_with', $$
WITH true AS b
@@ -117,162 +105,139 @@ $$) AS (b bool);
-- WITH/WHERE
SELECT * FROM cypher('cypher_with', $$
- MATCH (m:City)-[:has_city]->(b:Country{iso2:'BE'})
- WITH b,m
- WHERE m.name='x'
- RETURN m.name,b.iso2
-$$) AS ( "m.name" agtype, "b" agtype);
- m.name | b
---------+---
-(0 rows)
-
-SELECT * FROM cypher('cypher_with', $$
- MATCH (b:Country {iso2 : 'AT'})
- WITH b.name AS name, id(b) AS id
- WHERE name = 'Austria'
- RETURN name,id
-$$) AS (Country agtype, Country_id agtype);
- country | country_id
------------+-----------------
- "Austria" | 844424930131983
+MATCH (george {name: 'George'})<-[]-(otherPerson)
+ WITH otherPerson, toUpper(otherPerson.name) AS upperCaseName
+ WHERE upperCaseName STARTS WITH 'C'
+ RETURN otherPerson.name
+$$) as (name agtype);
+ name
+----------
+ "Caesar"
(1 row)
SELECT * FROM cypher('cypher_with', $$
- MATCH (b:Country {iso2 : 'AT'})
- WITH b.name AS name, id(b) AS id
- WHERE name = 'Austria' OR name = 'Kosovo'
- RETURN name,id
-$$) AS (Country agtype, Country_id agtype);
- country | country_id
------------+-----------------
- "Austria" | 844424930131983
+ MATCH (david {name: 'David'})-[]-(otherPerson)-[]->()
+ WITH otherPerson, count(*) AS foaf
+ WHERE foaf > 1
+ RETURN otherPerson.name
+$$) as (name agtype);
+ name
+----------
+ "Andres"
(1 row)
SELECT * FROM cypher('cypher_with', $$
- MATCH p = (m:City)-[:has_city*1..2]->(b:Country {iso2 : 'AT'})
+ MATCH p = (m)-[*1..2]->(b)
WITH p, length(p) AS path_length
WHERE path_length > 1
RETURN p
$$) AS (pattern agtype);
- pattern
----------
-(0 rows)
+ pattern [...]
+------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ [...]
+ [{"id": 281474976710657, "label": "_ag_label_vertex", "properties": {"age": 36, "name": "Andres"}}::vertex, {"id": 1125899906842625, "label": "KNOWS", "end_id": 281474976710659, "start_id": 281474976710657, "properties": {}}::edge, {"id": 281474976710659, "label": "_ag_label_vertex", "properties": {"age": 55, "name": "Bossman"}}::vertex, {"id": 1125899906842627, "label": "KNOWS", "end_id": 281474976710661, "start_id": 281474976710659, "properties": {}}::edge, {"id": 281474976710661, "la [...]
+ [{"id": 281474976710657, "label": "_ag_label_vertex", "properties": {"age": 36, "name": "Andres"}}::vertex, {"id": 1125899906842625, "label": "KNOWS", "end_id": 281474976710659, "start_id": 281474976710657, "properties": {}}::edge, {"id": 281474976710659, "label": "_ag_label_vertex", "properties": {"age": 55, "name": "Bossman"}}::vertex, {"id": 844424930131970, "label": "BLOCKS", "end_id": 281474976710660, "start_id": 281474976710659, "properties": {}}::edge, {"id": 281474976710660, "la [...]
+ [{"id": 281474976710657, "label": "_ag_label_vertex", "properties": {"age": 36, "name": "Andres"}}::vertex, {"id": 844424930131969, "label": "BLOCKS", "end_id": 281474976710658, "start_id": 281474976710657, "properties": {}}::edge, {"id": 281474976710658, "label": "_ag_label_vertex", "properties": {"age": 25, "name": "Caesar"}}::vertex, {"id": 1125899906842626, "label": "KNOWS", "end_id": 281474976710661, "start_id": 281474976710658, "properties": {}}::edge, {"id": 281474976710661, "lab [...]
+ [{"id": 281474976710659, "label": "_ag_label_vertex", "properties": {"age": 55, "name": "Bossman"}}::vertex, {"id": 844424930131970, "label": "BLOCKS", "end_id": 281474976710660, "start_id": 281474976710659, "properties": {}}::edge, {"id": 281474976710660, "label": "_ag_label_vertex", "properties": {"age": 35, "name": "David"}}::vertex, {"id": 1125899906842628, "label": "KNOWS", "end_id": 281474976710657, "start_id": 281474976710660, "properties": {}}::edge, {"id": 281474976710657, "lab [...]
+ [{"id": 281474976710660, "label": "_ag_label_vertex", "properties": {"age": 35, "name": "David"}}::vertex, {"id": 1125899906842628, "label": "KNOWS", "end_id": 281474976710657, "start_id": 281474976710660, "properties": {}}::edge, {"id": 281474976710657, "label": "_ag_label_vertex", "properties": {"age": 36, "name": "Andres"}}::vertex, {"id": 1125899906842625, "label": "KNOWS", "end_id": 281474976710659, "start_id": 281474976710657, "properties": {}}::edge, {"id": 281474976710659, "labe [...]
+ [{"id": 281474976710660, "label": "_ag_label_vertex", "properties": {"age": 35, "name": "David"}}::vertex, {"id": 1125899906842628, "label": "KNOWS", "end_id": 281474976710657, "start_id": 281474976710660, "properties": {}}::edge, {"id": 281474976710657, "label": "_ag_label_vertex", "properties": {"age": 36, "name": "Andres"}}::vertex, {"id": 844424930131969, "label": "BLOCKS", "end_id": 281474976710658, "start_id": 281474976710657, "properties": {}}::edge, {"id": 281474976710658, "labe [...]
+(6 rows)
-- MATCH/WHERE with WITH/WHERE
SELECT * FROM cypher('cypher_with', $$
- MATCH (m:City)-[:has_city]->(b:Country {iso2 : 'AT'})
- WHERE b.name = 'Austria'
- WITH m.name AS city,b.name AS country
- WHERE city = 'Vienna'
- RETURN city,country
-$$) AS (City agtype, Country agtype);
- city | country
-----------+-----------
- "Vienna" | "Austria"
+ MATCH (m)-[e]->(b)
+ WHERE label(e) = 'KNOWS'
+ WITH *
+ WHERE m.name = 'Andres'
+ RETURN m.name,label(e),b.name
+$$) AS (N1 agtype, edge agtype, N2 agtype);
+ n1 | edge | n2
+----------+---------+-----------
+ "Andres" | "KNOWS" | "Bossman"
(1 row)
-- WITH/ORDER BY
SELECT * FROM cypher('cypher_with', $$
- MATCH (m:City)-[:has_city]->(b:Country {iso2 : 'AT'})
- WITH m AS city,b AS country
- ORDER BY id(m) DESC LIMIT 10
- RETURN id(city),city
-$$) AS (id agtype, city agtype);
- id | city
-------------------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
- 1125899906986495 | {"id": 1125899906986495, "label": "City", "properties": {"id": "143871", "name": "Zell", "__id__": 143871, "latitude": "46.47222222", "state_id": "2057", "longitude": "14.38888889", "country_id": "15", "state_code": "2", "country_code": "AT"}}::vertex
- 1125899906986492 | {"id": 1125899906986492, "label": "City", "properties": {"id": "143868", "name": "Weißenstein", "__id__": 143868, "latitude": "46.68222222", "state_id": "2057", "longitude": "13.72361111", "country_id": "15", "state_code": "2", "country_code": "AT"}}::vertex
- 1125899906986491 | {"id": 1125899906986491, "label": "City", "properties": {"id": "143867", "name": "Weißensee", "__id__": 143867, "latitude": "46.71666667", "state_id": "2057", "longitude": "13.3", "country_id": "15", "state_code": "2", "country_code": "AT"}}::vertex
- 1125899906986490 | {"id": 1125899906986490, "label": "City", "properties": {"id": "143866", "name": "Weitensfeld im Gurktal", "__id__": 143866, "latitude": "46.84861111", "state_id": "2057", "longitude": "14.19166667", "country_id": "15", "state_code": "2", "country_code": "AT"}}::vertex
- 1125899906986489 | {"id": 1125899906986489, "label": "City", "properties": {"id": "143865", "name": "Velden am Wörther See", "__id__": 143865, "latitude": "46.6125", "state_id": "2057", "longitude": "14.04194444", "country_id": "15", "state_code": "2", "country_code": "AT"}}::vertex
- 1125899906986488 | {"id": 1125899906986488, "label": "City", "properties": {"id": "143864", "name": "Umberg", "__id__": 143864, "latitude": "46.64833", "state_id": "2057", "longitude": "13.9575", "country_id": "15", "state_code": "2", "country_code": "AT"}}::vertex
- 1125899906986487 | {"id": 1125899906986487, "label": "City", "properties": {"id": "143863", "name": "Tschachoritsch", "__id__": 143863, "latitude": "46.55352", "state_id": "2057", "longitude": "14.21461", "country_id": "15", "state_code": "2", "country_code": "AT"}}::vertex
- 1125899906986486 | {"id": 1125899906986486, "label": "City", "properties": {"id": "143862", "name": "Treffen", "__id__": 143862, "latitude": "46.66833333", "state_id": "2057", "longitude": "13.85555556", "country_id": "15", "state_code": "2", "country_code": "AT"}}::vertex
- 1125899906986484 | {"id": 1125899906986484, "label": "City", "properties": {"id": "143860", "name": "Timenitz", "__id__": 143860, "latitude": "46.68333", "state_id": "2057", "longitude": "14.41667", "country_id": "15", "state_code": "2", "country_code": "AT"}}::vertex
- 1125899906986483 | {"id": 1125899906986483, "label": "City", "properties": {"id": "143859", "name": "Techelsberg", "__id__": 143859, "latitude": "46.65861111", "state_id": "2057", "longitude": "14.10194444", "country_id": "15", "state_code": "2", "country_code": "AT"}}::vertex
-(10 rows)
+ MATCH (n)
+ WITH n
+ ORDER BY id(n)
+ RETURN n
+$$) as (name agtype);
+ name
+--------------------------------------------------------------------------------------------
+ {"id": 281474976710657, "label": "", "properties": {"age": 36, "name": "Andres"}}::vertex
+ {"id": 281474976710658, "label": "", "properties": {"age": 25, "name": "Caesar"}}::vertex
+ {"id": 281474976710659, "label": "", "properties": {"age": 55, "name": "Bossman"}}::vertex
+ {"id": 281474976710660, "label": "", "properties": {"age": 35, "name": "David"}}::vertex
+ {"id": 281474976710661, "label": "", "properties": {"age": 37, "name": "George"}}::vertex
+(5 rows)
+-- WITH/ORDER BY/DESC
SELECT * FROM cypher('cypher_with', $$
- MATCH (m:City)
- WITH m AS city
- ORDER BY id(m) ASC LIMIT 10
- RETURN id(city),city.name
-$$) AS (id agtype, names agtype);
- id | names
-------------------+-----------------------
- 1125899906842625 | "Andorra la Vella"
- 1125899906842626 | "Arinsal"
- 1125899906842627 | "Canillo"
- 1125899906842628 | "El Tarter"
- 1125899906842629 | "Encamp"
- 1125899906842630 | "Ordino"
- 1125899906842631 | "Pas de la Casa"
- 1125899906842632 | "Sant Julià de Lòria"
- 1125899906842633 | "la Massana"
- 1125899906842634 | "les Escaldes"
-(10 rows)
+ MATCH (n)
+ WITH n
+ ORDER BY n.name DESC LIMIT 3
+ RETURN collect(n.name)
+$$) as (names agtype);
+ names
+-------------------------------
+ ["George", "David", "Caesar"]
+(1 row)
--- WITH/ORDER BY/DESC/WHERE
SELECT * FROM cypher('cypher_with', $$
- MATCH (m:City)-[:has_city]->(b:Country {iso2 : 'AT'})
- WITH m AS city,b AS country
- ORDER BY id(m) DESC LIMIT 10
- WHERE city.name = 'Zell' OR city.name = 'Umberg'
- RETURN id(city),city.name,country.name
-$$) AS (id agtype, city agtype, country agtype);
- id | city | country
-------------------+----------+-----------
- 1125899906986495 | "Zell" | "Austria"
- 1125899906986488 | "Umberg" | "Austria"
+ MATCH (n {name: 'Andres'})-[]-(m)
+ WITH m
+ ORDER BY m.name DESC LIMIT 1
+ MATCH (m)-[]-(o)
+ RETURN o.name ORDER BY o.name
+$$) as (name agtype);
+ name
+-----------
+ "Andres"
+ "Bossman"
(2 rows)
-- multiple WITH clauses
SELECT * FROM cypher('cypher_with', $$
- MATCH (m:City)-[:has_city]->(b:Country {iso2 : 'AT'})
- WITH m AS city,b AS country
- WITH city LIMIT 10
- RETURN city.name
-$$) AS (city agtype);
- city
----------------------
- "Andau"
- "Antau"
- "Apetlon"
- "Bad Sauerbrunn"
- "Bad Tatzmannsdorf"
- "Badersdorf"
- "Bernstein"
- "Bocksdorf"
- "Breitenbrunn"
- "Bruckneudorf"
-(10 rows)
+ MATCH (n)-[e]->(m)
+ WITH n, e, m
+ WHERE label(e) = 'KNOWS'
+ WITH n.name as n1, label(e) as edge, m.name as n2
+ WHERE n1 = 'Andres'
+ RETURN n1,edge,n2
+$$) AS (N1 agtype, edge agtype, N2 agtype);
+ n1 | edge | n2
+----------+---------+-----------
+ "Andres" | "KNOWS" | "Bossman"
+(1 row)
SELECT * FROM cypher('cypher_with', $$
- MATCH (m:City)-[:has_city]->(b:Country {iso2 : 'AT'})
- WITH m AS city,b AS country
- ORDER BY id(m) DESC LIMIT 10
- WITH city
- WHERE city.name = 'Zell'
- RETURN id(city),city.name
-$$) AS (id agtype, city agtype);
- id | city
-------------------+--------
- 1125899906986495 | "Zell"
-(1 row)
+ UNWIND [1, 2, 3, 4, 5, 6] AS x
+ WITH x
+ WHERE x > 2
+ WITH x
+ LIMIT 5
+ RETURN x
+$$) as (name agtype);
+ name
+------
+ 3
+ 4
+ 5
+ 6
+(4 rows)
SELECT * FROM cypher('cypher_with', $$
- MATCH (m:City)-[:has_city]->(b:Country {iso2 : 'AT'})
- WITH m AS city,b AS country
- WHERE country.name = 'Austria'
- WITH city
- ORDER BY id(city) DESC
- WHERE city.name = 'Zell'
- RETURN id(city),city.name
-$$) AS (id agtype, city agtype);
- id | city
-------------------+--------
- 1125899906986495 | "Zell"
- 1125899906846495 | "Zell"
+ MATCH (m)-[]->(b)
+ WITH m,b
+ ORDER BY id(m) DESC LIMIT 5
+ WITH m as start_node, b as end_node
+ WHERE end_node.name = 'George'
+ RETURN id(start_node),start_node.name,id(end_node),end_node.name
+$$) AS (id1 agtype, name1 agtype, id2 agtype, name2 agtype);
+ id1 | name1 | id2 | name2
+-----------------+-----------+-----------------+----------
+ 281474976710659 | "Bossman" | 281474976710661 | "George"
+ 281474976710658 | "Caesar" | 281474976710661 | "George"
(2 rows)
-- Expression item must be aliased.
@@ -285,42 +250,41 @@ LINE 2: WITH 1 + 1
^
HINT: Items can be aliased by using AS.
SELECT * FROM cypher('cypher_with', $$
- MATCH (m:City)-[:has_city]->(b:Country {iso2 : 'AT'})
+ MATCH (m)-[]->(b)
WITH id(m)
RETURN m
-$$) AS (id agtype, city agtype);
+$$) AS (id agtype);
ERROR: expression item must be aliased
LINE 3: WITH id(m)
^
HINT: Items can be aliased by using AS.
-- Reference undefined variable in WITH clause (should error out)
SELECT count(*) FROM cypher('cypher_with', $$
- MATCH (m:City)-[:has_city]->(b:Country {iso2 : 'AT'})
+ MATCH (m)-[]->(b)
WITH m
RETURN m,b
-$$) AS (City agtype, Country agtype);
+$$) AS (a agtype, b agtype);
ERROR: could not find rte for b
LINE 4: RETURN m,b
^
SELECT * FROM cypher('cypher_with', $$
- MATCH (m:City)-[:has_city]->(b:Country {iso2 : 'AT'})
- WITH m AS city,b AS country
- WHERE country.name = 'Austria'
- WITH city
- WHERE city.name = 'Zell'
- RETURN id(city),country.name
-$$) AS (id agtype, country agtype);
-ERROR: could not find rte for country
-LINE 7: RETURN id(city),country.name
- ^
+ MATCH (m)-[]->(b)
+ WITH m AS start_node,b AS end_node
+ WHERE start_node.name = 'Andres'
+ WITH start_node
+ WHERE start_node.name = 'George'
+ RETURN id(start_node),end_node.name
+$$) AS (id agtype, node agtype);
+ERROR: could not find rte for end_node
+LINE 7: RETURN id(start_node),end_node.name
+ ^
-- Clean up
SELECT drop_graph('cypher_with', true);
-NOTICE: drop cascades to 5 other objects
+NOTICE: drop cascades to 4 other objects
DETAIL: drop cascades to table cypher_with._ag_label_vertex
drop cascades to table cypher_with._ag_label_edge
-drop cascades to table cypher_with."Country"
-drop cascades to table cypher_with."City"
-drop cascades to table cypher_with.has_city
+drop cascades to table cypher_with."BLOCKS"
+drop cascades to table cypher_with."KNOWS"
NOTICE: graph "cypher_with" has been dropped
drop_graph
------------
@@ -358,22 +322,22 @@ LINE 8: RETURN c,d
^
-- Issue 396 (should error out)
SELECT * FROM cypher('graph',$$
- CREATE (v),(u),(w),
- (v)-[:hasFriend]->(u),
- (u)-[:hasFriend]->(w)
+ CREATE (v),(u),(w),
+ (v)-[:hasFriend]->(u),
+ (u)-[:hasFriend]->(w)
$$) as (a agtype);
a
---
(0 rows)
SELECT * FROM cypher('graph',$$
- MATCH p=(v)-[*1..2]->(u)
- WITH p,length(p) AS path_length
- RETURN v,path_length
+ MATCH p=(v)-[*1..2]->(u)
+ WITH p,length(p) AS path_length
+ RETURN v,path_length
$$) as (a agtype,b agtype);
ERROR: could not find rte for v
-LINE 4: RETURN v,path_length
- ^
+LINE 4: RETURN v,path_length
+ ^
-- Clean up
SELECT drop_graph('graph', true);
NOTICE: drop cascades to 6 other objects
diff --git a/regress/sql/cypher_with.sql b/regress/sql/cypher_with.sql
index 95b18c50..93413f2c 100644
--- a/regress/sql/cypher_with.sql
+++ b/regress/sql/cypher_with.sql
@@ -17,8 +17,6 @@
* under the License.
*/
- \! cp -r regress/age_load/data regress/instance/data/age_load
-
LOAD 'age';
SET search_path TO ag_catalog;
@@ -27,42 +25,43 @@ SET search_path TO ag_catalog;
--
SELECT create_graph('cypher_with');
-SELECT create_vlabel('cypher_with','Country');
-SELECT load_labels_from_file('cypher_with', 'Country',
- 'age_load/countries.csv');
-
-SELECT create_vlabel('cypher_with','City');
-SELECT load_labels_from_file('cypher_with', 'City',
- 'age_load/cities.csv');
-
-SELECT create_elabel('cypher_with','has_city');
-SELECT load_edges_from_file('cypher_with', 'has_city',
- 'age_load/edges.csv');
+SELECT * FROM cypher('cypher_with', $$
+ CREATE (andres {name : 'Andres', age : 36}),
+ (caesar {name : 'Caesar', age : 25}),
+ (bossman {name : 'Bossman', age : 55}),
+ (david {name : 'David', age : 35}),
+ (george {name : 'George', age : 37}),
+ (andres)-[:BLOCKS]->(caesar),
+ (andres)-[:KNOWS]->(bossman),
+ (caesar)-[:KNOWS]->(george),
+ (bossman)-[:BLOCKS]->(david),
+ (bossman)-[:KNOWS]->(george),
+ (david)-[:KNOWS]->(andres)
+$$) as (a agtype);
--
-- Test WITH clause
--
-SELECT count(*) FROM cypher('cypher_with', $$
- MATCH (m:City)-[:has_city]->(b:Country {iso2 : 'AT'})
- WITH m,b
- RETURN m,b
-$$) AS (City agtype, Country agtype);
+SELECT * FROM cypher('cypher_with', $$
+ MATCH (n)-[e]->(m)
+ WITH n,e,m
+ RETURN n,e,m
+$$) AS (N1 agtype, edge agtype, N2 agtype);
-- WITH/AS
SELECT * FROM cypher('cypher_with', $$
- MATCH (b:Country {iso2 : 'AT'})
- WITH b.name AS name, id(b) AS id
- RETURN name,id
-$$) AS (Country agtype, Country_id agtype);
+ MATCH (n)-[e]->(m)
+ WITH n.name AS n1, e as edge, m.name as n2
+ RETURN n1,label(edge),n2
+$$) AS (start_node agtype,edge agtype, end_node agtype);
-SELECT * FROM cypher('cypher_with', $$
- MATCH (m:City {name: 'Zell'})-[]-(b:Country {iso2 : 'AT'})
- WITH b as country, count(*) AS foaf
- WHERE foaf > 1
- RETURN country.name, foaf
-$$) as (name agtype, foaf agtype);
+SELECT * FROM cypher('cypher_with',$$
+ MATCH (person)-[r]->(otherPerson)
+ WITH *, type(r) AS connectionType
+ RETURN person.name, connectionType, otherPerson.name
+$$) AS (start_node agtype, connection agtype, end_node agtype);
SELECT * FROM cypher('cypher_with', $$
WITH true AS b
@@ -72,28 +71,21 @@ $$) AS (b bool);
-- WITH/WHERE
SELECT * FROM cypher('cypher_with', $$
- MATCH (m:City)-[:has_city]->(b:Country{iso2:'BE'})
- WITH b,m
- WHERE m.name='x'
- RETURN m.name,b.iso2
-$$) AS ( "m.name" agtype, "b" agtype);
-
-SELECT * FROM cypher('cypher_with', $$
- MATCH (b:Country {iso2 : 'AT'})
- WITH b.name AS name, id(b) AS id
- WHERE name = 'Austria'
- RETURN name,id
-$$) AS (Country agtype, Country_id agtype);
+MATCH (george {name: 'George'})<-[]-(otherPerson)
+ WITH otherPerson, toUpper(otherPerson.name) AS upperCaseName
+ WHERE upperCaseName STARTS WITH 'C'
+ RETURN otherPerson.name
+$$) as (name agtype);
SELECT * FROM cypher('cypher_with', $$
- MATCH (b:Country {iso2 : 'AT'})
- WITH b.name AS name, id(b) AS id
- WHERE name = 'Austria' OR name = 'Kosovo'
- RETURN name,id
-$$) AS (Country agtype, Country_id agtype);
+ MATCH (david {name: 'David'})-[]-(otherPerson)-[]->()
+ WITH otherPerson, count(*) AS foaf
+ WHERE foaf > 1
+ RETURN otherPerson.name
+$$) as (name agtype);
SELECT * FROM cypher('cypher_with', $$
- MATCH p = (m:City)-[:has_city*1..2]->(b:Country {iso2 : 'AT'})
+ MATCH p = (m)-[*1..2]->(b)
WITH p, length(p) AS path_length
WHERE path_length > 1
RETURN p
@@ -102,66 +94,67 @@ $$) AS (pattern agtype);
-- MATCH/WHERE with WITH/WHERE
SELECT * FROM cypher('cypher_with', $$
- MATCH (m:City)-[:has_city]->(b:Country {iso2 : 'AT'})
- WHERE b.name = 'Austria'
- WITH m.name AS city,b.name AS country
- WHERE city = 'Vienna'
- RETURN city,country
-$$) AS (City agtype, Country agtype);
+ MATCH (m)-[e]->(b)
+ WHERE label(e) = 'KNOWS'
+ WITH *
+ WHERE m.name = 'Andres'
+ RETURN m.name,label(e),b.name
+$$) AS (N1 agtype, edge agtype, N2 agtype);
-- WITH/ORDER BY
SELECT * FROM cypher('cypher_with', $$
- MATCH (m:City)-[:has_city]->(b:Country {iso2 : 'AT'})
- WITH m AS city,b AS country
- ORDER BY id(m) DESC LIMIT 10
- RETURN id(city),city
-$$) AS (id agtype, city agtype);
+ MATCH (n)
+ WITH n
+ ORDER BY id(n)
+ RETURN n
+$$) as (name agtype);
-SELECT * FROM cypher('cypher_with', $$
- MATCH (m:City)
- WITH m AS city
- ORDER BY id(m) ASC LIMIT 10
- RETURN id(city),city.name
-$$) AS (id agtype, names agtype);
+-- WITH/ORDER BY/DESC
--- WITH/ORDER BY/DESC/WHERE
+SELECT * FROM cypher('cypher_with', $$
+ MATCH (n)
+ WITH n
+ ORDER BY n.name DESC LIMIT 3
+ RETURN collect(n.name)
+$$) as (names agtype);
SELECT * FROM cypher('cypher_with', $$
- MATCH (m:City)-[:has_city]->(b:Country {iso2 : 'AT'})
- WITH m AS city,b AS country
- ORDER BY id(m) DESC LIMIT 10
- WHERE city.name = 'Zell' OR city.name = 'Umberg'
- RETURN id(city),city.name,country.name
-$$) AS (id agtype, city agtype, country agtype);
+ MATCH (n {name: 'Andres'})-[]-(m)
+ WITH m
+ ORDER BY m.name DESC LIMIT 1
+ MATCH (m)-[]-(o)
+ RETURN o.name ORDER BY o.name
+$$) as (name agtype);
-- multiple WITH clauses
SELECT * FROM cypher('cypher_with', $$
- MATCH (m:City)-[:has_city]->(b:Country {iso2 : 'AT'})
- WITH m AS city,b AS country
- WITH city LIMIT 10
- RETURN city.name
-$$) AS (city agtype);
+ MATCH (n)-[e]->(m)
+ WITH n, e, m
+ WHERE label(e) = 'KNOWS'
+ WITH n.name as n1, label(e) as edge, m.name as n2
+ WHERE n1 = 'Andres'
+ RETURN n1,edge,n2
+$$) AS (N1 agtype, edge agtype, N2 agtype);
SELECT * FROM cypher('cypher_with', $$
- MATCH (m:City)-[:has_city]->(b:Country {iso2 : 'AT'})
- WITH m AS city,b AS country
- ORDER BY id(m) DESC LIMIT 10
- WITH city
- WHERE city.name = 'Zell'
- RETURN id(city),city.name
-$$) AS (id agtype, city agtype);
+ UNWIND [1, 2, 3, 4, 5, 6] AS x
+ WITH x
+ WHERE x > 2
+ WITH x
+ LIMIT 5
+ RETURN x
+$$) as (name agtype);
SELECT * FROM cypher('cypher_with', $$
- MATCH (m:City)-[:has_city]->(b:Country {iso2 : 'AT'})
- WITH m AS city,b AS country
- WHERE country.name = 'Austria'
- WITH city
- ORDER BY id(city) DESC
- WHERE city.name = 'Zell'
- RETURN id(city),city.name
-$$) AS (id agtype, city agtype);
+ MATCH (m)-[]->(b)
+ WITH m,b
+ ORDER BY id(m) DESC LIMIT 5
+ WITH m as start_node, b as end_node
+ WHERE end_node.name = 'George'
+ RETURN id(start_node),start_node.name,id(end_node),end_node.name
+$$) AS (id1 agtype, name1 agtype, id2 agtype, name2 agtype);
-- Expression item must be aliased.
@@ -171,27 +164,27 @@ SELECT * FROM cypher('cypher_with', $$
$$) AS (i int);
SELECT * FROM cypher('cypher_with', $$
- MATCH (m:City)-[:has_city]->(b:Country {iso2 : 'AT'})
+ MATCH (m)-[]->(b)
WITH id(m)
RETURN m
-$$) AS (id agtype, city agtype);
+$$) AS (id agtype);
-- Reference undefined variable in WITH clause (should error out)
SELECT count(*) FROM cypher('cypher_with', $$
- MATCH (m:City)-[:has_city]->(b:Country {iso2 : 'AT'})
+ MATCH (m)-[]->(b)
WITH m
RETURN m,b
-$$) AS (City agtype, Country agtype);
+$$) AS (a agtype, b agtype);
SELECT * FROM cypher('cypher_with', $$
- MATCH (m:City)-[:has_city]->(b:Country {iso2 : 'AT'})
- WITH m AS city,b AS country
- WHERE country.name = 'Austria'
- WITH city
- WHERE city.name = 'Zell'
- RETURN id(city),country.name
-$$) AS (id agtype, country agtype);
+ MATCH (m)-[]->(b)
+ WITH m AS start_node,b AS end_node
+ WHERE start_node.name = 'Andres'
+ WITH start_node
+ WHERE start_node.name = 'George'
+ RETURN id(start_node),end_node.name
+$$) AS (id agtype, node agtype);
-- Clean up
@@ -219,15 +212,15 @@ $$) AS (n agtype, d agtype);
-- Issue 396 (should error out)
SELECT * FROM cypher('graph',$$
- CREATE (v),(u),(w),
- (v)-[:hasFriend]->(u),
- (u)-[:hasFriend]->(w)
+ CREATE (v),(u),(w),
+ (v)-[:hasFriend]->(u),
+ (u)-[:hasFriend]->(w)
$$) as (a agtype);
SELECT * FROM cypher('graph',$$
- MATCH p=(v)-[*1..2]->(u)
- WITH p,length(p) AS path_length
- RETURN v,path_length
+ MATCH p=(v)-[*1..2]->(u)
+ WITH p,length(p) AS path_length
+ RETURN v,path_length
$$) as (a agtype,b agtype);
-- Clean up
[age] 03/15: Barbell graph generation (#648)
Posted by jg...@apache.org.
This is an automated email from the ASF dual-hosted git repository.
jgemignani pushed a commit to branch PG12
in repository https://gitbox.apache.org/repos/asf/age.git
commit a9099c9be0422275c4ed58dcdeaae1f2466657e0
Author: Marco Aurélio Silva de Souza Júnior <57...@users.noreply.github.com>
AuthorDate: Fri Feb 17 15:45:05 2023 -0300
Barbell graph generation (#648)
* Added function to create a Barbell Graph
* Created auxiliary function to get_next_val_internal;
* Currently creates a bridge of no vertexes between graphs,
will be implemented next.
* Added signature to age_create_barbell_graph
* Moved bracket to next line to macth code standards
* Fixed patterns to match code standards
* moved PG_FUNCTION_INFO_V1(age_create_barbell_graph) next to
age_create_barbell_graph function;
* added brackets to if/else conditions if just one line;
* moved get_nextval_internal auxiliary function to the beggining of
the file.
Conflicts:
src/backend/utils/graph_generation.c
Co-authored-by: John Gemignani <jr...@gmail.com>
---
age--1.1.1.sql | 13 +++
regress/expected/catalog.out | 2 +-
src/backend/utils/graph_generation.c | 154 ++++++++++++++++++++++++++++++++++-
3 files changed, 166 insertions(+), 3 deletions(-)
diff --git a/age--1.1.1.sql b/age--1.1.1.sql
index 08d4021c..e9aceffe 100644
--- a/age--1.1.1.sql
+++ b/age--1.1.1.sql
@@ -4186,6 +4186,19 @@ CALLED ON NULL INPUT
PARALLEL SAFE
AS 'MODULE_PATHNAME';
+CREATE FUNCTION ag_catalog.age_create_barbell_graph(graph_name name,
+ graph_size int,
+ bridge_size int,
+ node_label name = NULL,
+ node_properties agtype = NULL,
+ edge_label name = NULL,
+ edge_properties agtype = NULL)
+RETURNS void
+LANGUAGE c
+CALLED ON NULL INPUT
+PARALLEL SAFE
+AS 'MODULE_PATHNAME';
+
CREATE FUNCTION ag_catalog.age_prepare_cypher(cstring, cstring)
RETURNS boolean
LANGUAGE c
diff --git a/regress/expected/catalog.out b/regress/expected/catalog.out
index 282970b8..1e62cf9e 100644
--- a/regress/expected/catalog.out
+++ b/regress/expected/catalog.out
@@ -31,7 +31,7 @@ NOTICE: graph "graph" has been created
SELECT * FROM ag_graph WHERE name = 'graph';
graphid | name | namespace
---------+-------+-----------
- 17591 | graph | graph
+ 17593 | graph | graph
(1 row)
-- create a label to test drop_label()
diff --git a/src/backend/utils/graph_generation.c b/src/backend/utils/graph_generation.c
index c11be6ab..f51f6271 100644
--- a/src/backend/utils/graph_generation.c
+++ b/src/backend/utils/graph_generation.c
@@ -41,14 +41,35 @@
#include "catalog/ag_graph.h"
#include "catalog/ag_label.h"
+#include "commands/graph_commands.h"
#include "commands/label_commands.h"
#include "utils/graphid.h"
-#include "commands/graph_commands.h"
#include "utils/load/age_load.h"
#include "utils/load/ag_load_edges.h"
#include "utils/load/ag_load_labels.h"
+int64 get_nextval_internal(graph_cache_data* graph_cache,
+ label_cache_data* label_cache);
+/*
+ * Auxiliary function to get the next internal value in the graph,
+ * so a new object (node or edge) graph id can be composed.
+ */
+
+int64 get_nextval_internal(graph_cache_data* graph_cache,
+ label_cache_data* label_cache)
+{
+ Oid obj_seq_id;
+ char* label_seq_name_str;
+
+ label_seq_name_str = NameStr(label_cache->seq_name);
+ obj_seq_id = get_relname_relid(label_seq_name_str,
+ graph_cache->namespace);
+
+ return nextval_internal(obj_seq_id, true);
+}
+
+
PG_FUNCTION_INFO_V1(create_complete_graph);
/*
@@ -195,7 +216,136 @@ Datum create_complete_graph(PG_FUNCTION_ARGS)
end_vertex_graph_id, props);
}
}
-
PG_RETURN_VOID();
}
+
+PG_FUNCTION_INFO_V1(age_create_barbell_graph);
+
+/*
+ * The barbell graph is two complete graphs connected by a bridge path
+ * Syntax:
+ * ag_catalog.age_create_barbell_graph(graph_name Name,
+ * m int,
+ * n int,
+ * vertex_label_name Name DEFAULT = NULL,
+ * vertex_properties agtype DEFAULT = NULL,
+ * edge_label_name Name DEAULT = NULL,
+ * edge_properties agtype DEFAULT = NULL)
+ * Input:
+ *
+ * graph_name - Name of the graph to be created.
+ * m - number of vertices in one complete graph.
+ * n - number of vertices in the bridge path.
+ * vertex_label_name - Name of the label to assign each vertex to.
+ * vertex_properties - Property values to assign each vertex. Default is NULL
+ * edge_label_name - Name of the label to assign each edge to.
+ * edge_properties - Property values to assign each edge. Default is NULL
+ *
+ * https://en.wikipedia.org/wiki/Barbell_graph
+ */
+
+Datum age_create_barbell_graph(PG_FUNCTION_ARGS)
+{
+ FunctionCallInfo arguments;
+ Oid graph_oid;
+ Name graph_name;
+ char* graph_name_str;
+
+ int64 start_node_index, end_node_index, nextval;
+
+ Name node_label_name = NULL;
+ int32 node_label_id;
+ char* node_label_str;
+
+ Name edge_label_name;
+ int32 edge_label_id;
+ char* edge_label_str;
+
+ graphid object_graph_id;
+ graphid start_node_graph_id;
+ graphid end_node_graph_id;
+
+ graph_cache_data* graph_cache;
+ label_cache_data* edge_cache;
+
+ agtype* properties = NULL;
+
+ arguments = fcinfo;
+
+ // create two separate complete graphs
+ DirectFunctionCall4(create_complete_graph, arguments->args[0].value,
+ arguments->args[1].value,
+ arguments->args[5].value,
+ arguments->args[3].value);
+ DirectFunctionCall4(create_complete_graph, arguments->args[0].value,
+ arguments->args[1].value,
+ arguments->args[5].value,
+ arguments->args[3].value);
+
+ // Handling remaining arguments
+ /*
+ * graph_name: doesn't need to validate, since the create_complete_graph
+ * function already does that.
+ */
+ graph_name = PG_GETARG_NAME(0);
+ graph_name_str = NameStr(*graph_name);
+ graph_oid = get_graph_oid(graph_name_str);
+
+ /*
+ * int64 bridge_size: currently only stays at zero.
+ * to do: implement bridge with variable number of nodes.
+ */
+ if (PG_ARGISNULL(2) || PG_GETARG_INT32(2) < 0 )
+ {
+ ereport(ERROR, (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+ errmsg("Bridge size must not be NULL or lower than 0")));
+ }
+
+ // node label: if null, gets default label, which is "_ag_label_vertex"
+ if (PG_ARGISNULL(3))
+ {
+ namestrcpy(node_label_name, AG_DEFAULT_LABEL_VERTEX);
+ }
+ else
+ {
+ node_label_name = PG_GETARG_NAME(3);
+ }
+ node_label_str = NameStr(*node_label_name);
+ node_label_id = get_label_id(node_label_str, graph_oid);
+
+ /*
+ * edge_label: doesn't need to validate, since the create_complete_graph
+ * function already does that.
+ */
+ edge_label_name = PG_GETARG_NAME(5);
+ edge_label_str = NameStr(*edge_label_name);
+ edge_label_id = get_label_id(edge_label_str, graph_oid);
+
+ /*
+ * Fetching caches to get next values for graph id's, and access nodes
+ * to be connected with edges.
+ */
+ graph_cache = search_graph_name_cache(graph_name_str);
+ edge_cache = search_label_name_graph_cache(edge_label_str,graph_oid);
+
+ // connect a node from each graph
+ start_node_index = 1; // first created node, from the first complete graph
+ end_node_index = arguments->args[1].value*2; // last created node, second graph
+
+ // next index to be assigned to a node or edge
+ nextval = get_nextval_internal(graph_cache, edge_cache);
+
+ // build the graph id's of the edge to be created
+ object_graph_id = make_graphid(edge_label_id, nextval);
+ start_node_graph_id = make_graphid(node_label_id, start_node_index);
+ end_node_graph_id = make_graphid(node_label_id, end_node_index);
+ properties = create_empty_agtype();
+
+ // connect two nodes
+ insert_edge_simple(graph_oid, edge_label_str,
+ object_graph_id, start_node_graph_id,
+ end_node_graph_id, properties);
+
+ PG_RETURN_VOID();
+}
[age] 02/15: Removed async from function definitions (#680)
Posted by jg...@apache.org.
This is an automated email from the ASF dual-hosted git repository.
jgemignani pushed a commit to branch PG12
in repository https://gitbox.apache.org/repos/asf/age.git
commit f6fccd8c1bed438af5d86e8bd1fa70d871095ca8
Author: Fahad Zaheer <52...@users.noreply.github.com>
AuthorDate: Fri Feb 17 00:41:00 2023 +0500
Removed async from function definitions (#680)
Found async function without any await expressions in NodeJS Driver. Async keyword is used in Asynchronous programming and for the functions that wait for some responses. These functions dont need to be Asynchronous.
---
drivers/nodejs/test/Agtype.test.ts | 8 ++++----
1 file changed, 4 insertions(+), 4 deletions(-)
diff --git a/drivers/nodejs/test/Agtype.test.ts b/drivers/nodejs/test/Agtype.test.ts
index 27f5f4e4..5ae26b54 100644
--- a/drivers/nodejs/test/Agtype.test.ts
+++ b/drivers/nodejs/test/Agtype.test.ts
@@ -20,7 +20,7 @@
import { AGTypeParse } from '../src'
describe('Parsing', () => {
- it('Vertex', async () => {
+ it('Vertex', () => {
expect(
AGTypeParse('{"id": 844424930131969, "label": "Part", "properties": {"part_num": "123", "number": 3141592653589793, "float": 3.141592653589793}}::vertex')
).toStrictEqual(new Map<string, any>(Object.entries({
@@ -34,7 +34,7 @@ describe('Parsing', () => {
})))
})
- it('Edge', async () => {
+ it('Edge', () => {
expect(
AGTypeParse('{"id": 1125899906842625, "label": "used_by", "end_id": 844424930131970, "start_id": 844424930131969, "properties": {"quantity": 1}}::edge')
).toStrictEqual(new Map(Object.entries({
@@ -46,7 +46,7 @@ describe('Parsing', () => {
})))
})
- it('Path', async () => {
+ it('Path', () => {
expect(
AGTypeParse('[{"id": 844424930131969, "label": "Part", "properties": {"part_num": "123"}}::vertex, {"id": 1125899906842625, "label": "used_by", "end_id": 844424930131970, "start_id": 844424930131969, "properties": {"quantity": 1}}::edge, {"id": 844424930131970, "label": "Part", "properties": {"part_num": "345"}}::vertex]::path')
).toStrictEqual([
@@ -70,7 +70,7 @@ describe('Parsing', () => {
])
})
- it('Null Properties', async () => {
+ it('Null Properties', () => {
expect(
AGTypeParse('{"id": 1688849860263937, "label": "car", "properties": {}}::vertex')
).toStrictEqual(new Map<string, any>(Object.entries({
[age] 05/15: Fix WITH ignoring WHERE clause (#646)
Posted by jg...@apache.org.
This is an automated email from the ASF dual-hosted git repository.
jgemignani pushed a commit to branch PG12
in repository https://gitbox.apache.org/repos/asf/age.git
commit ceebd754a193864eb95feecfb95b6a1eb5567bf3
Author: Muhammad Taha Naveed <85...@users.noreply.github.com>
AuthorDate: Sat Feb 25 05:52:21 2023 +0500
Fix WITH ignoring WHERE clause (#646)
* Fix WITH ignoring WHERE clause
- When transforming WITH clause into return clause, WHERE was getting
ignored because that case wasn't handled in tranform_cypher_clause_with_where.
- Had to revert to the previous function header of tranform_cypher_clause_with_where
which had a explicit parameter of where node, because after tranforming cypher_with
into cypher_return, the wrapper clause now has cypher_return and the where node of
cypher_with was lost.
* Fix WITH clause segmentation fault
- When transforming columnrefs in WITH clause, it should only check
referenced Columns in its parsestate or parent parsestate.
- If only expr_kind is WHERE, try to find the columnRef as a transform_entity
and extract the expr. This will happen in case of (MATCH/WHERE).
* Add regression tests for cypher_with
---
regress/expected/cypher_with.out | 353 ++++++++++++++++++++++++++++++++++++-
regress/sql/cypher_with.sql | 211 +++++++++++++++++++++-
src/backend/parser/cypher_clause.c | 57 +++---
src/backend/parser/cypher_expr.c | 16 +-
4 files changed, 582 insertions(+), 55 deletions(-)
diff --git a/regress/expected/cypher_with.out b/regress/expected/cypher_with.out
index 7f5faca3..1166fff3 100644
--- a/regress/expected/cypher_with.out
+++ b/regress/expected/cypher_with.out
@@ -16,8 +16,12 @@
* specific language governing permissions and limitations
* under the License.
*/
+ \! cp -r regress/age_load/data regress/instance/data/age_load
LOAD 'age';
SET search_path TO ag_catalog;
+--
+-- Load data
+--
SELECT create_graph('cypher_with');
NOTICE: graph "cypher_with" has been created
create_graph
@@ -25,31 +29,366 @@ NOTICE: graph "cypher_with" has been created
(1 row)
+SELECT create_vlabel('cypher_with','Country');
+NOTICE: VLabel "Country" has been created
+ create_vlabel
+---------------
+
+(1 row)
+
+SELECT load_labels_from_file('cypher_with', 'Country',
+ 'age_load/countries.csv');
+ load_labels_from_file
+-----------------------
+
+(1 row)
+
+SELECT create_vlabel('cypher_with','City');
+NOTICE: VLabel "City" has been created
+ create_vlabel
+---------------
+
+(1 row)
+
+SELECT load_labels_from_file('cypher_with', 'City',
+ 'age_load/cities.csv');
+ load_labels_from_file
+-----------------------
+
+(1 row)
+
+SELECT create_elabel('cypher_with','has_city');
+NOTICE: ELabel "has_city" has been created
+ create_elabel
+---------------
+
+(1 row)
+
+SELECT load_edges_from_file('cypher_with', 'has_city',
+ 'age_load/edges.csv');
+ load_edges_from_file
+----------------------
+
+(1 row)
+
+--
+-- Test WITH clause
+--
+SELECT count(*) FROM cypher('cypher_with', $$
+ MATCH (m:City)-[:has_city]->(b:Country {iso2 : 'AT'})
+ WITH m,b
+ RETURN m,b
+$$) AS (City agtype, Country agtype);
+ count
+-------
+ 2361
+(1 row)
+
+-- WITH/AS
+SELECT * FROM cypher('cypher_with', $$
+ MATCH (b:Country {iso2 : 'AT'})
+ WITH b.name AS name, id(b) AS id
+ RETURN name,id
+$$) AS (Country agtype, Country_id agtype);
+ country | country_id
+-----------+-----------------
+ "Austria" | 844424930131983
+(1 row)
+
SELECT * FROM cypher('cypher_with', $$
-WITH true AS b
-RETURN b
+ MATCH (m:City {name: 'Zell'})-[]-(b:Country {iso2 : 'AT'})
+ WITH b as country, count(*) AS foaf
+ WHERE foaf > 1
+ RETURN country.name, foaf
+$$) as (name agtype, foaf agtype);
+ name | foaf
+-----------+------
+ "Austria" | 2
+(1 row)
+
+SELECT * FROM cypher('cypher_with', $$
+ WITH true AS b
+ RETURN b
$$) AS (b bool);
b
---
t
(1 row)
+-- WITH/WHERE
+SELECT * FROM cypher('cypher_with', $$
+ MATCH (m:City)-[:has_city]->(b:Country{iso2:'BE'})
+ WITH b,m
+ WHERE m.name='x'
+ RETURN m.name,b.iso2
+$$) AS ( "m.name" agtype, "b" agtype);
+ m.name | b
+--------+---
+(0 rows)
+
+SELECT * FROM cypher('cypher_with', $$
+ MATCH (b:Country {iso2 : 'AT'})
+ WITH b.name AS name, id(b) AS id
+ WHERE name = 'Austria'
+ RETURN name,id
+$$) AS (Country agtype, Country_id agtype);
+ country | country_id
+-----------+-----------------
+ "Austria" | 844424930131983
+(1 row)
+
+SELECT * FROM cypher('cypher_with', $$
+ MATCH (b:Country {iso2 : 'AT'})
+ WITH b.name AS name, id(b) AS id
+ WHERE name = 'Austria' OR name = 'Kosovo'
+ RETURN name,id
+$$) AS (Country agtype, Country_id agtype);
+ country | country_id
+-----------+-----------------
+ "Austria" | 844424930131983
+(1 row)
+
+SELECT * FROM cypher('cypher_with', $$
+ MATCH p = (m:City)-[:has_city*1..2]->(b:Country {iso2 : 'AT'})
+ WITH p, length(p) AS path_length
+ WHERE path_length > 1
+ RETURN p
+$$) AS (pattern agtype);
+ pattern
+---------
+(0 rows)
+
+-- MATCH/WHERE with WITH/WHERE
+SELECT * FROM cypher('cypher_with', $$
+ MATCH (m:City)-[:has_city]->(b:Country {iso2 : 'AT'})
+ WHERE b.name = 'Austria'
+ WITH m.name AS city,b.name AS country
+ WHERE city = 'Vienna'
+ RETURN city,country
+$$) AS (City agtype, Country agtype);
+ city | country
+----------+-----------
+ "Vienna" | "Austria"
+(1 row)
+
+-- WITH/ORDER BY
+SELECT * FROM cypher('cypher_with', $$
+ MATCH (m:City)-[:has_city]->(b:Country {iso2 : 'AT'})
+ WITH m AS city,b AS country
+ ORDER BY id(m) DESC LIMIT 10
+ RETURN id(city),city
+$$) AS (id agtype, city agtype);
+ id | city
+------------------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
+ 1125899906986495 | {"id": 1125899906986495, "label": "City", "properties": {"id": "143871", "name": "Zell", "__id__": 143871, "latitude": "46.47222222", "state_id": "2057", "longitude": "14.38888889", "country_id": "15", "state_code": "2", "country_code": "AT"}}::vertex
+ 1125899906986492 | {"id": 1125899906986492, "label": "City", "properties": {"id": "143868", "name": "Weißenstein", "__id__": 143868, "latitude": "46.68222222", "state_id": "2057", "longitude": "13.72361111", "country_id": "15", "state_code": "2", "country_code": "AT"}}::vertex
+ 1125899906986491 | {"id": 1125899906986491, "label": "City", "properties": {"id": "143867", "name": "Weißensee", "__id__": 143867, "latitude": "46.71666667", "state_id": "2057", "longitude": "13.3", "country_id": "15", "state_code": "2", "country_code": "AT"}}::vertex
+ 1125899906986490 | {"id": 1125899906986490, "label": "City", "properties": {"id": "143866", "name": "Weitensfeld im Gurktal", "__id__": 143866, "latitude": "46.84861111", "state_id": "2057", "longitude": "14.19166667", "country_id": "15", "state_code": "2", "country_code": "AT"}}::vertex
+ 1125899906986489 | {"id": 1125899906986489, "label": "City", "properties": {"id": "143865", "name": "Velden am Wörther See", "__id__": 143865, "latitude": "46.6125", "state_id": "2057", "longitude": "14.04194444", "country_id": "15", "state_code": "2", "country_code": "AT"}}::vertex
+ 1125899906986488 | {"id": 1125899906986488, "label": "City", "properties": {"id": "143864", "name": "Umberg", "__id__": 143864, "latitude": "46.64833", "state_id": "2057", "longitude": "13.9575", "country_id": "15", "state_code": "2", "country_code": "AT"}}::vertex
+ 1125899906986487 | {"id": 1125899906986487, "label": "City", "properties": {"id": "143863", "name": "Tschachoritsch", "__id__": 143863, "latitude": "46.55352", "state_id": "2057", "longitude": "14.21461", "country_id": "15", "state_code": "2", "country_code": "AT"}}::vertex
+ 1125899906986486 | {"id": 1125899906986486, "label": "City", "properties": {"id": "143862", "name": "Treffen", "__id__": 143862, "latitude": "46.66833333", "state_id": "2057", "longitude": "13.85555556", "country_id": "15", "state_code": "2", "country_code": "AT"}}::vertex
+ 1125899906986484 | {"id": 1125899906986484, "label": "City", "properties": {"id": "143860", "name": "Timenitz", "__id__": 143860, "latitude": "46.68333", "state_id": "2057", "longitude": "14.41667", "country_id": "15", "state_code": "2", "country_code": "AT"}}::vertex
+ 1125899906986483 | {"id": 1125899906986483, "label": "City", "properties": {"id": "143859", "name": "Techelsberg", "__id__": 143859, "latitude": "46.65861111", "state_id": "2057", "longitude": "14.10194444", "country_id": "15", "state_code": "2", "country_code": "AT"}}::vertex
+(10 rows)
+
+SELECT * FROM cypher('cypher_with', $$
+ MATCH (m:City)
+ WITH m AS city
+ ORDER BY id(m) ASC LIMIT 10
+ RETURN id(city),city.name
+$$) AS (id agtype, names agtype);
+ id | names
+------------------+-----------------------
+ 1125899906842625 | "Andorra la Vella"
+ 1125899906842626 | "Arinsal"
+ 1125899906842627 | "Canillo"
+ 1125899906842628 | "El Tarter"
+ 1125899906842629 | "Encamp"
+ 1125899906842630 | "Ordino"
+ 1125899906842631 | "Pas de la Casa"
+ 1125899906842632 | "Sant Julià de Lòria"
+ 1125899906842633 | "la Massana"
+ 1125899906842634 | "les Escaldes"
+(10 rows)
+
+-- WITH/ORDER BY/DESC/WHERE
+SELECT * FROM cypher('cypher_with', $$
+ MATCH (m:City)-[:has_city]->(b:Country {iso2 : 'AT'})
+ WITH m AS city,b AS country
+ ORDER BY id(m) DESC LIMIT 10
+ WHERE city.name = 'Zell' OR city.name = 'Umberg'
+ RETURN id(city),city.name,country.name
+$$) AS (id agtype, city agtype, country agtype);
+ id | city | country
+------------------+----------+-----------
+ 1125899906986495 | "Zell" | "Austria"
+ 1125899906986488 | "Umberg" | "Austria"
+(2 rows)
+
+-- multiple WITH clauses
+SELECT * FROM cypher('cypher_with', $$
+ MATCH (m:City)-[:has_city]->(b:Country {iso2 : 'AT'})
+ WITH m AS city,b AS country
+ WITH city LIMIT 10
+ RETURN city.name
+$$) AS (city agtype);
+ city
+---------------------
+ "Andau"
+ "Antau"
+ "Apetlon"
+ "Bad Sauerbrunn"
+ "Bad Tatzmannsdorf"
+ "Badersdorf"
+ "Bernstein"
+ "Bocksdorf"
+ "Breitenbrunn"
+ "Bruckneudorf"
+(10 rows)
+
+SELECT * FROM cypher('cypher_with', $$
+ MATCH (m:City)-[:has_city]->(b:Country {iso2 : 'AT'})
+ WITH m AS city,b AS country
+ ORDER BY id(m) DESC LIMIT 10
+ WITH city
+ WHERE city.name = 'Zell'
+ RETURN id(city),city.name
+$$) AS (id agtype, city agtype);
+ id | city
+------------------+--------
+ 1125899906986495 | "Zell"
+(1 row)
+
+SELECT * FROM cypher('cypher_with', $$
+ MATCH (m:City)-[:has_city]->(b:Country {iso2 : 'AT'})
+ WITH m AS city,b AS country
+ WHERE country.name = 'Austria'
+ WITH city
+ ORDER BY id(city) DESC
+ WHERE city.name = 'Zell'
+ RETURN id(city),city.name
+$$) AS (id agtype, city agtype);
+ id | city
+------------------+--------
+ 1125899906986495 | "Zell"
+ 1125899906846495 | "Zell"
+(2 rows)
+
-- Expression item must be aliased.
SELECT * FROM cypher('cypher_with', $$
-WITH 1 + 1
-RETURN i
+ WITH 1 + 1
+ RETURN i
$$) AS (i int);
ERROR: expression item must be aliased
-LINE 2: WITH 1 + 1
- ^
+LINE 2: WITH 1 + 1
+ ^
HINT: Items can be aliased by using AS.
+SELECT * FROM cypher('cypher_with', $$
+ MATCH (m:City)-[:has_city]->(b:Country {iso2 : 'AT'})
+ WITH id(m)
+ RETURN m
+$$) AS (id agtype, city agtype);
+ERROR: expression item must be aliased
+LINE 3: WITH id(m)
+ ^
+HINT: Items can be aliased by using AS.
+-- Reference undefined variable in WITH clause (should error out)
+SELECT count(*) FROM cypher('cypher_with', $$
+ MATCH (m:City)-[:has_city]->(b:Country {iso2 : 'AT'})
+ WITH m
+ RETURN m,b
+$$) AS (City agtype, Country agtype);
+ERROR: could not find rte for b
+LINE 4: RETURN m,b
+ ^
+SELECT * FROM cypher('cypher_with', $$
+ MATCH (m:City)-[:has_city]->(b:Country {iso2 : 'AT'})
+ WITH m AS city,b AS country
+ WHERE country.name = 'Austria'
+ WITH city
+ WHERE city.name = 'Zell'
+ RETURN id(city),country.name
+$$) AS (id agtype, country agtype);
+ERROR: could not find rte for country
+LINE 7: RETURN id(city),country.name
+ ^
+-- Clean up
SELECT drop_graph('cypher_with', true);
-NOTICE: drop cascades to 2 other objects
+NOTICE: drop cascades to 5 other objects
DETAIL: drop cascades to table cypher_with._ag_label_vertex
drop cascades to table cypher_with._ag_label_edge
+drop cascades to table cypher_with."Country"
+drop cascades to table cypher_with."City"
+drop cascades to table cypher_with.has_city
NOTICE: graph "cypher_with" has been dropped
drop_graph
------------
(1 row)
+-- Issue 329 (should error out)
+SELECT create_graph('graph');
+NOTICE: graph "graph" has been created
+ create_graph
+--------------
+
+(1 row)
+
+SELECT * FROM cypher('graph', $$
+ CREATE (a:A)-[:incs]->(:C), (a)-[:incs]->(:C)
+ RETURN a
+$$) AS (n agtype);
+ n
+-----------------------------------------------------------------
+ {"id": 844424930131969, "label": "A", "properties": {}}::vertex
+(1 row)
+
+SELECT * FROM cypher('graph', $$
+ MATCH (a:A)
+ WHERE ID(a)=0
+ WITH a
+ OPTIONAL MATCH (a)-[:incs]->(c)-[d:incs]-()
+ WITH a,c,COUNT(d) AS deps
+ WHERE deps<=1
+ RETURN c,d
+$$) AS (n agtype, d agtype);
+ERROR: could not find rte for d
+LINE 8: RETURN c,d
+ ^
+-- Issue 396 (should error out)
+SELECT * FROM cypher('graph',$$
+ CREATE (v),(u),(w),
+ (v)-[:hasFriend]->(u),
+ (u)-[:hasFriend]->(w)
+$$) as (a agtype);
+ a
+---
+(0 rows)
+
+SELECT * FROM cypher('graph',$$
+ MATCH p=(v)-[*1..2]->(u)
+ WITH p,length(p) AS path_length
+ RETURN v,path_length
+$$) as (a agtype,b agtype);
+ERROR: could not find rte for v
+LINE 4: RETURN v,path_length
+ ^
+-- Clean up
+SELECT drop_graph('graph', true);
+NOTICE: drop cascades to 6 other objects
+DETAIL: drop cascades to table graph._ag_label_vertex
+drop cascades to table graph._ag_label_edge
+drop cascades to table graph."A"
+drop cascades to table graph.incs
+drop cascades to table graph."C"
+drop cascades to table graph."hasFriend"
+NOTICE: graph "graph" has been dropped
+ drop_graph
+------------
+
+(1 row)
+
+--
+-- End of test
+--
diff --git a/regress/sql/cypher_with.sql b/regress/sql/cypher_with.sql
index 623487b4..95b18c50 100644
--- a/regress/sql/cypher_with.sql
+++ b/regress/sql/cypher_with.sql
@@ -17,20 +17,223 @@
* under the License.
*/
+ \! cp -r regress/age_load/data regress/instance/data/age_load
+
LOAD 'age';
SET search_path TO ag_catalog;
+--
+-- Load data
+--
SELECT create_graph('cypher_with');
+SELECT create_vlabel('cypher_with','Country');
+SELECT load_labels_from_file('cypher_with', 'Country',
+ 'age_load/countries.csv');
+
+SELECT create_vlabel('cypher_with','City');
+SELECT load_labels_from_file('cypher_with', 'City',
+ 'age_load/cities.csv');
+
+SELECT create_elabel('cypher_with','has_city');
+SELECT load_edges_from_file('cypher_with', 'has_city',
+ 'age_load/edges.csv');
+
+--
+-- Test WITH clause
+--
+
+SELECT count(*) FROM cypher('cypher_with', $$
+ MATCH (m:City)-[:has_city]->(b:Country {iso2 : 'AT'})
+ WITH m,b
+ RETURN m,b
+$$) AS (City agtype, Country agtype);
+
+-- WITH/AS
+
SELECT * FROM cypher('cypher_with', $$
-WITH true AS b
-RETURN b
+ MATCH (b:Country {iso2 : 'AT'})
+ WITH b.name AS name, id(b) AS id
+ RETURN name,id
+$$) AS (Country agtype, Country_id agtype);
+
+SELECT * FROM cypher('cypher_with', $$
+ MATCH (m:City {name: 'Zell'})-[]-(b:Country {iso2 : 'AT'})
+ WITH b as country, count(*) AS foaf
+ WHERE foaf > 1
+ RETURN country.name, foaf
+$$) as (name agtype, foaf agtype);
+
+SELECT * FROM cypher('cypher_with', $$
+ WITH true AS b
+ RETURN b
$$) AS (b bool);
+-- WITH/WHERE
+
+SELECT * FROM cypher('cypher_with', $$
+ MATCH (m:City)-[:has_city]->(b:Country{iso2:'BE'})
+ WITH b,m
+ WHERE m.name='x'
+ RETURN m.name,b.iso2
+$$) AS ( "m.name" agtype, "b" agtype);
+
+SELECT * FROM cypher('cypher_with', $$
+ MATCH (b:Country {iso2 : 'AT'})
+ WITH b.name AS name, id(b) AS id
+ WHERE name = 'Austria'
+ RETURN name,id
+$$) AS (Country agtype, Country_id agtype);
+
+SELECT * FROM cypher('cypher_with', $$
+ MATCH (b:Country {iso2 : 'AT'})
+ WITH b.name AS name, id(b) AS id
+ WHERE name = 'Austria' OR name = 'Kosovo'
+ RETURN name,id
+$$) AS (Country agtype, Country_id agtype);
+
+SELECT * FROM cypher('cypher_with', $$
+ MATCH p = (m:City)-[:has_city*1..2]->(b:Country {iso2 : 'AT'})
+ WITH p, length(p) AS path_length
+ WHERE path_length > 1
+ RETURN p
+$$) AS (pattern agtype);
+
+-- MATCH/WHERE with WITH/WHERE
+
+SELECT * FROM cypher('cypher_with', $$
+ MATCH (m:City)-[:has_city]->(b:Country {iso2 : 'AT'})
+ WHERE b.name = 'Austria'
+ WITH m.name AS city,b.name AS country
+ WHERE city = 'Vienna'
+ RETURN city,country
+$$) AS (City agtype, Country agtype);
+
+-- WITH/ORDER BY
+
+SELECT * FROM cypher('cypher_with', $$
+ MATCH (m:City)-[:has_city]->(b:Country {iso2 : 'AT'})
+ WITH m AS city,b AS country
+ ORDER BY id(m) DESC LIMIT 10
+ RETURN id(city),city
+$$) AS (id agtype, city agtype);
+
+SELECT * FROM cypher('cypher_with', $$
+ MATCH (m:City)
+ WITH m AS city
+ ORDER BY id(m) ASC LIMIT 10
+ RETURN id(city),city.name
+$$) AS (id agtype, names agtype);
+
+-- WITH/ORDER BY/DESC/WHERE
+
+SELECT * FROM cypher('cypher_with', $$
+ MATCH (m:City)-[:has_city]->(b:Country {iso2 : 'AT'})
+ WITH m AS city,b AS country
+ ORDER BY id(m) DESC LIMIT 10
+ WHERE city.name = 'Zell' OR city.name = 'Umberg'
+ RETURN id(city),city.name,country.name
+$$) AS (id agtype, city agtype, country agtype);
+
+-- multiple WITH clauses
+
+SELECT * FROM cypher('cypher_with', $$
+ MATCH (m:City)-[:has_city]->(b:Country {iso2 : 'AT'})
+ WITH m AS city,b AS country
+ WITH city LIMIT 10
+ RETURN city.name
+$$) AS (city agtype);
+
+SELECT * FROM cypher('cypher_with', $$
+ MATCH (m:City)-[:has_city]->(b:Country {iso2 : 'AT'})
+ WITH m AS city,b AS country
+ ORDER BY id(m) DESC LIMIT 10
+ WITH city
+ WHERE city.name = 'Zell'
+ RETURN id(city),city.name
+$$) AS (id agtype, city agtype);
+
+SELECT * FROM cypher('cypher_with', $$
+ MATCH (m:City)-[:has_city]->(b:Country {iso2 : 'AT'})
+ WITH m AS city,b AS country
+ WHERE country.name = 'Austria'
+ WITH city
+ ORDER BY id(city) DESC
+ WHERE city.name = 'Zell'
+ RETURN id(city),city.name
+$$) AS (id agtype, city agtype);
+
-- Expression item must be aliased.
+
SELECT * FROM cypher('cypher_with', $$
-WITH 1 + 1
-RETURN i
+ WITH 1 + 1
+ RETURN i
$$) AS (i int);
+SELECT * FROM cypher('cypher_with', $$
+ MATCH (m:City)-[:has_city]->(b:Country {iso2 : 'AT'})
+ WITH id(m)
+ RETURN m
+$$) AS (id agtype, city agtype);
+
+-- Reference undefined variable in WITH clause (should error out)
+
+SELECT count(*) FROM cypher('cypher_with', $$
+ MATCH (m:City)-[:has_city]->(b:Country {iso2 : 'AT'})
+ WITH m
+ RETURN m,b
+$$) AS (City agtype, Country agtype);
+
+SELECT * FROM cypher('cypher_with', $$
+ MATCH (m:City)-[:has_city]->(b:Country {iso2 : 'AT'})
+ WITH m AS city,b AS country
+ WHERE country.name = 'Austria'
+ WITH city
+ WHERE city.name = 'Zell'
+ RETURN id(city),country.name
+$$) AS (id agtype, country agtype);
+
+-- Clean up
+
SELECT drop_graph('cypher_with', true);
+
+-- Issue 329 (should error out)
+
+SELECT create_graph('graph');
+
+SELECT * FROM cypher('graph', $$
+ CREATE (a:A)-[:incs]->(:C), (a)-[:incs]->(:C)
+ RETURN a
+$$) AS (n agtype);
+
+SELECT * FROM cypher('graph', $$
+ MATCH (a:A)
+ WHERE ID(a)=0
+ WITH a
+ OPTIONAL MATCH (a)-[:incs]->(c)-[d:incs]-()
+ WITH a,c,COUNT(d) AS deps
+ WHERE deps<=1
+ RETURN c,d
+$$) AS (n agtype, d agtype);
+
+-- Issue 396 (should error out)
+
+SELECT * FROM cypher('graph',$$
+ CREATE (v),(u),(w),
+ (v)-[:hasFriend]->(u),
+ (u)-[:hasFriend]->(w)
+$$) as (a agtype);
+
+SELECT * FROM cypher('graph',$$
+ MATCH p=(v)-[*1..2]->(u)
+ WITH p,length(p) AS path_length
+ RETURN v,path_length
+$$) as (a agtype,b agtype);
+
+-- Clean up
+
+SELECT drop_graph('graph', true);
+
+--
+-- End of test
+--
diff --git a/src/backend/parser/cypher_clause.c b/src/backend/parser/cypher_clause.c
index 98bfa4d4..226284c4 100644
--- a/src/backend/parser/cypher_clause.c
+++ b/src/backend/parser/cypher_clause.c
@@ -123,7 +123,8 @@ static Query *transform_cypher_with(cypher_parsestate *cpstate,
cypher_clause *clause);
static Query *transform_cypher_clause_with_where(cypher_parsestate *cpstate,
transform_method transform,
- cypher_clause *clause);
+ cypher_clause *clause,
+ Node *where);
// match clause
static Query *transform_cypher_match(cypher_parsestate *cpstate,
cypher_clause *clause);
@@ -1076,7 +1077,7 @@ static Query * transform_cypher_call_stmt(cypher_parsestate *cpstate,
return transform_cypher_clause_with_where(cpstate,
transform_cypher_call_subquery,
- clause);
+ clause, self->where);
}
return NULL;
@@ -2203,7 +2204,7 @@ static Query *transform_cypher_with(cypher_parsestate *cpstate,
wrapper->prev = clause->prev;
return transform_cypher_clause_with_where(cpstate, transform_cypher_return,
- wrapper);
+ wrapper, self->where);
}
static bool match_check_valid_label(cypher_match *match,
@@ -2263,28 +2264,14 @@ static bool match_check_valid_label(cypher_match *match,
return true;
}
-
static Query *transform_cypher_clause_with_where(cypher_parsestate *cpstate,
transform_method transform,
- cypher_clause *clause)
+ cypher_clause *clause, Node *where)
{
ParseState *pstate = (ParseState *)cpstate;
Query *query;
Node *self = clause->self;
- cypher_match *match_self;
- cypher_call *call_self;
- Node *where;
-
- if (is_ag_node(self, cypher_call))
- {
- call_self = (cypher_call*) clause->self;
- where = call_self->where;
- }
- else
- {
- match_self = (cypher_match*) clause->self;
- where = match_self->where;
- }
+ Node *where_qual = NULL;
if (where)
{
@@ -2299,32 +2286,27 @@ static Query *transform_cypher_clause_with_where(cypher_parsestate *cpstate,
rtindex = list_length(pstate->p_rtable);
Assert(rtindex == 1); // rte is the only RangeTblEntry in pstate
+ /*
+ * add all the target entries in rte to the current target list to pass
+ * all the variables that are introduced in the previous clause to the
+ * next clause
+ */
query->targetList = expandRelAttrs(pstate, rte, rtindex, 0, -1);
markTargetListOrigins(pstate, query->targetList);
query->rtable = pstate->p_rtable;
- if (is_ag_node(clause->self, cypher_call))
+ if (!is_ag_node(self, cypher_match))
{
- cypher_call *call = (cypher_call *)clause->self;
-
- if (call->where != NULL)
- {
- Expr *where_qual = NULL;
-
- where_qual = (Expr *)transform_cypher_expr(cpstate, call->where,
- EXPR_KIND_WHERE);
+ where_qual = transform_cypher_expr(cpstate, where,
+ EXPR_KIND_WHERE);
- where_qual = (Expr *)coerce_to_boolean(pstate, (Node *)where_qual,
- "WHERE");
- query->jointree = makeFromExpr(pstate->p_joinlist, (Node *)where_qual);
- }
- }
- else
- {
- query->jointree = makeFromExpr(pstate->p_joinlist, NULL);
+ where_qual = coerce_to_boolean(pstate, where_qual,
+ "WHERE");
}
+
+ query->jointree = makeFromExpr(pstate->p_joinlist, where_qual);
assign_query_collations(pstate, query);
}
else
@@ -2361,7 +2343,8 @@ static Query *transform_cypher_match(cypher_parsestate *cpstate,
}
return transform_cypher_clause_with_where(
- cpstate, transform_cypher_match_pattern, clause);
+ cpstate, transform_cypher_match_pattern, clause,
+ match_self->where);
}
/*
diff --git a/src/backend/parser/cypher_expr.c b/src/backend/parser/cypher_expr.c
index c00ca846..00261dd1 100644
--- a/src/backend/parser/cypher_expr.c
+++ b/src/backend/parser/cypher_expr.c
@@ -319,16 +319,18 @@ static Node *transform_ColumnRef(cypher_parsestate *cpstate, ColumnRef *cref)
}
/*
- * Try to find the columnRef as a transform_entity and extract
- * the expr.
+ * If expr_kind is WHERE, Try to find the columnRef as a
+ * transform_entity and extract the expr.
*/
- te = find_variable(cpstate, colname) ;
- if (te != NULL && te->expr != NULL)
+ if (pstate->p_expr_kind == EXPR_KIND_WHERE)
{
- node = (Node *)te->expr;
- break;
+ te = find_variable(cpstate, colname) ;
+ if (te != NULL && te->expr != NULL)
+ {
+ node = (Node *)te->expr;
+ break;
+ }
}
-
/*
* Not known as a column of any range-table entry.
* Try to find the name as a relation. Note that only
[age] 01/15: Updated Readme for drivers folder (#642)
Posted by jg...@apache.org.
This is an automated email from the ASF dual-hosted git repository.
jgemignani pushed a commit to branch PG12
in repository https://gitbox.apache.org/repos/asf/age.git
commit 888d0fa8d5471ade62f75430f3a790908396d2c1
Author: fatima <63...@users.noreply.github.com>
AuthorDate: Thu Feb 16 00:16:22 2023 +0000
Updated Readme for drivers folder (#642)
* Updated Readme for drivers folder and added important links related to
Apache-AGE project which were included in other readme files.
* Updated NodeJS driver readme and added important links of Apache-AGE
project as included in other readme files.
---
drivers/README | 5 +++++
drivers/nodejs/README.md | 4 ++++
2 files changed, 9 insertions(+)
diff --git a/drivers/README b/drivers/README
index 2026129f..19f34d8b 100644
--- a/drivers/README
+++ b/drivers/README
@@ -16,3 +16,8 @@
# under the License.
This folder contains drivers for specific languages
+
+### For more information about [Apache AGE](https://age.apache.org/)
+* Apache Age : https://age.apache.org/
+* Github : https://github.com/apache/age
+* Document : https://age.apache.org/age-manual/master/index.html
diff --git a/drivers/nodejs/README.md b/drivers/nodejs/README.md
index f2d4228c..c1563220 100644
--- a/drivers/nodejs/README.md
+++ b/drivers/nodejs/README.md
@@ -46,3 +46,7 @@ const results: QueryResultRow = await client?.query<QueryResultRow>(`
$$) as (a agtype);
`)!
```
+### For more information about [Apache AGE](https://age.apache.org/)
+* Apache Age : https://age.apache.org/
+* Github : https://github.com/apache/age
+* Document : https://age.apache.org/age-manual/master/index.html
[age] 06/15: Implement isEmpty() predicate function (#710)
Posted by jg...@apache.org.
This is an automated email from the ASF dual-hosted git repository.
jgemignani pushed a commit to branch PG12
in repository https://gitbox.apache.org/repos/asf/age.git
commit fb9547246faf4c9d1f2230af94d3b29affb3aa52
Author: Muhammad Taha Naveed <85...@users.noreply.github.com>
AuthorDate: Tue Feb 28 01:23:31 2023 +0500
Implement isEmpty() predicate function (#710)
- Added isEmpty() predicate function to check if a List, map or string is
empty.
- Added regression tests for isEmpty() function.
Co-authored-by: John Gemignani <jr...@gmail.com>
---
age--1.1.1.sql | 8 ++
regress/expected/catalog.out | 2 +-
regress/expected/cypher_match.out | 160 ++++++++++++++++++++++++++++++++++++++
regress/sql/cypher_match.sql | 97 +++++++++++++++++++++++
src/backend/utils/adt/agtype.c | 81 +++++++++++++++++++
5 files changed, 347 insertions(+), 1 deletion(-)
diff --git a/age--1.1.1.sql b/age--1.1.1.sql
index e9aceffe..304d27db 100644
--- a/age--1.1.1.sql
+++ b/age--1.1.1.sql
@@ -3555,6 +3555,14 @@ STABLE
PARALLEL SAFE
AS 'MODULE_PATHNAME';
+CREATE FUNCTION ag_catalog.age_isempty(agtype)
+RETURNS boolean
+LANGUAGE c
+IMMUTABLE
+RETURNS NULL ON NULL INPUT
+PARALLEL SAFE
+AS 'MODULE_PATHNAME';
+
CREATE FUNCTION ag_catalog.age_label(agtype)
RETURNS agtype
LANGUAGE c
diff --git a/regress/expected/catalog.out b/regress/expected/catalog.out
index 1e62cf9e..c7d5cc3a 100644
--- a/regress/expected/catalog.out
+++ b/regress/expected/catalog.out
@@ -31,7 +31,7 @@ NOTICE: graph "graph" has been created
SELECT * FROM ag_graph WHERE name = 'graph';
graphid | name | namespace
---------+-------+-----------
- 17593 | graph | graph
+ 17595 | graph | graph
(1 row)
-- create a label to test drop_label()
diff --git a/regress/expected/cypher_match.out b/regress/expected/cypher_match.out
index 6ff9a3c6..9ec4112d 100644
--- a/regress/expected/cypher_match.out
+++ b/regress/expected/cypher_match.out
@@ -816,6 +816,166 @@ AS (u agtype);
ERROR: syntax error at or near ")"
LINE 2: $$MATCH (u) WHERE EXISTS(u) RETURN u$$)
^
+--
+-- MATCH with WHERE isEmpty(property)
+--
+SELECT create_graph('for_isEmpty');
+NOTICE: graph "for_isEmpty" has been created
+ create_graph
+--------------
+
+(1 row)
+
+-- Create vertices
+SELECT * FROM cypher('for_isEmpty',
+ $$CREATE (u:for_pred {id:1, type: "empty", list: [], map: {}, string: ""}),
+ (v:for_pred {id:2, type: "filled", list: [1], map: {a:1}, string: "a"}),
+ (w:for_pred)$$)
+AS (a agtype);
+ a
+---
+(0 rows)
+
+-- Match vertices with empty properties
+SELECT * FROM cypher('for_isEmpty',
+ $$MATCH (u:for_pred) WHERE isEmpty(u.list) RETURN properties(u) $$)
+AS (u agtype);
+ u
+-----------------------------------------------------------------
+ {"id": 1, "map": {}, "list": [], "type": "empty", "string": ""}
+(1 row)
+
+SELECT * FROM cypher('for_isEmpty',
+ $$MATCH (u:for_pred) WHERE isEmpty(u.map) RETURN properties(u) $$)
+AS (u agtype);
+ u
+-----------------------------------------------------------------
+ {"id": 1, "map": {}, "list": [], "type": "empty", "string": ""}
+(1 row)
+
+SELECT * FROM cypher('for_isEmpty',
+ $$MATCH (u:for_pred) WHERE isEmpty(u.string) RETURN properties(u) $$)
+AS (u agtype);
+ u
+-----------------------------------------------------------------
+ {"id": 1, "map": {}, "list": [], "type": "empty", "string": ""}
+(1 row)
+
+-- Match vertices with non-empty properties
+SELECT * FROM cypher('for_isEmpty',
+ $$MATCH (u:for_pred) WHERE NOT isEmpty(u.list) RETURN properties(u) $$)
+AS (u agtype);
+ u
+--------------------------------------------------------------------------
+ {"id": 2, "map": {"a": 1}, "list": [1], "type": "filled", "string": "a"}
+(1 row)
+
+SELECT * FROM cypher('for_isEmpty',
+ $$MATCH (u:for_pred) WHERE NOT isEmpty(u.map) RETURN properties(u) $$)
+AS (u agtype);
+ u
+--------------------------------------------------------------------------
+ {"id": 2, "map": {"a": 1}, "list": [1], "type": "filled", "string": "a"}
+(1 row)
+
+SELECT * FROM cypher('for_isEmpty',
+ $$MATCH (u:for_pred) WHERE NOT isEmpty(u.string) RETURN properties(u) $$)
+AS (u agtype);
+ u
+--------------------------------------------------------------------------
+ {"id": 2, "map": {"a": 1}, "list": [1], "type": "filled", "string": "a"}
+(1 row)
+
+-- Match vertices with no properties
+SELECT * FROM cypher('for_isEmpty',
+ $$MATCH (u:for_pred) WHERE isEmpty(properties(u)) RETURN properties(u) $$)
+AS (u agtype);
+ u
+----
+ {}
+(1 row)
+
+-- Match vertices with properties
+SELECT * FROM cypher('for_isEmpty',
+ $$MATCH (u:for_pred) WHERE NOT isEmpty(properties(u)) RETURN properties(u) $$)
+AS (u agtype);
+ u
+--------------------------------------------------------------------------
+ {"id": 1, "map": {}, "list": [], "type": "empty", "string": ""}
+ {"id": 2, "map": {"a": 1}, "list": [1], "type": "filled", "string": "a"}
+(2 rows)
+
+-- Match vertices with null property (should return nothing since WHERE null)
+SELECT * FROM cypher('for_isEmpty',
+ $$MATCH (u:for_pred) WHERE isEmpty(u.tree) RETURN properties(u) $$)
+AS (u agtype);
+ u
+---
+(0 rows)
+
+-- Match and Return bool
+SELECT * FROM cypher('for_isEmpty',
+ $$MATCH (u:for_pred) WHERE isEmpty(u.list) RETURN isEmpty(u.list), u.type $$)
+AS (b agtype, type agtype);
+ b | type
+------+---------
+ true | "empty"
+(1 row)
+
+SELECT * FROM cypher('for_isEmpty',
+ $$MATCH (u:for_pred) WHERE NOT isEmpty(u.list) RETURN isEmpty(u.list), u.type $$)
+AS (b agtype, type agtype);
+ b | type
+-------+----------
+ false | "filled"
+(1 row)
+
+-- Return null on null
+SELECT * FROM cypher('for_isEmpty',
+ $$MATCH (u:for_pred) RETURN isEmpty(u.tree) $$)
+AS (b agtype);
+ b
+---
+
+
+
+(3 rows)
+
+-- Should give an error
+SELECT * FROM cypher('for_isEmpty',
+ $$MATCH (u:for_pred) WHERE isEmpty(u) RETURN properties(u) $$)
+AS (u agtype);
+ERROR: isEmpty() unsupported argument, expected a List, Map, or String
+SELECT * FROM cypher('for_isEmpty',
+ $$MATCH (u:for_pred) WHERE isEmpty(1) RETURN properties(u) $$)
+AS (u agtype);
+ERROR: isEmpty() unsupported argument, expected a List, Map, or String
+SELECT * FROM cypher('for_isEmpty',
+ $$MATCH (u:for_pred) WHERE isEmpty(1,2,3) RETURN properties(u) $$)
+AS (u agtype);
+ERROR: function ag_catalog.age_isempty(agtype, agtype, agtype) does not exist
+LINE 2: $$MATCH (u:for_pred) WHERE isEmpty(1,2,3) RETURN properties...
+ ^
+HINT: No function matches the given name and argument types. You might need to add explicit type casts.
+SELECT * FROM cypher('for_isEmpty',
+ $$MATCH (u:for_pred) WHERE isEmpty() RETURN properties(u) $$)
+AS (u agtype);
+ERROR: function ag_catalog.age_isempty() does not exist
+LINE 2: $$MATCH (u:for_pred) WHERE isEmpty() RETURN properties(u) $...
+ ^
+HINT: No function matches the given name and argument types. You might need to add explicit type casts.
+-- clean up
+SELECT drop_graph('for_isEmpty', true);
+NOTICE: drop cascades to 3 other objects
+DETAIL: drop cascades to table "for_isEmpty"._ag_label_vertex
+drop cascades to table "for_isEmpty"._ag_label_edge
+drop cascades to table "for_isEmpty".for_pred
+NOTICE: graph "for_isEmpty" has been dropped
+ drop_graph
+------------
+
+(1 row)
+
--
--Distinct
--
diff --git a/regress/sql/cypher_match.sql b/regress/sql/cypher_match.sql
index c4c48bf7..aeed9f38 100644
--- a/regress/sql/cypher_match.sql
+++ b/regress/sql/cypher_match.sql
@@ -415,6 +415,103 @@ SELECT * FROM cypher('cypher_match',
$$MATCH (u) WHERE EXISTS(u) RETURN u$$)
AS (u agtype);
+--
+-- MATCH with WHERE isEmpty(property)
+--
+
+SELECT create_graph('for_isEmpty');
+
+-- Create vertices
+
+SELECT * FROM cypher('for_isEmpty',
+ $$CREATE (u:for_pred {id:1, type: "empty", list: [], map: {}, string: ""}),
+ (v:for_pred {id:2, type: "filled", list: [1], map: {a:1}, string: "a"}),
+ (w:for_pred)$$)
+AS (a agtype);
+
+-- Match vertices with empty properties
+
+SELECT * FROM cypher('for_isEmpty',
+ $$MATCH (u:for_pred) WHERE isEmpty(u.list) RETURN properties(u) $$)
+AS (u agtype);
+
+SELECT * FROM cypher('for_isEmpty',
+ $$MATCH (u:for_pred) WHERE isEmpty(u.map) RETURN properties(u) $$)
+AS (u agtype);
+
+SELECT * FROM cypher('for_isEmpty',
+ $$MATCH (u:for_pred) WHERE isEmpty(u.string) RETURN properties(u) $$)
+AS (u agtype);
+
+-- Match vertices with non-empty properties
+
+SELECT * FROM cypher('for_isEmpty',
+ $$MATCH (u:for_pred) WHERE NOT isEmpty(u.list) RETURN properties(u) $$)
+AS (u agtype);
+
+SELECT * FROM cypher('for_isEmpty',
+ $$MATCH (u:for_pred) WHERE NOT isEmpty(u.map) RETURN properties(u) $$)
+AS (u agtype);
+
+SELECT * FROM cypher('for_isEmpty',
+ $$MATCH (u:for_pred) WHERE NOT isEmpty(u.string) RETURN properties(u) $$)
+AS (u agtype);
+
+-- Match vertices with no properties
+
+SELECT * FROM cypher('for_isEmpty',
+ $$MATCH (u:for_pred) WHERE isEmpty(properties(u)) RETURN properties(u) $$)
+AS (u agtype);
+
+-- Match vertices with properties
+
+SELECT * FROM cypher('for_isEmpty',
+ $$MATCH (u:for_pred) WHERE NOT isEmpty(properties(u)) RETURN properties(u) $$)
+AS (u agtype);
+
+-- Match vertices with null property (should return nothing since WHERE null)
+
+SELECT * FROM cypher('for_isEmpty',
+ $$MATCH (u:for_pred) WHERE isEmpty(u.tree) RETURN properties(u) $$)
+AS (u agtype);
+
+-- Match and Return bool
+
+SELECT * FROM cypher('for_isEmpty',
+ $$MATCH (u:for_pred) WHERE isEmpty(u.list) RETURN isEmpty(u.list), u.type $$)
+AS (b agtype, type agtype);
+
+SELECT * FROM cypher('for_isEmpty',
+ $$MATCH (u:for_pred) WHERE NOT isEmpty(u.list) RETURN isEmpty(u.list), u.type $$)
+AS (b agtype, type agtype);
+
+-- Return null on null
+
+SELECT * FROM cypher('for_isEmpty',
+ $$MATCH (u:for_pred) RETURN isEmpty(u.tree) $$)
+AS (b agtype);
+
+-- Should give an error
+
+SELECT * FROM cypher('for_isEmpty',
+ $$MATCH (u:for_pred) WHERE isEmpty(u) RETURN properties(u) $$)
+AS (u agtype);
+
+SELECT * FROM cypher('for_isEmpty',
+ $$MATCH (u:for_pred) WHERE isEmpty(1) RETURN properties(u) $$)
+AS (u agtype);
+
+SELECT * FROM cypher('for_isEmpty',
+ $$MATCH (u:for_pred) WHERE isEmpty(1,2,3) RETURN properties(u) $$)
+AS (u agtype);
+
+SELECT * FROM cypher('for_isEmpty',
+ $$MATCH (u:for_pred) WHERE isEmpty() RETURN properties(u) $$)
+AS (u agtype);
+
+-- clean up
+SELECT drop_graph('for_isEmpty', true);
+
--
--Distinct
--
diff --git a/src/backend/utils/adt/agtype.c b/src/backend/utils/adt/agtype.c
index 7c745faa..4aa724c2 100644
--- a/src/backend/utils/adt/agtype.c
+++ b/src/backend/utils/adt/agtype.c
@@ -5516,6 +5516,87 @@ Datum age_exists(PG_FUNCTION_ARGS)
PG_RETURN_BOOL(true);
}
+PG_FUNCTION_INFO_V1(age_isempty);
+/*
+ * Executor function for isEmpty(property).
+ */
+
+Datum age_isempty(PG_FUNCTION_ARGS)
+{
+ Datum *args;
+ Datum arg;
+ bool *nulls;
+ Oid *types;
+ char *string = NULL;
+ Oid type;
+ int64 result;
+
+ /* extract argument values */
+ extract_variadic_args(fcinfo, 0, true, &args, &types, &nulls);
+
+ /*
+ * isEmpty() supports cstring, text, or the agtype string or list input
+ */
+ arg = args[0];
+ type = types[0];
+
+ if (type == CSTRINGOID)
+ {
+ string = DatumGetCString(arg);
+ result = strlen(string);
+ }
+ else if (type == TEXTOID)
+ {
+ string = text_to_cstring(DatumGetTextPP(arg));
+ result = strlen(string);
+ }
+ else if (type == AGTYPEOID)
+ {
+ agtype *agt_arg;
+
+ /* get the agtype argument */
+ agt_arg = DATUM_GET_AGTYPE_P(arg);
+
+ if (AGT_ROOT_IS_SCALAR(agt_arg))
+ {
+ agtype_value *agtv_value;
+
+ agtv_value = get_ith_agtype_value_from_container(&agt_arg->root, 0);
+
+ if (agtv_value->type == AGTV_STRING)
+ {
+ result = agtv_value->val.string.len;
+ }
+ else
+ {
+ ereport(ERROR, (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+ errmsg("isEmpty() unsupported argument, expected a List, Map, or String")));
+ }
+ }
+ else if (AGT_ROOT_IS_ARRAY(agt_arg))
+ {
+ result = AGT_ROOT_COUNT(agt_arg);
+ }
+ else if (AGT_ROOT_IS_OBJECT(agt_arg))
+ {
+ result = AGT_ROOT_COUNT(agt_arg);
+ }
+ else
+ {
+ ereport(ERROR, (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+ errmsg("isEmpty() unsupported argument, expected a List, Map, or String")));
+ }
+ }
+ else
+ {
+ ereport(ERROR, (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+ errmsg("isEmpty() unsupported argument, expected a List, Map, or String")));
+ }
+
+ /* build the result */
+ PG_RETURN_BOOL(result == 0);
+}
+
PG_FUNCTION_INFO_V1(age_label);
/*
* Executor function for label(edge/vertex).
[age] 11/15: Fix issue #733 - create_complete_graph() terminates the server (#734)
Posted by jg...@apache.org.
This is an automated email from the ASF dual-hosted git repository.
jgemignani pushed a commit to branch PG12
in repository https://gitbox.apache.org/repos/asf/age.git
commit 9fefd3659ed1d29445d009d094013e9179299400
Author: Muhammad Taha Naveed <mn...@seecs.edu.pk>
AuthorDate: Tue Mar 7 03:26:44 2023 +0500
Fix issue #733 - create_complete_graph() terminates the server (#734)
- A vertex and an edge cannot have the same label within the same
graph, so added a check for that.
- insert_simple_vertex() and insert_simple_edge() functions were not
checking for the kind of label. Since a vertex cannot be
inserted into a label relation of edge and vice versa, this was
causing the server termination. Added a check for that.
- Added regression tests.
Conflicts:
src/backend/utils/graph_generation.c
src/include/catalog/ag_label.h
Co-authored-by: John Gemignani <jr...@gmail.com>
---
regress/expected/graph_generation.out | 8 ++++++++
regress/sql/graph_generation.sql | 8 ++++++++
src/backend/catalog/ag_label.c | 15 +++++++++++++++
src/backend/utils/graph_generation.c | 15 +++++++++++++--
src/backend/utils/load/age_load.c | 15 ++++++++++++++-
src/include/catalog/ag_label.h | 2 ++
6 files changed, 60 insertions(+), 3 deletions(-)
diff --git a/regress/expected/graph_generation.out b/regress/expected/graph_generation.out
index b9ed65b1..235052a0 100644
--- a/regress/expected/graph_generation.out
+++ b/regress/expected/graph_generation.out
@@ -80,12 +80,17 @@ NOTICE: ELabel "edges" has been created
(1 row)
+-- SHOULD FAIL
SELECT * FROM create_complete_graph('gp3',5, NULL);
ERROR: edge label can not be NULL
SELECT * FROM create_complete_graph('gp4',NULL,NULL);
ERROR: number of nodes can not be NULL
SELECT * FROM create_complete_graph(NULL,NULL,NULL);
ERROR: graph name can not be NULL
+-- Should error out because same labels are used for both vertices and edges
+SELECT * FROM create_complete_graph('gp5',5,'label','label');
+ERROR: vertex and edge label can not be same
+-- DROPPING GRAPHS
SELECT drop_graph('gp1', true);
NOTICE: drop cascades to 4 other objects
DETAIL: drop cascades to table gp1._ag_label_vertex
@@ -195,6 +200,9 @@ SELECT * FROM age_create_barbell_graph('gp4',NULL,0,'vertices',NULL,'edges',NULL
ERROR: Graph size cannot be NULL or lower than 3
SELECT * FROM age_create_barbell_graph('gp5',5,0,'vertices',NULL,NULL,NULL);
ERROR: edge label can not be NULL
+-- Should error out because same labels are used for both vertices and edges
+SELECT * FROM age_create_barbell_graph('gp6',5,10,'label',NULL,'label',NULL);
+ERROR: vertex and edge label can not be same
-- DROPPING GRAPHS
SELECT drop_graph('gp1', true);
NOTICE: drop cascades to 4 other objects
diff --git a/regress/sql/graph_generation.sql b/regress/sql/graph_generation.sql
index 32fa56dd..e9ee8ea8 100644
--- a/regress/sql/graph_generation.sql
+++ b/regress/sql/graph_generation.sql
@@ -36,12 +36,17 @@ SELECT COUNT(*) FROM gp1."vertices";
SELECT * FROM create_complete_graph('gp2',5,'edges');
+-- SHOULD FAIL
SELECT * FROM create_complete_graph('gp3',5, NULL);
SELECT * FROM create_complete_graph('gp4',NULL,NULL);
SELECT * FROM create_complete_graph(NULL,NULL,NULL);
+-- Should error out because same labels are used for both vertices and edges
+SELECT * FROM create_complete_graph('gp5',5,'label','label');
+
+-- DROPPING GRAPHS
SELECT drop_graph('gp1', true);
SELECT drop_graph('gp2', true);
@@ -68,6 +73,9 @@ SELECT * FROM age_create_barbell_graph('gp3',5,NULL,'vertices',NULL,'edges',NULL
SELECT * FROM age_create_barbell_graph('gp4',NULL,0,'vertices',NULL,'edges',NULL);
SELECT * FROM age_create_barbell_graph('gp5',5,0,'vertices',NULL,NULL,NULL);
+-- Should error out because same labels are used for both vertices and edges
+SELECT * FROM age_create_barbell_graph('gp6',5,10,'label',NULL,'label',NULL);
+
-- DROPPING GRAPHS
SELECT drop_graph('gp1', true);
SELECT drop_graph('gp2', true);
diff --git a/src/backend/catalog/ag_label.c b/src/backend/catalog/ag_label.c
index 768d63d2..9cd89235 100644
--- a/src/backend/catalog/ag_label.c
+++ b/src/backend/catalog/ag_label.c
@@ -159,6 +159,21 @@ char *get_label_relation_name(const char *label_name, Oid graph_oid)
return get_rel_name(get_label_relation(label_name, graph_oid));
}
+char get_label_kind(const char *label_name, Oid label_graph)
+{
+ label_cache_data *cache_data;
+
+ cache_data = search_label_name_graph_cache(label_name, label_graph);
+ if (cache_data)
+ {
+ return cache_data->kind;
+ }
+ else
+ {
+ return INVALID_LABEL_ID;
+ }
+}
+
PG_FUNCTION_INFO_V1(_label_name);
/*
diff --git a/src/backend/utils/graph_generation.c b/src/backend/utils/graph_generation.c
index e4672ea2..ca12e9c5 100644
--- a/src/backend/utils/graph_generation.c
+++ b/src/backend/utils/graph_generation.c
@@ -141,6 +141,19 @@ Datum create_complete_graph(PG_FUNCTION_ARGS)
vtx_name_str = AG_DEFAULT_LABEL_VERTEX;
edge_name_str = NameStr(*edge_label_name);
+ if (!PG_ARGISNULL(3))
+ {
+ vtx_label_name = PG_GETARG_NAME(3);
+ vtx_name_str = NameStr(*vtx_label_name);
+
+ // Check if vertex and edge label are same
+ if (strcmp(vtx_name_str, edge_name_str) == 0)
+ {
+ ereport(ERROR, (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+ errmsg("vertex and edge label can not be same")));
+ }
+ }
+
if (!graph_exists(graph_name_str))
{
DirectFunctionCall1(create_graph, CStringGetDatum(graph_name));
@@ -150,8 +163,6 @@ Datum create_complete_graph(PG_FUNCTION_ARGS)
if (!PG_ARGISNULL(3))
{
- vtx_label_name = PG_GETARG_NAME(3);
- vtx_name_str = NameStr(*vtx_label_name);
// Check if label with the input name already exists
if (!label_exists(vtx_name_str, graph_id))
{
diff --git a/src/backend/utils/load/age_load.c b/src/backend/utils/load/age_load.c
index ff64e1fa..bc64db32 100644
--- a/src/backend/utils/load/age_load.c
+++ b/src/backend/utils/load/age_load.c
@@ -127,6 +127,12 @@ void insert_edge_simple(Oid graph_oid, char *label_name, graphid edge_id,
Relation label_relation;
HeapTuple tuple;
+ // Check if label provided exists as vertex label, then throw error
+ if (get_label_kind(label_name, graph_oid) == LABEL_KIND_VERTEX)
+ {
+ ereport(ERROR, (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+ errmsg("label %s already exists as vertex label", label_name)));
+ }
values[0] = GRAPHID_GET_DATUM(edge_id);
values[1] = GRAPHID_GET_DATUM(start_id);
@@ -153,6 +159,13 @@ void insert_vertex_simple(Oid graph_oid, char *label_name, graphid vertex_id,
Relation label_relation;
HeapTuple tuple;
+ // Check if label provided exists as edge label, then throw error
+ if (get_label_kind(label_name, graph_oid) == LABEL_KIND_EDGE)
+ {
+ ereport(ERROR, (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+ errmsg("label %s already exists as edge label", label_name)));
+ }
+
values[0] = GRAPHID_GET_DATUM(vertex_id);
values[1] = AGTYPE_P_GET_DATUM((vertex_properties));
@@ -262,4 +275,4 @@ Datum load_edges_from_file(PG_FUNCTION_ARGS)
label_name_str, label_id);
PG_RETURN_VOID();
-}
\ No newline at end of file
+}
diff --git a/src/include/catalog/ag_label.h b/src/include/catalog/ag_label.h
index 90a4ef84..e1e90f3f 100644
--- a/src/include/catalog/ag_label.h
+++ b/src/include/catalog/ag_label.h
@@ -76,6 +76,8 @@ void delete_label(Oid relation);
int32 get_label_id(const char *label_name, Oid graph_oid);
Oid get_label_relation(const char *label_name, Oid graph_oid);
char *get_label_relation_name(const char *label_name, Oid graph_oid);
+Oid get_label_oid(const char *label_name, Oid label_graph);
+char get_label_kind(const char *label_name, Oid label_graph);
bool label_id_exists(Oid graph_oid, int32 label_id);
RangeVar *get_label_range_var(char *graph_name, Oid graph_oid,
[age] 09/15: Fix Issue #693 - server termination from return exists(path) (#721)
Posted by jg...@apache.org.
This is an automated email from the ASF dual-hosted git repository.
jgemignani pushed a commit to branch PG12
in repository https://gitbox.apache.org/repos/asf/age.git
commit 0f9c895814e06527383fa12ae28f94230ebb7d85
Author: Muhammad Taha Naveed <mn...@seecs.edu.pk>
AuthorDate: Fri Mar 3 00:16:18 2023 +0500
Fix Issue #693 - server termination from return exists(path) (#721)
- Updated assert to pass EXPR_KIND_SELECT_TARGET.
- Added regression tests.
---
regress/expected/cypher_match.out | 96 ++++++++++++++++++++++++++++++++++++++
regress/sql/cypher_match.sql | 23 +++++++++
src/backend/parser/cypher_clause.c | 1 +
3 files changed, 120 insertions(+)
diff --git a/regress/expected/cypher_match.out b/regress/expected/cypher_match.out
index 9ec4112d..8f325cbe 100644
--- a/regress/expected/cypher_match.out
+++ b/regress/expected/cypher_match.out
@@ -720,6 +720,57 @@ AS (u agtype, e agtype, v agtype);
{"id": 2814749767106561, "label": "loop", "properties": {"id": "initial"}}::vertex | {"id": 3096224743817217, "label": "self", "end_id": 2814749767106561, "start_id": 2814749767106561, "properties": {}}::edge | {"id": 2814749767106561, "label": "loop", "properties": {"id": "initial"}}::vertex
(1 row)
+-- Return exists(pattern)
+SELECT * FROM cypher('cypher_match',
+ $$MATCH (u) RETURN EXISTS((u)-[]->()) $$)
+AS (exists agtype);
+ exists
+--------
+ false
+ false
+ false
+ false
+ false
+ true
+ true
+ false
+ false
+ true
+ false
+ true
+ false
+ true
+ true
+(15 rows)
+
+SELECT * FROM cypher('cypher_match',
+ $$MATCH p=(u)-[e]->(v) RETURN EXISTS((p)) $$)
+AS (exists agtype);
+ exists
+--------
+ true
+ true
+ true
+ true
+ true
+ true
+ true
+(7 rows)
+
+SELECT * FROM cypher('cypher_match',
+ $$MATCH (u)-[e]->(v) RETURN EXISTS((u)-[e]->(v)-[e]->(u))$$)
+AS (exists agtype);
+ exists
+--------
+ false
+ false
+ false
+ false
+ false
+ false
+ false
+(7 rows)
+
-- These should error
-- Bad pattern
SELECT * FROM cypher('cypher_match',
@@ -809,6 +860,51 @@ AS (u agtype);
{"id": 2814749767106561, "label": "loop", "properties": {"id": "initial"}}::vertex
(1 row)
+-- Return exists(property)
+SELECT * FROM cypher('cypher_match',
+ $$MATCH (u) RETURN EXISTS(u.id), properties(u) $$)
+AS (exists agtype, properties agtype);
+ exists | properties
+--------+-------------------------------------------------------------------------------------------------------------
+ false | {"int_key": 1, "map_key": {"key": "value"}, "list_key": [1, 2, 3], "float_key": 3.14, "string_key": "test"}
+ false | {"lst": [1, null, 3.14, "string", {"key": "value"}, []]}
+ false | {}
+ false | {"i": 0}
+ false | {"i": 1}
+ true | {"id": "initial"}
+ true | {"id": "middle"}
+ true | {"id": "end"}
+ true | {"id": "initial"}
+ true | {"id": "middle"}
+ true | {"id": "end"}
+ true | {"id": "initial"}
+ true | {"id": "middle"}
+ true | {"id": "end"}
+ true | {"id": "initial"}
+(15 rows)
+
+SELECT * FROM cypher('cypher_match',
+ $$MATCH (u) RETURN EXISTS(u.name), properties(u) $$)
+AS (exists agtype, properties agtype);
+ exists | properties
+--------+-------------------------------------------------------------------------------------------------------------
+ false | {"int_key": 1, "map_key": {"key": "value"}, "list_key": [1, 2, 3], "float_key": 3.14, "string_key": "test"}
+ false | {"lst": [1, null, 3.14, "string", {"key": "value"}, []]}
+ false | {}
+ false | {"i": 0}
+ false | {"i": 1}
+ false | {"id": "initial"}
+ false | {"id": "middle"}
+ false | {"id": "end"}
+ false | {"id": "initial"}
+ false | {"id": "middle"}
+ false | {"id": "end"}
+ false | {"id": "initial"}
+ false | {"id": "middle"}
+ false | {"id": "end"}
+ false | {"id": "initial"}
+(15 rows)
+
-- should give an error
SELECT * FROM cypher('cypher_match',
$$MATCH (u) WHERE EXISTS(u) RETURN u$$)
diff --git a/regress/sql/cypher_match.sql b/regress/sql/cypher_match.sql
index aeed9f38..70f638df 100644
--- a/regress/sql/cypher_match.sql
+++ b/regress/sql/cypher_match.sql
@@ -372,6 +372,20 @@ SELECT * FROM cypher('cypher_match',
$$MATCH (u)-[e]->(v) WHERE EXISTS((u)-[e]->(u)) AND EXISTS((v)-[e]->(v)) RETURN u, e, v $$)
AS (u agtype, e agtype, v agtype);
+-- Return exists(pattern)
+
+SELECT * FROM cypher('cypher_match',
+ $$MATCH (u) RETURN EXISTS((u)-[]->()) $$)
+AS (exists agtype);
+
+SELECT * FROM cypher('cypher_match',
+ $$MATCH p=(u)-[e]->(v) RETURN EXISTS((p)) $$)
+AS (exists agtype);
+
+SELECT * FROM cypher('cypher_match',
+ $$MATCH (u)-[e]->(v) RETURN EXISTS((u)-[e]->(v)-[e]->(u))$$)
+AS (exists agtype);
+
-- These should error
-- Bad pattern
SELECT * FROM cypher('cypher_match',
@@ -410,6 +424,15 @@ SELECT * FROM cypher('cypher_match',
$$MATCH (u) WHERE EXISTS(u.id) AND EXISTS((u)-[]->(u)) RETURN u$$)
AS (u agtype);
+-- Return exists(property)
+SELECT * FROM cypher('cypher_match',
+ $$MATCH (u) RETURN EXISTS(u.id), properties(u) $$)
+AS (exists agtype, properties agtype);
+
+SELECT * FROM cypher('cypher_match',
+ $$MATCH (u) RETURN EXISTS(u.name), properties(u) $$)
+AS (exists agtype, properties agtype);
+
-- should give an error
SELECT * FROM cypher('cypher_match',
$$MATCH (u) WHERE EXISTS(u) RETURN u$$)
diff --git a/src/backend/parser/cypher_clause.c b/src/backend/parser/cypher_clause.c
index 226284c4..5db7a3c8 100644
--- a/src/backend/parser/cypher_clause.c
+++ b/src/backend/parser/cypher_clause.c
@@ -5151,6 +5151,7 @@ transform_cypher_clause_as_subquery(cypher_parsestate *cpstate,
Assert(pstate->p_expr_kind == EXPR_KIND_NONE ||
pstate->p_expr_kind == EXPR_KIND_OTHER ||
pstate->p_expr_kind == EXPR_KIND_WHERE ||
+ pstate->p_expr_kind == EXPR_KIND_SELECT_TARGET ||
pstate->p_expr_kind == EXPR_KIND_FROM_SUBSELECT);
/*
[age] 08/15: Fix cypher function input argument checks (issue #718)
Posted by jg...@apache.org.
This is an automated email from the ASF dual-hosted git repository.
jgemignani pushed a commit to branch PG12
in repository https://gitbox.apache.org/repos/asf/age.git
commit eda403d69d94030244d6cb384985c4a55571fd83
Author: John Gemignani <jr...@gmail.com>
AuthorDate: Wed Mar 1 10:43:30 2023 -0800
Fix cypher function input argument checks (issue #718)
Fixed issue #718 which was due to a lack of input checks for an
edge case and added regression tests.
Addition of the driver parameter patch caused 2 additional edge
cases that needed to be checked -
SELECT * FROM cypher() AS (result agtype);
SELECT * FROM cypher(NULL) AS (result agtype);
---
regress/expected/analyze.out | 5 +++++
regress/sql/analyze.sql | 4 ++++
src/backend/parser/cypher_analyze.c | 9 +++++++++
3 files changed, 18 insertions(+)
diff --git a/regress/expected/analyze.out b/regress/expected/analyze.out
index 5b248385..676dab9d 100644
--- a/regress/expected/analyze.out
+++ b/regress/expected/analyze.out
@@ -119,6 +119,11 @@ SELECT * FROM cypher(NULL, NULL) AS (result agtype);
ERROR: unexpected character at or near "$"
LINE 1: SELECT * FROM cypher(NULL, NULL) AS (result agtype);
^
+-- should return errors
+SELECT * FROM cypher() AS (result agtype);
+ERROR: cypher function requires a minimum of 2 arguments
+SELECT * FROM cypher(NULL) AS (result agtype);
+ERROR: cypher function requires a minimum of 2 arguments
-- drop graphs
SELECT * FROM drop_graph('analyze', true);
NOTICE: drop cascades to 2 other objects
diff --git a/regress/sql/analyze.sql b/regress/sql/analyze.sql
index 881ef3ff..1da70b23 100644
--- a/regress/sql/analyze.sql
+++ b/regress/sql/analyze.sql
@@ -52,6 +52,10 @@ SELECT * FROM cypher(NULL, NULL) AS (result agtype);
SELECT * FROM age_prepare_cypher('analyze', '$$ $$');
SELECT * FROM cypher(NULL, NULL) AS (result agtype);
+-- should return errors
+SELECT * FROM cypher() AS (result agtype);
+SELECT * FROM cypher(NULL) AS (result agtype);
+
-- drop graphs
SELECT * FROM drop_graph('analyze', true);
diff --git a/src/backend/parser/cypher_analyze.c b/src/backend/parser/cypher_analyze.c
index b67eb641..c321f231 100644
--- a/src/backend/parser/cypher_analyze.c
+++ b/src/backend/parser/cypher_analyze.c
@@ -329,6 +329,15 @@ static void convert_cypher_to_subquery(RangeTblEntry *rte, ParseState *pstate)
parser_errposition(pstate, exprLocation((Node *)funcexpr))));
}
+ /* verify that we have 2 input parameters as it is possible to get 1 or 0 */
+ if (funcexpr->args == NULL || list_length(funcexpr->args) < 2)
+ {
+ ereport(ERROR,
+ (errcode(ERRCODE_SYNTAX_ERROR),
+ errmsg("cypher function requires a minimum of 2 arguments"),
+ parser_errposition(pstate, -1)));
+ }
+
/* get our first 2 arguments */
arg1 = linitial(funcexpr->args);
arg2 = lsecond(funcexpr->args);