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

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
23 changes: 18 additions & 5 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,6 @@ OBJS = src/backend/age.o \
src/backend/utils/load/ag_load_labels.o \
src/backend/utils/load/ag_load_edges.o \
src/backend/utils/load/age_load.o \
src/backend/utils/load/libcsv.o \
src/backend/utils/name_validation.o \
src/backend/utils/ag_guc.o

Expand Down Expand Up @@ -112,7 +111,9 @@ REGRESS = scan \
name_validation \
jsonb_operators \
list_comprehension \
map_projection
map_projection \
direct_field_access \
security

ifneq ($(EXTRA_TESTS),)
REGRESS += $(EXTRA_TESTS)
Expand All @@ -138,6 +139,10 @@ PG_CONFIG ?= pg_config
PGXS := $(shell $(PG_CONFIG) --pgxs)
include $(PGXS)

# 32-bit platform support: pass SIZEOF_DATUM=4 to enable (e.g., make SIZEOF_DATUM=4)
# When SIZEOF_DATUM=4, PASSEDBYVALUE is stripped from graphid type for pass-by-reference.
# If not specified, normal 64-bit behavior is used (PASSEDBYVALUE preserved).

src/backend/parser/cypher_keywords.o: src/include/parser/cypher_kwlist_d.h

src/include/parser/cypher_kwlist_d.h: src/include/parser/cypher_kwlist.h $(GEN_KEYWORDLIST_DEPS)
Expand All @@ -147,11 +152,19 @@ src/include/parser/cypher_gram_def.h: src/backend/parser/cypher_gram.c

src/backend/parser/cypher_gram.c: BISONFLAGS += --defines=src/include/parser/cypher_gram_def.h

src/backend/parser/cypher_parser.o: src/backend/parser/cypher_gram.c
src/backend/parser/cypher_keywords.o: src/backend/parser/cypher_gram.c
src/backend/parser/cypher_parser.o: src/backend/parser/cypher_gram.c src/include/parser/cypher_gram_def.h
src/backend/parser/cypher_parser.bc: src/backend/parser/cypher_gram.c src/include/parser/cypher_gram_def.h
src/backend/parser/cypher_keywords.o: src/backend/parser/cypher_gram.c src/include/parser/cypher_gram_def.h
src/backend/parser/cypher_keywords.bc: src/backend/parser/cypher_gram.c src/include/parser/cypher_gram_def.h

$(age_sql):
# Strip PASSEDBYVALUE on 32-bit (SIZEOF_DATUM=4) for graphid pass-by-reference
$(age_sql): $(SQLS)
@cat $(SQLS) > $@
ifeq ($(SIZEOF_DATUM),4)
@echo "32-bit build: removing PASSEDBYVALUE from graphid type"
@sed 's/^ PASSEDBYVALUE,$$/ -- PASSEDBYVALUE removed for 32-bit (see Makefile)/' $@ > $@.tmp && mv $@.tmp $@
@grep -q 'PASSEDBYVALUE removed for 32-bit' $@ || { echo "Error: PASSEDBYVALUE replacement failed in $@"; exit 1; }
endif

src/backend/parser/ag_scanner.c: FLEX_NO_BACKUP=yes

