pax_global_header 0000666 0000000 0000000 00000000064 14546062412 0014516 g ustar 00root root 0000000 0000000 52 comment=0048900f372d4dfd5c236f78b8a8453bcdbb458e
age-PG16-v1.5.0-rc0/ 0000775 0000000 0000000 00000000000 14546062412 0013600 5 ustar 00root root 0000000 0000000 age-PG16-v1.5.0-rc0/.asf.yaml 0000664 0000000 0000000 00000002573 14546062412 0015322 0 ustar 00root root 0000000 0000000 # 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.
notifications:
commits: commits@age.apache.org
pullrequests: commits@age.apache.org
github:
description: "Graph database optimized for fast analysis and real-time data processing.
It is provided as an extension to PostgreSQL."
homepage: https://age.apache.org
labels:
- postgresql
- graph-database
- analytics
- postgresql-extension
- graphdb
- multi-model-dbms
- agensgraph
- age-database
features:
wiki: true
issues: true
projects: true
discussions: true
enabled_merge_buttons:
squash: true
merge: false
rebase: true
age-PG16-v1.5.0-rc0/.dockerignore 0000664 0000000 0000000 00000000104 14546062412 0016247 0 ustar 00root root 0000000 0000000 *.o
*.so
.gitignore
build.sh
.idea
.deps
.DS_Store
*.tokens
*.interp age-PG16-v1.5.0-rc0/.github/ 0000775 0000000 0000000 00000000000 14546062412 0015140 5 ustar 00root root 0000000 0000000 age-PG16-v1.5.0-rc0/.github/ISSUE_TEMPLATE/ 0000775 0000000 0000000 00000000000 14546062412 0017323 5 ustar 00root root 0000000 0000000 age-PG16-v1.5.0-rc0/.github/ISSUE_TEMPLATE/bug_report.md 0000664 0000000 0000000 00000002141 14546062412 0022013 0 ustar 00root root 0000000 0000000 ---
name: Bug report
about: Create a report to help us improve
title: ''
labels: bug
assignees: ''
---
**Describe the bug**
A clear and concise description of what the bug is.
**How are you accessing AGE (Command line, driver, etc.)?**
- [e.g. JDBC]
**What data setup do we need to do?**
```pgsql
...
SELECT * from cypher('my_graph_name', $$
CREATE (a:Part {part_num: '123'}),
(b:Part {part_num: '345'}),
(c:Part {part_num: '456'}),
(d:Part {part_num: '789'})
$$) as (a agtype);
...
```
**What is the necessary configuration info needed?**
- [e.g. Installed PostGIS]
**What is the command that caused the error?**
```pgsql
SELECT * from cypher('my_graph_name', $$
MATCH (a:Part {part_num: '123'}), (b:Part {part_num: '345'})
CREATE (a)-[u:used_by { quantity: 1 }]->(b)
$$) as (a agtype);
```
```
ERROR: something failed to execute
```
**Expected behavior**
A clear and concise description of what you expected to happen.
**Environment (please complete the following information):**
- Version: [e.g. 0.4.0]
**Additional context**
Add any other context about the problem here.
age-PG16-v1.5.0-rc0/.github/ISSUE_TEMPLATE/feature_request.md 0000664 0000000 0000000 00000001134 14546062412 0023047 0 ustar 00root root 0000000 0000000 ---
name: Feature request
about: Suggest an idea for this project
title: ''
labels: enhancement
assignees: ''
---
**Is your feature request related to a problem? Please describe.**
A clear and concise description of what the problem is. Ex. I'm always frustrated when [...]
**Describe the solution you'd like**
A clear and concise description of what you want to happen.
**Describe alternatives you've considered**
A clear and concise description of any alternative solutions or features you've considered.
**Additional context**
Add any other context or screenshots about the feature request here.
age-PG16-v1.5.0-rc0/.github/ISSUE_TEMPLATE/question.md 0000664 0000000 0000000 00000000134 14546062412 0021512 0 ustar 00root root 0000000 0000000 ---
name: Question
about: Create a question
title: ''
labels: question
assignees: ''
---
age-PG16-v1.5.0-rc0/.github/labeler.yml 0000664 0000000 0000000 00000000260 14546062412 0017267 0 ustar 00root root 0000000 0000000 PG11:
- base-branch: 'PG11'
PG12:
- base-branch: 'PG12'
PG13:
- base-branch: 'PG13'
PG14:
- base-branch: 'PG14'
PG15:
- base-branch: 'PG15'
master:
- base-branch: 'master' age-PG16-v1.5.0-rc0/.github/workflows/ 0000775 0000000 0000000 00000000000 14546062412 0017175 5 ustar 00root root 0000000 0000000 age-PG16-v1.5.0-rc0/.github/workflows/go-driver.yml 0000664 0000000 0000000 00000001424 14546062412 0021617 0 ustar 00root root 0000000 0000000 name: Go Driver Tests
on:
push:
branches: [ "PG16" ]
pull_request:
branches: [ "PG16" ]
jobs:
build:
runs-on: ubuntu-latest
strategy:
matrix:
go-version: [ '1.18', '1.19' ]
defaults:
run:
working-directory: drivers/golang/age/
steps:
- uses: actions/checkout@v3
- name: Set tag based on branch
run: |
echo "TAG=PG16_latest" >> $GITHUB_ENV
- name: Run apache/age docker image
run: |
export TAG=$TAG
docker-compose up -d
- name: Set up Go
uses: actions/setup-go@v3
with:
go-version: ${{ matrix.go-version }}
- name: Generate
run: go generate ./../...
- name: Build
run: go build -v ./...
- name: Test
run: go test . -v
age-PG16-v1.5.0-rc0/.github/workflows/installcheck.yaml 0000664 0000000 0000000 00000003002 14546062412 0022520 0 ustar 00root root 0000000 0000000 name: Build / Regression
on:
push:
branches: [ 'PG16' ]
pull_request:
branches: [ 'PG16' ]
jobs:
build:
runs-on: ubuntu-latest
steps:
- name: Get latest commit id of PostgreSQL 16
run: |
echo "PG_COMMIT_HASH=$(git ls-remote git://git.postgresql.org/git/postgresql.git refs/heads/REL_16_STABLE | awk '{print $1}')" >> $GITHUB_ENV
- name: Cache PostgreSQL 16
uses: actions/cache@v3
id: pg16cache
with:
path: ~/pg16
key: ${{ runner.os }}-v1-pg16-${{ env.PG_COMMIT_HASH }}
- name: Install PostgreSQL 16
if: steps.pg16cache.outputs.cache-hit != 'true'
run: |
git clone --depth 1 --branch REL_16_STABLE git://git.postgresql.org/git/postgresql.git ~/pg16source
cd ~/pg16source
./configure --prefix=$HOME/pg16 CFLAGS="-std=gnu99 -ggdb -O0" --enable-cassert
make install -j$(nproc) > /dev/null
- uses: actions/checkout@v3
- name: Build
id: build
run: |
make PG_CONFIG=$HOME/pg16/bin/pg_config install -j$(nproc)
- name: Regression tests
id: regression_tests
run: |
make PG_CONFIG=$HOME/pg16/bin/pg_config installcheck
continue-on-error: true
- name: Dump regression test errors
if: steps.regression_tests.outcome != 'success'
run: |
echo "Dump section begin."
cat $HOME/work/age/age/regress/regression.diffs
echo "Dump section end."
exit 1
age-PG16-v1.5.0-rc0/.github/workflows/jdbc-driver.yaml 0000664 0000000 0000000 00000001065 14546062412 0022256 0 ustar 00root root 0000000 0000000 name: JDBC Driver Tests
on:
push:
branches: [ "PG16" ]
pull_request:
branches: [ "PG16" ]
jobs:
build:
runs-on: ubuntu-latest
defaults:
run:
working-directory: drivers/jdbc
steps:
- uses: actions/checkout@v3
- name: Set up Java
uses: actions/setup-java@v3
with:
distribution: 'zulu'
java-version: '17'
- name: Set tag based on branch
run: |
echo "TAG=PG16_latest" >> $GITHUB_ENV
- name: Build and Test
run: |
export TAG=$TAG
gradle build
age-PG16-v1.5.0-rc0/.github/workflows/labeler.yml 0000664 0000000 0000000 00000000331 14546062412 0021323 0 ustar 00root root 0000000 0000000 name: "Pull Request Labeler"
on:
- pull_request_target
jobs:
triage:
permissions:
contents: read
pull-requests: write
runs-on: ubuntu-latest
steps:
- uses: actions/labeler@v5.0.0-alpha.1 age-PG16-v1.5.0-rc0/.github/workflows/nodejs-driver.yaml 0000664 0000000 0000000 00000001300 14546062412 0022626 0 ustar 00root root 0000000 0000000 name: Nodejs Driver Tests
on:
push:
branches: [ "PG16" ]
pull_request:
branches: [ "PG16" ]
jobs:
build:
runs-on: ubuntu-latest
defaults:
run:
working-directory: drivers/nodejs/
steps:
- uses: actions/checkout@v3
- name: Set tag based on branch
run: |
echo "TAG=PG16_latest" >> $GITHUB_ENV
- name: Run apache/age docker image
run: |
export TAG=$TAG
docker-compose up -d
- name: Set up Node
uses: actions/setup-node@v3
with:
node-version: latest
- name: Install dependencies
run: npm install
- name: Build
run: npm run build
- name: Test
run: npm test
age-PG16-v1.5.0-rc0/.github/workflows/python-driver.yaml 0000664 0000000 0000000 00000001634 14546062412 0022677 0 ustar 00root root 0000000 0000000 name: Python Driver Tests
on:
push:
branches: [ "PG16" ]
pull_request:
branches: [ "PG16" ]
jobs:
build:
runs-on: ubuntu-latest
defaults:
run:
working-directory: drivers/python
steps:
- uses: actions/checkout@v3
- name: Set tag based on branch
run: |
echo "TAG=PG16_latest" >> $GITHUB_ENV
- name: Run apache/age docker image
run: |
export TAG=$TAG
docker-compose up -d
- name: Set up python
uses: actions/setup-python@v4
with:
python-version: '3.10'
- name: Install pre-requisites
run: |
sudo apt-get install python3-dev libpq-dev
pip install -r requirements.txt
- name: Build
run: |
python setup.py install
- name: Test
run: |
python test_age_py.py -db "postgres" -u "postgres" -pass "agens"
python -m unittest -v test_agtypes.py
age-PG16-v1.5.0-rc0/.gitignore 0000664 0000000 0000000 00000000121 14546062412 0015562 0 ustar 00root root 0000000 0000000 *.o
*.so
build.sh
age--*.*.*.sql
.idea
.deps
.DS_Store
*.tokens
*.interp
*.dylib
age-PG16-v1.5.0-rc0/CONTRIBUTING.md 0000664 0000000 0000000 00000006710 14546062412 0016035 0 ustar 00root root 0000000 0000000 # Contributing to Apache AGE
Welcome, future Contributor!
First off, thank you for considering contributing to Apache AGE. Team AGE welcomes anyone who is willing to help us mature AGE to become a fully-featured graph database extension for PostgreSQL.
There are multiple ways you can contribute to the Apache AGE and [Apache AGE Viewer](https://github.com/apache/age-viewer) projects. We hope that adding features, fixing bugs, and changing documentations can be fun and educational for anyone and everyone.
## Code of Conduct
The community members of Apache AGE are expected to follow the 'Apache Way'. Please read the [Code of Conduct](https://www.apache.org/foundation/policies/conduct) provided by Apache Software Foundation.
## How to Start
A great way to get involved in the project is to ask questions on the mailing lists, Apache AGE Discord, or the Apache Reddit forum (r/apacheage). Reviewing the list of projects in the Apache AGE and AGE Viewer GitHubs may help you understand the overall roadmap.
Once you understand the ins and outs of Apache AGE, share your knowledge by helping the newcomers as well. Spending a few minutes to answer questions are a valuable open source community service, which also demonstrates your expertise.
We strongly recommend you to subscribe the mailing lists, join the Apache AGE Discord and Apache AGE Reddit community (r/apacheage) to keep up to date on what's happening in AGE. Visit [joinus](https://age.apache.org/joinus) for pathways you can follow to help you get started.
## Pull Requests
Changes to AGE source code are proposed, reviewed, and committed via Github pull requests (described in Code Convention). Pull requests are a great way to get your ideas into this repository. Anyone can view and comment on active changes here. Reviewing others' changes are a good way to learn how the change process works and gain exposure to activity in various parts of the code. You can help by reviewing the changes, asking questions, or pointing out issues as simple as typos.
## Documentation Changes
You can propose changes to Apache AGE documentation, edit the Markdown source files for the Apache AGE website pages.
## Bug Reports
Ideally, bug reports are accompanied by a proposed code change to fix the bug. This isn't always possible, as those who discover a bug may not have the experience to fix it. A bug may be reported by creating a GitHub issue, but without creating a pull request.
Bug reports are only useful, however, if they include enough information to understand, isolate and ideally reproduce the bug. Simply encountering an error does not mean a bug should be reported; search GitHub and inquire on the Apache AGE's dev mailing list first. Unreproducible bugs or simple error reports without context shall be closed.
The more context about a bug, the better, such as: how the bug was introduced, by which commit, etc. It assists the committers in the decision process on how far the bug fix should be backported, when the pull request is merged. The pull request to fix the bug should narrow down the problem to the root cause. Data correctness/data loss bugs are very serious. Make sure the corresponding bug report GitHub issue is labeled as correctness or data-loss. Please send an email to dev@age.apache.org after submitting the bug report, to quickly draw attention to the issue. Performance issues are classified as bugs. The pull request to fix a performance bug must provide a benchmark to prove the problem is indeed fixed.
age-PG16-v1.5.0-rc0/LICENSE 0000664 0000000 0000000 00000030432 14546062412 0014607 0 ustar 00root root 0000000 0000000
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
1. Definitions.
"License" shall mean the terms and conditions for use, reproduction,
and distribution as defined by Sections 1 through 9 of this document.
"Licensor" shall mean the copyright owner or entity authorized by
the copyright owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all
other entities that control, are controlled by, or are under common
control with that entity. For the purposes of this definition,
"control" means (i) the power, direct or indirect, to cause the
direction or management of such entity, whether by contract or
otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity
exercising permissions granted by this License.
"Source" form shall mean the preferred form for making modifications,
including but not limited to software source code, documentation
source, and configuration files.
"Object" form shall mean any form resulting from mechanical
transformation or translation of a Source form, including but
not limited to compiled object code, generated documentation,
and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or
Object form, made available under the License, as indicated by a
copyright notice that is included in or attached to the work
(an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object
form, that is based on (or derived from) the Work and for which the
editorial revisions, annotations, elaborations, or other modifications
represent, as a whole, an original work of authorship. For the purposes
of this License, Derivative Works shall not include works that remain
separable from, or merely link (or bind by name) to the interfaces of,
the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including
the original version of the Work and any modifications or additions
to that Work or Derivative Works thereof, that is intentionally
submitted to Licensor for inclusion in the Work by the copyright owner
or by an individual or Legal Entity authorized to submit on behalf of
the copyright owner. For the purposes of this definition, "submitted"
means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems,
and issue tracking systems that are managed by, or on behalf of, the
Licensor for the purpose of discussing and improving the Work, but
excluding communication that is conspicuously marked or otherwise
designated in writing by the copyright owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity
on behalf of whom a Contribution has been received by Licensor and
subsequently incorporated within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the
Work and such Derivative Works in Source or Object form.
3. Grant of Patent License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
(except as stated in this section) patent license to make, have made,
use, offer to sell, sell, import, and otherwise transfer the Work,
where such license applies only to those patent claims licensable
by such Contributor that are necessarily infringed by their
Contribution(s) alone or by combination of their Contribution(s)
with the Work to which such Contribution(s) was submitted. If You
institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work
or a Contribution incorporated within the Work constitutes direct
or contributory patent infringement, then any patent licenses
granted to You under this License for that Work shall terminate
as of the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the
Work or Derivative Works thereof in any medium, with or without
modifications, and in Source or Object form, provided that You
meet the following conditions:
(a) You must give any other recipients of the Work or
Derivative Works a copy of this License; and
(b) You must cause any modified files to carry prominent notices
stating that You changed the files; and
(c) You must retain, in the Source form of any Derivative Works
that You distribute, all copyright, patent, trademark, and
attribution notices from the Source form of the Work,
excluding those notices that do not pertain to any part of
the Derivative Works; and
(d) If the Work includes a "NOTICE" text file as part of its
distribution, then any Derivative Works that You distribute must
include a readable copy of the attribution notices contained
within such NOTICE file, excluding those notices that do not
pertain to any part of the Derivative Works, in at least one
of the following places: within a NOTICE text file distributed
as part of the Derivative Works; within the Source form or
documentation, if provided along with the Derivative Works; or,
within a display generated by the Derivative Works, if and
wherever such third-party notices normally appear. The contents
of the NOTICE file are for informational purposes only and
do not modify the License. You may add Your own attribution
notices within Derivative Works that You distribute, alongside
or as an addendum to the NOTICE text from the Work, provided
that such additional attribution notices cannot be construed
as modifying the License.
You may add Your own copyright statement to Your modifications and
may provide additional or different license terms and conditions
for use, reproduction, or distribution of Your modifications, or
for any such Derivative Works as a whole, provided Your use,
reproduction, and distribution of the Work otherwise complies with
the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise,
any Contribution intentionally submitted for inclusion in the Work
by You to the Licensor shall be under the terms and conditions of
this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify
the terms of any separate license agreement you may have executed
with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade
names, trademarks, service marks, or product names of the Licensor,
except as required for reasonable and customary use in describing the
origin of the Work and reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or
agreed to in writing, Licensor provides the Work (and each
Contributor provides its Contributions) on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied, including, without limitation, any warranties or conditions
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
PARTICULAR PURPOSE. You are solely responsible for determining the
appropriateness of using or redistributing the Work and assume any
risks associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory,
whether in tort (including negligence), contract, or otherwise,
unless required by applicable law (such as deliberate and grossly
negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special,
incidental, or consequential damages of any character arising as a
result of this License or out of the use or inability to use the
Work (including but not limited to damages for loss of goodwill,
work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses), even if such Contributor
has been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability. While redistributing
the Work or Derivative Works thereof, You may choose to offer,
and charge a fee for, acceptance of support, warranty, indemnity,
or other liability obligations and/or rights consistent with this
License. However, in accepting such obligations, You may act only
on Your own behalf and on Your sole responsibility, not on behalf
of any other Contributor, and only if You agree to indemnify,
defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason
of your accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS
APPENDIX: How to apply the Apache License to your work.
To apply the Apache License to your work, attach the following
boilerplate notice, with the fields enclosed by brackets "[]"
replaced with your own identifying information. (Don't include
the brackets!) The text should be enclosed in the appropriate
comment syntax for the file format. We also recommend that a
file or class name and description of purpose be included on the
same "printed page" as the copyright notice for easier
identification within third-party archives.
Copyright [yyyy] [name of copyright owner]
Licensed 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.
For PostgreSQL Database Management System:
(formerly known as Postgres, then as Postgres95)
Portions Copyright (c) 1996-2010, The PostgreSQL Global Development Group
Portions Copyright (c) 1994, The Regents of the University of California
Permission to use, copy, modify, and distribute this software and its documentation for any purpose,
without fee, and without a written agreement is hereby granted, provided that the above copyright notice
and this paragraph and the following two paragraphs appear in all copies.
IN NO EVENT SHALL THE UNIVERSITY OF CALIFORNIA BE LIABLE TO ANY PARTY FOR DIRECT,
INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES, INCLUDING LOST PROFITS,
ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF THE UNIVERSITY
OF CALIFORNIA HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
THE UNIVERSITY OF CALIFORNIA SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING,
BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
THE SOFTWARE PROVIDED HEREUNDER IS ON AN "AS IS" BASIS, AND THE UNIVERSITY OF CALIFORNIA
HAS NO OBLIGATIONS TO PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
age-PG16-v1.5.0-rc0/META.json 0000664 0000000 0000000 00000004362 14546062412 0015226 0 ustar 00root root 0000000 0000000 {
"name": "ApacheAGE",
"abstract": "Apache AGE is a PostgreSQL Extension that provides graph database functionality",
"description": "Apache AGE is a PostgreSQL Extension that provides graph database functionality. AGE is an acronym for A Graph Extension, and is inspired by Bitnine's fork of PostgreSQL 10, AgensGraph, which is a multi-model database. The goal of the project is to create single storage that can handle both relational and graph model data so that users can use standard ANSI SQL along with openCypher, the Graph query language. A graph consists of a set of vertices (also called nodes) and edges, where each individual vertex and edge possesses a map of properties. A vertex is the basic object of a graph, that can exist independently of everything else in the graph. An edge creates a directed connection between two vertices. A graph database is simply composed of vertices and edges. This type of database is useful when the meaning is in the relationships between the data. Relational databases can easily handle direct relationships, but indirect relationships are more difficult to deal with in relational databases. A graph database stores relationship information as a first-class entity. Apache AGE gives you the best of both worlds, simultaneously.",
"version": "1.3.0",
"maintainer": [
"users@age.apache.org"
],
"license": "apache_2_0",
"provides": {
"ApacheAGE": {
"abstract": "Apache AGE is a PostgreSQL Extension that provides graph database functionality",
"file": "age--1.3.0.sql",
"docfile": "README.md",
"version": "1.3.0"
}
},
"prereqs": {
"runtime": {
"requires": {
"PostgreSQL": "14.0.0"
}
}
},
"resources": {
"homepage": "https://github.com/apache/age/tree/master",
"bugtracker": {
"web": "https://github.com/apache/age/issues"
},
"repository": {
"url": "https://github.com/apache/age.git",
"web": "https://github.com/apache/age",
"type": "git"
}
},
"generated_by": "users@age.apache.org",
"meta-spec": {
"version": "1.0.0",
"url": "http://pgxn.org/meta/spec.txt"
},
"tags": [
"graphdb",
"graph-database"
]
}
age-PG16-v1.5.0-rc0/Makefile 0000664 0000000 0000000 00000012723 14546062412 0015245 0 ustar 00root root 0000000 0000000 # 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.
MODULE_big = age
age_sql = age--1.5.0.sql
OBJS = src/backend/age.o \
src/backend/catalog/ag_catalog.o \
src/backend/catalog/ag_graph.o \
src/backend/catalog/ag_label.o \
src/backend/catalog/ag_namespace.o \
src/backend/commands/graph_commands.o \
src/backend/commands/label_commands.o \
src/backend/executor/cypher_create.o \
src/backend/executor/cypher_merge.o \
src/backend/executor/cypher_set.o \
src/backend/executor/cypher_utils.o \
src/backend/nodes/ag_nodes.o \
src/backend/nodes/cypher_copyfuncs.o \
src/backend/nodes/cypher_outfuncs.o \
src/backend/nodes/cypher_readfuncs.o \
src/backend/optimizer/cypher_createplan.o \
src/backend/optimizer/cypher_pathnode.o \
src/backend/optimizer/cypher_paths.o \
src/backend/parser/ag_scanner.o \
src/backend/parser/cypher_analyze.o \
src/backend/parser/cypher_clause.o \
src/backend/executor/cypher_delete.o \
src/backend/parser/cypher_expr.o \
src/backend/parser/cypher_gram.o \
src/backend/parser/cypher_item.o \
src/backend/parser/cypher_keywords.o \
src/backend/parser/cypher_parse_agg.o \
src/backend/parser/cypher_parse_node.o \
src/backend/parser/cypher_parser.o \
src/backend/parser/cypher_transform_entity.o \
src/backend/utils/adt/age_graphid_ds.o \
src/backend/utils/adt/agtype.o \
src/backend/utils/adt/agtype_ext.o \
src/backend/utils/adt/agtype_gin.o \
src/backend/utils/adt/agtype_ops.o \
src/backend/utils/adt/agtype_parser.o \
src/backend/utils/adt/agtype_util.o \
src/backend/utils/adt/agtype_raw.o \
src/backend/utils/adt/age_global_graph.o \
src/backend/utils/adt/age_session_info.o \
src/backend/utils/adt/age_vle.o \
src/backend/utils/adt/cypher_funcs.o \
src/backend/utils/adt/ag_float8_supp.o \
src/backend/utils/adt/graphid.o \
src/backend/utils/ag_func.o \
src/backend/utils/graph_generation.o \
src/backend/utils/cache/ag_cache.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
EXTENSION = age
# to allow cleaning of previous (old) age--.sql files
all_age_sql = $(shell find . -maxdepth 1 -type f -regex './age--[0-9]+\.[0-9]+\.[0-9]+\.sql')
SQLS := $(shell cat sql/sql_files)
SQLS := $(addprefix sql/,$(SQLS))
SQLS := $(addsuffix .sql,$(SQLS))
DATA_built = $(age_sql)
# sorted in dependency order
REGRESS = scan \
graphid \
agtype \
catalog \
cypher \
expr \
cypher_create \
cypher_match \
cypher_unwind \
cypher_set \
cypher_remove \
cypher_delete \
cypher_with \
cypher_vle \
cypher_union \
cypher_call \
cypher_merge \
age_global_graph \
age_load \
index \
analyze \
graph_generation \
name_validation \
jsonb_operators \
drop
srcdir=`pwd`
ag_regress_dir = $(srcdir)/regress
REGRESS_OPTS = --load-extension=age --inputdir=$(ag_regress_dir) --outputdir=$(ag_regress_dir) --temp-instance=$(ag_regress_dir)/instance --port=61958 --encoding=UTF-8 --temp-config $(ag_regress_dir)/age_regression.conf
ag_regress_out = instance/ log/ results/ regression.*
EXTRA_CLEAN = $(addprefix $(ag_regress_dir)/, $(ag_regress_out)) src/backend/parser/cypher_gram.c src/include/parser/cypher_gram_def.h src/include/parser/cypher_kwlist_d.h $(all_age_sql)
GEN_KEYWORDLIST = $(PERL) -I ./tools/ ./tools/gen_keywordlist.pl
GEN_KEYWORDLIST_DEPS = ./tools/gen_keywordlist.pl tools/PerfectHash.pm
ag_include_dir = $(srcdir)/src/include
PG_CPPFLAGS = -I$(ag_include_dir) -I$(ag_include_dir)/parser
PG_CONFIG ?= pg_config
PGXS := $(shell $(PG_CONFIG) --pgxs)
include $(PGXS)
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)
$(GEN_KEYWORDLIST) --extern --varname CypherKeyword --output src/include/parser $<
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
$(age_sql):
@cat $(SQLS) > $@
src/backend/parser/ag_scanner.c: FLEX_NO_BACKUP=yes
installcheck: export LC_COLLATE=C
age-PG16-v1.5.0-rc0/NOTICE 0000664 0000000 0000000 00000000501 14546062412 0014500 0 ustar 00root root 0000000 0000000 Apache AGE
Copyright 2023 The Apache Software Foundation.
This product includes software developed at
The Apache Software Foundation (http://www.apache.org/).
Portions of Apache AGE were originally developed by Bitnine Co., Ltd. and were
donated to the Apache Software Foundation. Copyright 2019-2020 Bitnine Co., Ltd.
age-PG16-v1.5.0-rc0/README.md 0000664 0000000 0000000 00000030605 14546062412 0015063 0 ustar 00root root 0000000 0000000
is a leading multi-model graph database
Graph Processing & Analytics for Relational Databases
What is Apache AGE?
[Apache AGE](https://age.apache.org/#) is an extension for PostgreSQL that enables users to leverage a graph database on top of the existing relational databases. AGE is an acronym for A Graph Extension and is inspired by Bitnine's AgensGraph, a multi-model database fork of PostgreSQL. The basic principle of the project is to create a single storage that handles both the relational and graph data model so that the users can use the standard ANSI SQL along with openCypher, one of the most popular graph query languages today.
Since AGE is based on the powerful PostgreSQL RDBMS, it is robust and fully featured. AGE is optimized for handling complex connected graph data. It provides plenty of robust database features essential to the database environment, including ACID transactions, multi-version concurrency control (MVCC), stored procedure, triggers, constraints, sophisticated monitoring, and a flexible data model (JSON). Users with a relational database background who require graph data analytics can use this extension with minimal effort because they can use existing data without going through migration.
There is a strong need for cohesive, easy-to-implement multi-model databases. As an extension of PostgreSQL, AGE supports all the functionalities and features of PostgreSQL while also offering a graph model to boot.
Overview
Apache AGE is :
- **Powerful**: adds graph database support to the already popular PostgreSQL database: PostgreSQL is used by organizations including Apple, Spotify, and NASA.
- **Flexible**: allows you to perform openCypher queries, which makes complex queries much easier to write. It also enables querying multiple graphs at the same time.
- **Intelligent**: allows you to perform graph queries that are the basis for many next-level web services such as fraud detection, master data management, product recommendations, identity and relationship management, experience personalization, knowledge management, and more.
Features
- **Cypher Query**: supports graph query language
- **Hybrid Querying**: enables SQL and/or Cypher
- **Querying**: enables multiple graphs
- **Hierarchical**: graph label organization
- **Property Indexes**: on both vertices(nodes) and edges
- **Full PostgreSQL**: supports PG features
Documentation
Refer to our latest [Apache AGE documentation](https://age.apache.org/age-manual/master/index.html) to learn about installation, features, built-in functions, and Cypher queries.
Pre-Installation
Install the following essential libraries according to each OS. Building AGE from the source depends on the following Linux libraries (Ubuntu package names shown below):
- **CentOS**
```bash
yum install gcc glibc glib-common readline readline-devel zlib zlib-devel flex bison
```
- **Fedora**
```bash
dnf install gcc glibc bison flex readline readline-devel zlib zlib-devel
```
- **Ubuntu**
```bash
sudo apt-get install build-essential libreadline-dev zlib1g-dev flex bison
```
Installation
Apache AGE is intended to be simple to install and run. It can be installed with Docker and other traditional ways.
You can download the Postgres source code and install your own instance of Postgres. You can read instructions on how to install from source code for different versions on the official Postgres Website.
Install AGE on Linux and MacOS
Clone the github repository or download the download an official release.
Run the pg_config utility and check the version of PostgreSQL. Currently, only PostgreSQL versions 11, 12, 13, 14, 15 & 16 are supported. If you have any other version of Postgres, you will need to install PostgreSQL version 11, 12, 13, 14, 15 or 16.
```bash
pg_config
```
Run the following command in the source code directory of Apache AGE to build and install the extension.
```bash
make install
```
If the path to your Postgres installation is not in the PATH variable, add the path in the arguments:
```bash
make PG_CONFIG=/path/to/postgres/bin/pg_config install
```
For every connection of AGE you start, you will need to load the AGE extension.
```bash
CREATE EXTENSION age;
```
```bash
LOAD 'age';
```
```bash
SET search_path = ag_catalog, "$user", public;
```
Quick Start
To create a graph, use the create_graph function located in the ag_catalog namespace.
```bash
SELECT create_graph('graph_name');
```
To create a single vertex, use the CREATE clause.
```bash
SELECT *
FROM cypher('graph_name', $$
CREATE (n)
$$) as (v agtype);
```
To create a single vertex with the label, use the CREATE clause.
```bash
SELECT *
FROM cypher('graph_name', $$
CREATE (:label)
$$) as (v agtype);
```
To create a single vertex with label and properties, use the CREATE clause.
```bash
SELECT *
FROM cypher('graph_name', $$
CREATE (:label {property:value})
$$) as (v agtype);
```
To query the graph, you can use the MATCH clause.
```bash
SELECT *
FROM cypher('graph_name', $$
MATCH (v)
RETURN v
$$) as (v agtype);
```
You can use the following to create an edge, for example, between two nodes.
```bash
SELECT *
FROM cypher('graph_name', $$
MATCH (a:label), (b:label)
WHERE a.property = 'Node A' AND b.property = 'Node B'
CREATE (a)-[e:RELTYPE]->(b)
RETURN e
$$) as (e agtype);
```
To create an edge and set properties.
```bash
SELECT *
FROM cypher('graph_name', $$
MATCH (a:label), (b:label)
WHERE a.property = 'Node A' AND b.property = 'Node B'
CREATE (a)-[e:RELTYPE {property:a.property + '<->' + b.property}]->(b)
RETURN e
$$) as (e agtype);
```
Example
```bash
SELECT *
FROM cypher('graph_name', $$
MATCH (a:Person), (b:Person)
WHERE a.name = 'Node A' AND b.name = 'Node B'
CREATE (a)-[e:RELTYPE {name:a.name + '<->' + b.name}]->(b)
RETURN e
$$) as (e agtype);
```
Language Specific Drivers
Starting with Apache AGE is very simple. You can easily select your platform and incorporate the relevant SDK into your code.
- [Apache AGE Rust Driver](https://github.com/Dzordzu/rust-apache-age.git)
Community
Join the AGE community for help, questions, discussions, and contributions.
- Check our [website](https://age.apache.org/)
- Join the Live Chat on [Discord](https://discord.gg/EuK6EEg3k7)
- Follow us on [Twitter](https://twitter.com/apache_age?s=20&t=7Hu8Txk4vjvuEp-ryakacg)
- Subscribe to our developer mailing list by sending an email to dev-subscribe@age.apache.org
- Subscribe to our user mailing list by sending an email to users-subscribe@age.apache.org
- Subscribe to our committer mailing list (To become a committer) by sending an email to commits-subscribe@age.apache.org
Graph Visualization Tool for AGE
Apache AGE Viewer is a user interface for Apache AGE that provides visualization and exploration of data.
This web visualization tool allows users to enter complex graph queries and explore the results in graph and table forms.
Apache AGE Viewer is enhanced to proceed with extensive graph data and discover insights through various graph algorithms.
Apache AGE Viewer will become a graph data administration and development platform for Apache AGE to support multiple relational databases: .
**This is a visualization tool.**
After installing AGE Extension, you may use this tool to get access to the visualization features.

Video Links
You can also get help from these videos.
- Install on [Windows](https://www.youtube.com/watch?v=ddk8VX8Hm-I&list=PLGp3huJbWNDjgwP7s99Q-9_w1vxpjNHXG)
- Install on [MacOS](https://www.youtube.com/watch?v=0-qMwpDh0CA)
Contributing
You can improve ongoing efforts or initiate new ones by sending pull requests to [this repository](https://github.com/apache/age).
Also, you can learn from the code review process, how to merge pull requests, and from code style compliance to documentation by visiting the [Apache AGE official site - Developer Guidelines](https://age.apache.org/contribution/guide).
Send all your comments and inquiries to the user mailing list, users@age.apache.org.
age-PG16-v1.5.0-rc0/RELEASE 0000664 0000000 0000000 00000007462 14546062412 0014614 0 ustar 00root root 0000000 0000000 # 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.
Release Notes for Apache AGE release 1.5.0 for PostgreSQL version 16
Apache AGE 1.5.0 - Release Notes
NOTE: This is an initial release of Apache AGE for PostgreSQL version
16. There are no upgrade scripts to this release.
Add additional index support and performance enhancements (#1232)
Add an additional way to find a previous variable ref (#1450)
Add auto apply labeler workflow for PRs (#1161)
Add checks for array functions to recognize and decode VPC (#1064)
Add concat || operator to agtype (#1198)
Add exist(?, ?|, ?&) operators for agtype (#1218)
Add missing dependency in cypher_expr.c (#1256)
Add path extraction(#>, #>>)operators to agtype (#1224)
Add support for chained expressions in CASE (#1431)
Clean up agtype_to_int8, agtype_to_int4, & agtype_to_int2 (#1354)
Clean up #included files in parser directory (#1465)
Converted SQL main file into multiple files. (#1401)
docs: Add to Docker setup (#1204)
Extend access(->, ->>), addition and subtraction operators (#1258)
Extend agtype containment operators (@>, <@) (#1285)
Extend EXPLAIN and add config param to switch transformation of property filter (#1262)
Fix ambiguous conditions (#1373)
Fix Docker file to reflect PostgreSQL version 15 (#1449)
Fix DockerHub warning messages for latest (#1380)
Fix issue #1045 - error using path var in WHERE (#1295)
Fix Issue #1159 - Server terminates for SET plus-equal (#1160)
Fix issue #1219 - MERGE not seeing previous clause var (#1441)
Fix issue #1302 - crash on NULL input to UNWIND (#1304)
Fix issue #1303: Server crashes on executing SELECT * FROM agtype(null); (#1317)
Fix Issue #1305 - drop_label NULL cases (#1306)
Fix Issue #1329 - agtype_to_int4 crash (#1339)
Fix issue #1347 - unknow type of agtype container 0 (#1349)
Fix issue #1389 - Server crash on using null operand for access operators (#1390)
Fix issue #1393 - previous clause variables not seen with EXISTS (#1426)
Fix issue #1398 - SET followed by DELETE does not delete (#1412)
Fix issue #1399 EXISTS doesn't handle non-existent labels (#1400)
Fix Issue #945 - incorrect count(*) return values (#1288)
Fix typo in agtype_raw.h header guard (#1368)
Implement chained expression order of operations (#1402)
Implemented age_tail function (#1283)
Implemented the toBooleanList() function (#1014)
Implement EXISTS subquery for CASE (#1345)
Master to PostgreSQL version 16 (#1451)
Minor fix in `agtype_volatile_wrapper` function (#1172)
Modify COUNT() to output agtype (#1311)
Optimize performance of detach delete (#1271)
Optimize vertex and edge builder functions (#1252)
py_driver : optimised Antlr4ResultHandler to improve time (#1107)
Refactor Regression Tests for CASE statement (#1268)
Refactor the IN operator to use '= ANY()' syntax (#1236)
Removed unnecessary assignment (#1185)
Remove redundant job from CIs (#1473)
Remove unnecessary #include in src/backend/utils
Updated age.control.
Updated files, format, and version information.
Updated Makefile.
Updated RELEASE.
Update Discord channel in README.md (#1253)
Update README.md file for PostgreSQL version 16 support (#1463)
Update the Python Driver (#1246)
age-PG16-v1.5.0-rc0/age.control 0000664 0000000 0000000 00000001604 14546062412 0015737 0 ustar 00root root 0000000 0000000 # 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.
default_version = '1.5.0'
comment = 'AGE database extension'
module_pathname = '$libdir/age'
schema = 'ag_catalog'
age-PG16-v1.5.0-rc0/clang-format.5 0000664 0000000 0000000 00000005105 14546062412 0016241 0 ustar 00root root 0000000 0000000 # 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.
AlignAfterOpenBracket: Align
AlignConsecutiveAssignments: false
AlignConsecutiveDeclarations: false
AlignEscapedNewlines: DontAlign
AlignOperands: true
AlignTrailingComments: false
AllowAllParametersOfDeclarationOnNextLine: false
AllowShortBlocksOnASingleLine: false
AllowShortCaseLabelsOnASingleLine: false
AllowShortFunctionsOnASingleLine: None
AllowShortIfStatementsOnASingleLine: false
AllowShortLoopsOnASingleLine: false
AlwaysBreakAfterReturnType: None
AlwaysBreakBeforeMultilineStrings: false
BinPackArguments: true
BinPackParameters: true
BraceWrapping:
AfterControlStatement: true
AfterEnum: true
AfterFunction: true
AfterStruct: true
AfterUnion: true
BeforeElse: true
IndentBraces: false
SplitEmptyFunction: true
SplitEmptyRecord: true
BreakBeforeBinaryOperators: None
BreakBeforeBraces: Custom
BreakBeforeTernaryOperators: false
BreakStringLiterals: false
ColumnLimit: 79
CommentPragmas: ''
ContinuationIndentWidth: 4
Cpp11BracedListStyle: true
DerivePointerAlignment: false
DisableFormat: false
ForEachMacros:
- 'foreach'
IncludeCategories:
IncludeIsMainRegex: '$'
IndentCaseLabels: false
IndentWidth: 4
IndentWrappedFunctionNames: false
KeepEmptyLinesAtTheStartOfBlocks: false
Language: Cpp
MacroBlockBegin: ''
MacroBlockEnd: ''
MaxEmptyLinesToKeep: 1
PenaltyBreakAssignment: 30
PenaltyBreakBeforeFirstCallParameter: 30
PenaltyBreakComment: 10
PenaltyBreakString: 10
PenaltyExcessCharacter: 100
PenaltyReturnTypeOnItsOwnLine: 60
PointerAlignment: Right
ReflowComments: false
SortIncludes: true
SpaceAfterCStyleCast: false
SpaceBeforeAssignmentOperators: true
SpaceBeforeParens: ControlStatements
SpaceInEmptyParentheses: false
SpacesBeforeTrailingComments: 1
SpacesInCStyleCastParentheses: false
SpacesInContainerLiterals: false
SpacesInParentheses: false
SpacesInSquareBrackets: false
TabWidth: 4
UseTab: Never
age-PG16-v1.5.0-rc0/docker/ 0000775 0000000 0000000 00000000000 14546062412 0015047 5 ustar 00root root 0000000 0000000 age-PG16-v1.5.0-rc0/docker/Dockerfile 0000664 0000000 0000000 00000002610 14546062412 0017040 0 ustar 00root root 0000000 0000000 #
# 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.
#
FROM postgres:16
RUN apt-get update \
&& apt-get install -y --no-install-recommends --no-install-suggests \
bison \
build-essential \
flex \
postgresql-server-dev-16 \
locales
ENV LANG=en_US.UTF-8
ENV LC_COLLATE=en_US.UTF-8
ENV LC_CTYPE=en_US.UTF-8
RUN echo "en_US.UTF-8 UTF-8" > /etc/locale.gen \
&& locale-gen \
&& update-locale LANG=en_US.UTF-8
COPY . /age
WORKDIR /age
RUN make && make install
COPY docker/docker-entrypoint-initdb.d/00-create-extension-age.sql /docker-entrypoint-initdb.d/00-create-extension-age.sql
CMD ["postgres", "-c", "shared_preload_libraries=age"]
age-PG16-v1.5.0-rc0/docker/Dockerfile.dev 0000664 0000000 0000000 00000002740 14546062412 0017621 0 ustar 00root root 0000000 0000000 #
# 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.
#
FROM postgres:16
RUN apt-get update
RUN apt-get install --assume-yes --no-install-recommends --no-install-suggests \
bison \
build-essential \
flex \
postgresql-server-dev-16 \
locales
ENV LANG=en_US.UTF-8
ENV LC_COLLATE=en_US.UTF-8
ENV LC_CTYPE=en_US.UTF-8
RUN echo "en_US.UTF-8 UTF-8" > /etc/locale.gen \
&& locale-gen \
&& update-locale LANG=en_US.UTF-8
COPY . /age
# Set current working directory to /age/ and build.
WORKDIR /age
RUN make install
RUN chown -R postgres /age
USER postgres
RUN make installcheck
COPY docker/docker-entrypoint-initdb.d/00-create-extension-age.sql /docker-entrypoint-initdb.d/00-create-extension-age.sql
CMD ["postgres", "-c", "shared_preload_libraries=age"]
age-PG16-v1.5.0-rc0/docker/docker-entrypoint-initdb.d/ 0000775 0000000 0000000 00000000000 14546062412 0022220 5 ustar 00root root 0000000 0000000 age-PG16-v1.5.0-rc0/docker/docker-entrypoint-initdb.d/00-create-extension-age.sql 0000664 0000000 0000000 00000001467 14546062412 0027175 0 ustar 00root root 0000000 0000000 --
-- 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.
--
CREATE EXTENSION age; age-PG16-v1.5.0-rc0/drivers/ 0000775 0000000 0000000 00000000000 14546062412 0015256 5 ustar 00root root 0000000 0000000 age-PG16-v1.5.0-rc0/drivers/Agtype.g4 0000664 0000000 0000000 00000003551 14546062412 0016747 0 ustar 00root root 0000000 0000000 /*
* 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.
*/
grammar Agtype;
agType
: agValue EOF
;
agValue
: value typeAnnotation?
;
value
: STRING #StringValue
| INTEGER #IntegerValue
| floatLiteral #FloatValue
| 'true' #TrueBoolean
| 'false' #FalseBoolean
| 'null' #NullValue
| obj #ObjectValue
| array #ArrayValue
;
obj
: '{' pair (',' pair)* '}'
| '{' '}'
;
pair
: STRING ':' agValue
;
array
: '[' agValue (',' agValue)* ']'
| '[' ']'
;
typeAnnotation
: '::' IDENT
;
IDENT
: [A-Z_a-z][$0-9A-Z_a-z]*
;
STRING
: '"' (ESC | SAFECODEPOINT)* '"'
;
fragment ESC
: '\\' (["\\/bfnrt] | UNICODE)
;
fragment UNICODE
: 'u' HEX HEX HEX HEX
;
fragment HEX
: [0-9a-fA-F]
;
fragment SAFECODEPOINT
: ~ ["\\\u0000-\u001F]
;
INTEGER
: '-'? INT
;
fragment INT
: '0' | [1-9] [0-9]*
;
floatLiteral
: RegularFloat
| ExponentFloat
| '-'? 'Infinity'
| 'NaN'
;
RegularFloat
: '-'? INT DECIMAL
;
ExponentFloat
: '-'? INT DECIMAL? SCIENTIFIC
;
fragment DECIMAL
: '.' [0-9]+
;
fragment SCIENTIFIC
: [Ee][+-]? [0-9]+
;
WS
: [ \t\n\r] + -> skip
;
age-PG16-v1.5.0-rc0/drivers/README 0000664 0000000 0000000 00000002036 14546062412 0016137 0 ustar 00root root 0000000 0000000 # 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.
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
age-PG16-v1.5.0-rc0/drivers/docker-compose.yml 0000664 0000000 0000000 00000000306 14546062412 0020712 0 ustar 00root root 0000000 0000000 version: "3.3"
services:
db:
image: apache/age:${TAG}
environment:
- POSTGRES_USER=postgres
- POSTGRES_PASSWORD=agens
- POSTGRES_DB=postgres
ports:
- 5432:5432
age-PG16-v1.5.0-rc0/drivers/golang/ 0000775 0000000 0000000 00000000000 14546062412 0016525 5 ustar 00root root 0000000 0000000 age-PG16-v1.5.0-rc0/drivers/golang/LICENSE 0000664 0000000 0000000 00000026135 14546062412 0017541 0 ustar 00root root 0000000 0000000 Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
1. Definitions.
"License" shall mean the terms and conditions for use, reproduction,
and distribution as defined by Sections 1 through 9 of this document.
"Licensor" shall mean the copyright owner or entity authorized by
the copyright owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all
other entities that control, are controlled by, or are under common
control with that entity. For the purposes of this definition,
"control" means (i) the power, direct or indirect, to cause the
direction or management of such entity, whether by contract or
otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity
exercising permissions granted by this License.
"Source" form shall mean the preferred form for making modifications,
including but not limited to software source code, documentation
source, and configuration files.
"Object" form shall mean any form resulting from mechanical
transformation or translation of a Source form, including but
not limited to compiled object code, generated documentation,
and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or
Object form, made available under the License, as indicated by a
copyright notice that is included in or attached to the work
(an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object
form, that is based on (or derived from) the Work and for which the
editorial revisions, annotations, elaborations, or other modifications
represent, as a whole, an original work of authorship. For the purposes
of this License, Derivative Works shall not include works that remain
separable from, or merely link (or bind by name) to the interfaces of,
the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including
the original version of the Work and any modifications or additions
to that Work or Derivative Works thereof, that is intentionally
submitted to Licensor for inclusion in the Work by the copyright owner
or by an individual or Legal Entity authorized to submit on behalf of
the copyright owner. For the purposes of this definition, "submitted"
means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems,
and issue tracking systems that are managed by, or on behalf of, the
Licensor for the purpose of discussing and improving the Work, but
excluding communication that is conspicuously marked or otherwise
designated in writing by the copyright owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity
on behalf of whom a Contribution has been received by Licensor and
subsequently incorporated within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the
Work and such Derivative Works in Source or Object form.
3. Grant of Patent License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
(except as stated in this section) patent license to make, have made,
use, offer to sell, sell, import, and otherwise transfer the Work,
where such license applies only to those patent claims licensable
by such Contributor that are necessarily infringed by their
Contribution(s) alone or by combination of their Contribution(s)
with the Work to which such Contribution(s) was submitted. If You
institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work
or a Contribution incorporated within the Work constitutes direct
or contributory patent infringement, then any patent licenses
granted to You under this License for that Work shall terminate
as of the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the
Work or Derivative Works thereof in any medium, with or without
modifications, and in Source or Object form, provided that You
meet the following conditions:
(a) You must give any other recipients of the Work or
Derivative Works a copy of this License; and
(b) You must cause any modified files to carry prominent notices
stating that You changed the files; and
(c) You must retain, in the Source form of any Derivative Works
that You distribute, all copyright, patent, trademark, and
attribution notices from the Source form of the Work,
excluding those notices that do not pertain to any part of
the Derivative Works; and
(d) If the Work includes a "NOTICE" text file as part of its
distribution, then any Derivative Works that You distribute must
include a readable copy of the attribution notices contained
within such NOTICE file, excluding those notices that do not
pertain to any part of the Derivative Works, in at least one
of the following places: within a NOTICE text file distributed
as part of the Derivative Works; within the Source form or
documentation, if provided along with the Derivative Works; or,
within a display generated by the Derivative Works, if and
wherever such third-party notices normally appear. The contents
of the NOTICE file are for informational purposes only and
do not modify the License. You may add Your own attribution
notices within Derivative Works that You distribute, alongside
or as an addendum to the NOTICE text from the Work, provided
that such additional attribution notices cannot be construed
as modifying the License.
You may add Your own copyright statement to Your modifications and
may provide additional or different license terms and conditions
for use, reproduction, or distribution of Your modifications, or
for any such Derivative Works as a whole, provided Your use,
reproduction, and distribution of the Work otherwise complies with
the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise,
any Contribution intentionally submitted for inclusion in the Work
by You to the Licensor shall be under the terms and conditions of
this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify
the terms of any separate license agreement you may have executed
with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade
names, trademarks, service marks, or product names of the Licensor,
except as required for reasonable and customary use in describing the
origin of the Work and reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or
agreed to in writing, Licensor provides the Work (and each
Contributor provides its Contributions) on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied, including, without limitation, any warranties or conditions
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
PARTICULAR PURPOSE. You are solely responsible for determining the
appropriateness of using or redistributing the Work and assume any
risks associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory,
whether in tort (including negligence), contract, or otherwise,
unless required by applicable law (such as deliberate and grossly
negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special,
incidental, or consequential damages of any character arising as a
result of this License or out of the use or inability to use the
Work (including but not limited to damages for loss of goodwill,
work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses), even if such Contributor
has been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability. While redistributing
the Work or Derivative Works thereof, You may choose to offer,
and charge a fee for, acceptance of support, warranty, indemnity,
or other liability obligations and/or rights consistent with this
License. However, in accepting such obligations, You may act only
on Your own behalf and on Your sole responsibility, not on behalf
of any other Contributor, and only if You agree to indemnify,
defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason
of your accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS
APPENDIX: How to apply the Apache License to your work.
To apply the Apache License to your work, attach the following
boilerplate notice, with the fields enclosed by brackets "[]"
replaced with your own identifying information. (Don't include
the brackets!) The text should be enclosed in the appropriate
comment syntax for the file format. We also recommend that a
file or class name and description of purpose be included on the
same "printed page" as the copyright notice for easier
identification within third-party archives.
Copyright [yyyy] [name of copyright owner]
Licensed 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.
age-PG16-v1.5.0-rc0/drivers/golang/README.md 0000664 0000000 0000000 00000003311 14546062412 0020002 0 ustar 00root root 0000000 0000000 # age AGType parser and driver support for Golang
AGType parser and driver support for [Apache AGE](https://age.apache.org/), graph extension for PostgreSQL.
### Features
* Unmarshal AGE result data(AGType) to Vertex, Edge, Path
* Cypher query support for 3rd. Party sql driver (enables to use cypher queries directly)
### Prerequisites
* over Go 1.18 / 1.19
* This module runs on golang standard api [database/sql](https://golang.org/pkg/database/sql/) and [antlr4-python3](https://github.com/antlr/antlr4/tree/master/runtime/Go/antlr)
### Installation (From source)
Run (Windows): install.bat
Run (Linux & OSX):
```
sh install.sh
```
### Go get
```
go get github.com/apache/age/drivers/golang
```
### gomod
```
require github.com/apache/age/drivers/golang {version}
```
Check [latest version](https://github.com/apache/age/releases)
### 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/docs/
### Check AGE loaded on your PostgreSQL
Connect to your containerized Postgres instance and then run the following commands:
```(sql)
# psql
CREATE EXTENSION age;
LOAD 'age';
SET search_path = ag_catalog, "$user", public;
```
### Test
Check out and rewrite DSN in age/drivers/golang/age/age_test.go
```
cd age/drivers/golang/age
go test . -v
```
### Samples
* Usage 1: using database/sql API and Cypher execution function 'ExecCypher'
Sample : [samples/sql_api_sample.go](samples/sql_api_sample.go)
* Usage 2: using Age Wrapper
Sample : [samples/age_wrapper_sample.go](samples/age_wrapper_sample.go)
* Run Samples : [samples/main.go](samples/main.go)
### License
Apache-2.0 License
age-PG16-v1.5.0-rc0/drivers/golang/TYPES.md 0000664 0000000 0000000 00000001727 14546062412 0017762 0 ustar 00root root 0000000 0000000 # Apache AGE - Go driver Type mapping
* For more information about Apache AGE result types : https://age.apache.org/docs/Apache_AGE_Guide.pdf
| Type | AGE Result | Go Type |
|------|------------|---------|
|Vertex|::vertex |Vertex vertex.Id() int64 vertex.Label() string vertex.Prop(string) interface{} |
|Edge |::edge |Edge edge.Id() int64 edge.Label() string edge.StartId() int64 edge.EndId() int64 edge.Prop(string) interface{} |
|Path |::path |Path path.Size() int // quantity of vertices and edges in this path path.Get(index int) Entity // *Vertex or *Edge path.GetAsVertex(index int) *Vertex path.GetAsEdge(index int) *Edge |
|Integer |int |int64 |
|Float |float NaN, -Infinity, Infinity |float64 math.Nan(), math.Inf(-1),math.Inf(1) |
|Numeric |::numeric |*big.Int *big.Float |
|String|string |string |
|Boolean|bool |bool |
|Null|empty result |nil |
age-PG16-v1.5.0-rc0/drivers/golang/age/ 0000775 0000000 0000000 00000000000 14546062412 0017261 5 ustar 00root root 0000000 0000000 age-PG16-v1.5.0-rc0/drivers/golang/age/age.go 0000664 0000000 0000000 00000016544 14546062412 0020356 0 ustar 00root root 0000000 0000000 /*
* 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.
*/
package age
import (
"bytes"
"database/sql"
"fmt"
"reflect"
)
// GetReady prepare AGE extension
// load AGE extension
// set graph path
func GetReady(db *sql.DB, graphName string) (bool, error) {
tx, err := db.Begin()
if err != nil {
return false, err
}
_, err = tx.Exec("LOAD 'age';")
if err != nil {
return false, err
}
_, err = tx.Exec("SET search_path = ag_catalog, '$user', public;")
if err != nil {
return false, err
}
var count int = 0
err = tx.QueryRow("SELECT count(*) FROM ag_graph WHERE name=$1", graphName).Scan(&count)
if err != nil {
return false, err
}
if count == 0 {
_, err = tx.Exec("SELECT create_graph($1);", graphName)
if err != nil {
return false, err
}
}
tx.Commit()
return true, nil
}
type CursorProvider func(columnCount int, rows *sql.Rows) Cursor
type Cursor interface {
Next() bool
Close() error
}
func execCypher(cursorProvider CursorProvider, tx *sql.Tx, graphName string, columnCount int, cypher string, args ...interface{}) (Cursor, error) {
var buf bytes.Buffer
cypherStmt := fmt.Sprintf(cypher, args...)
buf.WriteString("SELECT * from cypher(NULL,NULL) as (v0 agtype")
for i := 1; i < columnCount; i++ {
buf.WriteString(fmt.Sprintf(", v%d agtype", i))
}
buf.WriteString(");")
stmt := buf.String()
// Pass in the graph name and cypher statement via parameters to prepare
// the cypher function call for session info.
prepare_stmt := "SELECT * FROM age_prepare_cypher($1, $2);"
_, perr := tx.Exec(prepare_stmt, graphName, cypherStmt)
if perr != nil {
fmt.Println(prepare_stmt + " " + graphName + " " + cypher)
return nil, perr
}
if columnCount == 0 {
_, err := tx.Exec(stmt)
if err != nil {
fmt.Println(stmt)
return nil, err
}
return nil, nil
} else {
rows, err := tx.Query(stmt)
if err != nil {
fmt.Println(stmt)
return nil, err
}
return cursorProvider(columnCount, rows), nil
}
}
// ExecCypher : execute cypher query
// CREATE , DROP ....
// MATCH .... RETURN ....
// CREATE , DROP .... RETURN ...
func ExecCypher(tx *sql.Tx, graphName string, columnCount int, cypher string, args ...interface{}) (*CypherCursor, error) {
cursor, err := execCypher(NewCypherCursor, tx, graphName, columnCount, cypher, args...)
var cypherCursor *CypherCursor
if cursor != nil {
cypherCursor = cursor.(*CypherCursor)
}
return cypherCursor, err
}
// ExecCypherMap
// CREATE , DROP ....
// MATCH .... RETURN ....
// CREATE , DROP .... RETURN ...
func ExecCypherMap(tx *sql.Tx, graphName string, columnCount int, cypher string, args ...interface{}) (*CypherMapCursor, error) {
cursor, err := execCypher(NewCypherMapCursor, tx, graphName, columnCount, cypher, args...)
var cypherMapCursor *CypherMapCursor
if cursor != nil {
cypherMapCursor = cursor.(*CypherMapCursor)
}
return cypherMapCursor, err
}
type Age struct {
db *sql.DB
graphName string
}
type AgeTx struct {
age *Age
tx *sql.Tx
}
/**
@param dsn host=127.0.0.1 port=5432 dbname=postgres user=postgres password=agens sslmode=disable
*/
func ConnectAge(graphName string, dsn string) (*Age, error) {
db, err := sql.Open("postgres", dsn)
if err != nil {
return nil, err
}
age := &Age{db: db, graphName: graphName}
_, err = age.GetReady()
if err != nil {
db.Close()
age = nil
}
return age, err
}
func NewAge(graphName string, db *sql.DB) *Age {
return &Age{db: db, graphName: graphName}
}
func (age *Age) GetReady() (bool, error) {
tx, err := age.db.Begin()
if err != nil {
return false, err
}
_, err = tx.Exec("LOAD 'age';")
if err != nil {
return false, err
}
_, err = tx.Exec("SET search_path = ag_catalog, '$user', public;")
if err != nil {
return false, err
}
var count int = 0
err = tx.QueryRow("SELECT count(*) FROM ag_graph WHERE name=$1", age.graphName).Scan(&count)
if err != nil {
return false, err
}
if count == 0 {
_, err = tx.Exec("SELECT create_graph($1);", age.graphName)
if err != nil {
return false, err
}
}
tx.Commit()
return true, nil
}
func (a *Age) Close() error {
return a.db.Close()
}
func (a *Age) DB() *sql.DB {
return a.db
}
func (a *Age) Begin() (*AgeTx, error) {
ageTx := &AgeTx{age: a}
tx, err := a.db.Begin()
if err != nil {
return nil, err
}
ageTx.tx = tx
return ageTx, err
}
func (t *AgeTx) Commit() error {
return t.tx.Commit()
}
func (t *AgeTx) Rollback() error {
return t.tx.Rollback()
}
/** CREATE , DROP .... */
func (a *AgeTx) ExecCypher(columnCount int, cypher string, args ...interface{}) (*CypherCursor, error) {
return ExecCypher(a.tx, a.age.graphName, columnCount, cypher, args...)
}
func (a *AgeTx) ExecCypherMap(columnCount int, cypher string, args ...interface{}) (*CypherMapCursor, error) {
return ExecCypherMap(a.tx, a.age.graphName, columnCount, cypher, args...)
}
type CypherCursor struct {
Cursor
columnCount int
rows *sql.Rows
unmarshaler Unmarshaller
}
func NewCypherCursor(columnCount int, rows *sql.Rows) Cursor {
return &CypherCursor{columnCount: columnCount, rows: rows, unmarshaler: NewAGUnmarshaler()}
}
func (c *CypherCursor) Next() bool {
return c.rows.Next()
}
func (c *CypherCursor) GetRow() ([]Entity, error) {
var gstrs = make([]interface{}, c.columnCount)
for i := 0; i < c.columnCount; i++ {
gstrs[i] = new(string)
}
err := c.rows.Scan(gstrs...)
if err != nil {
return nil, fmt.Errorf("CypherCursor.GetRow:: %s", err)
}
entArr := make([]Entity, c.columnCount)
for i := 0; i < c.columnCount; i++ {
gstr := gstrs[i].(*string)
e, err := c.unmarshaler.unmarshal(*gstr)
if err != nil {
fmt.Println(i, ">>", gstr)
return nil, err
}
entArr[i] = e
}
return entArr, nil
}
func (c *CypherCursor) Close() error {
return c.rows.Close()
}
//
type CypherMapCursor struct {
CypherCursor
mapper *AGMapper
}
func NewCypherMapCursor(columnCount int, rows *sql.Rows) Cursor {
mapper := NewAGMapper(make(map[string]reflect.Type))
pcursor := CypherCursor{columnCount: columnCount, rows: rows, unmarshaler: mapper}
return &CypherMapCursor{CypherCursor: pcursor, mapper: mapper}
}
func (c *CypherMapCursor) PutType(label string, tp reflect.Type) {
c.mapper.PutType(label, tp)
}
func (c *CypherMapCursor) GetRow() ([]interface{}, error) {
entities, err := c.CypherCursor.GetRow()
if err != nil {
return nil, fmt.Errorf("CypherMapCursor.GetRow:: %s", err)
}
elArr := make([]interface{}, c.columnCount)
for i := 0; i < c.columnCount; i++ {
ent := entities[i]
if ent.GType() == G_MAP_PATH {
elArr[i] = ent
} else {
elArr[i] = ent.(*SimpleEntity).Value()
}
}
return elArr, nil
}
age-PG16-v1.5.0-rc0/drivers/golang/age/age_test.go 0000664 0000000 0000000 00000023746 14546062412 0021417 0 ustar 00root root 0000000 0000000 /*
* 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.
*/
package age
import (
"fmt"
"reflect"
"testing"
"database/sql"
_ "github.com/lib/pq"
)
var dsn string = "host=127.0.0.1 port=5432 dbname=postgres user=postgres password=agens sslmode=disable"
var graphName string = "testGraph"
func TestAdditional(t *testing.T) {
db, err := sql.Open("postgres", dsn)
if err != nil {
t.Fatal(err)
}
_, err = GetReady(db, graphName)
if err != nil {
t.Fatal(err)
}
cursor, err := db.Begin()
if err != nil {
t.Fatal(err)
}
_, err = ExecCypher(cursor, graphName, 0, "CREATE (n:Person {name: '%s', weight:%f})", "Joe", 67.3)
if err != nil {
t.Fatal(err)
}
_, err = ExecCypher(cursor, graphName, 0, "CREATE (n:Person {name: '%s', weight:77.3, roles:['Dev','marketing']})", "Jack")
if err != nil {
t.Fatal(err)
}
_, err = ExecCypher(cursor, graphName, 0, "CREATE (n:Person {name: '%s', weight:%d})", "Andy", 59)
if err != nil {
t.Fatal(err)
}
cursor.Commit()
cursor, err = db.Begin()
if err != nil {
t.Fatal(err)
}
cypherCursor, err := ExecCypher(cursor, graphName, 1, "MATCH (n:Person) RETURN n")
if err != nil {
t.Fatal(err)
}
for cypherCursor.Next() {
entities, err := cypherCursor.GetRow()
if err != nil {
t.Fatal(err)
}
vertex := entities[0].(*Vertex)
fmt.Println(vertex.Id(), vertex.Label(), vertex.Props())
}
_, err = ExecCypher(cursor, graphName, 0, "MATCH (a:Person), (b:Person) WHERE a.name='%s' AND b.name='%s' CREATE (a)-[r:workWith {weight: %d}]->(b)",
"Jack", "Joe", 3)
if err != nil {
t.Fatal(err)
}
_, err = ExecCypher(cursor, graphName, 0, "MATCH (a:Person {name: '%s'}), (b:Person {name: '%s'}) CREATE (a)-[r:workWith {weight: %d}]->(b)",
"Joe", "Andy", 7)
if err != nil {
t.Fatal(err)
}
cursor.Commit()
cursor, err = db.Begin()
if err != nil {
t.Fatal(err)
}
cypherCursor, err = ExecCypher(cursor, graphName, 1, "MATCH p=()-[:workWith]-() RETURN p")
if err != nil {
t.Fatal(err)
}
for cypherCursor.Next() {
entities, err := cypherCursor.GetRow()
if err != nil {
t.Fatal(err)
}
path := entities[0].(*Path)
vertexStart := path.GetAsVertex(0)
edge := path.GetAsEdge(1)
vertexEnd := path.GetAsVertex(2)
fmt.Println(vertexStart, edge, vertexEnd)
}
_, err = ExecCypher(cursor, graphName, 0, "MATCH (n:Person) DETACH DELETE n RETURN *")
if err != nil {
t.Fatal(err)
}
cursor.Commit()
}
func TestAgeWrapper(t *testing.T) {
ag, err := ConnectAge(graphName, dsn)
if err != nil {
t.Fatal(err)
}
tx, err := ag.Begin()
if err != nil {
t.Fatal(err)
}
_, err = tx.ExecCypher(0, "CREATE (n:Person {name: '%s'})", "Joe")
if err != nil {
t.Fatal(err)
}
_, err = tx.ExecCypher(0, "CREATE (n:Person {name: '%s', age: %d})", "Smith", 10)
if err != nil {
t.Fatal(err)
}
_, err = tx.ExecCypher(0, "CREATE (n:Person {name: '%s', weight:%f})", "Jack", 70.3)
if err != nil {
t.Fatal(err)
}
tx.Commit()
tx, err = ag.Begin()
if err != nil {
t.Fatal(err)
}
cursor, err := tx.ExecCypher(1, "MATCH (n:Person) RETURN n")
if err != nil {
t.Fatal(err)
}
count := 0
for cursor.Next() {
entities, err := cursor.GetRow()
if err != nil {
t.Fatal(err)
}
count++
vertex := entities[0].(*Vertex)
fmt.Println(count, "]", vertex.Id(), vertex.Label(), vertex.Props())
}
fmt.Println("Vertex Count:", count)
_, err = tx.ExecCypher(0, "MATCH (a:Person), (b:Person) WHERE a.name='%s' AND b.name='%s' CREATE (a)-[r:workWith {weight: %d}]->(b)",
"Jack", "Joe", 3)
if err != nil {
t.Fatal(err)
}
_, err = tx.ExecCypher(0, "MATCH (a:Person {name: '%s'}), (b:Person {name: '%s'}) CREATE (a)-[r:workWith {weight: %d}]->(b)",
"Joe", "Smith", 7)
if err != nil {
t.Fatal(err)
}
tx.Commit()
tx, err = ag.Begin()
if err != nil {
t.Fatal(err)
}
cursor, err = tx.ExecCypher(1, "MATCH p=()-[:workWith]-() RETURN p")
if err != nil {
t.Fatal(err)
}
count = 0
for cursor.Next() {
entities, err := cursor.GetRow()
if err != nil {
t.Fatal(err)
}
count++
path := entities[0].(*Path)
fmt.Println(count, "]", path.GetAsVertex(0), path.GetAsEdge(1).props, path.GetAsVertex(2))
}
_, err = tx.ExecCypher(0, "MATCH (n:Person) DETACH DELETE n RETURN *")
if err != nil {
t.Fatal(err)
}
tx.Commit()
}
func TestQueryWithMapper(t *testing.T) {
db, err := sql.Open("postgres", dsn)
if err != nil {
t.Fatal(err)
}
// Confirm graph_path created
_, err = GetReady(db, graphName)
if err != nil {
t.Fatal(err)
}
// Tx begin for execute create vertex
tx, err := db.Begin()
if err != nil {
t.Fatal(err)
}
// Create Vertex
ExecCypher(tx, graphName, 0, "CREATE (n:Person {name: '%s'})", "Joe")
ExecCypher(tx, graphName, 0, "CREATE (n:Person {name: '%s', age: %d})", "Smith", 10)
ExecCypher(tx, graphName, 0, "CREATE (n:Person {name: '%s', weight:%f})", "Jack", 70.3)
tx.Commit()
tx, err = db.Begin()
if err != nil {
t.Fatal(err)
}
// Match
mapCursor, err := ExecCypherMap(tx, graphName, 1, "MATCH (n:Person) RETURN n")
if err != nil {
t.Fatal(err)
}
mapCursor.PutType("Person", reflect.TypeOf(VPerson{}))
count := 0
for mapCursor.Next() {
entities, err := mapCursor.GetRow()
if err != nil {
t.Fatal(err)
}
count++
person := entities[0].(VPerson)
fmt.Println(count, "]", person.Name, person.Age, person.Weight)
}
// Create Path
ExecCypher(tx, graphName, 0, "MATCH (a:Person), (b:Person) WHERE a.name='%s' AND b.name='%s' CREATE (a)-[r:workWith {weight: %d}]->(b)",
"Jack", "Joe", 3)
ExecCypher(tx, graphName, 0, "MATCH (a:Person {name: '%s'}), (b:Person {name: '%s'}) CREATE (a)-[r:workWith {weight: %d}]->(b)",
"Joe", "Smith", 7)
tx.Commit()
tx, err = db.Begin()
if err != nil {
t.Fatal(err)
}
// Query Path
mapCursor, err = ExecCypherMap(tx, graphName, 3, "MATCH (a)-[b:workWith]-(c) RETURN a,b,c")
if err != nil {
t.Fatal(err)
}
mapCursor.PutType("Person", reflect.TypeOf(VPerson{}))
mapCursor.PutType("workWith", reflect.TypeOf(EWorkWith{}))
count = 0
for mapCursor.Next() {
entities, err := mapCursor.GetRow()
if err != nil {
t.Fatal(err)
}
count++
person1 := entities[0].(VPerson)
workWith := entities[1].(EWorkWith)
person2 := entities[2].(VPerson)
fmt.Println(count, "]", person1, workWith, person2)
}
// Clear Data
_, err = ExecCypher(tx, graphName, 0, "MATCH (n:Person) DETACH DELETE n RETURN *")
if err != nil {
t.Fatal(err)
}
tx.Commit()
}
func TestCudReturn(t *testing.T) {
db, err := sql.Open("postgres", dsn)
if err != nil {
t.Fatal(err)
}
// Confirm graph_path created
_, err = GetReady(db, graphName)
if err != nil {
t.Fatal(err)
}
// Tx begin for execute create vertex
tx, err := db.Begin()
if err != nil {
t.Fatal(err)
}
// Create Vertex
cursor, err := ExecCypher(tx, graphName, 1, "CREATE (n:Person {name: '%s'}) RETURN n", "Joe")
if err != nil {
t.Fatal(err)
}
for cursor.Next() {
fmt.Println(cursor.GetRow())
}
cursor, err = ExecCypher(tx, graphName, 1, "CREATE (n:Person {name: '%s', age: %d}) RETURN n", "Smith", 10)
if err != nil {
t.Fatal(err)
}
for cursor.Next() {
fmt.Println(cursor.GetRow())
}
cursor, err = ExecCypher(tx, graphName, 1, "CREATE (n:Person {name: '%s', weight:%f}) RETURN n", "Jack", 70.3)
if err != nil {
t.Fatal(err)
}
for cursor.Next() {
fmt.Println(cursor.GetRow())
}
tx.Commit()
tx, err = db.Begin()
if err != nil {
t.Fatal(err)
}
cursor, err = ExecCypher(tx, graphName, 1, "MATCH (n:Person) RETURN n")
if err != nil {
t.Fatal(err)
}
for cursor.Next() {
fmt.Println(cursor.GetRow())
}
}
func TestQueryManyReturn(t *testing.T) {
ag, err := ConnectAge(graphName, dsn)
if err != nil {
t.Fatal(err)
}
tx, err := ag.Begin()
if err != nil {
t.Fatal(err)
}
// Create Vertex
tx.ExecCypher(0, "CREATE (n:Person {name: '%s'})", "Joe")
tx.ExecCypher(0, "CREATE (n:Person {name: '%s', age: %d})", "Smith", 10)
tx.ExecCypher(0, "CREATE (n:Person {name: '%s', weight:%f})", "Jack", 70.3)
tx.Commit()
tx, err = ag.Begin()
if err != nil {
t.Fatal(err)
}
// Create Path
tx.ExecCypher(0, "MATCH (a:Person), (b:Person) WHERE a.name='%s' AND b.name='%s' CREATE (a)-[r:workWith {weight: %d}]->(b)",
"Jack", "Joe", 3)
tx.ExecCypher(0, "MATCH (a:Person {name: '%s'}), (b:Person {name: '%s'}) CREATE (a)-[r:workWith {weight: %d}]->(b)",
"Joe", "Smith", 7)
tx.Commit()
tx, err = ag.Begin()
if err != nil {
t.Fatal(err)
}
// Query Path1
cursor, err := tx.ExecCypher(3, "MATCH (a:Person)-[l:workWith]-(b:Person) RETURN a, l, b")
if err != nil {
t.Fatal(err)
}
count := 0
for cursor.Next() {
entities, err := cursor.GetRow()
if err != nil {
t.Fatal(err)
}
count++
v1 := entities[0].(*Vertex)
edge := entities[1].(*Edge)
v2 := entities[2].(*Vertex)
fmt.Println("ROW ", count, ">>", "\n\t", v1, "\n\t", edge, "\n\t", v2)
}
// Query Path2
cursor, err = tx.ExecCypher(1, "MATCH p=(a:Person)-[l:workWith]-(b:Person) WHERE a.name = '%s' RETURN p", "Joe")
if err != nil {
t.Fatal(err)
}
count = 0
for cursor.Next() {
entities, err := cursor.GetRow()
if err != nil {
t.Fatal(err)
}
count++
path := entities[0].(*Path)
fmt.Println("ROW ", count, ">>", "\n\t", path.GetAsVertex(0),
"\n\t", path.GetAsEdge(1),
"\n\t", path.GetAsVertex(2))
}
// Clear Data
_, err = tx.ExecCypher(0, "MATCH (n:Person) DETACH DELETE n RETURN *")
if err != nil {
t.Fatal(err)
}
tx.Commit()
}
age-PG16-v1.5.0-rc0/drivers/golang/age/builder.go 0000664 0000000 0000000 00000017305 14546062412 0021244 0 ustar 00root root 0000000 0000000 /*
* 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.
*/
package age
import (
"fmt"
"math"
"math/big"
"strconv"
"strings"
"github.com/antlr/antlr4/runtime/Go/antlr/v4"
"github.com/apache/age/drivers/golang/parser"
)
const MaxUint = ^uint(0)
const MaxInt = int(MaxUint >> 1)
const MinUint = 0
const MinInt = -MaxInt - 1
type Unmarshaller interface {
unmarshal(text string) (Entity, error)
}
type AGUnmarshaler struct {
Unmarshaller
ageParser *parser.AgeParser
visitor parser.AgeVisitor
errListener *AGErrorListener
vcache map[int64]interface{}
}
func NewAGUnmarshaler() *AGUnmarshaler {
vcache := make(map[int64]interface{})
m := &AGUnmarshaler{ageParser: parser.NewAgeParser(nil),
visitor: &UnmarshalVisitor{vcache: vcache},
errListener: NewAGErrorListener(),
vcache: vcache,
}
m.ageParser.AddErrorListener(m.errListener)
return m
}
func (p *AGUnmarshaler) unmarshal(text string) (Entity, error) {
if len(text) == 0 {
return NewSimpleEntity(nil), nil
}
input := antlr.NewInputStream(text)
lexer := parser.NewAgeLexer(input)
stream := antlr.NewCommonTokenStream(lexer, 0)
p.ageParser.SetInputStream(stream)
tree := p.ageParser.Ageout()
rst := tree.Accept(p.visitor)
if len(p.errListener.errList) > 0 {
var ape *AgeParseError = nil
errs := make([]string, len(p.errListener.errList))
for idx, re := range p.errListener.errList {
errs[idx] = re.GetMessage()
fmt.Println(re)
}
p.errListener.clearErrs()
ape = &AgeParseError{msg: "Cypher query:" + text, errors: errs}
return nil, ape
}
if !IsEntity(rst) {
rst = NewSimpleEntity(rst)
}
return rst.(Entity), nil
}
type AGErrorListener struct {
*antlr.DefaultErrorListener
errList []antlr.RecognitionException
}
func NewAGErrorListener() *AGErrorListener {
return &AGErrorListener{DefaultErrorListener: &antlr.DefaultErrorListener{}, errList: []antlr.RecognitionException{}}
}
func (el *AGErrorListener) SyntaxError(recognizer antlr.Recognizer, offendingSymbol interface{}, line, column int, msg string, e antlr.RecognitionException) {
el.errList = append(el.errList, e)
}
func (el *AGErrorListener) getErrs() []antlr.RecognitionException {
return el.errList
}
func (el *AGErrorListener) clearErrs() {
el.errList = []antlr.RecognitionException{}
}
type UnmarshalVisitor struct {
parser.AgeVisitor
vcache map[int64]interface{}
}
func (v *UnmarshalVisitor) Visit(tree antlr.ParseTree) interface{} { return nil }
func (v *UnmarshalVisitor) VisitChildren(node antlr.RuleNode) interface{} {
var rtn interface{}
for _, c := range node.GetChildren() {
pt := c.(antlr.ParseTree)
rtn = pt.Accept(v)
}
return rtn
}
func (v *UnmarshalVisitor) VisitTerminal(node antlr.TerminalNode) interface{} { return nil }
func (v *UnmarshalVisitor) VisitErrorNode(node antlr.ErrorNode) interface{} { return nil }
func (v *UnmarshalVisitor) VisitAgeout(ctx *parser.AgeoutContext) interface{} {
rtn := v.VisitChildren(ctx)
return rtn
}
// Visit a parse tree produced by AgeParser#vertex.
func (v *UnmarshalVisitor) VisitVertex(ctx *parser.VertexContext) interface{} {
propCtx := ctx.Properties()
props := propCtx.Accept(v).(map[string]interface{})
// fmt.Println(" * VisitVertex:", props)
vid := int64(props["id"].(int64))
vertex, ok := v.vcache[vid]
if !ok {
vertex = NewVertex(vid, props["label"].(string), props["properties"].(map[string]interface{}))
v.vcache[vid] = vertex
}
return vertex
}
// Visit a parse tree produced by AgeParser#edge.
func (v *UnmarshalVisitor) VisitEdge(ctx *parser.EdgeContext) interface{} {
propCtx := ctx.Properties()
props := propCtx.Accept(v).(map[string]interface{})
// fmt.Println(" * VisitEdge:", props)
edge := NewEdge(int64(props["id"].(int64)), props["label"].(string),
int64(props["start_id"].(int64)), int64(props["end_id"].(int64)),
props["properties"].(map[string]interface{}))
return edge
}
// Visit a parse tree produced by AgeParser#path.
func (v *UnmarshalVisitor) VisitPath(ctx *parser.PathContext) interface{} {
entities := []Entity{}
for _, child := range ctx.GetChildren() {
switch child.(type) {
case *parser.VertexContext:
v := child.(*parser.VertexContext).Accept(v)
// fmt.Println(v)
entities = append(entities, v.(Entity))
case *parser.EdgeContext:
e := child.(*parser.EdgeContext).Accept(v)
// fmt.Println(e)
entities = append(entities, e.(Entity))
default:
}
}
path := NewPath(entities)
return path
}
// Visit a parse tree produced by AgeParser#value.
func (v *UnmarshalVisitor) VisitValue(ctx *parser.ValueContext) interface{} {
child := ctx.GetChild(0)
switch child.(type) {
case *antlr.TerminalNodeImpl:
rtn, err := unmarshalTerm(child.(*antlr.TerminalNodeImpl))
if err != nil {
panic(err)
}
return rtn
default:
return child.(antlr.ParserRuleContext).Accept(v)
}
}
// Visit a parse tree produced by AgeParser#properties.
func (v *UnmarshalVisitor) VisitProperties(ctx *parser.PropertiesContext) interface{} {
props := make(map[string]interface{})
for _, pairCtx := range ctx.AllPair() {
pairCtx.Accept(v)
pair := pairCtx.(*parser.PairContext)
key := strings.Trim(pair.STRING().GetText(), "\"")
// fmt.Println("Pair KEY:", key)
value := pair.Value().Accept(v)
props[key] = value
}
return props
}
// Visit a parse tree produced by AgeParser#pair.
func (v *UnmarshalVisitor) VisitPair(ctx *parser.PairContext) interface{} {
return nil
}
// Visit a parse tree produced by AgeParser#arr.
func (v *UnmarshalVisitor) VisitArr(ctx *parser.ArrContext) interface{} {
var arr []interface{}
for _, child := range ctx.GetChildren() {
switch child.(type) {
case *antlr.TerminalNodeImpl:
// skip
break
default:
el := child.(antlr.ParserRuleContext).Accept(v)
arr = append(arr, el)
}
}
return arr
}
func unmarshalTerm(ctx *antlr.TerminalNodeImpl) (interface{}, error) {
txt := ctx.GetText()
switch ctx.GetSymbol().GetTokenType() {
case parser.AgeLexerSTRING:
return strings.Trim(txt, "\""), nil
case parser.AgeLexerNUMERIC:
numStr := txt[:len(txt)-9]
// fmt.Println("txt ", txt)
// fmt.Println("numStr", numStr)
if strings.Contains(numStr, ".") {
bi := new(big.Float)
bi, ok := bi.SetString(numStr)
if !ok {
return nil, &AgeParseError{msg: "Parse big float " + txt}
}
return bi, nil
} else {
bi := new(big.Int)
bi, ok := bi.SetString(numStr, 10)
if !ok {
return nil, &AgeParseError{msg: "Parse big int " + txt}
}
return bi, nil
}
case parser.AgeLexerNUMBER:
if strings.Contains(txt, ".") {
return strconv.ParseFloat(txt, 64)
} else {
return strconv.ParseInt(txt, 10, 64)
}
case parser.AgeLexerFLOAT_EXPR:
switch txt {
case "NaN":
return math.NaN(), nil
case "-Infinity":
return math.Inf(-1), nil
case "Infinity":
return math.Inf(1), nil
default:
return nil, &AgeParseError{msg: "Unknown float expression" + txt}
}
case parser.AgeLexerBOOL:
s, err := strconv.ParseBool(txt)
if err != nil {
return nil, err
} else {
return s, nil
}
case parser.AgeLexerNULL:
return nil, nil
default:
return nil, nil
}
}
age-PG16-v1.5.0-rc0/drivers/golang/age/builder_test.go 0000664 0000000 0000000 00000016346 14546062412 0022307 0 ustar 00root root 0000000 0000000 /*
* 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.
*/
package age
import (
"fmt"
"math"
"math/big"
"testing"
"github.com/stretchr/testify/assert"
)
func TestPathParsing(t *testing.T) {
rstStr1 := `[{"id": 2251799813685425, "label": "Person", "properties": {"name": "Smith"}}::vertex,
{"id": 2533274790396576, "label": "workWith", "end_id": 2251799813685425, "start_id": 2251799813685424,
"properties": {"weight": 3, "bigFloat":123456789123456789123456789.12345::numeric}}::edge,
{"id": 2251799813685424, "label": "Person", "properties": {"name": "Joe"}}::vertex]::path`
rstStr2 := `[{"id": 2251799813685424, "label": "Person", "properties": {"name": "Joe"}}::vertex,
{"id": 2533274790396576, "label": "workWith", "end_id": 2251799813685425, "start_id": 2251799813685424, "properties": {"weight": 3}}::edge,
{"id": 2251799813685425, "label": "Person", "properties": {"name": "Smith"}}::vertex]::path`
rstStr3 := `[{"id": 2251799813685424, "label": "Person", "properties": {"name": "Joe"}}::vertex,
{"id": 2533274790396579, "label": "workWith", "end_id": 2251799813685426, "start_id": 2251799813685424, "properties": {"weight": 5}}::edge,
{"id": 2251799813685426, "label": "Person", "properties": {"name": "Jack", "arrVal":["A","B"]}}::vertex]::path`
unmarshaler := NewAGUnmarshaler()
entity1, _ := unmarshaler.unmarshal(rstStr1)
entity2, _ := unmarshaler.unmarshal(rstStr2)
entity3, _ := unmarshaler.unmarshal(rstStr3)
assert.Equal(t, entity1.GType(), entity2.GType(), "Type Check")
p1 := entity1.(*Path)
p2 := entity2.(*Path)
p3 := entity3.(*Path)
assert.Equal(t, p1.GetAsVertex(0).props["name"], p2.GetAsVertex(2).props["name"])
assert.Equal(t, p2.GetAsVertex(0).props["name"], p3.GetAsVertex(0).props["name"])
bf := new(big.Float)
bf, _ = bf.SetString("123456789123456789123456789.12345")
bigFloat := p1.GetAsEdge(1).props["bigFloat"]
assert.Equal(t, bf, bigFloat)
fmt.Println(entity1)
fmt.Println(entity2)
fmt.Println(entity3)
}
func TestVertexParsing(t *testing.T) {
rstStr := `{"id": 2251799813685425, "label": "Person",
"properties": {"name": "Smith", "numInt":123, "numIntBig":12345678901235555555555555555::numeric, "numFloat": 384.23424,
"yn":true, "nullVal": null}}::vertex`
unmarshaler := NewAGUnmarshaler()
entity, _ := unmarshaler.unmarshal(rstStr)
// fmt.Println(entity)
assert.Equal(t, G_VERTEX, entity.GType())
v := entity.(*Vertex)
assert.Equal(t, "Smith", v.props["name"])
assert.True(t, (int64(123) == v.props["numInt"]))
assert.Equal(t, int64(123), v.props["numInt"])
bi := new(big.Int)
bi, ok := bi.SetString("12345678901235555555555555555", 10)
if !ok {
fmt.Println("Cannot reach this line. ")
}
assert.Equal(t, bi, v.props["numIntBig"])
assert.True(t, (384.23424 == v.props["numFloat"]))
assert.Equal(t, float64(384.23424), v.props["numFloat"])
assert.Equal(t, true, v.props["yn"])
assert.Nil(t, v.props["nullVal"])
}
func TestNormalValueParsing(t *testing.T) {
mapStr := `{"name": "Smith", "num":123, "yn":true}`
arrStr := `["name", "Smith", "num", 123, "yn", true]`
strStr := `"abcd"`
intStr := `1234`
floatStr := `1234.56789`
floatStr2 := `6.45161290322581e+46`
numericStr1 := `12345678901234567890123456.789::numeric`
numericStr2 := `12345678901234567890123456::numeric`
boolStr := `true`
nullStr := ""
nanStr := "NaN"
infpStr := "Infinity"
infnStr := "-Infinity"
unmarshaler := NewAGUnmarshaler()
mapv, _ := unmarshaler.unmarshal(mapStr)
arrv, _ := unmarshaler.unmarshal(arrStr)
str2, _ := unmarshaler.unmarshal(strStr)
intv, _ := unmarshaler.unmarshal(intStr)
fl, _ := unmarshaler.unmarshal(floatStr)
fl2, _ := unmarshaler.unmarshal(floatStr2)
numeric1, _ := unmarshaler.unmarshal(numericStr1)
numeric2, _ := unmarshaler.unmarshal(numericStr2)
b, _ := unmarshaler.unmarshal(boolStr)
nullVal, _ := unmarshaler.unmarshal(nullStr)
nanVal, _ := unmarshaler.unmarshal(nanStr)
infpVal, _ := unmarshaler.unmarshal(infpStr)
infnVal, _ := unmarshaler.unmarshal(infnStr)
// fmt.Println("intv", intv.GType(), reflect.TypeOf(intv.(*SimpleEntity).Value()), intv)
assert.Equal(t, G_MAP, mapv.GType())
assert.Equal(t, G_ARR, arrv.GType())
assert.Equal(t, G_STR, str2.GType())
assert.Equal(t, G_INT, intv.GType())
assert.Equal(t, G_FLOAT, fl.GType())
assert.Equal(t, G_FLOAT, fl2.GType())
assert.Equal(t, G_FLOATBIG, numeric1.GType())
assert.Equal(t, G_INTBIG, numeric2.GType())
assert.Equal(t, G_BOOL, b.GType())
assert.Equal(t, G_NULL, nullVal.GType())
assert.Equal(t, G_FLOAT, nanVal.GType())
assert.Equal(t, G_FLOAT, infpVal.GType())
assert.Equal(t, G_FLOAT, infnVal.GType())
assert.Equal(t, map[string]interface{}{"name": "Smith", "num": int64(123), "yn": true}, mapv.(*SimpleEntity).Value())
assert.Equal(t, []interface{}{"name", "Smith", "num", int64(123), "yn", true}, arrv.(*SimpleEntity).Value())
assert.Equal(t, "abcd", str2.(*SimpleEntity).Value())
assert.Equal(t, int64(1234), intv.(*SimpleEntity).Value())
assert.Equal(t, 1234.56789, fl.(*SimpleEntity).Value())
assert.Equal(t, 6.45161290322581e+46, fl2.(*SimpleEntity).Value())
assert.Equal(t, true, b.(*SimpleEntity).Value())
assert.Equal(t, nil, nullVal.(*SimpleEntity).Value())
assert.True(t, math.IsNaN(nanVal.(*SimpleEntity).Value().(float64)))
assert.Equal(t, math.Inf(1), infpVal.(*SimpleEntity).Value())
assert.Equal(t, math.Inf(-1), infnVal.(*SimpleEntity).Value())
bf := new(big.Float)
bf, _ = bf.SetString("12345678901234567890123456.789")
assert.Equal(t, bf, numeric1.(*SimpleEntity).Value())
bi := new(big.Int)
bi, _ = bi.SetString("12345678901234567890123456", 10)
assert.Equal(t, bi, numeric2.(*SimpleEntity).Value())
}
func TestMap(t *testing.T) {
mapStr := `{"name": "Smith", "num":123, "yn":true, "arr":["A","B",1], "map":{"a":1, "b":"bv"}}`
unmarshaler := NewAGUnmarshaler()
mapv, _ := unmarshaler.unmarshal(mapStr)
assert.Equal(t, G_MAP, mapv.GType())
mapValue := mapv.(*SimpleEntity).Value().(map[string]interface{})
assert.Equal(t, "Smith", mapValue["name"])
assert.Equal(t, []interface{}{"A", "B", int64(1)}, mapValue["arr"])
assert.Equal(t, map[string]interface{}{"a": int64(1), "b": "bv"}, mapValue["map"])
}
func TestArray(t *testing.T) {
arrayStr := `[ "Smith", 123, true, ["A","B",1], {"a":1, "b":"bv"}]`
unmarshaler := NewAGUnmarshaler()
arrayv, _ := unmarshaler.unmarshal(arrayStr)
assert.Equal(t, G_ARR, arrayv.GType())
arrValue := arrayv.(*SimpleEntity).Value().([]interface{})
assert.Equal(t, "Smith", arrValue[0])
assert.Equal(t, []interface{}{"A", "B", int64(1)}, arrValue[3])
assert.Equal(t, map[string]interface{}{"a": int64(1), "b": "bv"}, arrValue[4])
}
age-PG16-v1.5.0-rc0/drivers/golang/age/errors.go 0000664 0000000 0000000 00000002461 14546062412 0021127 0 ustar 00root root 0000000 0000000 /*
* 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.
*/
package age
import (
"bytes"
"fmt"
)
type AgeError struct {
cause error
msg string
}
func (e *AgeError) Error() string {
if e.cause != nil {
return fmt.Sprintf("%s >> Cause:%s", e.msg, e.cause.Error())
}
return e.msg
}
type AgeParseError struct {
msg string
errors []string
}
func (e *AgeParseError) Error() string {
var buf bytes.Buffer
buf.WriteString(e.msg)
buf.WriteString(" >> Causes:\n")
for _, err := range e.errors {
buf.WriteString(err)
buf.WriteString("\n")
}
return buf.String()
}
age-PG16-v1.5.0-rc0/drivers/golang/age/mapper.go 0000664 0000000 0000000 00000011355 14546062412 0021101 0 ustar 00root root 0000000 0000000 /*
* 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.
*/
package age
import (
"fmt"
"reflect"
"strings"
"github.com/antlr/antlr4/runtime/Go/antlr/v4"
"github.com/apache/age/drivers/golang/parser"
)
type AGMapper struct {
AGUnmarshaler
}
func NewAGMapper(typeMap map[string]reflect.Type) *AGMapper {
vcache := make(map[int64]interface{})
if typeMap == nil {
typeMap = make(map[string]reflect.Type)
}
m := AGUnmarshaler{ageParser: parser.NewAgeParser(nil),
visitor: &MapperVisitor{UnmarshalVisitor: UnmarshalVisitor{vcache: vcache},
typeMap: typeMap},
errListener: NewAGErrorListener(),
vcache: vcache,
}
agm := &AGMapper{AGUnmarshaler: m}
agm.ageParser.AddErrorListener(agm.errListener)
return agm
}
func (m *AGMapper) PutType(label string, tp reflect.Type) {
m.visitor.(*MapperVisitor).PutType(label, tp)
}
type MapperVisitor struct {
UnmarshalVisitor
typeMap map[string]reflect.Type
}
func (v *MapperVisitor) PutType(label string, tp reflect.Type) {
v.typeMap[label] = tp
}
func (v *MapperVisitor) VisitAgeout(ctx *parser.AgeoutContext) interface{} {
rtn := v.VisitChildren(ctx)
return rtn
}
func (v *MapperVisitor) VisitChildren(node antlr.RuleNode) interface{} {
var rtn interface{}
for _, c := range node.GetChildren() {
pt := c.(antlr.ParseTree)
rtn = pt.Accept(v)
}
return rtn
}
func (v *MapperVisitor) VisitPath(ctx *parser.PathContext) interface{} {
entities := []interface{}{}
for _, child := range ctx.GetChildren() {
switch child.(type) {
case *parser.VertexContext:
v := child.(*parser.VertexContext).Accept(v)
// fmt.Println(v)
entities = append(entities, v)
case *parser.EdgeContext:
e := child.(*parser.EdgeContext).Accept(v)
// fmt.Println(e)
entities = append(entities, e)
default:
}
}
// vctxArr := ctx.AllVertex()
// start := vctxArr[0].Accept(v)
// rel := ctx.Edge().Accept(v)
// end := vctxArr[1].Accept(v)
// fmt.Println("VisitPath:", reflect.TypeOf(start), reflect.TypeOf(rel), reflect.TypeOf(rel))
path := NewMapPath(entities)
return path
}
func (v *MapperVisitor) VisitVertex(ctx *parser.VertexContext) interface{} {
propCtx := ctx.Properties()
props := propCtx.Accept(v).(map[string]interface{})
vid := int64(props["id"].(int64))
vertex, ok := v.vcache[vid]
var err error
if !ok {
vertex, err = v.mapVertex(vid, props["label"].(string), props["properties"].(map[string]interface{}))
if err != nil {
panic(err)
}
v.vcache[vid] = vertex
}
// fmt.Println(" * VisitVertex:", vertex)
return vertex
}
// Visit a parse tree produced by AgeParser#edge.
func (v *MapperVisitor) VisitEdge(ctx *parser.EdgeContext) interface{} {
propCtx := ctx.Properties()
props := propCtx.Accept(v).(map[string]interface{})
vid := props["id"].(int64)
edge, ok := v.vcache[vid]
var err error
if !ok {
edge, err = v.mapEdge(vid, props["label"].(string), props["start_id"].(int64), props["end_id"].(int64),
props["properties"].(map[string]interface{}))
if err != nil {
panic(err)
}
v.vcache[vid] = edge
}
return edge
}
func (v *MapperVisitor) mapVertex(vid int64, label string, properties map[string]interface{}) (interface{}, error) {
tp, ok := v.typeMap[label]
if !ok {
return NewVertex(vid, label, properties), nil
}
return mapStruct(tp, properties)
}
func (v *MapperVisitor) mapEdge(vid int64, label string, start int64, end int64, properties map[string]interface{}) (interface{}, error) {
tp, ok := v.typeMap[label]
if !ok {
return NewEdge(vid, label, start, end, properties), nil
}
return mapStruct(tp, properties)
}
func mapStruct(tp reflect.Type, properties map[string]interface{}) (interface{}, error) {
value := reflect.New(tp).Elem()
for k, v := range properties {
k = strings.Title(k)
f, ok := tp.FieldByName(k)
if ok {
field := value.FieldByIndex(f.Index)
val := reflect.ValueOf(v)
if field.Type().ConvertibleTo(val.Type()) {
field.Set(val)
} else {
return nil, fmt.Errorf("Property[%s] value[%v] type is not convertable to %v", k, v, field.Type())
}
}
}
return value.Interface(), nil
}
age-PG16-v1.5.0-rc0/drivers/golang/age/mapper_test.go 0000664 0000000 0000000 00000005466 14546062412 0022146 0 ustar 00root root 0000000 0000000 /*
* 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.
*/
package age
import (
"fmt"
"reflect"
"testing"
"github.com/stretchr/testify/assert"
)
type VPerson struct {
Name string
Age int64
Weight float64
}
type EWorkWith struct {
Weight int64
}
func TestPathMapping(t *testing.T) {
rstStr1 := `[{"id": 2251799813685425, "label": "Person", "properties": {"name": "Smith"}}::vertex,
{"id": 2533274790396576, "label": "workWith", "end_id": 2251799813685425, "start_id": 2251799813685424, "properties": {"weight": 3}}::edge,
{"id": 2251799813685424, "label": "Person", "properties": {"name": "Joe"}}::vertex]::path`
rstStr2 := `[{"id": 2251799813685424, "label": "Person", "properties": {"name": "Joe"}}::vertex,
{"id": 2533274790396576, "label": "workWith", "end_id": 2251799813685425, "start_id": 2251799813685424, "properties": {"weight": 3}}::edge,
{"id": 2251799813685425, "label": "Person", "properties": {"name": "Smith"}}::vertex]::path`
rstStr3 := `[{"id": 2251799813685424, "label": "Person", "properties": {"name": "Joe"}}::vertex,
{"id": 2533274790396579, "label": "workWith", "end_id": 2251799813685426, "start_id": 2251799813685424, "properties": {"weight": 5}}::edge,
{"id": 2251799813685426, "label": "Person", "properties": {"name": "Jack"}}::vertex]::path`
mapper := NewAGMapper(nil)
mapper.PutType("Person", reflect.TypeOf(VPerson{}))
mapper.PutType("workWith", reflect.TypeOf(EWorkWith{}))
entity1, _ := mapper.unmarshal(rstStr1)
entity2, _ := mapper.unmarshal(rstStr2)
entity3, _ := mapper.unmarshal(rstStr3)
fmt.Println(" **** ", entity1)
fmt.Println(" **** ", entity2)
fmt.Println(" **** ", entity3)
assert.Equal(t, entity1.GType(), entity2.GType(), "Type Check")
p1 := entity1.(*MapPath)
p2 := entity2.(*MapPath)
p3 := entity3.(*MapPath)
assert.Equal(t, p1.Get(2).(VPerson).Name, p2.Get(0).(VPerson).Name)
assert.Equal(t, p2.Get(0).(VPerson).Name, p3.Get(0).(VPerson).Name)
assert.Equal(t, p1.Get(1).(EWorkWith).Weight, int64(3))
assert.Equal(t, p2.Get(1).(EWorkWith).Weight, int64(3))
assert.Equal(t, p3.Get(1).(EWorkWith).Weight, int64(5))
}
age-PG16-v1.5.0-rc0/drivers/golang/age/models.go 0000664 0000000 0000000 00000015151 14546062412 0021076 0 ustar 00root root 0000000 0000000 /*
* 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.
*/
package age
import (
"bytes"
"fmt"
"math/big"
"reflect"
)
// GTYPE representing entity types for AGE result data : Vertex, Edge, Path and SimpleEntity
type GTYPE uint8
const (
G_OTHER GTYPE = 1 + iota
G_VERTEX
G_EDGE
G_PATH
G_MAP_PATH
G_STR
G_INT
G_INTBIG
G_FLOAT
G_FLOATBIG
G_BOOL
G_NULL
G_MAP
G_ARR
)
var _TpV = reflect.TypeOf(&Vertex{})
var _TpE = reflect.TypeOf(&Edge{})
var _TpP = reflect.TypeOf(&Path{})
var _TpMP = reflect.TypeOf(&MapPath{})
var _TpStr = reflect.TypeOf(string(""))
var _TpInt = reflect.TypeOf(int64(0))
var _TpIntBig = reflect.TypeOf(big.NewInt(0))
var _TpFloat = reflect.TypeOf(float64(0))
var _TpFloatBig = reflect.TypeOf(big.NewFloat(0))
var _TpBool = reflect.TypeOf(bool(false))
var _TpMap = reflect.TypeOf(map[string]interface{}{})
var _TpArr = reflect.TypeOf([]interface{}{})
// Entity object interface for parsed AGE result data : Vertex, Edge, Path and SimpleEntity
type Entity interface {
GType() GTYPE
String() string
}
func IsEntity(v interface{}) bool {
_, ok := v.(Entity)
return ok
}
type SimpleEntity struct {
Entity
typ GTYPE
value interface{}
}
func NewSimpleEntity(value interface{}) *SimpleEntity {
if value == nil {
return &SimpleEntity{typ: G_NULL, value: nil}
}
switch value.(type) {
case string:
return &SimpleEntity{typ: G_STR, value: value}
case int64:
return &SimpleEntity{typ: G_INT, value: value}
case *big.Int:
return &SimpleEntity{typ: G_INTBIG, value: value}
case float64:
return &SimpleEntity{typ: G_FLOAT, value: value}
case *big.Float:
return &SimpleEntity{typ: G_FLOATBIG, value: value}
case bool:
return &SimpleEntity{typ: G_BOOL, value: value}
case map[string]interface{}:
return &SimpleEntity{typ: G_MAP, value: value}
case []interface{}:
return &SimpleEntity{typ: G_ARR, value: value}
default:
return &SimpleEntity{typ: G_OTHER, value: value}
}
}
func (e *SimpleEntity) GType() GTYPE {
return e.typ
}
func (e *SimpleEntity) IsNull() bool {
return e.value == nil
}
func (e *SimpleEntity) Value() interface{} {
return e.value
}
func (e *SimpleEntity) String() string {
return fmt.Sprintf("%v", e.value)
}
func (e *SimpleEntity) AsStr() string {
return e.value.(string)
}
func (e *SimpleEntity) AsInt() int {
return e.value.(int)
}
func (e *SimpleEntity) AsInt64() int64 {
return e.value.(int64)
}
func (e *SimpleEntity) AsBigInt() *big.Int {
return e.value.(*big.Int)
}
func (e *SimpleEntity) AsFloat() float64 {
return e.value.(float64)
}
func (e *SimpleEntity) AsBigFloat() *big.Float {
return e.value.(*big.Float)
}
func (e *SimpleEntity) AsBool() bool {
return e.value.(bool)
}
func (e *SimpleEntity) AsMap() map[string]interface{} {
return e.value.(map[string]interface{})
}
func (e *SimpleEntity) AsArr() []interface{} {
return e.value.([]interface{})
}
type LabeledEntity struct {
Entity
id int64
label string
props map[string]interface{}
}
func newLabeledEntity(id int64, label string, props map[string]interface{}) *LabeledEntity {
return &LabeledEntity{id: id, label: label, props: props}
}
func (n *LabeledEntity) Id() int64 {
return n.id
}
func (n *LabeledEntity) Label() string {
return n.label
}
func (n *LabeledEntity) Prop(key string) interface{} {
return n.props[key]
}
// return properties
func (n *LabeledEntity) Props() map[string]interface{} {
return n.props
}
type Vertex struct {
*LabeledEntity
}
func NewVertex(id int64, label string, props map[string]interface{}) *Vertex {
return &Vertex{newLabeledEntity(id, label, props)}
}
func (v *Vertex) GType() GTYPE {
return G_VERTEX
}
func (v *Vertex) String() string {
return fmt.Sprintf("V{id:%d, label:%s, props:%v}", v.id, v.label, v.props)
}
type Edge struct {
*LabeledEntity
start_id int64
end_id int64
}
func NewEdge(id int64, label string, start int64, end int64, props map[string]interface{}) *Edge {
return &Edge{LabeledEntity: newLabeledEntity(id, label, props), start_id: start, end_id: end}
}
func (e *Edge) GType() GTYPE {
return G_EDGE
}
func (e *Edge) StartId() int64 {
return e.start_id
}
func (e *Edge) EndId() int64 {
return e.end_id
}
func (e *Edge) String() string {
return fmt.Sprintf("E{id:%d, label:%s, start:%d, end:%d, props:%v}",
e.id, e.label, e.start_id, e.end_id, e.props)
}
type Path struct {
Entity
entities []Entity
}
func NewPath(entities []Entity) *Path {
return &Path{entities: entities}
}
func (e *Path) GType() GTYPE {
return G_PATH
}
func (e *Path) Size() int {
return len(e.entities)
}
func (e *Path) Get(index int) Entity {
if index < 0 && index >= len(e.entities) {
panic(fmt.Errorf("Entity index[%d] is out of range (%d) ", index, len(e.entities)))
}
return e.entities[index]
}
func (e *Path) GetAsVertex(index int) *Vertex {
v := e.Get(index)
if v.GType() != G_VERTEX {
panic(fmt.Errorf("Entity[%d] is not Vertex", index))
}
return v.(*Vertex)
}
func (e *Path) GetAsEdge(index int) *Edge {
v := e.Get(index)
if v.GType() != G_EDGE {
panic(fmt.Errorf("Entity[%d] is not Edge", index))
}
return v.(*Edge)
}
func (p *Path) String() string {
var buf bytes.Buffer
buf.WriteString("P[")
for _, e := range p.entities {
buf.WriteString(e.String())
buf.WriteString(",")
}
buf.WriteString("]")
return buf.String()
}
type MapPath struct {
Entity
entities []interface{}
}
func NewMapPath(entities []interface{}) *MapPath {
return &MapPath{entities: entities}
}
func (e *MapPath) GType() GTYPE {
return G_MAP_PATH
}
func (e *MapPath) Size() int {
return len(e.entities)
}
func (e *MapPath) Get(index int) interface{} {
if index < 0 && index >= len(e.entities) {
panic(fmt.Errorf("Entity index[%d] is out of range (%d) ", index, len(e.entities)))
}
return e.entities[index]
}
func (p *MapPath) String() string {
var buf bytes.Buffer
buf.WriteString("P[")
for _, e := range p.entities {
buf.WriteString(fmt.Sprintf("%v", e))
buf.WriteString(",")
}
buf.WriteString("]")
return buf.String()
}
age-PG16-v1.5.0-rc0/drivers/golang/go.mod 0000664 0000000 0000000 00000002436 14546062412 0017640 0 ustar 00root root 0000000 0000000 // /*
// * 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.
// */
module github.com/apache/age/drivers/golang
go 1.19
require (
github.com/antlr/antlr4/runtime/Go/antlr/v4 v4.0.0-20230321174746-8dcc6526cfb1
github.com/lib/pq v1.10.7
github.com/stretchr/testify v1.7.0
)
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-20230321023759-10a507213a29 // indirect
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c // indirect
)
age-PG16-v1.5.0-rc0/drivers/golang/go.sum 0000664 0000000 0000000 00000004103 14546062412 0017656 0 ustar 00root root 0000000 0000000 github.com/antlr/antlr4/runtime/Go/antlr/v4 v4.0.0-20230219212500-1f9a474cc2dc h1:ikxgKfnYm4kXCOohe1uCkVFwZcABDZbVsqginko+GY8=
github.com/antlr/antlr4/runtime/Go/antlr/v4 v4.0.0-20230219212500-1f9a474cc2dc/go.mod h1:pSwJ0fSY5KhvocuWSx4fz3BA8OrA1bQn+K1Eli3BRwM=
github.com/antlr/antlr4/runtime/Go/antlr/v4 v4.0.0-20230321174746-8dcc6526cfb1 h1:X8MJ0fnN5FPdcGF5Ij2/OW+HgiJrRg3AfHAx1PJtIzM=
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/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-20230223210539-50820d90acfd h1:wtFuj4DoOcAdb82Zh2PI90xiaqgp7maYA7KxjQXVtkY=
golang.org/x/exp v0.0.0-20230223210539-50820d90acfd/go.mod h1:CxIveKay+FTh1D0yPZemJVgC/95VzuuOLq5Qi4xnoYc=
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=
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 h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
age-PG16-v1.5.0-rc0/drivers/golang/install.bat 0000664 0000000 0000000 00000006430 14546062412 0020666 0 ustar 00root root 0000000 0000000 @echo off
rem Install Java Development Kit (JDK)
echo Installing JDK...
if not exist "%ProgramFiles%\Java\jdk-19\" (
mkdir C:\temp
cd C:\temp
powershell Invoke-WebRequest -Uri "https://download.oracle.com/java/19/archive/jdk-19.0.2_windows-x64_bin.exe" -OutFile jdk-19.0.2_windows-x64_bin.exe
start /wait jdk-19.0.2_windows-x64_bin.exe /s ADDLOCAL="ToolsFeature" /s
del /f jdk-19.0.2_windows-x64_bin.exe
setx /M JAVA_HOME "C:\Program Files\Java\jdk-19.0.2"
setx /M PATH "%PATH%;%JAVA_HOME%\bin"
) else (
echo JDK already installed.
)
rem Install Apache Maven
echo Installing Apache Maven...
if not exist "%ProgramFiles%\Apache Maven\apache-maven-3.9.0\" (
mkdir C:\temp
cd C:\temp
powershell Invoke-WebRequest -Uri "https://dlcdn.apache.org/maven/maven-3/3.9.0/binaries/apache-maven-3.9.0-bin.zip" -OutFile apache-maven-3.9.0-bin.zip
powershell Expand-Archive apache-maven-3.9.0-bin.zip -DestinationPath \"%ProgramFiles%\Apache Maven\"
del /f apache-maven-3.9.0-bin.zip
setx /M PATH "%PATH%;%ProgramFiles%\Apache Maven\apache-maven-3.9.0-bin\bin"
) else (
echo Apache Maven already installed.
)
rem Download and install ANTLR
echo Downloading ANTLR...
if not exist "%ProgramFiles%\ANTLR" (
mkdir "%ProgramFiles%\ANTLR"
cd "%ProgramFiles%\ANTLR"
powershell Invoke-WebRequest -Uri "https://www.antlr.org/download/antlr-4.11.1-complete.jar" -OutFile "antlr-4.11.1-complete.jar"
setx /M PATH "%PATH%;%ProgramFiles%\ANTLR\"
setx /M CLASSPATH ".;%ProgramFiles%\ANTLR\antlr-4.11.1-complete.jar;%CLASSPATH%"
)
echo ANTLR installation complete.
rem Checking Compatibility for Golang
echo Checking if current version of Golang >= Go 1.18.....
set "minimum_version=1.18"
set "installed_version="
set "download_url=https://go.dev/dl/go1.19.7.windows-amd64.msi"
:: Check if Go is installed and get its version
set "go_path="
for %%i in (go.exe) do set "go_path=%%~$PATH:i"
if defined go_path (
for /f "tokens=3" %%v in ('go version 2^>^&1') do set "installed_version=%%v"
)
:: If Go is not installed or the version is less than 1.18, prompt the user to install a new version
if not defined installed_version (
echo installing Go
:: Download and install the latest version of Go
powershell -Command "& {Invoke-WebRequest -Uri "%download_url%" -OutFile '%TEMP%\go-minimum-version.msi'}"
start /wait msiexec /i "%TEMP%\go-minimum-version.msi"
for /f "tokens=3" %%v in ('go version 2^>^&1') do set "installed_version=%%v"
) else if "%installed_version%" lss "%minimum_version%" (
set /p "install_new_version=Go version %minimum_version% or higher is required. Would you like to install the latest version? (y/n)"
if /i "%install_new_version%"=="y" (
:: Download and install the latest version of Go
powershell -Command "& {Invoke-WebRequest -Uri "%download_url%" -OutFile '%TEMP%\go-minimum-version.msi'}"
start /wait msiexec /i "%TEMP%\go-minimum_version.msi"
for /f "tokens=3" %%v in ('go version 2^>^&1') do set "installed_version=%%v"
) else (
echo Please update Go version before installing driver.
goto skip
)
)
rem Installing Driver
echo --^> Generating ANTLR parser ^& lexer ^for Golang%
java org.antlr.v4.Tool -Dlanguage=Go -visitor Age.g4 -o parser/
echo --^> Installing Driver
go get -u ./...
goto end
:skip
echo Aborted
:end
pause
endlocal age-PG16-v1.5.0-rc0/drivers/golang/install.sh 0000775 0000000 0000000 00000011121 14546062412 0020526 0 ustar 00root root 0000000 0000000 #!/bin/sh
os=$(uname)
arch=$(uname -m)
java=$(java -version 2>&1 | head -n 1 | cut -d ' ' -f 3 | cut -d '.' -f 1 | cut -d '"' -f 2)
# Check JDK version
echo "Checking for JDK..."
if ! java -version >/dev/null 2>&1 || [ $java -lt 11 ]; then
echo "JDK not found or less than version 11, would you like to install? ()"
echo "Y/N ->"
read answer
if [ "$answer" = y ] || [ "$answer" = Y ]; then
echo "Installing..."
if [[ "$os" == "Darwin" ]]; then
cd /tmp
if [[ "$arch" == "x86_64" ]]; then
curl "https://download.oracle.com/java/20/latest/jdk-20_macos-x64_bin.dmg" -o jdk-20_bin.dmg
elif [[ "$arch" == "arm64" ]]; then
curl "https://download.oracle.com/java/20/latest/jdk-20_macos-aarch64_bin.dmg" -o jdk-20_bin.dmg
fi
hdiutil mount jdk-20_bin.dmg
# sudo installer -pkg "/Volumes/JDK 20/JDK 20.pkg" -target "/"
sudo open -w "/Volumes/JDK 20/JDK 20.pkg"
hdiutil unmount "/Volumes/JDK 20"
rm jdk-20_bin.dmg
elif [[ "$os" == "Linux" ]]; then
mkdir -p ~/tmp/jdk20
cd ~/tmp/jdk20
if [[ "$arch" == "x86_64" ]]; then
curl "https://download.oracle.com/java/20/latest/jdk-20_linux-x64_bin.tar.gz" -o jdk-20_bin.tar.gz
elif [[ "$arch" == "arm64" ]]; then
curl "https://download.oracle.com/java/20/latest/jdk-20_linux-aarch64_bin.tar.gz" -o jdk-20_bin.tar.gz
fi
sudo tar zxvf jdk-20_bin.tar.gz -C /usr/local/
cd ~/
rm -rf ~/tmp/jdk20/
fi
echo "JDK installation complete."
else
echo "Please install JDK >= 11.0.16"
exit 0
fi
else
echo "JDK already installed."
fi
antlr=$(/usr/local/jdk-20/bin/jar xf /usr/local/antlr/antlr-*-complete.jar META-INF/MANIFEST.MF >/dev/null 2>&1 && grep 'Implementation-Version' META-INF/MANIFEST.MF | cut -d ' ' -f 2 && rm -rf META-INF)
# Check ANTLR installation and version
echo "Checking for ANTLR..."
check_antlr () {
if [ ! -z $antlr ]; then
if ([ "$(echo $antlr | cut -d '.' -f 1)" -lt 4 ] && [ "$(echo $antlr | cut -d '.' -f 2)" -lt 11 ] && [ "$(echo $antlr | cut -d '.' -f 3)" -lt 1 ]); then
return 0
else
return 1
fi
else
return 0
fi
}
if check_antlr; then
echo "ANTLR not found in Default Location or less than version 4.11.1, would you like to Install?"
echo "(If installed in other location, please edit the location inside the shell script before running)"
echo "Y/N ->"
read answer
if [ "$answer" = y ] || [ "$answer" = Y ]; then
mkdir -p ~/tmp/antlr4.11.1
cd ~/tmp/antlr4.11.1
curl -LO "https://www.antlr.org/download/antlr-4.11.1-complete.jar"
sudo mkdir -p /usr/local/antlr
sudo mv ~/tmp/antlr4.11.1/antlr-4.11.1-complete.jar /usr/local/antlr/antlr-4.11.1-complete.jar
echo 'export CLASSPATH=".:/usr/local/antlr/antlr-4.11.1-complete.jar"' >>~/.bashrc
. ~/.bashrc
echo "ANTLR installation complete."
else
echo "Please install ANTLR >= 4.11.1"
exit 0
fi
#
else
echo "ANTLR already installed."
fi
# Check Go installation and version
echo "Checking for Go..."
if ! command -v go >/dev/null 2>&1 || { [ $(go version | grep -o -E '[0-9]+\.[0-9]+' | head -n 1 | cut -d '.' -f 1) -lt 1 ] && [ $(go version | grep -o -E '[0-9]+\.[0-9]+' | head -n 1 | cut -d '.' -f 2) -lt 19 ]; }; then
echo "Go not installed or version is less than 1.19, would you like to install?"
echo "Y/N ->"
read answer
if [ "$answer" = y ] || [ "$answer" = Y ]; then
if [[ "$os" == "Darwin" ]]; then
cd /tmp
if [[ "$arch" == "x86_64" ]]; then
curl "https://go.dev/dl/go1.20.2.darwin-amd64.pkg" -o go1.20.2.pkg
elif [[ "$arch" == "arm64" ]]; then
curl "https://go.dev/dl/go1.20.2.darwin-arm64.pkg" -o go1.20.2.pkg
fi
#sudo installer -pkg "go1.20.2.pkg" -target "/"
sudo open -w golang.pkg
elif [[ "$os" == "Linux" ]]; then
mkdir -p ~/tmp/go1.20.2
cd ~/tmp/go1.20.2
if [[ "$arch" == "x86_64" ]]; then
curl "https://go.dev/dl/go1.20.2.linux-amd64.tar.gz" -o go1.20.2.tar.gz
elif [[ "$arch" == "arm64" ]]; then
curl "https://go.dev/dl/go1.20.2.linux-arm64.tar.gz" -o go1.20.2.tar.gz
fi
if command -v go >/dev/null 2>&1; then
rm -rf /usr/local/go
fi
sudo tar -C /usr/local -xzf go1.20.2.linux-amd64.tar.gz
if ! [[ ":$PATH:" == *":/usr/local/go/bin:"* ]]; then
export PATH=$PATH:/usr/local/go/bin
fi
fi
echo "Go installation complete"
else
echo "Please install Go >= 1.19"
exit 0
fi
else
echo "Go already installed."
fi
echo "Generating Parser & Lexer..."
java -Xmx500M -cp "/usr/local/lib/antlr-4.11.1-complete.jar:$CLASSPATH" org.antlr.v4.Tool -Dlanguage=Go -visitor Age.g4
echo "Installing Driver..."
go get -u ./...
echo "Successfully Installed Driver!"
exit 0
age-PG16-v1.5.0-rc0/drivers/golang/parser/ 0000775 0000000 0000000 00000000000 14546062412 0020021 5 ustar 00root root 0000000 0000000 age-PG16-v1.5.0-rc0/drivers/golang/parser/Age.g4 0000664 0000000 0000000 00000004221 14546062412 0020750 0 ustar 00root root 0000000 0000000 /*
* 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.
*/
/* Apache AGE output data grammar */
grammar Age;
ageout
: value
| vertex
| edge
| path
;
vertex
: properties KW_VERTEX
;
edge
: properties KW_EDGE
;
path
: '[' vertex (',' edge ',' vertex)* ']' KW_PATH
;
//Keywords
KW_VERTEX : '::vertex';
KW_EDGE : '::edge';
KW_PATH : '::path';
KW_NUMERIC : '::numeric';
// Common Values Rule
value
: STRING
| NUMBER
| NUMERIC
| FLOAT_EXPR
| BOOL
| NULL
| properties
| arr
;
properties
: '{' pair (',' pair)* '}'
| '{' '}'
;
pair
: STRING ':' value
;
arr
: '[' value (',' value)* ']'
| '[' ']'
;
STRING
: '"' (ESC | SAFECODEPOINT)* '"'
;
BOOL
: 'true'|'false'
;
NULL
: 'null'
;
fragment ESC
: '\\' (["\\/bfnrt] | UNICODE)
;
fragment UNICODE
: 'u' HEX HEX HEX HEX
;
fragment HEX
: [0-9a-fA-F]
;
fragment SAFECODEPOINT
: ~ ["\\\u0000-\u001F]
;
NUMBER
: '-'? INT ('.' [0-9] +)? EXP?
;
FLOAT_EXPR
: 'NaN' | '-Infinity' | 'Infinity'
;
NUMERIC
: '-'? INT ('.' [0-9] +)? EXP? KW_NUMERIC
;
fragment INT
: '0' | [1-9] [0-9]*
;
// no leading zeros
fragment EXP
: [Ee] [+\-]? INT
;
// \- since - means "range" inside [...]
WS
: [ \t\n\r] + -> skip
;
age-PG16-v1.5.0-rc0/drivers/golang/parser/generate.go 0000664 0000000 0000000 00000000054 14546062412 0022141 0 ustar 00root root 0000000 0000000 package parser
//go:generate ./generate.sh
age-PG16-v1.5.0-rc0/drivers/golang/parser/generate.sh 0000775 0000000 0000000 00000000620 14546062412 0022150 0 ustar 00root root 0000000 0000000 #!/bin/sh
GRAMMAR_LOC="$(dirname $(pwd))/parser"
mkdir -p ~/tmp/antlr
cd ~/tmp/antlr
curl -LO "https://www.antlr.org/download/antlr-4.11.1-complete.jar"
echo "ANTLR installation complete."
echo "Generating Parser & Lexer..."
java -Xmx500M -cp "$HOME/tmp/antlr/antlr-4.11.1-complete.jar:$HOME/tmp/antlr/antlr-4.11.1-complete.jar" org.antlr.v4.Tool -Dlanguage=Go -visitor $GRAMMAR_LOC/Age.g4
exit 0
age-PG16-v1.5.0-rc0/drivers/golang/samples/ 0000775 0000000 0000000 00000000000 14546062412 0020171 5 ustar 00root root 0000000 0000000 age-PG16-v1.5.0-rc0/drivers/golang/samples/age_wrapper_sample.go 0000664 0000000 0000000 00000006345 14546062412 0024365 0 ustar 00root root 0000000 0000000 /*
* 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.
*/
package main
import (
"fmt"
"github.com/apache/age/drivers/golang/age"
)
// Do cypher query to AGE with Age API
func doWithAgeWrapper(dsn string, graphName string) {
ag, err := age.ConnectAge(graphName, dsn)
if err != nil {
panic(err)
}
tx, err := ag.Begin()
if err != nil {
panic(err)
}
_, err = tx.ExecCypher(0, "CREATE (n:Person {name: '%s'})", "Joe")
if err != nil {
panic(err)
}
_, err = tx.ExecCypher(0, "CREATE (n:Person {name: '%s', age: %d})", "Smith", 10)
if err != nil {
panic(err)
}
_, err = tx.ExecCypher(0, "CREATE (n:Person {name: '%s', weight:%f})", "Jack", 70.3)
if err != nil {
panic(err)
}
tx.Commit()
tx, err = ag.Begin()
if err != nil {
panic(err)
}
cursor, err := tx.ExecCypher(1, "MATCH (n:Person) RETURN n")
if err != nil {
panic(err)
}
count := 0
for cursor.Next() {
entities, err := cursor.GetRow()
if err != nil {
panic(err)
}
count++
vertex := entities[0].(*age.Vertex)
fmt.Println(count, "]", vertex.Id(), vertex.Label(), vertex.Props())
}
fmt.Println("Vertex Count:", count)
_, err = tx.ExecCypher(0, "MATCH (a:Person), (b:Person) WHERE a.name='%s' AND b.name='%s' CREATE (a)-[r:workWith {weight: %d}]->(b)",
"Jack", "Joe", 3)
if err != nil {
panic(err)
}
_, err = tx.ExecCypher(0, "MATCH (a:Person {name: '%s'}), (b:Person {name: '%s'}) CREATE (a)-[r:workWith {weight: %d}]->(b)",
"Joe", "Smith", 7)
if err != nil {
panic(err)
}
tx.Commit()
tx, err = ag.Begin()
if err != nil {
panic(err)
}
cursor, err = tx.ExecCypher(1, "MATCH p=()-[:workWith]-() RETURN p")
if err != nil {
panic(err)
}
count = 0
for cursor.Next() {
entities, err := cursor.GetRow()
if err != nil {
panic(err)
}
count++
path := entities[0].(*age.Path)
vertexStart := path.GetAsVertex(0)
edge := path.GetAsEdge(1)
vertexEnd := path.GetAsVertex(2)
fmt.Println(count, "]", vertexStart, edge.Props(), vertexEnd)
}
// Query with return many columns
cursor, err = tx.ExecCypher(3, "MATCH (a:Person)-[l:workWith]-(b:Person) RETURN a, l, b")
if err != nil {
panic(err)
}
count = 0
for cursor.Next() {
row, err := cursor.GetRow()
if err != nil {
panic(err)
}
count++
v1 := row[0].(*age.Vertex)
edge := row[1].(*age.Edge)
v2 := row[2].(*age.Vertex)
fmt.Println("ROW ", count, ">>", "\n\t", v1, "\n\t", edge, "\n\t", v2)
}
_, err = tx.ExecCypher(0, "MATCH (n:Person) DETACH DELETE n RETURN *")
if err != nil {
panic(err)
}
tx.Commit()
}
age-PG16-v1.5.0-rc0/drivers/golang/samples/main.go 0000664 0000000 0000000 00000002650 14546062412 0021447 0 ustar 00root root 0000000 0000000 /*
* 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.
*/
package main
import (
"fmt"
_ "github.com/lib/pq"
)
// var dsn string = "host={host} port={port} dbname={dbname} user={username} password={password} sslmode=disable"
var dsn string = "host=127.0.0.1 port=5432 dbname=postgres user=postgres password=agens sslmode=disable"
// var graphName string = "{graph_path}"
var graphName string = "testGraph"
func main() {
// Do cypher query to AGE with database/sql Tx API transaction control
fmt.Println("# Do cypher query with SQL API")
doWithSqlAPI(dsn, graphName)
// Do cypher query to AGE with Age API
fmt.Println("# Do cypher query with Age API")
doWithAgeWrapper(dsn, graphName)
}
age-PG16-v1.5.0-rc0/drivers/golang/samples/sql_api_sample.go 0000664 0000000 0000000 00000007321 14546062412 0023514 0 ustar 00root root 0000000 0000000 /*
* 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.
*/
package main
import (
"database/sql"
"fmt"
"github.com/apache/age/drivers/golang/age"
)
// Do cypher query to AGE with database/sql Tx API transaction control
func doWithSqlAPI(dsn string, graphName string) {
// Connect to PostgreSQL
db, err := sql.Open("postgres", dsn)
if err != nil {
panic(err)
}
// Confirm graph_path created
_, err = age.GetReady(db, graphName)
if err != nil {
panic(err)
}
// Tx begin for execute create vertex
tx, err := db.Begin()
if err != nil {
panic(err)
}
// Create vertices with Cypher
_, err = age.ExecCypher(tx, graphName, 0, "CREATE (n:Person {name: '%s', weight:%f})", "Joe", 67.3)
if err != nil {
panic(err)
}
_, err = age.ExecCypher(tx, graphName, 0, "CREATE (n:Person {name: '%s', weight:77.3, roles:['Dev','marketing']})", "Jack")
if err != nil {
panic(err)
}
_, err = age.ExecCypher(tx, graphName, 0, "CREATE (n:Person {name: '%s', weight:%d})", "Andy", 59)
if err != nil {
panic(err)
}
// Commit Tx
tx.Commit()
// Tx begin for queries
tx, err = db.Begin()
if err != nil {
panic(err)
}
// Query cypher
cypherCursor, err := age.ExecCypher(tx, graphName, 1, "MATCH (n:Person) RETURN n")
if err != nil {
panic(err)
}
// Unmarshal result data to Vertex row by row
for cypherCursor.Next() {
row, err := cypherCursor.GetRow()
if err != nil {
panic(err)
}
vertex := row[0].(*age.Vertex)
fmt.Println(vertex.Id(), vertex.Label(), vertex.Props())
}
// Create Paths (Edges)
_, err = age.ExecCypher(tx, graphName, 0, "MATCH (a:Person), (b:Person) WHERE a.name='%s' AND b.name='%s' CREATE (a)-[r:workWith {weight: %d}]->(b)", "Jack", "Joe", 3)
if err != nil {
panic(err)
}
_, err = age.ExecCypher(tx, graphName, 0, "MATCH (a:Person {name: '%s'}), (b:Person {name: '%s'}) CREATE (a)-[r:workWith {weight: %d}]->(b)", "Joe", "Andy", 7)
if err != nil {
panic(err)
}
tx.Commit()
tx, err = db.Begin()
if err != nil {
panic(err)
}
// Query Paths with Cypher
cypherCursor, err = age.ExecCypher(tx, graphName, 1, "MATCH p=()-[:workWith]-() RETURN p")
if err != nil {
panic(err)
}
for cypherCursor.Next() {
row, err := cypherCursor.GetRow()
if err != nil {
panic(err)
}
path := row[0].(*age.Path)
vertexStart := path.GetAsVertex(0)
edge := path.GetAsEdge(1)
vertexEnd := path.GetAsVertex(2)
fmt.Println(vertexStart, edge, vertexEnd)
}
// Query with return many columns
cursor, err := age.ExecCypher(tx, graphName, 3, "MATCH (a:Person)-[l:workWith]-(b:Person) RETURN a, l, b")
if err != nil {
panic(err)
}
count := 0
for cursor.Next() {
row, err := cursor.GetRow()
if err != nil {
panic(err)
}
count++
v1 := row[0].(*age.Vertex)
edge := row[1].(*age.Edge)
v2 := row[2].(*age.Vertex)
fmt.Println("ROW ", count, ">>", "\n\t", v1, "\n\t", edge, "\n\t", v2)
}
// Delete Vertices
_, err = age.ExecCypher(tx, graphName, 0, "MATCH (n:Person) DETACH DELETE n RETURN *")
if err != nil {
panic(err)
}
tx.Commit()
}
age-PG16-v1.5.0-rc0/drivers/jdbc/ 0000775 0000000 0000000 00000000000 14546062412 0016160 5 ustar 00root root 0000000 0000000 age-PG16-v1.5.0-rc0/drivers/jdbc/.gitattributes 0000664 0000000 0000000 00000001462 14546062412 0021056 0 ustar 00root root 0000000 0000000 # 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.
*.bat text eol=crlf
age-PG16-v1.5.0-rc0/drivers/jdbc/.gitignore 0000664 0000000 0000000 00000001665 14546062412 0020160 0 ustar 00root root 0000000 0000000 # 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.
# Ignore Gradle Wapper
gradle/wrapper/gradle-wrapper.jar
# Ignore Gradle project-specific cache directory
.gradle
# Ignore Gradle build output directory
build
age-PG16-v1.5.0-rc0/drivers/jdbc/README.md 0000664 0000000 0000000 00000007317 14546062412 0017447 0 ustar 00root root 0000000 0000000 # **AGE AGType parser and driver support for Java**
AGType parser and driver support for [Apache AGE](https://age.apache.org/), graph extension for PostgreSQL.
## Prerequisites
You should have installed following jar files and packages.
- [gradle](https://gradle.org/install/) build tool
- [postgres JDBC driver](https://jdbc.postgresql.org/download/)
- [antlr4-4.9.2-complete](https://repo1.maven.org/maven2/org/antlr/antlr4/4.9.2/)
- [common-lang3](http://www.java2s.com/Code/Jar/c/Downloadcommonlang3jar.htm)
- [commons-text-1.6](http://www.java2s.com/ref/jar/download-commonstext16jar-file.html)
Kindly unzip the jars if they are zipped before using them.
## Build from source
```bash
git clone https://github.com/apache/age.git
cd age/drivers/jdbc
gradle assemble
```
After the build completes successfully, a jar file will be created at path `age/drivers/jdbc/lib/build/libs/lib.jar`. Now add this JAR file to class path for your java project.
## Getting Started
* Install AGE on your machine. [https://age.apache.org/age-manual/master/index.html](https://age.apache.org/age-manual/master/index.html)
* Add the downloaded jar files to class path.
* Connect to the postgres server using pg JDBC drivers.
Lets say we have a graph named `demo_graph` having some nodes. In order to extract its nodes we can try following steps. To create some more graphs. [https://github.com/apache/age#quick-start](https://github.com/apache/age#quick-start). Following sample code shows how to return query result as `Agtype`.
```java
import org.apache.age.jdbc.base.Agtype;
import org.postgresql.jdbc.PgConnection;
import java.sql.*;
public class Sample {
static final String DB_URL = "jdbc:postgresql://localhost:5432/demo";
static final String USER = "postgres";
static final String PASS = "pass";
public static void main(String[] args) {
// Open a connection
try {
PgConnection connection = DriverManager.getConnection(DB_URL, USER, PASS).unwrap(PgConnection.class);
connection.addDataType("agtype", Agtype.class);
// configure AGE
Statement stmt = connection.createStatement();
stmt.execute("CREATE EXTENSION IF NOT EXISTS age;");
stmt.execute("LOAD 'age'");
stmt.execute("SET search_path = ag_catalog, \"$user\", public;");
// Run cypher
ResultSet rs = stmt.executeQuery("SELECT * from cypher('demo_graph', $$ MATCH (n) RETURN n $$) as (n agtype);");
while (rs.next()) {
// Returning Result as Agtype
Agtype returnedAgtype = rs.getObject(1, Agtype.class);
String nodeLabel = returnedAgtype.getMap().getObject("label").toString();
String nodeProp = returnedAgtype.getMap().getObject("properties").toString();
System.out.println("Vertex : " + nodeLabel + ", \tProps : " + nodeProp);
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
```
Output
```
Vertex : Person, Props : {bornIn=Pakistan, name=imran}
Vertex : Person, Props : {bornIn=Pakistan, name=ali}
Vertex : Person, Props : {bornIn=US, name=james}
Vertex : Person, Props : {bornIn=Pakistan, name=ali}
Vertex : Person, Props : {bornIn=Pakistan, name=usama}
Vertex : Person, Props : {bornIn=Pakistan, name=akabr}
Vertex : Country, Props : {name=Pakistan}
Vertex : Country, Props : {name=US}
```
## For more information about [Apache AGE](https://age.apache.org/)
- Apache Age : [https://age.apache.org/](https://age.apache.org/)
- GitHub : [https://github.com/apache/age](https://github.com/apache/age)
- Document : [https://age.apache.org/age-manual/master/index.html](https://age.apache.org/age-manual/master/index.html)
age-PG16-v1.5.0-rc0/drivers/jdbc/gradle/ 0000775 0000000 0000000 00000000000 14546062412 0017416 5 ustar 00root root 0000000 0000000 age-PG16-v1.5.0-rc0/drivers/jdbc/gradle/wrapper/ 0000775 0000000 0000000 00000000000 14546062412 0021076 5 ustar 00root root 0000000 0000000 age-PG16-v1.5.0-rc0/drivers/jdbc/gradle/wrapper/gradle-wrapper.properties 0000664 0000000 0000000 00000001733 14546062412 0026134 0 ustar 00root root 0000000 0000000 # 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.
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-7.0-bin.zip
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
age-PG16-v1.5.0-rc0/drivers/jdbc/gradlew 0000775 0000000 0000000 00000013477 14546062412 0017547 0 ustar 00root root 0000000 0000000 #!/usr/bin/env sh
# 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.
##############################################################################
##
## Gradle start up script for UN*X
##
##############################################################################
# Attempt to set APP_HOME
# Resolve links: $0 may be a link
PRG="$0"
# Need this for relative symlinks.
while [ -h "$PRG" ] ; do
ls=`ls -ld "$PRG"`
link=`expr "$ls" : '.*-> \(.*\)$'`
if expr "$link" : '/.*' > /dev/null; then
PRG="$link"
else
PRG=`dirname "$PRG"`"/$link"
fi
done
SAVED="`pwd`"
cd "`dirname \"$PRG\"`/" >/dev/null
APP_HOME="`pwd -P`"
cd "$SAVED" >/dev/null
APP_NAME="Gradle"
APP_BASE_NAME=`basename "$0"`
# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
# Use the maximum available, or set MAX_FD != -1 to use that value.
MAX_FD="maximum"
warn () {
echo "$*"
}
die () {
echo
echo "$*"
echo
exit 1
}
# OS specific support (must be 'true' or 'false').
cygwin=false
msys=false
darwin=false
nonstop=false
case "`uname`" in
CYGWIN* )
cygwin=true
;;
Darwin* )
darwin=true
;;
MINGW* )
msys=true
;;
NONSTOP* )
nonstop=true
;;
esac
CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
# Determine the Java command to use to start the JVM.
if [ -n "$JAVA_HOME" ] ; then
if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
# IBM's JDK on AIX uses strange locations for the executables
JAVACMD="$JAVA_HOME/jre/sh/java"
else
JAVACMD="$JAVA_HOME/bin/java"
fi
if [ ! -x "$JAVACMD" ] ; then
die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
Please set the JAVA_HOME variable in your environment to match the
location of your Java installation."
fi
else
JAVACMD="java"
which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
Please set the JAVA_HOME variable in your environment to match the
location of your Java installation."
fi
# Increase the maximum file descriptors if we can.
if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then
MAX_FD_LIMIT=`ulimit -H -n`
if [ $? -eq 0 ] ; then
if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
MAX_FD="$MAX_FD_LIMIT"
fi
ulimit -n $MAX_FD
if [ $? -ne 0 ] ; then
warn "Could not set maximum file descriptor limit: $MAX_FD"
fi
else
warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
fi
fi
# For Darwin, add options to specify how the application appears in the dock
if $darwin; then
GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
fi
# For Cygwin or MSYS, switch paths to Windows format before running java
if [ "$cygwin" = "true" -o "$msys" = "true" ] ; then
APP_HOME=`cygpath --path --mixed "$APP_HOME"`
CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
JAVACMD=`cygpath --unix "$JAVACMD"`
# We build the pattern for arguments to be converted via cygpath
ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
SEP=""
for dir in $ROOTDIRSRAW ; do
ROOTDIRS="$ROOTDIRS$SEP$dir"
SEP="|"
done
OURCYGPATTERN="(^($ROOTDIRS))"
# Add a user-defined pattern to the cygpath arguments
if [ "$GRADLE_CYGPATTERN" != "" ] ; then
OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
fi
# Now convert the arguments - kludge to limit ourselves to /bin/sh
i=0
for arg in "$@" ; do
CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
else
eval `echo args$i`="\"$arg\""
fi
i=`expr $i + 1`
done
case $i in
0) set -- ;;
1) set -- "$args0" ;;
2) set -- "$args0" "$args1" ;;
3) set -- "$args0" "$args1" "$args2" ;;
4) set -- "$args0" "$args1" "$args2" "$args3" ;;
5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
esac
fi
# Escape application args
save () {
for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done
echo " "
}
APP_ARGS=`save "$@"`
# Collect all arguments for the java command, following the shell quoting and substitution rules
eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS"
exec "$JAVACMD" "$@"
age-PG16-v1.5.0-rc0/drivers/jdbc/gradlew.bat 0000664 0000000 0000000 00000005624 14546062412 0020304 0 ustar 00root root 0000000 0000000 @rem
@rem Licensed to the Apache Software Foundation (ASF) under one
@rem or more contributor license agreements. See the NOTICE file
@rem distributed with this work for additional information
@rem regarding copyright ownership. The ASF licenses this file
@rem to you under the Apache License, Version 2.0 (the
@rem "License"); you may not use this file except in compliance
@rem with the License. You may obtain a copy of the License at
@rem
@rem http://www.apache.org/licenses/LICENSE-2.0
@rem
@rem Unless required by applicable law or agreed to in writing,
@rem software distributed under the License is distributed on an
@rem "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
@rem KIND, either express or implied. See the License for the
@rem specific language governing permissions and limitations
@rem under the License.
@rem
@if "%DEBUG%" == "" @echo off
@rem ##########################################################################
@rem
@rem Gradle startup script for Windows
@rem
@rem ##########################################################################
@rem Set local scope for the variables with windows NT shell
if "%OS%"=="Windows_NT" setlocal
set DIRNAME=%~dp0
if "%DIRNAME%" == "" set DIRNAME=.
set APP_BASE_NAME=%~n0
set APP_HOME=%DIRNAME%
@rem Resolve any "." and ".." in APP_HOME to make it shorter.
for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi
@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m"
@rem Find java.exe
if defined JAVA_HOME goto findJavaFromJavaHome
set JAVA_EXE=java.exe
%JAVA_EXE% -version >NUL 2>&1
if "%ERRORLEVEL%" == "0" goto execute
echo.
echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
echo.
echo Please set the JAVA_HOME variable in your environment to match the
echo location of your Java installation.
goto fail
:findJavaFromJavaHome
set JAVA_HOME=%JAVA_HOME:"=%
set JAVA_EXE=%JAVA_HOME%/bin/java.exe
if exist "%JAVA_EXE%" goto execute
echo.
echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
echo.
echo Please set the JAVA_HOME variable in your environment to match the
echo location of your Java installation.
goto fail
:execute
@rem Setup the command line
set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
@rem Execute Gradle
"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %*
:end
@rem End local scope for the variables with windows NT shell
if "%ERRORLEVEL%"=="0" goto mainEnd
:fail
rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
rem the _cmd.exe /c_ return code!
if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
exit /b 1
:mainEnd
if "%OS%"=="Windows_NT" endlocal
:omega
age-PG16-v1.5.0-rc0/drivers/jdbc/lib/ 0000775 0000000 0000000 00000000000 14546062412 0016726 5 ustar 00root root 0000000 0000000 age-PG16-v1.5.0-rc0/drivers/jdbc/lib/build.gradle.kts 0000664 0000000 0000000 00000004362 14546062412 0022012 0 ustar 00root root 0000000 0000000 /*
* 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.
*/
import org.gradle.api.tasks.testing.logging.TestExceptionFormat
import org.gradle.api.tasks.testing.logging.TestLogEvent
plugins {
`java-library`
antlr
}
repositories {
mavenCentral()
}
dependencies {
implementation("org.postgresql:postgresql:42.6.0")
api("org.apache.commons:commons-text:1.10.0")
antlr("org.antlr:antlr4:4.12.0")
testImplementation("org.junit.jupiter:junit-jupiter-api:5.9.3")
testRuntimeOnly("org.junit.jupiter:junit-jupiter-engine")
testImplementation("org.testcontainers:testcontainers:1.18.0")
testImplementation("org.postgresql:postgresql:42.6.0")
testImplementation("org.slf4j:slf4j-api:2.0.7")
testImplementation("org.slf4j:slf4j-simple:2.0.7")
}
tasks.generateGrammarSource {
maxHeapSize = "64m"
source = project.objects
.sourceDirectorySet("antlr", "antlr")
.srcDir("${projectDir}/../../").apply {
include("*.g4")
}
arguments.addAll(arrayOf("-package", "org.apache.age.jdbc.antlr4"))
outputDirectory = file("$outputDirectory/org/apache/age/jdbc/antlr4")
}
tasks.test {
useJUnitPlatform();
testLogging {
// set options for log level LIFECYCLE
events(TestLogEvent.FAILED,
TestLogEvent.PASSED,
TestLogEvent.SKIPPED,
TestLogEvent.STANDARD_OUT)
exceptionFormat = TestExceptionFormat.FULL
showExceptions = true
showCauses = true
showStackTraces = true
}
}
age-PG16-v1.5.0-rc0/drivers/jdbc/lib/src/ 0000775 0000000 0000000 00000000000 14546062412 0017515 5 ustar 00root root 0000000 0000000 age-PG16-v1.5.0-rc0/drivers/jdbc/lib/src/main/ 0000775 0000000 0000000 00000000000 14546062412 0020441 5 ustar 00root root 0000000 0000000 age-PG16-v1.5.0-rc0/drivers/jdbc/lib/src/main/java/ 0000775 0000000 0000000 00000000000 14546062412 0021362 5 ustar 00root root 0000000 0000000 age-PG16-v1.5.0-rc0/drivers/jdbc/lib/src/main/java/org/ 0000775 0000000 0000000 00000000000 14546062412 0022151 5 ustar 00root root 0000000 0000000 age-PG16-v1.5.0-rc0/drivers/jdbc/lib/src/main/java/org/apache/ 0000775 0000000 0000000 00000000000 14546062412 0023372 5 ustar 00root root 0000000 0000000 age-PG16-v1.5.0-rc0/drivers/jdbc/lib/src/main/java/org/apache/age/ 0000775 0000000 0000000 00000000000 14546062412 0024126 5 ustar 00root root 0000000 0000000 age-PG16-v1.5.0-rc0/drivers/jdbc/lib/src/main/java/org/apache/age/jdbc/ 0000775 0000000 0000000 00000000000 14546062412 0025030 5 ustar 00root root 0000000 0000000 age-PG16-v1.5.0-rc0/drivers/jdbc/lib/src/main/java/org/apache/age/jdbc/AgtypeUnrecognizedList.java 0000664 0000000 0000000 00000002505 14546062412 0032337 0 ustar 00root root 0000000 0000000 /*
* 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.
*/
package org.apache.age.jdbc;
import org.apache.age.jdbc.base.type.AgtypeAnnotation;
import org.apache.age.jdbc.base.type.AgtypeListImpl;
import org.apache.age.jdbc.base.type.UnrecognizedObject;
public class AgtypeUnrecognizedList extends AgtypeListImpl implements UnrecognizedObject,
AgtypeAnnotation {
private String annotation;
@Override
public String getAnnotation() {
return this.annotation;
}
@Override
public void setAnnotation(String annotation) {
this.annotation = annotation;
}
}
age-PG16-v1.5.0-rc0/drivers/jdbc/lib/src/main/java/org/apache/age/jdbc/AgtypeUnrecognizedMap.java 0000664 0000000 0000000 00000002502 14546062412 0032136 0 ustar 00root root 0000000 0000000 /*
* 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.
*/
package org.apache.age.jdbc;
import org.apache.age.jdbc.base.type.AgtypeAnnotation;
import org.apache.age.jdbc.base.type.AgtypeMapImpl;
import org.apache.age.jdbc.base.type.UnrecognizedObject;
public class AgtypeUnrecognizedMap extends AgtypeMapImpl implements UnrecognizedObject,
AgtypeAnnotation {
private String annotation;
@Override
public String getAnnotation() {
return this.annotation;
}
@Override
public void setAnnotation(String annotation) {
this.annotation = annotation;
}
}
age-PG16-v1.5.0-rc0/drivers/jdbc/lib/src/main/java/org/apache/age/jdbc/base/ 0000775 0000000 0000000 00000000000 14546062412 0025742 5 ustar 00root root 0000000 0000000 age-PG16-v1.5.0-rc0/drivers/jdbc/lib/src/main/java/org/apache/age/jdbc/base/Agtype.java 0000664 0000000 0000000 00000016304 14546062412 0030042 0 ustar 00root root 0000000 0000000 /*
* 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.
*/
package org.apache.age.jdbc.base;
import java.sql.SQLException;
import org.apache.age.jdbc.base.type.AgtypeAnnotation;
import org.apache.age.jdbc.base.type.AgtypeList;
import org.apache.age.jdbc.base.type.AgtypeMap;
import org.postgresql.util.PGobject;
import org.postgresql.util.PSQLException;
import org.postgresql.util.PSQLState;
/**
* Stores values of various kinds in a single object for use in PostgreSQL. The text representation
* is built on top of the JSON format
* specification. The goal of the text representation is making it compatible with JSON as much
* as possible so that valid JSON values can be parsed without effort.
*
* Valid Agtypes:
*
*
null
*
int
*
long
*
double
*
boolean
*
String
*
{@link AgtypeList}
*
{@link AgtypeMap}
*
*/
public class Agtype extends PGobject implements Cloneable {
private Object obj;
/**
* Public constructor for Agtype. Do not call directly, use the AgtypeFactory when creating
* Agtype objects on the client-side and casting the received object in the ResultSet when the
* object is created on the server-side.
*/
public Agtype() {
super.setType("ag_catalog.agtype");
}
Agtype(Object obj) {
this();
this.obj = obj;
}
/**
* TODO: need to define for PreparedStatement.
*/
@Override
public String getValue() {
if (value == null) {
value = AgtypeUtil.serializeAgtype(obj);
}
return value;
}
/**
* Parses the serialized value to it's Agtype value. {@inheritDoc}
*
* @param value Serialized representation of Agtype value.
* @throws SQLException throws if the String value cannot be parsed to a valid Agtype.
* @see AgtypeUtil#parse(String)
*/
@Override
public void setValue(String value) throws SQLException {
try {
obj = AgtypeUtil.parse(value);
} catch (Exception e) {
throw new PSQLException("Parsing AgType failed", PSQLState.DATA_ERROR, e);
}
super.setValue(value);
}
/**
* Returns the value stored in Agtype as a String. Attempts to perform an implicit conversion of
* types stored as non-strings values.
*
* @return value stored in Agtype as a String.
* @throws InvalidAgtypeException Throws if the stored Agtype value cannot be represented as a
* String.
* @see AgtypeUtil#getString(Object)
*/
public String getString() throws InvalidAgtypeException {
return AgtypeUtil.getString(obj);
}
/**
* Returns the value stored in Agtype as a generic object.
*
* @return value stored in Agtype as a generic object.
*/
public Object getObject() throws InvalidAgtypeException {
return obj;
}
/**
* Returns the value stored in Agtype as an int. Attempts to perform an implicit conversion of
* types stored as non-int values.
*
* @return value stored in Agtype as an int.
* @throws InvalidAgtypeException Throws if the stored Agtype value cannot be represented as an
* int.
* @see AgtypeUtil#getInt(Object)
*/
public int getInt() throws InvalidAgtypeException {
return AgtypeUtil.getInt(obj);
}
/**
* Returns the value stored in Agtype as a long. Attempts to perform an implicit conversion of
* types stored as non-long values.
*
* @return value stored in Agtype as a long.
* @throws InvalidAgtypeException Throws if the stored Agtype value cannot be represented as an
* long.
* @see AgtypeUtil#getLong(Object)
*/
public long getLong() throws InvalidAgtypeException {
return AgtypeUtil.getLong(obj);
}
/**
* Returns the value stored in Agtype as a double. Attempts to perform an implicit conversion of
* types stored as non-double values.
*
* @return value stored in Agtype as a double.
* @throws InvalidAgtypeException Throws if the stored Agtype value cannot be represented as an
* double.
* @see AgtypeUtil#getDouble(Object)
*/
public double getDouble() throws InvalidAgtypeException {
return AgtypeUtil.getDouble(obj);
}
/**
* Returns the value stored in Agtype as a boolean. Attempts to perform an implicit conversion
* of types stored as non-boolean values.
*
* @return value stored in Agtype as a long.
* @throws InvalidAgtypeException Throws if the stored Agtype value cannot be represented as an
* boolean.
* @see AgtypeUtil#getBoolean(Object)
*/
public boolean getBoolean() throws InvalidAgtypeException {
return AgtypeUtil.getBoolean(obj);
}
/**
* Returns the value stored in Agtype as an AgtypeList.
*
* @return value stored in Agtype as an AgtypeList.
* @throws InvalidAgtypeException Throws if the stored Agtype value cannot be represented as an
* AgtypeList.
* @see AgtypeUtil#getList(Object)
*/
public AgtypeList getList() throws InvalidAgtypeException {
return AgtypeUtil.getList(obj);
}
/**
* Returns the value stored in Agtype as an AgtypeMap.
*
* @return value stored in Agtype as an AgtypeMap.
* @throws InvalidAgtypeException Throws if the stored Agtype value cannot be represented as an
* AgtypeMap.
* @see AgtypeUtil#getMap(Object)
*/
public AgtypeMap getMap() throws InvalidAgtypeException {
return AgtypeUtil.getMap(obj);
}
/**
* Returns whether stored is Agtype Null.
*
* @return true if the value is Agtype null, false otherwise.
*/
public boolean isNull() {
return obj == null;
}
/**
* Returns a string representation of this Agtype object.
*
* @return a string representation of this Agtype object.
*/
@Override
public String toString() {
if (obj != null && obj instanceof AgtypeAnnotation) {
return obj
+ (type != null ? "::" + ((AgtypeAnnotation) obj).getAnnotation() : "");
}
return (obj != null ? obj.toString() : "null")
+ (type != null ? "::" + type : "");
}
}
age-PG16-v1.5.0-rc0/drivers/jdbc/lib/src/main/java/org/apache/age/jdbc/base/AgtypeFactory.java 0000664 0000000 0000000 00000004325 14546062412 0031372 0 ustar 00root root 0000000 0000000 /*
* 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.
*/
package org.apache.age.jdbc.base;
import org.apache.age.jdbc.base.type.AgtypeList;
import org.apache.age.jdbc.base.type.AgtypeMap;
/**
* Factory for creating Agtype objects.
*
* @see Agtype
*/
public class AgtypeFactory {
/**
* Creates an Agtype object.
*
* @param obj Object to store in the an Agtype Object.
* @return new Agtype Object
* @throws InvalidAgtypeException Thrown if the object passed is not a {@link Agtype valid
* Agtype}
*/
public static Agtype create(Object obj) throws InvalidAgtypeException {
if (obj == null) {
return new Agtype(null);
} else if (obj instanceof Integer) {
return new Agtype(((Integer) obj).longValue());
} else if (obj instanceof Long) {
return new Agtype(obj);
} else if (obj instanceof String) {
return new Agtype(obj);
} else if (obj instanceof Boolean) {
return new Agtype(obj);
} else if (obj instanceof Double) {
return new Agtype(obj);
} else if (obj instanceof AgtypeList) {
return new Agtype(obj);
} else if (obj instanceof AgtypeMap) {
return new Agtype(obj);
} else {
String s = String
.format("%s is not a valid Agtype value", obj.getClass().getSimpleName());
throw new InvalidAgtypeException(s);
}
}
}
age-PG16-v1.5.0-rc0/drivers/jdbc/lib/src/main/java/org/apache/age/jdbc/base/AgtypeListener.java 0000664 0000000 0000000 00000015415 14546062412 0031552 0 ustar 00root root 0000000 0000000 /*
* 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.
*/
package org.apache.age.jdbc.base;
import java.util.Stack;
import org.apache.age.jdbc.AgtypeUnrecognizedList;
import org.apache.age.jdbc.AgtypeUnrecognizedMap;
import org.apache.age.jdbc.antlr4.AgtypeBaseListener;
import org.apache.age.jdbc.antlr4.AgtypeParser.AgTypeContext;
import org.apache.age.jdbc.antlr4.AgtypeParser.ArrayValueContext;
import org.apache.age.jdbc.antlr4.AgtypeParser.FalseBooleanContext;
import org.apache.age.jdbc.antlr4.AgtypeParser.FloatValueContext;
import org.apache.age.jdbc.antlr4.AgtypeParser.IntegerValueContext;
import org.apache.age.jdbc.antlr4.AgtypeParser.NullValueContext;
import org.apache.age.jdbc.antlr4.AgtypeParser.ObjectValueContext;
import org.apache.age.jdbc.antlr4.AgtypeParser.PairContext;
import org.apache.age.jdbc.antlr4.AgtypeParser.StringValueContext;
import org.apache.age.jdbc.antlr4.AgtypeParser.TrueBooleanContext;
import org.apache.age.jdbc.antlr4.AgtypeParser.TypeAnnotationContext;
import org.apache.age.jdbc.base.type.AgtypeList;
import org.apache.age.jdbc.base.type.AgtypeListImpl;
import org.apache.age.jdbc.base.type.AgtypeMap;
import org.apache.age.jdbc.base.type.AgtypeMapImpl;
import org.apache.age.jdbc.base.type.AgtypeObject;
import org.apache.age.jdbc.base.type.UnrecognizedObject;
import org.apache.commons.text.StringEscapeUtils;
public class AgtypeListener extends AgtypeBaseListener {
// Will have List or Map
private final Stack objectStack = new Stack<>();
private final Stack annotationMap = new Stack<>();
Object rootObject;
Object lastValue;
boolean lastValueUndefined = true;
private long objectStackLength = 0;
private void pushObjectStack(AgtypeObject o) {
objectStackLength++;
this.objectStack.push(o);
}
private AgtypeObject popObjectStack() {
objectStackLength--;
return objectStack.pop();
}
private AgtypeObject peekObjectStack() {
return objectStack.peek();
}
private void mergeObjectIfTargetIsArray() {
if (objectStackLength >= 2) {
AgtypeObject firstObject = popObjectStack();
AgtypeObject secondObject = popObjectStack();
if (secondObject instanceof AgtypeListImpl) {
((AgtypeListImpl) secondObject).add(firstObject);
pushObjectStack(secondObject);
} else {
pushObjectStack(secondObject);
pushObjectStack(firstObject);
}
}
}
private void mergeObjectIfTargetIsMap(String key, Object value) {
AgtypeMapImpl agtypeMap = (AgtypeMapImpl) peekObjectStack();
agtypeMap.put(key, value);
}
private void addObjectValue() {
if (objectStackLength != 0) {
AgtypeObject currentObject = peekObjectStack();
if (currentObject instanceof AgtypeListImpl) {
((AgtypeListImpl) currentObject).add(this.lastValue);
lastValueUndefined = true;
return;
}
}
lastValueUndefined = false;
}
@Override
public void exitStringValue(StringValueContext ctx) {
this.lastValue = identString(ctx.STRING().getText());
addObjectValue();
}
@Override
public void exitIntegerValue(IntegerValueContext ctx) {
this.lastValue = Long.parseLong(ctx.INTEGER().getText());
addObjectValue();
}
@Override
public void exitFloatValue(FloatValueContext ctx) {
this.lastValue = Double.parseDouble(ctx.floatLiteral().getText());
addObjectValue();
}
@Override
public void exitTrueBoolean(TrueBooleanContext ctx) {
this.lastValue = true;
addObjectValue();
}
@Override
public void exitFalseBoolean(FalseBooleanContext ctx) {
this.lastValue = false;
addObjectValue();
}
@Override
public void exitNullValue(NullValueContext ctx) {
this.lastValue = null;
addObjectValue();
}
@Override
public void enterObjectValue(ObjectValueContext ctx) {
AgtypeMap agtypeMap = new AgtypeUnrecognizedMap();
pushObjectStack(agtypeMap);
}
@Override
public void exitObjectValue(ObjectValueContext ctx) {
mergeObjectIfTargetIsArray();
}
@Override
public void enterArrayValue(ArrayValueContext ctx) {
AgtypeList agtypeList = new AgtypeUnrecognizedList();
pushObjectStack(agtypeList);
}
@Override
public void exitArrayValue(ArrayValueContext ctx) {
mergeObjectIfTargetIsArray();
}
@Override
public void exitPair(PairContext ctx) {
String name = identString(ctx.STRING().getText());
if (!lastValueUndefined) {
mergeObjectIfTargetIsMap(name, this.lastValue);
lastValueUndefined = true;
} else {
Object lastValue = popObjectStack();
Object currentHeaderObject = peekObjectStack();
if (currentHeaderObject instanceof AgtypeListImpl) {
((AgtypeListImpl) currentHeaderObject).add(lastValue);
} else {
mergeObjectIfTargetIsMap(name, lastValue);
}
}
}
@Override
public void exitAgType(AgTypeContext ctx) {
if (objectStack.empty()) {
this.rootObject = this.lastValue;
return;
}
this.rootObject = popObjectStack();
}
@Override
public void enterTypeAnnotation(TypeAnnotationContext ctx) {
annotationMap.push(ctx.IDENT().getText());
}
@Override
public void exitTypeAnnotation(TypeAnnotationContext ctx) {
String annotation = annotationMap.pop();
Object currentObject = peekObjectStack();
if (currentObject instanceof UnrecognizedObject) {
((UnrecognizedObject) currentObject).setAnnotation(annotation);
}
}
private String identString(String quotesString) {
return StringEscapeUtils.unescapeJson(quotesString.substring(1, quotesString.length() - 1));
}
public Object getOutput() {
return this.rootObject;
}
}
age-PG16-v1.5.0-rc0/drivers/jdbc/lib/src/main/java/org/apache/age/jdbc/base/AgtypeUtil.java 0000664 0000000 0000000 00000033550 14546062412 0030702 0 ustar 00root root 0000000 0000000 /*
* 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.
*/
package org.apache.age.jdbc.base;
import java.util.StringJoiner;
import org.antlr.v4.runtime.BaseErrorListener;
import org.antlr.v4.runtime.CharStream;
import org.antlr.v4.runtime.CharStreams;
import org.antlr.v4.runtime.CommonTokenStream;
import org.antlr.v4.runtime.RecognitionException;
import org.antlr.v4.runtime.Recognizer;
import org.antlr.v4.runtime.TokenStream;
import org.antlr.v4.runtime.tree.ParseTreeWalker;
import org.apache.age.jdbc.antlr4.AgtypeLexer;
import org.apache.age.jdbc.antlr4.AgtypeParser;
import org.apache.age.jdbc.base.type.AgtypeList;
import org.apache.age.jdbc.base.type.AgtypeListBuilder;
import org.apache.age.jdbc.base.type.AgtypeMap;
import org.apache.age.jdbc.base.type.AgtypeMapBuilder;
import org.apache.commons.text.StringEscapeUtils;
/**
* A set of utility methods to assist in using Agtype.
*/
public class AgtypeUtil {
private static BaseErrorListener baseErrorListener = new BaseErrorListener() {
@Override
public void syntaxError(Recognizer, ?> recognizer, Object offendingSymbol, int line,
int charPositionInLine, String msg, RecognitionException e) {
throw new IllegalStateException(
"Failed to parse at line " + line + " due to " + msg, e);
}
};
/**
* Do not instantiate AgtypeUtil, all methods are static.
*/
private AgtypeUtil() {
}
/**
* Returns to object as a double, if it is a valid double, otherwise will throw an exception.
*
All other values will throw an InvalidAgtypeException
*
*
* @param obj Object to parse
* @return Object as a double
* @throws NumberFormatException if the given object is a number or a string that cannot be
* parsed to a double
* @throws InvalidAgtypeException if the given object is not a number or string and cannot be
* converted to a double
*/
public static double getDouble(Object obj)
throws NumberFormatException, InvalidAgtypeException {
if (obj == null) {
return 0.0;
} else if (obj instanceof Double) {
return (Double) obj;
} else if (obj instanceof Boolean) {
return (Boolean) obj ? 1.0 : 0.0;
} else if (obj instanceof String) {
return Double.parseDouble((String) obj);
} else if (obj instanceof Long) {
return ((Long) obj).doubleValue();
}
throw new InvalidAgtypeException("Not a double: " + obj);
}
/**
* Returns the object as an integer, if it is a valid integer, otherwise will throw an
* exception.
*
Strings - Parsed to its integer value, an Exception will be thrown if its not an
* integer
*
All other values will throw an InvalidAgtypeException
*
*
* @param obj Object to parse
* @return Object as an int
* @throws NumberFormatException if the given object is a number or string that cannot be
* parsed into an Integer
* @throws InvalidAgtypeException if the given object is not a number of a string
*/
public static int getInt(Object obj) throws NumberFormatException, InvalidAgtypeException {
long l;
try {
l = getLong(obj);
if (l >= Integer.MIN_VALUE && l <= Integer.MAX_VALUE) {
return (int) l;
}
} catch (InvalidAgtypeException ex) {
throw new InvalidAgtypeException("Not a int: " + obj, ex);
}
throw new NumberFormatException("Bad value for type int: " + l);
}
/**
* Returns to object as a long, if it is a valid long, otherwise will throw an exception.
*
Strings - Parsed to its integer value, an Exception will be thrown if its not an
* integer
*
All other values will throw an InvalidAgtypeException
*
*
* @param obj Object to parse
* @return Object as an long
* @throws InvalidAgtypeException if the object cannot be converted to a Long
* @throws NumberFormatException if the given object is a number or string that cannot be
* parsed into a double
*/
public static long getLong(Object obj) throws NumberFormatException, InvalidAgtypeException {
if (obj == null) {
return 0;
} else if (obj instanceof Long) {
return (Long) obj;
} else if (obj instanceof String) {
return (long) Double.parseDouble((String) obj);
} else if (obj instanceof Boolean) {
return (boolean) obj ? 1 : 0;
} else if (obj instanceof Double) {
return ((Double) obj).longValue();
}
throw new InvalidAgtypeException("Not a long: " + obj);
}
/**
* Returns to object as a string, if it is a valid string, otherwise will throw an exception.
*
* Rules for converting from other types:
*
*
null - Returns null
*
Long - Returns a String representation of the Long
*
Double - Returns a String representation of the double
*
Boolean - Returns a String representation of the boolean
*
All other values will throw an InvalidAgtypeException
*
*
* @param obj Object to parse to a String
* @return Object as an string
* @throws InvalidAgtypeException if the object cannot be converted to a String
*/
public static String getString(Object obj) throws InvalidAgtypeException {
if (obj == null) {
return null;
} else if (obj instanceof String) {
return (String) obj;
} else if (obj instanceof Long) {
return Long.toString((long) obj);
} else if (obj instanceof Double) {
return Double.toString((double) obj);
} else if (obj instanceof Boolean) {
return Boolean.toString((boolean) obj);
}
throw new InvalidAgtypeException("Not a string: " + obj);
}
/**
* Returns to object as a boolean, if it is a valid boolean, otherwise will throw an exception.
*
* Data Conversions from other types
*
*
null - Returns false
*
Long - Returns false is the value is 0, returns true otherwise.
*
Double - Returns false is the value is 0.0, returns true otherwise.
*
String - Returns false if the length of the String is 0, returns true otherwise.
*
* {@link AgtypeList} - Returns false if the size of the list is 0, returns true otherwise.
*
*
* {@link AgtypeMap} - Returns false if the size of the map is 0, returns true otherwise.
*
*
All other values will throw an InvalidAgtypeException
*
*
* @param obj Object to parse
* @return Object as an boolean
* @throws InvalidAgtypeException if the object cannot be converted to a boolean
*/
public static boolean getBoolean(Object obj) throws InvalidAgtypeException {
if (obj == null) {
return false;
} else if (obj instanceof Boolean) {
return (Boolean) obj;
} else if (obj instanceof String) {
return ((String) obj).length() > 0;
} else if (obj instanceof Long) {
return (Long) obj != 0L;
} else if (obj instanceof Double) {
return (Double) obj != 0.0;
} else if (obj instanceof AgtypeList) {
return ((AgtypeList) obj).size() > 0;
} else if (obj instanceof AgtypeMap) {
return ((AgtypeMap) obj).size() > 0;
}
throw new InvalidAgtypeException("Not a valid Agtype: " + obj);
}
/**
* Returns to object as an {@link AgtypeList}. If this obj is not an AgtypeList, an
* InvalidAgtypeException will be thrown.
*
* @param obj Object to parse and return as an AgtypeList
* @return Object as an agTypeArray
* @throws InvalidAgtypeException if the object cannot be converted to an AgtypeList
*/
public static AgtypeList getList(Object obj) throws InvalidAgtypeException {
if (obj == null) {
return null;
} else if (obj instanceof AgtypeList) {
return (AgtypeList) obj;
}
throw new InvalidAgtypeException("Not an AgtypeList: " + obj);
}
/**
* Returns to object as an {@link AgtypeMap}. If this obj is not an AgtypeMap, an
* InvalidAgtypeException will be thrown.
*
* @param obj Object to parse and return as an AgtypeMap
* @return Object as an AgtypeMap
* @throws InvalidAgtypeException if the object cannot be converted to an AgtypeMap
*/
public static AgtypeMap getMap(Object obj) throws InvalidAgtypeException {
if (obj == null) {
return null;
} else if (obj instanceof AgtypeMap) {
return (AgtypeMap) obj;
}
throw new InvalidAgtypeException("Not an AgtypeMap: " + obj);
}
/**
* Creates a new AgtypeMapBuilder.
*
* @return Newly created AgtypeMapBuilder
*/
public static AgtypeMapBuilder createMapBuilder() {
return new AgtypeMapBuilder();
}
/**
* Creates a new AgtypeListBuilder.
*
* @return Newly created AgtypeListBuilder
*/
public static AgtypeListBuilder createListBuilder() {
return new AgtypeListBuilder();
}
/**
* Converts a serialized Agtype value into it's non-serialized value.
*
* @param strAgtype Serialized Agtype value to be parsed.
* @return Parsed object that can be stored in {@link Agtype}
* @throws IllegalStateException if the value cannot be parsed into an Agtype.
*/
public static Object parse(String strAgtype) throws IllegalStateException {
CharStream charStream = CharStreams.fromString(strAgtype);
AgtypeLexer lexer = new AgtypeLexer(charStream);
TokenStream tokens = new CommonTokenStream(lexer);
AgtypeParser parser = new AgtypeParser(tokens);
lexer.removeErrorListeners();
lexer.addErrorListener(baseErrorListener);
parser.removeErrorListeners();
parser.addErrorListener(baseErrorListener);
AgtypeListener agtypeListener = new AgtypeListener();
ParseTreeWalker walker = new ParseTreeWalker();
walker.walk(agtypeListener, parser.agType());
return agtypeListener.getOutput();
}
/**
* Converts the passed object into its serialized Agtype form.
*
* @param obj Agtype object to convert into its serialized form
* @return Serialized Agtype object
*/
static String serializeAgtype(Object obj) {
if (obj == null) {
return "null";
} else if (obj instanceof String) {
return '"' + StringEscapeUtils.escapeJson((String) obj) + '"';
} else if (obj instanceof AgtypeMap) {
StringJoiner join = new StringJoiner(",", "{", "}");
((AgtypeMap) obj).entrySet()
.stream()
.map((entry) -> new StringJoiner(":")
.add(serializeAgtype(entry.getKey()))
.add(serializeAgtype(entry.getValue()))
)
.forEach(join::merge);
return join.toString();
} else if (obj instanceof AgtypeList) {
StringJoiner join = new StringJoiner(",", "[", "]");
((AgtypeList) obj)
.stream()
.map(AgtypeUtil::serializeAgtype)
.forEach(join::add);
return join.toString();
}
return String.valueOf(obj);
}
}
InvalidAgtypeException.java 0000664 0000000 0000000 00000002222 14546062412 0033143 0 ustar 00root root 0000000 0000000 age-PG16-v1.5.0-rc0/drivers/jdbc/lib/src/main/java/org/apache/age/jdbc/base /*
* 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.
*/
package org.apache.age.jdbc.base;
/**
* Runtime exception for when there is an invalid use of Agtype.
*/
public class InvalidAgtypeException extends RuntimeException {
public InvalidAgtypeException(String message) {
super(message);
}
public InvalidAgtypeException(String message, Throwable cause) {
super(message, cause);
}
}
age-PG16-v1.5.0-rc0/drivers/jdbc/lib/src/main/java/org/apache/age/jdbc/base/type/ 0000775 0000000 0000000 00000000000 14546062412 0026723 5 ustar 00root root 0000000 0000000 AgtypeAnnotation.java 0000664 0000000 0000000 00000001622 14546062412 0032774 0 ustar 00root root 0000000 0000000 age-PG16-v1.5.0-rc0/drivers/jdbc/lib/src/main/java/org/apache/age/jdbc/base/type /*
* 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.
*/
package org.apache.age.jdbc.base.type;
public interface AgtypeAnnotation {
String getAnnotation();
}
age-PG16-v1.5.0-rc0/drivers/jdbc/lib/src/main/java/org/apache/age/jdbc/base/type/AgtypeList.java 0000664 0000000 0000000 00000016351 14546062412 0031661 0 ustar 00root root 0000000 0000000 /*
* 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.
*/
package org.apache.age.jdbc.base.type;
import java.util.Iterator;
import java.util.Spliterator;
import java.util.function.Consumer;
import java.util.stream.Stream;
import org.apache.age.jdbc.base.Agtype;
import org.apache.age.jdbc.base.AgtypeUtil;
import org.apache.age.jdbc.base.InvalidAgtypeException;
/**
* Non-mutable list of Agtype values.
*
* @see AgtypeListBuilder
* @see Agtype
*/
public interface AgtypeList extends AgtypeObject {
/**
* Performs the given action for each element of the Iterable until all elements have been
* processed or the action throws an exception. Unless otherwise specified by the implementing
* class, actions are performed in the order of iteration (if an iteration order is specified).
* Exceptions thrown by the action are relayed to the caller.
*
* @param action The action to be performed for each element
* @throws NullPointerException - if the specified action is null
*/
void forEach(Consumer super Object> action);
/**
* Returns the String value at the specified position in this list. Throws an
* InvalidAgtypeException if the element is not a String.
*
* @param index index of the element to return
* @return the String value at the specified position in this list
* @throws InvalidAgtypeException if the value cannot be converted to a String
* @throws IndexOutOfBoundsException if the index is out of range (index {@literal <} 0 || index
* {@literal >}= size())
* @see AgtypeUtil#getString(Object)
*/
String getString(int index) throws InvalidAgtypeException;
/**
* Returns the int value at the specified position in this list. Throws an
* InvalidAgtypeException if the element is not an int.
*
* @param index index of the element to return
* @return the int value at the specified position in this list
* @throws InvalidAgtypeException if the value cannot be converted to an int
* @throws IndexOutOfBoundsException if the index is out of range (index {@literal <} 0 || index
* {@literal >}= size())
* @see AgtypeUtil#getInt(Object)
*/
int getInt(int index) throws InvalidAgtypeException;
/**
* Returns the long value at the specified position in this list. Throws an
* InvalidAgtypeException if the element is not a long.
*
* @param index index of the element to return
* @return the long value at the specified position in this list
* @throws InvalidAgtypeException if the value cannot be converted to a long
* @throws IndexOutOfBoundsException if the index is out of range (index {@literal <} 0 || index
* {@literal >}= size())
* @see AgtypeUtil#getLong(Object)
*/
long getLong(int index) throws InvalidAgtypeException;
/**
* Returns the double value at the specified position in this list. Throws an
* InvalidAgtypeException if the element is not a double.
*
* @param index index of the element to return
* @return the double value at the specified position in this list
* @throws InvalidAgtypeException if the value cannot be converted to a double
* @throws IndexOutOfBoundsException if the index is out of range (index {@literal <} 0 || index
* {@literal >}= size())
* @see AgtypeUtil#getDouble(Object)
*/
double getDouble(int index) throws InvalidAgtypeException;
/**
* Returns the double value at the specified position in this list. Throws an
* InvalidAgtypeException if the element is not a double.
*
* @param index index of the element to return
* @return the boolean value at the specified position in this list
* @throws InvalidAgtypeException if the value cannot be converted to a boolean
* @throws IndexOutOfBoundsException if the index is out of range (index {@literal <} 0 || index
* {@literal >}= size())
* @see AgtypeUtil#getBoolean(Object)
*/
boolean getBoolean(int index) throws InvalidAgtypeException;
/**
* Returns the AgtypeList at the specified position in this list. Throws an
* InvalidAgtypeException if the element is not an AgtypeList.
*
* @param index index of the element to return
* @return the AgtypeList at the specified position in this list
* @throws InvalidAgtypeException if the value stored at the index is not an AgtypeList
* @throws IndexOutOfBoundsException if the index is out of range (index {@literal <} 0 || index
* {@literal >}= size())
* @see AgtypeUtil#getList(Object)
*/
AgtypeList getList(int index) throws InvalidAgtypeException;
/**
* Returns the AgtypeMap at the specified position in this list. Throws an
* InvalidAgtypeException if the element is not an AgtypeMap.
*
* @param index index of the element to return
* @return the AgtypeList at the specified position in this list
* @throws InvalidAgtypeException if the value stored at the index is not an AgtypeMap
* @throws IndexOutOfBoundsException if the index is out of range (index {@literal <} 0 || index
* {@literal >}= size())
* @see AgtypeUtil#getMap(Object)
*/
AgtypeMap getMap(int index) throws InvalidAgtypeException;
/**
* Returns the object at the specified position in this list.
*
* @param index index of the element to return
* @return the object at the specified position in this list
* @throws IndexOutOfBoundsException if the index is out of range (index {@literal <} 0 || index
* {@literal >}= size())
*/
Object getObject(int index);
/**
* Returns an iterator over the elements.
*
* @return Iterator over the elements
*/
Iterator