Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
26 commits
Select commit Hold shift + click to select a range
f6e79a8
Fix issue with CALL/YIELD for user defined and qualified functions. (…
jrgemignani Sep 15, 2025
ea9d3ec
Bump gopkg.in/yaml.v3 from 3.0.0 to 3.0.1 in /drivers/golang (#2212)
dependabot[bot] Sep 24, 2025
8cade62
Add fast functions for checking edge uniqueness (#2227)
jrgemignani Oct 4, 2025
3404368
Fix issue 2243 - Regression in string concatenation (#2244)
jrgemignani Nov 13, 2025
314f097
Fix issue 2245 - Creating more than 41 vlabels causes crash in drop_g…
jrgemignani Nov 19, 2025
f61af9e
Add index on id columns (#2117)
MuhammadTahaNaveed Dec 3, 2025
a6a0836
Fix Issue 2256: segmentation fault when calling coalesce function (#2…
jrgemignani Dec 9, 2025
6b304fa
Adjust 'could not find rte for' ERROR message (#2266)
jrgemignani Dec 9, 2025
bbc9d44
Fix possible memory and file descriptors leaks (#2258)
ZigzagAK Dec 9, 2025
18e268b
Fix ORDER BY alias resolution with AS in Cypher queries (#2269)
jrgemignani Dec 10, 2025
e0d12c1
Update grammar file for maintainability (#2270)
jrgemignani Dec 11, 2025
303fcf6
Convert string to raw string to remove invalid escape sequence warnin…
jsell-rh Dec 11, 2025
7740c00
Migrate python driver configuration to pyproject.toml (#2272)
MuhammadTahaNaveed Dec 11, 2025
d382d53
Restrict age_load commands (#2274)
jrgemignani Dec 16, 2025
b40eaaf
Makefile: fix race condition on cypher_gram_def.h (#2273)
tureba Jan 6, 2026
079ae96
Revise README for Python driver updates (#2298)
M15terHyde Jan 6, 2026
cd325a3
Fix Issue 2289: handle empty list in IN expression (#2294)
jrgemignani Jan 9, 2026
2108171
Fix and improve index.sql regression test coverage (#2300)
jrgemignani Jan 9, 2026
60eeda1
Fix and improve index.sql addendum (#2301)
jrgemignani Jan 10, 2026
79bf3ad
feat: Add 32-bit platform support for graphid type (#2286)
jpabbuehl Jan 12, 2026
ea0915f
Optimize vertex/edge field access with direct array indexing (#2302)
jrgemignani Jan 16, 2026
76531bf
Upgrade Jest to v29 for node: protocol compatibility (#2307)
jrgemignani Jan 17, 2026
c9b7417
Fix Issue 1884: Ambiguous column reference (#2306)
jrgemignani Jan 18, 2026
7a1ca20
Replace libcsv with pg COPY for csv loading (#2310)
MuhammadTahaNaveed Jan 19, 2026
7bf52f6
Add RLS support and fix permission checks (#2309)
MuhammadTahaNaveed Jan 20, 2026
9ee143a
Fix upgrade script for 1.6.0 to 1.7.0 (#2320)
MuhammadTahaNaveed Jan 22, 2026
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
4 changes: 2 additions & 2 deletions .github/workflows/python-driver.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ jobs:
- name: Set up python
uses: actions/setup-python@v4
with:
python-version: '3.10'
python-version: '3.12'

- name: Install pre-requisites
run: |
Expand All @@ -33,7 +33,7 @@ jobs:

- name: Build
run: |
python setup.py install
pip install .

- name: Test
run: |
Expand Down
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -11,5 +11,7 @@ age--*.*.*.sql
!age--*--*sql
__pycache__
**/__pycache__
**/.venv
**/apache_age_python.egg-info

drivers/python/build
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
117 changes: 117 additions & 0 deletions age--1.6.0--y.y.y.sql
Original file line number Diff line number Diff line change
Expand Up @@ -31,3 +31,120 @@
--* file. We need to keep the order of these changes.
--* REMOVE ALL LINES ABOVE, and this one, that start with --*

CREATE FUNCTION ag_catalog._ag_enforce_edge_uniqueness2(graphid, graphid)
RETURNS bool
LANGUAGE c
STABLE
PARALLEL SAFE
as 'MODULE_PATHNAME';

CREATE FUNCTION ag_catalog._ag_enforce_edge_uniqueness3(graphid, graphid, graphid)
RETURNS bool
LANGUAGE c
STABLE
PARALLEL SAFE
as 'MODULE_PATHNAME';

CREATE FUNCTION ag_catalog._ag_enforce_edge_uniqueness4(graphid, graphid, graphid, graphid)
RETURNS bool
LANGUAGE c
STABLE
PARALLEL SAFE
as 'MODULE_PATHNAME';

-- Create indexes on id columns for existing labels
-- Vertex labels get PRIMARY KEY on id, Edge labels get indexes on start_id/end_id
DO $$
DECLARE
label_rec record;
schema_name text;
table_name text;
idx_exists boolean;
pk_exists boolean;
idx_name text;
BEGIN
FOR label_rec IN
SELECT l.relation, l.kind
FROM ag_catalog.ag_label l
LOOP
SELECT n.nspname, c.relname INTO schema_name, table_name
FROM pg_class c
JOIN pg_namespace n ON c.relnamespace = n.oid
WHERE c.oid = label_rec.relation;

IF label_rec.kind = 'e' THEN
-- Edge: check/create index on start_id
SELECT EXISTS (
SELECT 1 FROM pg_index i
JOIN pg_class c ON c.oid = i.indexrelid
JOIN pg_am am ON am.oid = c.relam
JOIN pg_attribute a ON a.attrelid = i.indrelid AND a.attnum = i.indkey[0]
WHERE i.indrelid = label_rec.relation
AND a.attname = 'start_id'
AND i.indpred IS NULL -- not a partial index
AND i.indexprs IS NULL -- not an expression index
AND am.amname = 'btree' -- btree access method
) INTO idx_exists;

IF NOT idx_exists THEN
EXECUTE format('CREATE INDEX %I ON %I.%I USING btree (start_id)',
table_name || '_start_id_idx', schema_name, table_name);
END IF;

-- Edge: check/create index on end_id
SELECT EXISTS (
SELECT 1 FROM pg_index i
JOIN pg_class c ON c.oid = i.indexrelid
JOIN pg_am am ON am.oid = c.relam
JOIN pg_attribute a ON a.attrelid = i.indrelid AND a.attnum = i.indkey[0]
WHERE i.indrelid = label_rec.relation
AND a.attname = 'end_id'
AND i.indpred IS NULL -- not a partial index
AND i.indexprs IS NULL -- not an expression index
AND am.amname = 'btree' -- btree access method
) INTO idx_exists;

IF NOT idx_exists THEN
EXECUTE format('CREATE INDEX %I ON %I.%I USING btree (end_id)',
table_name || '_end_id_idx', schema_name, table_name);
END IF;
ELSE
-- Vertex: check/create PRIMARY KEY on id
SELECT EXISTS (
SELECT 1 FROM pg_constraint
WHERE conrelid = label_rec.relation AND contype = 'p'
) INTO pk_exists;

IF NOT pk_exists THEN
-- Check if a usable unique index on id already exists
SELECT c.relname INTO idx_name
FROM pg_index i
JOIN pg_class c ON c.oid = i.indexrelid
JOIN pg_am am ON am.oid = c.relam
WHERE i.indrelid = label_rec.relation
AND i.indisunique = true
AND i.indpred IS NULL -- not a partial index
AND i.indexprs IS NULL -- not an expression index
AND am.amname = 'btree' -- btree access method
AND i.indnkeyatts = 1 -- single column index
AND EXISTS (
SELECT 1 FROM pg_attribute a
WHERE a.attrelid = i.indrelid
AND a.attnum = i.indkey[0]
AND a.attname = 'id'
)
LIMIT 1;

IF idx_name IS NOT NULL THEN
-- Reuse existing unique index for primary key
EXECUTE format('ALTER TABLE %I.%I ADD CONSTRAINT %I PRIMARY KEY USING INDEX %I',
schema_name, table_name, table_name || '_pkey', idx_name);
ELSE
-- Create new primary key
EXECUTE format('ALTER TABLE %I.%I ADD PRIMARY KEY (id)',
schema_name, table_name);
END IF;
END IF;
END IF;
END LOOP;
END $$;
2 changes: 1 addition & 1 deletion drivers/golang/go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -31,5 +31,5 @@ require (
github.com/davecgh/go-spew v1.1.0 // indirect
github.com/pmezard/go-difflib v1.0.0 // indirect
golang.org/x/exp v0.0.0-20240205201215-2c58cdc269a3 // indirect
gopkg.in/yaml.v3 v3.0.0 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect
)
10 changes: 2 additions & 8 deletions drivers/golang/go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -2,23 +2,17 @@ github.com/antlr/antlr4/runtime/Go/antlr/v4 v4.0.0-20230321174746-8dcc6526cfb1 h
github.com/antlr/antlr4/runtime/Go/antlr/v4 v4.0.0-20230321174746-8dcc6526cfb1/go.mod h1:pSwJ0fSY5KhvocuWSx4fz3BA8OrA1bQn+K1Eli3BRwM=
github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/lib/pq v1.10.7 h1:p7ZhMD+KsSRozJr34udlUrhboJwWAgCg34+/ZZNvZZw=
github.com/lib/pq v1.10.7/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o=
github.com/lib/pq v1.10.9 h1:YXG7RB+JIjhP29X+OtkiDnYaXQwpS4JEWq7dtCCRUEw=
github.com/lib/pq v1.10.9/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY=
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
golang.org/x/exp v0.0.0-20230321023759-10a507213a29 h1:ooxPy7fPvB4kwsA2h+iBNHkAbp/4JxTSwCmvdjEYmug=
golang.org/x/exp v0.0.0-20230321023759-10a507213a29/go.mod h1:CxIveKay+FTh1D0yPZemJVgC/95VzuuOLq5Qi4xnoYc=
golang.org/x/exp v0.0.0-20240119083558-1b970713d09a h1:Q8/wZp0KX97QFTc2ywcOE0YRjZPVIx+MXInMzdvQqcA=
golang.org/x/exp v0.0.0-20240119083558-1b970713d09a/go.mod h1:idGWGoKP1toJGkd5/ig9ZLuPcZBC3ewk7SzmH0uou08=
golang.org/x/exp v0.0.0-20240205201215-2c58cdc269a3 h1:/RIbNt/Zr7rVhIkQhooTxCxFcdWLGIKnZA4IXNFSrvo=
golang.org/x/exp v0.0.0-20240205201215-2c58cdc269a3/go.mod h1:idGWGoKP1toJGkd5/ig9ZLuPcZBC3ewk7SzmH0uou08=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gopkg.in/yaml.v3 v3.0.0 h1:hjy8E9ON/egN1tAYqKb61G10WtihqetD4sz2H+8nIeA=
gopkg.in/yaml.v3 v3.0.0/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
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"
}
}
13 changes: 7 additions & 6 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 @@ -62,7 +62,7 @@ python -m unittest -v test_agtypes.py

### Build from source
```
python setup.py install
pip install .
```

### For more information about [Apache AGE](https://age.apache.org/)
Expand All @@ -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" )
```

3 changes: 2 additions & 1 deletion drivers/python/age/age.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@
_EXCEPTION_NoConnection = NoConnection()
_EXCEPTION_GraphNotSet = GraphNotSet()

WHITESPACE = re.compile('\s')
WHITESPACE = re.compile(r'\s')


class AgeDumper(psycopg.adapt.Dumper):
Expand Down Expand Up @@ -233,3 +233,4 @@ def cypher(self, cursor:psycopg.cursor, cypherStmt:str, cols:list=None, params:t

# def queryCypher(self, cypherStmt:str, columns:list=None , params:tuple=None) -> psycopg.cursor :
# return queryCypher(self.connection, self.graphName, cypherStmt, columns, params)

48 changes: 48 additions & 0 deletions drivers/python/pyproject.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
# Licensed to the Apache Software Foundation (ASF) under one
# or more contributor license agreements. See the NOTICE file
# distributed with this work for additional information
# regarding copyright ownership. The ASF licenses this file
# to you under the Apache License, Version 2.0 (the
# "License"); you may not use this file except in compliance
# with the License. You may obtain a copy of the License at
# http://www.apache.org/licenses/LICENSE-2.0
# Unless required by applicable law or agreed to in writing,
# software distributed under the License is distributed on an
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
# KIND, either express or implied. See the License for the
# specific language governing permissions and limitations
# under the License.

[build-system]
requires = ["setuptools>=61.0", "wheel"]
build-backend = "setuptools.build_meta"

[project]
name = "apache-age-python"
version = "0.0.7"
description = "Python driver support for Apache AGE"
readme = "README.md"
requires-python = ">=3.9"
license = "Apache-2.0"
keywords = ["Graph Database", "Apache AGE", "PostgreSQL"]
authors = [
{name = "Ikchan Kwon, Apache AGE", email = "dev-subscribe@age.apache.org"}
]
classifiers = [
"Programming Language :: Python :: 3.10",
"Programming Language :: Python :: 3.11",
"Programming Language :: Python :: 3.12",
"Programming Language :: Python :: 3.13",
"Programming Language :: Python :: 3.14",
]
dependencies = [
"psycopg",
"antlr4-python3-runtime==4.11.1",
]

[project.urls]
Homepage = "https://github.com/apache/age/tree/master/drivers/python"
Download = "https://github.com/apache/age/releases"

[tool.setuptools]
packages = ["age", "age.gen", "age.networkx"]
Loading