Expand Down
8 changes: 4 additions & 4 deletions drivers/nodejs/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@
"pg": ">=6.0.0"
},
"devDependencies": {
"@types/jest": "^26.0.20",
"@types/jest": "^29.5.14",
"@types/pg": "^7.14.10",
"@typescript-eslint/eslint-plugin": "^4.22.1",
"@typescript-eslint/parser": "^4.22.1",
Expand All @@ -44,8 +44,8 @@
"eslint-plugin-jest": "^24.3.6",
"eslint-plugin-node": "^11.1.0",
"eslint-plugin-promise": "^4.3.1",
"jest": "^26.6.3",
"ts-jest": "^26.5.1",
"typescript": "^4.1.5"
"jest": "^29.7.0",
"ts-jest": "^29.4.6",
"typescript": "^4.9.5"
}
}
11 changes: 6 additions & 5 deletions drivers/python/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -28,11 +28,11 @@ AGType parser and driver support for [Apache AGE](https://age.apache.org/), grap

### Features
* Unmarshal AGE result data(AGType) to Vertex, Edge, Path
* Cypher query support for Psycopg2 PostgreSQL driver (enables to use cypher queries directly)
* Cypher query support for Psycopg3 PostgreSQL driver (enables to use cypher queries directly)

### Prerequisites
* over Python 3.9
* This module runs on [psycopg2](https://www.psycopg.org/) and [antlr4-python3](https://pypi.org/project/antlr4-python3-runtime/)
* This module runs on [psycopg3](https://www.psycopg.org/) and [antlr4-python3](https://pypi.org/project/antlr4-python3-runtime/)
```
sudo apt-get update
sudo apt-get install python3-dev libpq-dev
Expand Down Expand Up @@ -80,7 +80,7 @@ SET search_path = ag_catalog, "$user", public;
```

### Usage
* If you are not familiar with Psycopg2 driver : Go to [Jupyter Notebook : Basic Sample](samples/apache-age-basic.ipynb)
* If you are not familiar with Psycopg driver : Go to [Jupyter Notebook : Basic Sample](samples/apache-age-basic.ipynb)
* 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.

Expand Down Expand Up @@ -119,7 +119,7 @@ Here the following value required
Insert From networkx directed graph into an AGE database.
#### Parameters

- `connection` (psycopg2.connect): Connection object to the AGE database.
- `connection` (psycopg.connect): Connection object to the AGE database.

- `G` (networkx.DiGraph): Networkx directed graph to be converted and inserted.

Expand Down Expand Up @@ -152,7 +152,7 @@ Converts data from a Apache AGE graph database into a Networkx directed graph.

#### Parameters

- `connection` (psycopg2.connect): Connection object to the PostgreSQL database.
- `connection` (psycopg.connect): Connection object to the PostgreSQL database.
- `graphName` (str): Name of the graph.
- `G` (None | nx.DiGraph): Optional Networkx directed graph. If provided, the data will be added to this graph.
- `query` (str | None): Optional Cypher query to retrieve data from the database.
Expand All @@ -167,3 +167,4 @@ Converts data from a Apache AGE graph database into a Networkx directed graph.
# Call the function to convert data into a Networkx graph
graph = age_to_networkx(connection, graphName="MyGraph" )
```

189 changes: 189 additions & 0 deletions regress/expected/age_load.out
Original file line number Diff line number Diff line change
Expand Up @@ -454,6 +454,195 @@ NOTICE: graph "agload_conversion" has been dropped

(1 row)

--
-- Test security and permissions
--
SELECT create_graph('agload_security');
NOTICE: graph "agload_security" has been created
create_graph
--------------

(1 row)

SELECT create_vlabel('agload_security', 'Person1');
NOTICE: VLabel "Person1" has been created
create_vlabel
---------------

(1 row)

SELECT create_vlabel('agload_security', 'Person2');
NOTICE: VLabel "Person2" has been created
create_vlabel
---------------

(1 row)

SELECT create_elabel('agload_security', 'SecEdge');
NOTICE: ELabel "SecEdge" has been created
create_elabel
---------------

(1 row)

--
-- Test 1: File read permission (pg_read_server_files role)
--
-- Create a user without pg_read_server_files role
CREATE USER load_test_user;
GRANT USAGE ON SCHEMA ag_catalog TO load_test_user;
-- This should fail because load_test_user doesn't have pg_read_server_files
SET ROLE load_test_user;
SELECT load_labels_from_file('agload_security', 'Person1', 'age_load/conversion_vertices.csv', true);
ERROR: permission denied to LOAD from a file
DETAIL: Only roles with privileges of the "pg_read_server_files" role may LOAD from a file.
SELECT load_edges_from_file('agload_security', 'SecEdge', 'age_load/conversion_edges.csv');
ERROR: permission denied to LOAD from a file
DETAIL: Only roles with privileges of the "pg_read_server_files" role may LOAD from a file.
RESET ROLE;
-- Grant pg_read_server_files and try again - should fail on table permission now
GRANT pg_read_server_files TO load_test_user;
--
-- Test 2: Table INSERT permission (ACL_INSERT)
--
-- User has file read permission but no INSERT on the label table
SET ROLE load_test_user;
SELECT load_labels_from_file('agload_security', 'Person1', 'age_load/conversion_vertices.csv', true);
ERROR: permission denied for table Person1
SELECT load_edges_from_file('agload_security', 'SecEdge', 'age_load/conversion_edges.csv');
ERROR: permission denied for table SecEdge
RESET ROLE;
-- Grant INSERT permission and try again - should succeed
GRANT USAGE ON SCHEMA agload_security TO load_test_user;
GRANT INSERT ON agload_security."Person1" TO load_test_user;
GRANT INSERT ON agload_security."SecEdge" TO load_test_user;
GRANT UPDATE ON SEQUENCE agload_security."Person1_id_seq" TO load_test_user;
GRANT UPDATE ON SEQUENCE agload_security."SecEdge_id_seq" TO load_test_user;
GRANT SELECT ON ag_catalog.ag_label TO load_test_user;
GRANT SELECT ON ag_catalog.ag_graph TO load_test_user;
SET ROLE load_test_user;
SELECT load_labels_from_file('agload_security', 'Person1', 'age_load/conversion_vertices.csv', true);
load_labels_from_file
-----------------------

(1 row)

SELECT load_edges_from_file('agload_security', 'SecEdge', 'age_load/conversion_edges.csv');
load_edges_from_file
----------------------

(1 row)

RESET ROLE;
-- Verify data was loaded
SELECT COUNT(*) FROM agload_security."Person1";
count
-------
6
(1 row)

SELECT COUNT(*) FROM agload_security."SecEdge";
count
-------
6
(1 row)

-- cleanup
DELETE FROM agload_security."Person1";
DELETE FROM agload_security."SecEdge";
--
-- Test 3: Row-Level Security (RLS)
--
-- Enable RLS on the label tables
ALTER TABLE agload_security."Person1" ENABLE ROW LEVEL SECURITY;
ALTER TABLE agload_security."SecEdge" ENABLE ROW LEVEL SECURITY;
-- Switch to load_test_user
SET ROLE load_test_user;
-- Loading should fail when RLS is enabled
SELECT load_labels_from_file('agload_security', 'Person1', 'age_load/conversion_vertices.csv', true);
ERROR: LOAD from file is not supported with row-level security
HINT: Use Cypher CREATE clause instead.
SELECT load_edges_from_file('agload_security', 'SecEdge', 'age_load/conversion_edges.csv');
ERROR: LOAD from file is not supported with row-level security
HINT: Use Cypher CREATE clause instead.
RESET ROLE;
-- Disable RLS and try again - should succeed
ALTER TABLE agload_security."Person1" DISABLE ROW LEVEL SECURITY;
ALTER TABLE agload_security."SecEdge" DISABLE ROW LEVEL SECURITY;
SELECT load_labels_from_file('agload_security', 'Person1', 'age_load/conversion_vertices.csv', true);
load_labels_from_file
-----------------------

(1 row)

SELECT load_edges_from_file('agload_security', 'SecEdge', 'age_load/conversion_edges.csv');
load_edges_from_file
----------------------

(1 row)

-- Verify data was loaded
SELECT COUNT(*) FROM agload_security."Person1";
count
-------
6
(1 row)

SELECT COUNT(*) FROM agload_security."SecEdge";
count
-------
6
(1 row)

-- cleanup
DELETE FROM agload_security."Person1";
DELETE FROM agload_security."SecEdge";
--
-- Test 4: Constraint checking (CHECK constraint)
--
-- Add constraint on vertex properties - fail if bool property is false
ALTER TABLE agload_security."Person1" ADD CONSTRAINT check_bool_true
CHECK ((properties->>'"bool"')::boolean = true);
-- This should fail - constraint violation
SELECT load_labels_from_file('agload_security', 'Person1', 'age_load/conversion_vertices.csv', true);
ERROR: new row for relation "Person1" violates check constraint "check_bool_true"
DETAIL: Failing row contains (844424930131970, {"id": "2", "bool": "false", "__id__": 2, "string": "John", "num...).
-- Add constraint on edge properties - fail if bool property is false
ALTER TABLE agload_security."SecEdge" ADD CONSTRAINT check_bool_true
CHECK ((properties->>'"bool"')::boolean = true);
-- This should fail - some edges have bool = false
SELECT load_edges_from_file('agload_security', 'SecEdge', 'age_load/conversion_edges.csv');
ERROR: new row for relation "SecEdge" violates check constraint "check_bool_true"
DETAIL: Failing row contains (1407374883553294, 844424930131969, 1125899906842625, {"bool": "false", "string": "John", "numeric": "-2"}).
-- cleanup
ALTER TABLE agload_security."Person1" DROP CONSTRAINT check_bool_true;
ALTER TABLE agload_security."SecEdge" DROP CONSTRAINT check_bool_true;
--
-- Cleanup
--
REVOKE ALL ON agload_security."Person1" FROM load_test_user;
REVOKE ALL ON agload_security."SecEdge" FROM load_test_user;
REVOKE ALL ON SEQUENCE agload_security."Person1_id_seq" FROM load_test_user;
REVOKE ALL ON SEQUENCE agload_security."SecEdge_id_seq" FROM load_test_user;
REVOKE ALL ON ag_catalog.ag_label FROM load_test_user;
REVOKE ALL ON ag_catalog.ag_graph FROM load_test_user;
REVOKE ALL ON SCHEMA agload_security FROM load_test_user;
REVOKE ALL ON SCHEMA ag_catalog FROM load_test_user;
REVOKE pg_read_server_files FROM load_test_user;
DROP USER load_test_user;
SELECT drop_graph('agload_security', true);
NOTICE: drop cascades to 5 other objects
DETAIL: drop cascades to table agload_security._ag_label_vertex
drop cascades to table agload_security._ag_label_edge
drop cascades to table agload_security."Person1"
drop cascades to table agload_security."Person2"
drop cascades to table agload_security."SecEdge"
NOTICE: graph "agload_security" has been dropped
drop_graph
------------

(1 row)

--
-- End
--
Loading