Skip to content

Commit

Permalink
Merge branch 'apache:master' into master
Browse files Browse the repository at this point in the history
  • Loading branch information
markgomer authored Aug 5, 2024
2 parents 39cf989 + ee1bb95 commit 65f55d0
Show file tree
Hide file tree
Showing 74 changed files with 1,377 additions and 839 deletions.
6 changes: 2 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -131,7 +131,7 @@ You will need to install an AGE compatible version of Postgres<a>, for now AGE s
&nbsp;Installation via Package Manager
</h4>

You can use a <a href="https://www.postgresql.org/download/">package management </a> that your OS provides to download AGE.
You can use a <a href="https://www.postgresql.org/download/">package management </a> that your OS provides to download PostgreSQL.

<br>

Expand Down Expand Up @@ -283,9 +283,7 @@ Starting with Apache AGE is very simple. You can easily select your platform and
<h4>Community-driven Driver</h4>

- [Apache AGE Rust Driver](https://github.com/Dzordzu/rust-apache-age.git)



- [Apache AGE .NET Driver](https://github.com/Allison-E/pg-age)

<h2><img height="20" src="/img/visualization.svg">&nbsp;&nbsp;Graph Visualization Tool for AGE</h2>

Expand Down
21 changes: 21 additions & 0 deletions age--1.5.0--y.y.y.sql
Original file line number Diff line number Diff line change
Expand Up @@ -106,3 +106,24 @@ CREATE FUNCTION ag_catalog.age_graph_stats(agtype)
PARALLEL SAFE
AS 'MODULE_PATHNAME';

CREATE FUNCTION ag_catalog.graph_exists(graph_name name)
RETURNS agtype
LANGUAGE c
AS 'MODULE_PATHNAME', 'age_graph_exists';

CREATE FUNCTION ag_catalog.age_is_valid_label_name(agtype)
RETURNS boolean
LANGUAGE c
IMMUTABLE
PARALLEL SAFE
AS 'MODULE_PATHNAME';

CREATE OR REPLACE FUNCTION ag_catalog.create_vlabel(graph_name cstring, label_name cstring)
RETURNS void
LANGUAGE c
AS 'MODULE_PATHNAME';

CREATE OR REPLACE FUNCTION ag_catalog.create_elabel(graph_name cstring, label_name cstring)
RETURNS void
LANGUAGE c
AS 'MODULE_PATHNAME';
10 changes: 10 additions & 0 deletions drivers/python/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,16 @@ SET search_path = ag_catalog, "$user", public;
* Simpler way to access Apache AGE [AGE Sample](samples/apache-age-note.ipynb) in Samples.
* Agtype converting samples: [Agtype Sample](samples/apache-age-agtypes.ipynb) in Samples.

### Non-Superuser Usage
* For non-superuser usage see: [Allow Non-Superusers to Use Apache Age](https://age.apache.org/age-manual/master/intro/setup.html).
* Make sure to give your non-superuser db account proper permissions to the graph schemas and corresponding objects
* Make sure to initiate the Apache Age python driver with the ```load_from_plugins``` parameter. This parameter tries to
load the Apache Age extension from the PostgreSQL plugins directory located at ```$libdir/plugins/age```. Example:
```python.
ag = age.connect(host='localhost', port=5432, user='dbuser', password='strong_password',
dbname=postgres, load_from_plugins=True, graph='graph_name)
```

### License
Apache-2.0 License

Expand Down
8 changes: 5 additions & 3 deletions drivers/python/age/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,15 +24,17 @@ def version():
return VERSION.VERSION


def connect(dsn=None, graph=None, connection_factory=None, cursor_factory=ClientCursor, **kwargs):
def connect(dsn=None, graph=None, connection_factory=None, cursor_factory=ClientCursor, load_from_plugins=False,
**kwargs):

dsn = conninfo.make_conninfo('' if dsn is None else dsn, **kwargs)

ag = Age()
ag.connect(dsn=dsn, graph=graph, connection_factory=connection_factory, cursor_factory=cursor_factory, **kwargs)
ag.connect(dsn=dsn, graph=graph, connection_factory=connection_factory, cursor_factory=cursor_factory,
load_from_plugins=load_from_plugins, **kwargs)
return ag

# Dummy ResultHandler
rawPrinter = DummyResultHandler()

__name__="age"
__name__="age"
24 changes: 17 additions & 7 deletions drivers/python/age/age.py
Original file line number Diff line number Diff line change
Expand Up @@ -35,20 +35,29 @@ def dump(self, obj: Any) -> bytes | bytearray | memoryview:


class AgeLoader(psycopg.adapt.Loader):
def load(self, data: bytes | bytearray | memoryview) -> Any | None:
return parseAgeValue(data.decode('utf-8'))
def load(self, data: bytes | bytearray | memoryview) -> Any | None:
if isinstance(data, memoryview):
data_bytes = data.tobytes()
else:
data_bytes = data

return parseAgeValue(data_bytes.decode('utf-8'))

def setUpAge(conn:psycopg.connection, graphName:str):

def setUpAge(conn:psycopg.connection, graphName:str, load_from_plugins:bool=False):
with conn.cursor() as cursor:
cursor.execute("LOAD 'age';")
if load_from_plugins:
cursor.execute("LOAD '$libdir/plugins/age';")
else:
cursor.execute("LOAD 'age';")

cursor.execute("SET search_path = ag_catalog, '$user', public;")

ag_info = TypeInfo.fetch(conn, 'agtype')

if not ag_info:
raise AgeNotSet()

conn.adapters.register_loader(ag_info.oid, AgeLoader)
conn.adapters.register_loader(ag_info.array_oid, AgeLoader)

Expand Down Expand Up @@ -184,9 +193,10 @@ def __init__(self):
self.graphName = None

# Connect to PostgreSQL Server and establish session and type extension environment.
def connect(self, graph:str=None, dsn:str=None, connection_factory=None, cursor_factory=ClientCursor, **kwargs):
def connect(self, graph:str=None, dsn:str=None, connection_factory=None, cursor_factory=ClientCursor,
load_from_plugins:bool=False, **kwargs):
conn = psycopg.connect(dsn, cursor_factory=cursor_factory, **kwargs)
setUpAge(conn, graph)
setUpAge(conn, graph, load_from_plugins)
self.connection = conn
self.graphName = graph
return self
Expand Down
64 changes: 64 additions & 0 deletions regress/expected/catalog.out
Original file line number Diff line number Diff line change
Expand Up @@ -393,6 +393,70 @@ SELECT create_vlabel(NULL, NULL);
ERROR: graph name must not be NULL
SELECT create_elabel(NULL, NULL);
ERROR: graph name must not be NULL
-- age_graph_exists()
CREATE FUNCTION raise_notice(graph_name TEXT)
RETURNS void AS $$
DECLARE
res BOOLEAN;
BEGIN
-- this tests whether graph_exists works with IF-ELSE.
SELECT graph_exists('graph1') INTO res;
IF res THEN
RAISE NOTICE 'graph exists';
ELSE
RAISE NOTICE 'graph does not exist';
END IF;
END $$ LANGUAGE plpgsql;
SELECT graph_exists('graph1');
graph_exists
--------------
false
(1 row)

SELECT create_graph('graph1');
NOTICE: graph "graph1" has been created
create_graph
--------------

(1 row)

SELECT graph_exists('graph1');
graph_exists
--------------
true
(1 row)

SELECT raise_notice('graph1');
NOTICE: graph exists
raise_notice
--------------

(1 row)

SELECT drop_graph('graph1', true);
NOTICE: drop cascades to 2 other objects
DETAIL: drop cascades to table graph1._ag_label_vertex
drop cascades to table graph1._ag_label_edge
NOTICE: graph "graph1" has been dropped
drop_graph
------------

(1 row)

SELECT graph_exists('graph1');
graph_exists
--------------
false
(1 row)

SELECT raise_notice('graph1');
NOTICE: graph does not exist
raise_notice
--------------

(1 row)

DROP FUNCTION raise_notice(TEXT);
-- dropping the graph
SELECT drop_graph('graph', true);
NOTICE: drop cascades to 2 other objects
Expand Down
65 changes: 65 additions & 0 deletions regress/expected/cypher_vle.out
Original file line number Diff line number Diff line change
Expand Up @@ -1044,6 +1044,71 @@ NOTICE: graph "issue_1043" has been dropped

(1 row)

-- issue 1910
SELECT create_graph('issue_1910');
NOTICE: graph "issue_1910" has been created
create_graph
--------------

(1 row)

SELECT * FROM cypher('issue_1910', $$ MATCH (n) WHERE EXISTS((n)-[*1]-({name: 'Willem Defoe'}))
RETURN n.full_name $$) AS (full_name agtype);
full_name
-----------
(0 rows)

SELECT * FROM cypher('issue_1910', $$ CREATE ({name: 'Jane Doe'})-[:KNOWS]->({name: 'John Doe'}) $$) AS (result agtype);
result
--------
(0 rows)

SELECT * FROM cypher('issue_1910', $$ CREATE ({name: 'Donald Defoe'})-[:KNOWS]->({name: 'Willem Defoe'}) $$) AS (result agtype);
result
--------
(0 rows)

SELECT * FROM cypher('issue_1910', $$ MATCH (u {name: 'John Doe'})
MERGE (u)-[:KNOWS]->({name: 'Willem Defoe'}) $$) AS (result agtype);
result
--------
(0 rows)

SELECT * FROM cypher('issue_1910', $$ MATCH (n) WHERE EXISTS((n)-[*]-({name: 'Willem Defoe'}))
RETURN n.name $$) AS (name agtype);
name
----------------
"Jane Doe"
"John Doe"
"Donald Defoe"
(3 rows)

SELECT * FROM cypher('issue_1910', $$ MATCH (n) WHERE EXISTS((n)-[*1]-({name: 'Willem Defoe'}))
RETURN n.name $$) AS (name agtype);
name
----------------
"John Doe"
"Donald Defoe"
(2 rows)

SELECT * FROM cypher('issue_1910', $$ MATCH (n) WHERE EXISTS((n)-[*2..2]-({name: 'Willem Defoe'}))
RETURN n.name $$) AS (name agtype);
name
------------
"Jane Doe"
(1 row)

SELECT drop_graph('issue_1910', true);
NOTICE: drop cascades to 3 other objects
DETAIL: drop cascades to table issue_1910._ag_label_vertex
drop cascades to table issue_1910._ag_label_edge
drop cascades to table issue_1910."KNOWS"
NOTICE: graph "issue_1910" has been dropped
drop_graph
------------

(1 row)

--
-- Clean up
--
Expand Down
57 changes: 57 additions & 0 deletions regress/expected/expr.out
Original file line number Diff line number Diff line change
Expand Up @@ -8416,9 +8416,66 @@ SELECT * FROM cypher('expanded_map', $$ MATCH (u) RETURN u $$) as (result agtype
{"id": 281474976710664, "label": "", "properties": {"n0": 0, "n1": 1, "n2": 2, "n3": 3, "n4": 4, "n5": 5, "n6": 6, "n7": 7, "n8": 8, "n9": 9, "n10": 10, "n11": 11, "n12": 12, "n13": 13, "n14": 14, "n15": 15, "n16": 16, "n17": 17, "n18": 18, "n19": 19, "n20": 20, "n21": 21, "n22": 22, "n23": 23, "n24": 24, "n25": 25, "n26": 26, "n27": 27, "n28": 28, "n29": 29, "n30": 30, "n31": 31, "n32": 32, "n33": 33, "n34": 34, "n35": 35, "n36": 36, "n37": 37, "n38": 38, "n39": 39, "n40": 40, "n41": 41, "n42": 42, "n43": 43, "n44": 44, "n45": 45, "n46": 46, "n47": 47, "n48": 48, "n49": 49, "n50": 50, "n51": 51, "n52": 52, "n53": 53, "n54": 54, "n55": 55, "n56": 56, "n57": 57, "n58": 58, "n59": 59, "n60": 60, "n61": 61, "n62": 62, "n63": 63, "n64": 64, "n65": 65, "n66": 66, "n67": 67, "n68": 68, "n69": 69, "n70": 70, "n71": 71, "n72": 72, "n73": 73, "n74": 74, "n75": 75, "n76": 76, "n77": 77, "n78": 78, "n79": 79, "n80": 80, "n81": 81, "n82": 82, "n83": 83, "n84": 84, "n85": 85, "n86": 86, "n87": 87, "n88": 88, "n89": 89, "n90": 90, "n91": 91, "n92": 92, "n93": 93, "n94": 94, "n95": 95, "n96": 96, "n97": 97, "n98": 98, "n99": 99, "n100": 100, "n101": 101}}::vertex
(8 rows)

--
-- Issue 1956 - null key
--
SELECT agtype_build_map('null'::agtype, 1);
ERROR: argument 1: key must not be null
SELECT agtype_build_map(null, 1);
ERROR: argument 1: key must not be null
SELECT agtype_build_map('name', 'John', 'null'::agtype, 1);
ERROR: argument 3: key must not be null
--
-- Issue 1953 - crash when trying to use a boolean as an object
--
SELECT * FROM create_graph('issue_1953');
NOTICE: graph "issue_1953" has been created
create_graph
--------------

(1 row)

SELECT * FROM cypher('issue_1953', $$ RETURN delete_global_graphs('issue_1953')[{}][{}][{}][{}][{}] $$) AS (result agtype);
ERROR: A_indirection could not convert type boolean to agtype
LINE 1: ...ypher('issue_1953', $$ RETURN delete_global_graphs('issue_19...
^
SELECT * FROM cypher('issue_1953', $$ RETURN delete_global_graphs('issue_1953')[{}] $$) AS (result agtype);
ERROR: A_indirection could not convert type boolean to agtype
LINE 1: ...ypher('issue_1953', $$ RETURN delete_global_graphs('issue_19...
^
SELECT * FROM cypher('issue_1953', $$ RETURN delete_global_graphs('issue_1953')[0] $$) AS (result agtype);
ERROR: A_indirection could not convert type boolean to agtype
LINE 1: ...ypher('issue_1953', $$ RETURN delete_global_graphs('issue_19...
^
SELECT * FROM cypher('issue_1953', $$ RETURN delete_global_graphs('issue_1953')[0..1] $$) AS (result agtype);
ERROR: A_indirection could not convert type boolean to agtype
LINE 1: ...ypher('issue_1953', $$ RETURN delete_global_graphs('issue_19...
^
SELECT * FROM cypher('issue_1953', $$ RETURN is_valid_label_name('issue_1953')[{}] $$) AS (result agtype);
ERROR: A_indirection could not convert type boolean to agtype
LINE 1: ...cypher('issue_1953', $$ RETURN is_valid_label_name('issue_19...
^
SELECT * FROM cypher('issue_1953', $$ RETURN is_valid_label_name('issue_1953')[0] $$) AS (result agtype);
ERROR: A_indirection could not convert type boolean to agtype
LINE 1: ...cypher('issue_1953', $$ RETURN is_valid_label_name('issue_19...
^
SELECT * FROM cypher('issue_1953', $$ RETURN is_valid_label_name('issue_1953')[0..1] $$) AS (result agtype);
ERROR: A_indirection could not convert type boolean to agtype
LINE 1: ...cypher('issue_1953', $$ RETURN is_valid_label_name('issue_19...
^
--
-- Cleanup
--
SELECT * FROM drop_graph('issue_1953', true);
NOTICE: drop cascades to 2 other objects
DETAIL: drop cascades to table issue_1953._ag_label_vertex
drop cascades to table issue_1953._ag_label_edge
NOTICE: graph "issue_1953" has been dropped
drop_graph
------------

(1 row)

SELECT * FROM drop_graph('expanded_map', true);
NOTICE: drop cascades to 2 other objects
DETAIL: drop cascades to table expanded_map._ag_label_vertex
Expand Down
Loading

0 comments on commit 65f55d0

Please sign in to comment.