diff --git a/.github/workflows/go-driver.yml b/.github/workflows/go-driver.yml
index dfd1c0554..afba9cbe6 100644
--- a/.github/workflows/go-driver.yml
+++ b/.github/workflows/go-driver.yml
@@ -2,10 +2,10 @@ name: Go Driver Tests
on:
push:
- branches: [ "PG12" ]
-
+ branches: [ "master", "PG12" ]
+
pull_request:
- branches: [ "PG12" ]
+ branches: [ "master", "PG12" ]
jobs:
build:
@@ -13,21 +13,43 @@ jobs:
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: |
+ if [[ "$GITHUB_EVENT_NAME" == "push" ]]; then
+ if [[ "$GITHUB_REF" == "refs/heads/master" ]]; then
+ echo "TAG=latest" >> $GITHUB_ENV
+ elif [[ "$GITHUB_REF" == "refs/heads/PG12" ]]; then
+ echo "TAG=PG12_latest" >> $GITHUB_ENV
+ fi
+ elif [[ "$GITHUB_EVENT_NAME" == "pull_request" ]]; then
+ if [[ "$GITHUB_BASE_REF" == "master" ]]; then
+ echo "TAG=latest" >> $GITHUB_ENV
+ elif [[ "$GITHUB_BASE_REF" == "PG12" ]]; then
+ echo "TAG=PG12_latest" >> $GITHUB_ENV
+ fi
+ fi
+
- name: Run apache/age docker image
- run: docker-compose up -d
+ 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 ./...
diff --git a/.github/workflows/installcheck.yaml b/.github/workflows/installcheck.yaml
new file mode 100644
index 000000000..c35979cd5
--- /dev/null
+++ b/.github/workflows/installcheck.yaml
@@ -0,0 +1,52 @@
+name: Build / Regression
+
+on:
+ push:
+ branches: [ 'master', 'PG12' ]
+ pull_request:
+ branches: [ 'master', 'PG12' ]
+
+jobs:
+ build:
+ runs-on: ubuntu-latest
+
+ steps:
+ - name: Get latest commit id of PostgreSQL 12
+ run: |
+ echo "PG_COMMIT_HASH=$(git ls-remote git://git.postgresql.org/git/postgresql.git refs/heads/REL_12_STABLE | awk '{print $1}')" >> $GITHUB_ENV
+
+ - name: Cache PostgreSQL 12
+ uses: actions/cache@v3
+ id: pg12cache
+ with:
+ path: ~/pg12
+ key: ${{ runner.os }}-v1-pg12-${{ env.PG_COMMIT_HASH }}
+
+ - name: Install PostgreSQL 12
+ if: steps.pg12cache.outputs.cache-hit != 'true'
+ run: |
+ git clone --depth 1 --branch REL_12_STABLE git://git.postgresql.org/git/postgresql.git ~/pg12source
+ cd ~/pg12source
+ ./configure --prefix=$HOME/pg12 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/pg12/bin/pg_config install -j$(nproc)
+
+ - name: Regression tests
+ id: regression_tests
+ run: |
+ make PG_CONFIG=$HOME/pg12/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
diff --git a/.github/workflows/jdbc-driver.yaml b/.github/workflows/jdbc-driver.yaml
index 72f9abf42..d76ce6faa 100644
--- a/.github/workflows/jdbc-driver.yaml
+++ b/.github/workflows/jdbc-driver.yaml
@@ -2,19 +2,14 @@ name: JDBC Driver Tests
on:
push:
- branches: [ "PG12" ]
-
+ branches: [ "master", "PG12" ]
+
pull_request:
- branches: [ "PG12" ]
+ branches: [ "master", "PG12" ]
jobs:
build:
runs-on: ubuntu-latest
-
- strategy:
- matrix:
- distributions: ['zulu', 'temurin', 'microsoft']
-
defaults:
run:
working-directory: drivers/jdbc
@@ -25,8 +20,26 @@ jobs:
- name: Set up Java
uses: actions/setup-java@v3
with:
- distribution: ${{ matrix.distributions }}
+ distribution: 'zulu'
java-version: '17'
+ - name: Set tag based on branch
+ run: |
+ if [[ "$GITHUB_EVENT_NAME" == "push" ]]; then
+ if [[ "$GITHUB_REF" == "refs/heads/master" ]]; then
+ echo "TAG=latest" >> $GITHUB_ENV
+ elif [[ "$GITHUB_REF" == "refs/heads/PG12" ]]; then
+ echo "TAG=PG12_latest" >> $GITHUB_ENV
+ fi
+ elif [[ "$GITHUB_EVENT_NAME" == "pull_request" ]]; then
+ if [[ "$GITHUB_BASE_REF" == "master" ]]; then
+ echo "TAG=latest" >> $GITHUB_ENV
+ elif [[ "$GITHUB_BASE_REF" == "PG12" ]]; then
+ echo "TAG=PG12_latest" >> $GITHUB_ENV
+ fi
+ fi
+
- name: Build and Test
- run: gradle build
\ No newline at end of file
+ run: |
+ export TAG=$TAG
+ gradle build
diff --git a/.github/workflows/nodejs-driver.yaml b/.github/workflows/nodejs-driver.yaml
index 94134facc..b2a4e0ea9 100644
--- a/.github/workflows/nodejs-driver.yaml
+++ b/.github/workflows/nodejs-driver.yaml
@@ -2,10 +2,10 @@ name: Nodejs Driver Tests
on:
push:
- branches: [ "PG12" ]
+ branches: [ "master", "PG12" ]
pull_request:
- branches: [ "PG12" ]
+ branches: [ "master", "PG12" ]
jobs:
build:
@@ -14,12 +14,30 @@ jobs:
defaults:
run:
working-directory: drivers/nodejs/
-
+
steps:
- uses: actions/checkout@v3
-
+
+ - name: Set tag based on branch
+ run: |
+ if [[ "$GITHUB_EVENT_NAME" == "push" ]]; then
+ if [[ "$GITHUB_REF" == "refs/heads/master" ]]; then
+ echo "TAG=latest" >> $GITHUB_ENV
+ elif [[ "$GITHUB_REF" == "refs/heads/PG12" ]]; then
+ echo "TAG=PG12_latest" >> $GITHUB_ENV
+ fi
+ elif [[ "$GITHUB_EVENT_NAME" == "pull_request" ]]; then
+ if [[ "$GITHUB_BASE_REF" == "master" ]]; then
+ echo "TAG=latest" >> $GITHUB_ENV
+ elif [[ "$GITHUB_BASE_REF" == "PG12" ]]; then
+ echo "TAG=PG12_latest" >> $GITHUB_ENV
+ fi
+ fi
+
- name: Run apache/age docker image
- run: docker-compose up -d
+ run: |
+ export TAG=$TAG
+ docker-compose up -d
- name: Set up Node
uses: actions/setup-node@v3
@@ -33,4 +51,4 @@ jobs:
run: npm run build
- name: Test
- run: npm test
\ No newline at end of file
+ run: npm test
diff --git a/.github/workflows/python-driver.yaml b/.github/workflows/python-driver.yaml
index 1f15cf9ad..df3ed71be 100644
--- a/.github/workflows/python-driver.yaml
+++ b/.github/workflows/python-driver.yaml
@@ -2,10 +2,10 @@ name: Python Driver Tests
on:
push:
- branches: [ "PG12" ]
-
+ branches: [ "master", "PG12" ]
+
pull_request:
- branches: [ "PG12" ]
+ branches: [ "master", "PG12" ]
jobs:
build:
@@ -14,12 +14,30 @@ jobs:
defaults:
run:
working-directory: drivers/python
-
+
steps:
- uses: actions/checkout@v3
-
+
+ - name: Set tag based on branch
+ run: |
+ if [[ "$GITHUB_EVENT_NAME" == "push" ]]; then
+ if [[ "$GITHUB_REF" == "refs/heads/master" ]]; then
+ echo "TAG=latest" >> $GITHUB_ENV
+ elif [[ "$GITHUB_REF" == "refs/heads/PG12" ]]; then
+ echo "TAG=PG12_latest" >> $GITHUB_ENV
+ fi
+ elif [[ "$GITHUB_EVENT_NAME" == "pull_request" ]]; then
+ if [[ "$GITHUB_BASE_REF" == "master" ]]; then
+ echo "TAG=latest" >> $GITHUB_ENV
+ elif [[ "$GITHUB_BASE_REF" == "PG12" ]]; then
+ echo "TAG=PG12_latest" >> $GITHUB_ENV
+ fi
+ fi
+
- name: Run apache/age docker image
- run: docker-compose up -d
+ run: |
+ export TAG=$TAG
+ docker-compose up -d
- name: Set up python
uses: actions/setup-python@v4
@@ -37,5 +55,5 @@ jobs:
- name: Test
run: |
- python -m unittest -v test_age_py.py
+ python test_age_py.py -db "postgres" -u "postgres" -pass "agens"
python -m unittest -v test_agtypes.py
diff --git a/Dockerfile b/Dockerfile
index cd9622ca0..24e1e2d9c 100644
--- a/Dockerfile
+++ b/Dockerfile
@@ -16,19 +16,18 @@
# limitations under the License.
#
-
FROM postgres:12
-RUN apt-get update
+RUN apt-get update
RUN apt-get install --assume-yes --no-install-recommends --no-install-suggests \
bison \
build-essential \
flex \
- postgresql-server-dev-12
+ postgresql-server-dev-12
-COPY . /age
-RUN cd /age && make install
+COPY . /age
+RUN cd /age && make install
COPY 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"]
\ No newline at end of file
+CMD ["postgres", "-c", "shared_preload_libraries=age"]
diff --git a/META.json b/META.json
index e17fda2f3..92f797301 100644
--- a/META.json
+++ b/META.json
@@ -2,7 +2,7 @@
"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.1.0",
+ "version": "1.3.0",
"maintainer": [
"users@age.apache.org"
],
@@ -10,9 +10,9 @@
"provides": {
"ApacheAGE": {
"abstract": "Apache AGE is a PostgreSQL Extension that provides graph database functionality",
- "file": "age--1.1.0.sql",
+ "file": "age--1.3.0.sql",
"docfile": "README.md",
- "version": "1.1.0"
+ "version": "1.3.0"
}
},
"prereqs": {
@@ -23,7 +23,7 @@
}
},
"resources": {
- "homepage": "https://github.com/apache/age/tree/AGE_PG12.1.0_ALPHA",
+ "homepage": "https://github.com/apache/age/tree/PG12",
"bugtracker": {
"web": "https://github.com/apache/age/issues"
},
diff --git a/Makefile b/Makefile
index 83f4dfe93..199a0b595 100644
--- a/Makefile
+++ b/Makefile
@@ -18,7 +18,6 @@
MODULE_big = age
-
OBJS = src/backend/age.o \
src/backend/catalog/ag_catalog.o \
src/backend/catalog/ag_graph.o \
diff --git a/README.md b/README.md
index 4b8b968e2..de3a898a9 100644
--- a/README.md
+++ b/README.md
@@ -7,7 +7,7 @@
-
+
@@ -69,7 +69,7 @@
-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 databases 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 background who require graph data analytics can use this extension with minimal effort because they can use existing data without having to go through migration.
+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.
@@ -79,7 +79,7 @@ There is a strong need for cohesive, easy-to-implement multi-model databases. As
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 multiple graphs at the same time.
+- **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
@@ -128,13 +128,13 @@ sudo apt-get install build-essential libreadline-dev zlib1g-dev flex bison
Apache AGE is intended to be simple to install and run. It can be installed with Docker and other traditional ways.
- Install PosgtreSQL
+ Install PostgreSQL
-You will need to install an AGE compatible version of Postgres, for now AGE supports Postgres 11, 12 and 13. Supporting the latest versions is on AGE roadmap.
+You will need to install an AGE compatible version of Postgres, for now AGE supports Postgres 11, 12 & 13. Supporting the latest versions is on AGE roadmap.
- Install From Package Manager
+ Installation via Package Manager
You can use a package management that your OS provides to download AGE.
@@ -146,7 +146,7 @@ sudo apt install postgresql
```
- Install From Source Code
+ Installation From Source Code
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.
@@ -156,7 +156,7 @@ You can download the Postgres

Install AGE on Linux and MacOS
-Clone the github repository or download thedownload an official release.
+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 are supported. If you have any other version of Postgres, you will need to install PostgreSQL version 11, 12, or 13.
@@ -244,12 +244,22 @@ FROM cypher('graph_name', $$
$$) 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
+SELECT *
+FROM cypher('graph_name', $$
+ MATCH (v)
+ RETURN v
$$) as (v agtype);
```
@@ -294,7 +304,7 @@ $$) 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.
+Starting with Apache AGE is very simple. You can easily select your platform and incorporate the relevant SDK into your code.
@@ -322,7 +332,7 @@ Starting with Apache AGE is very simple. You can easily select your platform a
Join the AGE community for help, questions, discussions, and contributions.
- Check our [website](https://age.apache.org/)
-- Chat live with us on [Discord](https://discord.com/invite/NMsBs9X8Ss/)
+- Join the Live Chat on [Discord](https://discord.com/invite/NMsBs9X8Ss/)
- 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
diff --git a/age--1.2.0--1.3.0.sql b/age--1.2.0--1.3.0.sql
new file mode 100644
index 000000000..d06214c5c
--- /dev/null
+++ b/age--1.2.0--1.3.0.sql
@@ -0,0 +1,61 @@
+/*
+ * 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.
+ */
+
+-- complain if script is sourced in psql, rather than via CREATE EXTENSION
+\echo Use "ALTER EXTENSION age UPDATE TO '1.3.0'" to load this file. \quit
+
+CREATE FUNCTION ag_catalog.agtype_build_map_nonull(VARIADIC "any")
+RETURNS agtype
+LANGUAGE c
+STABLE
+CALLED ON NULL INPUT
+PARALLEL SAFE
+AS 'MODULE_PATHNAME';
+
+CREATE FUNCTION ag_catalog.age_isempty(agtype)
+RETURNS boolean
+LANGUAGE c
+IMMUTABLE
+RETURNS NULL ON NULL INPUT
+PARALLEL SAFE
+AS 'MODULE_PATHNAME';
+
+CREATE OR REPLACE FUNCTION ag_catalog.age_unnest(agtype)
+RETURNS SETOF agtype
+LANGUAGE c
+IMMUTABLE
+PARALLEL SAFE
+AS 'MODULE_PATHNAME';
+
+CREATE FUNCTION ag_catalog.age_create_barbell_graph(graph_name name,
+ graph_size int,
+ bridge_size int,
+ node_label name = NULL,
+ node_properties agtype = NULL,
+ edge_label name = NULL,
+ edge_properties agtype = NULL)
+RETURNS void
+LANGUAGE c
+CALLED ON NULL INPUT
+PARALLEL SAFE
+AS 'MODULE_PATHNAME';
+
+--
+-- End
+--
diff --git a/age--1.3.0.sql b/age--1.3.0.sql
index 3ee696d1a..70cdf323f 100644
--- a/age--1.3.0.sql
+++ b/age--1.3.0.sql
@@ -24,7 +24,6 @@
-- catalog tables
--
-
CREATE TABLE ag_graph (
graphid oid NOT NULL,
name name NOT NULL,
@@ -56,8 +55,8 @@ CREATE TABLE ag_label (
relation regclass NOT NULL,
seq_name name NOT NULL,
CONSTRAINT fk_graph_oid
- FOREIGN KEY(graph)
- REFERENCES ag_graph(graphid)
+ FOREIGN KEY(graph)
+ REFERENCES ag_graph(graphid)
);
-- include content of the ag_label table into the pg_dump output
@@ -3088,7 +3087,6 @@ CREATE CAST (agtype AS graphid)
WITH FUNCTION ag_catalog.agtype_to_graphid(agtype)
AS IMPLICIT;
-
--
-- agtype - path
--
@@ -3162,15 +3160,14 @@ AS 'MODULE_PATHNAME';
-- functions we need. Wrap the function with this to
-- prevent that from happening
--
-CREATE FUNCTION ag_catalog.agtype_volatile_wrapper(agt agtype)
-RETURNS agtype AS $return_value$
-BEGIN
- RETURN agt;
-END;
-$return_value$ LANGUAGE plpgsql
+
+CREATE FUNCTION ag_catalog.agtype_volatile_wrapper("any")
+RETURNS agtype
+LANGUAGE c
VOLATILE
CALLED ON NULL INPUT
-PARALLEL SAFE;
+PARALLEL SAFE
+AS 'MODULE_PATHNAME';
--
-- agtype - list literal (`[expr, ...]`)
@@ -3786,6 +3783,19 @@ IMMUTABLE
PARALLEL SAFE
AS 'MODULE_PATHNAME';
+CREATE FUNCTION ag_catalog.age_pi()
+RETURNS agtype
+LANGUAGE c
+IMMUTABLE
+PARALLEL SAFE
+AS 'MODULE_PATHNAME';
+
+CREATE FUNCTION ag_catalog.age_rand()
+RETURNS agtype
+LANGUAGE c
+PARALLEL SAFE
+AS 'MODULE_PATHNAME';
+
CREATE FUNCTION ag_catalog.age_exp(variadic "any")
RETURNS agtype
LANGUAGE c
@@ -4031,6 +4041,13 @@ IMMUTABLE
PARALLEL SAFE
AS 'MODULE_PATHNAME';
+CREATE FUNCTION ag_catalog.agtype_typecast_bool(variadic "any")
+RETURNS agtype
+LANGUAGE c
+IMMUTABLE
+PARALLEL SAFE
+AS 'MODULE_PATHNAME';
+
CREATE FUNCTION ag_catalog.agtype_typecast_vertex(variadic "any")
RETURNS agtype
LANGUAGE c
@@ -4187,20 +4204,23 @@ STABLE
PARALLEL SAFE
AS 'MODULE_PATHNAME';
-CREATE FUNCTION ag_catalog.create_complete_graph(graph_name name, nodes int, edge_label name, node_label name = NULL)
+CREATE FUNCTION ag_catalog.create_complete_graph(graph_name name,
+ nodes int,
+ edge_label name,
+ node_label name = NULL)
RETURNS void
LANGUAGE c
CALLED ON NULL INPUT
PARALLEL SAFE
AS 'MODULE_PATHNAME';
-CREATE FUNCTION ag_catalog.age_create_barbell_graph(graph_name name,
- graph_size int,
- bridge_size int,
- node_label name = NULL,
- node_properties agtype = NULL,
- edge_label name = NULL,
- edge_properties agtype = NULL)
+CREATE FUNCTION ag_catalog.age_create_barbell_graph(graph_name name,
+ graph_size int,
+ bridge_size int,
+ node_label name = NULL,
+ node_properties agtype = NULL,
+ edge_label name = NULL,
+ edge_properties agtype = NULL)
RETURNS void
LANGUAGE c
CALLED ON NULL INPUT
diff --git a/drivers/README b/drivers/README
index 19f34d8b5..b890537f6 100644
--- a/drivers/README
+++ b/drivers/README
@@ -19,5 +19,5 @@ 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
+* GitHub : https://github.com/apache/age
* Document : https://age.apache.org/age-manual/master/index.html
diff --git a/drivers/docker-compose.yml b/drivers/docker-compose.yml
index 3bcbb3542..bd9bc06c4 100644
--- a/drivers/docker-compose.yml
+++ b/drivers/docker-compose.yml
@@ -7,4 +7,4 @@ services:
- POSTGRES_PASSWORD=agens
- POSTGRES_DB=postgres
ports:
- - 5432:5432
\ No newline at end of file
+ - 5432:5432
diff --git a/drivers/golang/README.md b/drivers/golang/README.md
index 367031000..b4adc6646 100644
--- a/drivers/golang/README.md
+++ b/drivers/golang/README.md
@@ -10,6 +10,12 @@ AGType parser and driver support for [Apache AGE](https://age.apache.org/), grap
* 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
```
@@ -25,7 +31,7 @@ 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
+* GitHub : https://github.com/apache/age
* Document : https://age.apache.org/docs/
### Check AGE loaded on your PostgreSQL
diff --git a/drivers/golang/age/builder.go b/drivers/golang/age/builder.go
index be1c4bbb1..43024a39b 100644
--- a/drivers/golang/age/builder.go
+++ b/drivers/golang/age/builder.go
@@ -24,8 +24,7 @@ import (
"math/big"
"strconv"
"strings"
-
- "github.com/antlr/antlr4/runtime/Go/antlr"
+ "github.com/antlr/antlr4/runtime/Go/antlr/v4"
"github.com/apache/age/drivers/golang/parser"
)
diff --git a/drivers/golang/age/mapper.go b/drivers/golang/age/mapper.go
index 5857b1331..1744e53ae 100644
--- a/drivers/golang/age/mapper.go
+++ b/drivers/golang/age/mapper.go
@@ -22,8 +22,7 @@ import (
"fmt"
"reflect"
"strings"
-
- "github.com/antlr/antlr4/runtime/Go/antlr"
+ "github.com/antlr/antlr4/runtime/Go/antlr/v4"
"github.com/apache/age/drivers/golang/parser"
)
diff --git a/drivers/golang/go.mod b/drivers/golang/go.mod
index a6bd74bd6..a0774e756 100644
--- a/drivers/golang/go.mod
+++ b/drivers/golang/go.mod
@@ -22,13 +22,14 @@ module github.com/apache/age/drivers/golang
go 1.19
require (
- github.com/antlr/antlr4/runtime/Go/antlr v0.0.0-20210521184019-c5ad59b459ec
- github.com/lib/pq v1.10.2
+ 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
)
diff --git a/drivers/golang/go.sum b/drivers/golang/go.sum
index 89d1775ed..79dc4cecd 100644
--- a/drivers/golang/go.sum
+++ b/drivers/golang/go.sum
@@ -1,14 +1,20 @@
-github.com/antlr/antlr4/runtime/Go/antlr v0.0.0-20210521184019-c5ad59b459ec h1:EEyRvzmpEUZ+I8WmD5cw/vY8EqhambkOqy5iFr0908A=
-github.com/antlr/antlr4/runtime/Go/antlr v0.0.0-20210521184019-c5ad59b459ec/go.mod h1:F7bn7fEU90QkQ3tnmaTx3LTKLEDqnwWODIYppRQ5hnY=
+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.2 h1:AqzbZs4ZoCBp+GtejcpCpcxM3zlSMx29dXbUSeVtJb8=
-github.com/lib/pq v1.10.2/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o=
+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=
diff --git a/drivers/golang/install.bat b/drivers/golang/install.bat
new file mode 100644
index 000000000..dbf032e56
--- /dev/null
+++ b/drivers/golang/install.bat
@@ -0,0 +1,85 @@
+@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
\ No newline at end of file
diff --git a/drivers/golang/install.sh b/drivers/golang/install.sh
new file mode 100755
index 000000000..8cd1a7d80
--- /dev/null
+++ b/drivers/golang/install.sh
@@ -0,0 +1,141 @@
+#!/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
diff --git a/drivers/golang/parser/README.md b/drivers/golang/parser/README.md
deleted file mode 100644
index a70eede38..000000000
--- a/drivers/golang/parser/README.md
+++ /dev/null
@@ -1,13 +0,0 @@
-# ANTLR4 Go Query Result data parser generation rules for apache-age-go
-Go driver for Apache AGE, graph extension for PostgreSQL.
-
-
-### Build
-#### 1) Generate query result data parser with ANTLR4
-```
-# prerequisites :
-# - java over 8
-# - download ANTLR4 from https://www.antlr.org/download/antlr-4.9.2-complete.jar
-# - java -cp antlr-4.9.2-complete.jar org.antlr.v4.Tool -Dlanguage=Go -visitor Age.g4
-```
-
diff --git a/drivers/golang/parser/age_base_listener.go b/drivers/golang/parser/age_base_listener.go
deleted file mode 100644
index 009707d6d..000000000
--- a/drivers/golang/parser/age_base_listener.go
+++ /dev/null
@@ -1,88 +0,0 @@
-/*
- * 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.
- */
-// Code generated from Age.g4 by ANTLR 4.9.2. DO NOT EDIT.
-
-package parser // Age
-
-import "github.com/antlr/antlr4/runtime/Go/antlr"
-
-// BaseAgeListener is a complete listener for a parse tree produced by AgeParser.
-type BaseAgeListener struct{}
-
-var _ AgeListener = &BaseAgeListener{}
-
-// VisitTerminal is called when a terminal node is visited.
-func (s *BaseAgeListener) VisitTerminal(node antlr.TerminalNode) {}
-
-// VisitErrorNode is called when an error node is visited.
-func (s *BaseAgeListener) VisitErrorNode(node antlr.ErrorNode) {}
-
-// EnterEveryRule is called when any rule is entered.
-func (s *BaseAgeListener) EnterEveryRule(ctx antlr.ParserRuleContext) {}
-
-// ExitEveryRule is called when any rule is exited.
-func (s *BaseAgeListener) ExitEveryRule(ctx antlr.ParserRuleContext) {}
-
-// EnterAgeout is called when production ageout is entered.
-func (s *BaseAgeListener) EnterAgeout(ctx *AgeoutContext) {}
-
-// ExitAgeout is called when production ageout is exited.
-func (s *BaseAgeListener) ExitAgeout(ctx *AgeoutContext) {}
-
-// EnterVertex is called when production vertex is entered.
-func (s *BaseAgeListener) EnterVertex(ctx *VertexContext) {}
-
-// ExitVertex is called when production vertex is exited.
-func (s *BaseAgeListener) ExitVertex(ctx *VertexContext) {}
-
-// EnterEdge is called when production edge is entered.
-func (s *BaseAgeListener) EnterEdge(ctx *EdgeContext) {}
-
-// ExitEdge is called when production edge is exited.
-func (s *BaseAgeListener) ExitEdge(ctx *EdgeContext) {}
-
-// EnterPath is called when production path is entered.
-func (s *BaseAgeListener) EnterPath(ctx *PathContext) {}
-
-// ExitPath is called when production path is exited.
-func (s *BaseAgeListener) ExitPath(ctx *PathContext) {}
-
-// EnterValue is called when production value is entered.
-func (s *BaseAgeListener) EnterValue(ctx *ValueContext) {}
-
-// ExitValue is called when production value is exited.
-func (s *BaseAgeListener) ExitValue(ctx *ValueContext) {}
-
-// EnterProperties is called when production properties is entered.
-func (s *BaseAgeListener) EnterProperties(ctx *PropertiesContext) {}
-
-// ExitProperties is called when production properties is exited.
-func (s *BaseAgeListener) ExitProperties(ctx *PropertiesContext) {}
-
-// EnterPair is called when production pair is entered.
-func (s *BaseAgeListener) EnterPair(ctx *PairContext) {}
-
-// ExitPair is called when production pair is exited.
-func (s *BaseAgeListener) ExitPair(ctx *PairContext) {}
-
-// EnterArr is called when production arr is entered.
-func (s *BaseAgeListener) EnterArr(ctx *ArrContext) {}
-
-// ExitArr is called when production arr is exited.
-func (s *BaseAgeListener) ExitArr(ctx *ArrContext) {}
diff --git a/drivers/golang/parser/age_base_visitor.go b/drivers/golang/parser/age_base_visitor.go
deleted file mode 100644
index 42d59464a..000000000
--- a/drivers/golang/parser/age_base_visitor.go
+++ /dev/null
@@ -1,59 +0,0 @@
-/*
- * 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.
- */
-// Code generated from Age.g4 by ANTLR 4.9.2. DO NOT EDIT.
-
-package parser // Age
-
-import "github.com/antlr/antlr4/runtime/Go/antlr"
-
-type BaseAgeVisitor struct {
- *antlr.BaseParseTreeVisitor
-}
-
-func (v *BaseAgeVisitor) VisitAgeout(ctx *AgeoutContext) interface{} {
- return v.VisitChildren(ctx)
-}
-
-func (v *BaseAgeVisitor) VisitVertex(ctx *VertexContext) interface{} {
- return v.VisitChildren(ctx)
-}
-
-func (v *BaseAgeVisitor) VisitEdge(ctx *EdgeContext) interface{} {
- return v.VisitChildren(ctx)
-}
-
-func (v *BaseAgeVisitor) VisitPath(ctx *PathContext) interface{} {
- return v.VisitChildren(ctx)
-}
-
-func (v *BaseAgeVisitor) VisitValue(ctx *ValueContext) interface{} {
- return v.VisitChildren(ctx)
-}
-
-func (v *BaseAgeVisitor) VisitProperties(ctx *PropertiesContext) interface{} {
- return v.VisitChildren(ctx)
-}
-
-func (v *BaseAgeVisitor) VisitPair(ctx *PairContext) interface{} {
- return v.VisitChildren(ctx)
-}
-
-func (v *BaseAgeVisitor) VisitArr(ctx *ArrContext) interface{} {
- return v.VisitChildren(ctx)
-}
diff --git a/drivers/golang/parser/age_lexer.go b/drivers/golang/parser/age_lexer.go
deleted file mode 100644
index fface8327..000000000
--- a/drivers/golang/parser/age_lexer.go
+++ /dev/null
@@ -1,212 +0,0 @@
-/*
- * 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.
- */
-// Code generated from Age.g4 by ANTLR 4.9.2. DO NOT EDIT.
-
-package parser
-
-import (
- "fmt"
- "unicode"
-
- "github.com/antlr/antlr4/runtime/Go/antlr"
-)
-
-// Suppress unused import error
-var _ = fmt.Printf
-var _ = unicode.IsLetter
-
-var serializedLexerAtn = []uint16{
- 3, 24715, 42794, 33075, 47597, 16764, 15335, 30598, 22884, 2, 19, 212,
- 8, 1, 4, 2, 9, 2, 4, 3, 9, 3, 4, 4, 9, 4, 4, 5, 9, 5, 4, 6, 9, 6, 4, 7,
- 9, 7, 4, 8, 9, 8, 4, 9, 9, 9, 4, 10, 9, 10, 4, 11, 9, 11, 4, 12, 9, 12,
- 4, 13, 9, 13, 4, 14, 9, 14, 4, 15, 9, 15, 4, 16, 9, 16, 4, 17, 9, 17, 4,
- 18, 9, 18, 4, 19, 9, 19, 4, 20, 9, 20, 4, 21, 9, 21, 4, 22, 9, 22, 4, 23,
- 9, 23, 4, 24, 9, 24, 3, 2, 3, 2, 3, 3, 3, 3, 3, 4, 3, 4, 3, 5, 3, 5, 3,
- 6, 3, 6, 3, 7, 3, 7, 3, 8, 3, 8, 3, 8, 3, 8, 3, 8, 3, 8, 3, 8, 3, 8, 3,
- 8, 3, 9, 3, 9, 3, 9, 3, 9, 3, 9, 3, 9, 3, 9, 3, 10, 3, 10, 3, 10, 3, 10,
- 3, 10, 3, 10, 3, 10, 3, 11, 3, 11, 3, 11, 3, 11, 3, 11, 3, 11, 3, 11, 3,
- 11, 3, 11, 3, 11, 3, 12, 3, 12, 3, 12, 7, 12, 98, 10, 12, 12, 12, 14, 12,
- 101, 11, 12, 3, 12, 3, 12, 3, 13, 3, 13, 3, 13, 3, 13, 3, 13, 3, 13, 3,
- 13, 3, 13, 3, 13, 5, 13, 114, 10, 13, 3, 14, 3, 14, 3, 14, 3, 14, 3, 14,
- 3, 15, 3, 15, 3, 15, 5, 15, 124, 10, 15, 3, 16, 3, 16, 3, 16, 3, 16, 3,
- 16, 3, 16, 3, 17, 3, 17, 3, 18, 3, 18, 3, 19, 5, 19, 137, 10, 19, 3, 19,
- 3, 19, 3, 19, 6, 19, 142, 10, 19, 13, 19, 14, 19, 143, 5, 19, 146, 10,
- 19, 3, 19, 5, 19, 149, 10, 19, 3, 20, 3, 20, 3, 20, 3, 20, 3, 20, 3, 20,
- 3, 20, 3, 20, 3, 20, 3, 20, 3, 20, 3, 20, 3, 20, 3, 20, 3, 20, 3, 20, 3,
- 20, 3, 20, 3, 20, 3, 20, 5, 20, 171, 10, 20, 3, 21, 5, 21, 174, 10, 21,
- 3, 21, 3, 21, 3, 21, 6, 21, 179, 10, 21, 13, 21, 14, 21, 180, 5, 21, 183,
- 10, 21, 3, 21, 5, 21, 186, 10, 21, 3, 21, 3, 21, 3, 22, 3, 22, 3, 22, 7,
- 22, 193, 10, 22, 12, 22, 14, 22, 196, 11, 22, 5, 22, 198, 10, 22, 3, 23,
- 3, 23, 5, 23, 202, 10, 23, 3, 23, 3, 23, 3, 24, 6, 24, 207, 10, 24, 13,
- 24, 14, 24, 208, 3, 24, 3, 24, 2, 2, 25, 3, 3, 5, 4, 7, 5, 9, 6, 11, 7,
- 13, 8, 15, 9, 17, 10, 19, 11, 21, 12, 23, 13, 25, 14, 27, 15, 29, 2, 31,
- 2, 33, 2, 35, 2, 37, 16, 39, 17, 41, 18, 43, 2, 45, 2, 47, 19, 3, 2, 10,
- 10, 2, 36, 36, 49, 49, 94, 94, 100, 100, 104, 104, 112, 112, 116, 116,
- 118, 118, 5, 2, 50, 59, 67, 72, 99, 104, 5, 2, 2, 33, 36, 36, 94, 94, 3,
- 2, 50, 59, 3, 2, 51, 59, 4, 2, 71, 71, 103, 103, 4, 2, 45, 45, 47, 47,
- 5, 2, 11, 12, 15, 15, 34, 34, 2, 223, 2, 3, 3, 2, 2, 2, 2, 5, 3, 2, 2,
- 2, 2, 7, 3, 2, 2, 2, 2, 9, 3, 2, 2, 2, 2, 11, 3, 2, 2, 2, 2, 13, 3, 2,
- 2, 2, 2, 15, 3, 2, 2, 2, 2, 17, 3, 2, 2, 2, 2, 19, 3, 2, 2, 2, 2, 21, 3,
- 2, 2, 2, 2, 23, 3, 2, 2, 2, 2, 25, 3, 2, 2, 2, 2, 27, 3, 2, 2, 2, 2, 37,
- 3, 2, 2, 2, 2, 39, 3, 2, 2, 2, 2, 41, 3, 2, 2, 2, 2, 47, 3, 2, 2, 2, 3,
- 49, 3, 2, 2, 2, 5, 51, 3, 2, 2, 2, 7, 53, 3, 2, 2, 2, 9, 55, 3, 2, 2, 2,
- 11, 57, 3, 2, 2, 2, 13, 59, 3, 2, 2, 2, 15, 61, 3, 2, 2, 2, 17, 70, 3,
- 2, 2, 2, 19, 77, 3, 2, 2, 2, 21, 84, 3, 2, 2, 2, 23, 94, 3, 2, 2, 2, 25,
- 113, 3, 2, 2, 2, 27, 115, 3, 2, 2, 2, 29, 120, 3, 2, 2, 2, 31, 125, 3,
- 2, 2, 2, 33, 131, 3, 2, 2, 2, 35, 133, 3, 2, 2, 2, 37, 136, 3, 2, 2, 2,
- 39, 170, 3, 2, 2, 2, 41, 173, 3, 2, 2, 2, 43, 197, 3, 2, 2, 2, 45, 199,
- 3, 2, 2, 2, 47, 206, 3, 2, 2, 2, 49, 50, 7, 93, 2, 2, 50, 4, 3, 2, 2, 2,
- 51, 52, 7, 46, 2, 2, 52, 6, 3, 2, 2, 2, 53, 54, 7, 95, 2, 2, 54, 8, 3,
- 2, 2, 2, 55, 56, 7, 125, 2, 2, 56, 10, 3, 2, 2, 2, 57, 58, 7, 127, 2, 2,
- 58, 12, 3, 2, 2, 2, 59, 60, 7, 60, 2, 2, 60, 14, 3, 2, 2, 2, 61, 62, 7,
- 60, 2, 2, 62, 63, 7, 60, 2, 2, 63, 64, 7, 120, 2, 2, 64, 65, 7, 103, 2,
- 2, 65, 66, 7, 116, 2, 2, 66, 67, 7, 118, 2, 2, 67, 68, 7, 103, 2, 2, 68,
- 69, 7, 122, 2, 2, 69, 16, 3, 2, 2, 2, 70, 71, 7, 60, 2, 2, 71, 72, 7, 60,
- 2, 2, 72, 73, 7, 103, 2, 2, 73, 74, 7, 102, 2, 2, 74, 75, 7, 105, 2, 2,
- 75, 76, 7, 103, 2, 2, 76, 18, 3, 2, 2, 2, 77, 78, 7, 60, 2, 2, 78, 79,
- 7, 60, 2, 2, 79, 80, 7, 114, 2, 2, 80, 81, 7, 99, 2, 2, 81, 82, 7, 118,
- 2, 2, 82, 83, 7, 106, 2, 2, 83, 20, 3, 2, 2, 2, 84, 85, 7, 60, 2, 2, 85,
- 86, 7, 60, 2, 2, 86, 87, 7, 112, 2, 2, 87, 88, 7, 119, 2, 2, 88, 89, 7,
- 111, 2, 2, 89, 90, 7, 103, 2, 2, 90, 91, 7, 116, 2, 2, 91, 92, 7, 107,
- 2, 2, 92, 93, 7, 101, 2, 2, 93, 22, 3, 2, 2, 2, 94, 99, 7, 36, 2, 2, 95,
- 98, 5, 29, 15, 2, 96, 98, 5, 35, 18, 2, 97, 95, 3, 2, 2, 2, 97, 96, 3,
- 2, 2, 2, 98, 101, 3, 2, 2, 2, 99, 97, 3, 2, 2, 2, 99, 100, 3, 2, 2, 2,
- 100, 102, 3, 2, 2, 2, 101, 99, 3, 2, 2, 2, 102, 103, 7, 36, 2, 2, 103,
- 24, 3, 2, 2, 2, 104, 105, 7, 118, 2, 2, 105, 106, 7, 116, 2, 2, 106, 107,
- 7, 119, 2, 2, 107, 114, 7, 103, 2, 2, 108, 109, 7, 104, 2, 2, 109, 110,
- 7, 99, 2, 2, 110, 111, 7, 110, 2, 2, 111, 112, 7, 117, 2, 2, 112, 114,
- 7, 103, 2, 2, 113, 104, 3, 2, 2, 2, 113, 108, 3, 2, 2, 2, 114, 26, 3, 2,
- 2, 2, 115, 116, 7, 112, 2, 2, 116, 117, 7, 119, 2, 2, 117, 118, 7, 110,
- 2, 2, 118, 119, 7, 110, 2, 2, 119, 28, 3, 2, 2, 2, 120, 123, 7, 94, 2,
- 2, 121, 124, 9, 2, 2, 2, 122, 124, 5, 31, 16, 2, 123, 121, 3, 2, 2, 2,
- 123, 122, 3, 2, 2, 2, 124, 30, 3, 2, 2, 2, 125, 126, 7, 119, 2, 2, 126,
- 127, 5, 33, 17, 2, 127, 128, 5, 33, 17, 2, 128, 129, 5, 33, 17, 2, 129,
- 130, 5, 33, 17, 2, 130, 32, 3, 2, 2, 2, 131, 132, 9, 3, 2, 2, 132, 34,
- 3, 2, 2, 2, 133, 134, 10, 4, 2, 2, 134, 36, 3, 2, 2, 2, 135, 137, 7, 47,
- 2, 2, 136, 135, 3, 2, 2, 2, 136, 137, 3, 2, 2, 2, 137, 138, 3, 2, 2, 2,
- 138, 145, 5, 43, 22, 2, 139, 141, 7, 48, 2, 2, 140, 142, 9, 5, 2, 2, 141,
- 140, 3, 2, 2, 2, 142, 143, 3, 2, 2, 2, 143, 141, 3, 2, 2, 2, 143, 144,
- 3, 2, 2, 2, 144, 146, 3, 2, 2, 2, 145, 139, 3, 2, 2, 2, 145, 146, 3, 2,
- 2, 2, 146, 148, 3, 2, 2, 2, 147, 149, 5, 45, 23, 2, 148, 147, 3, 2, 2,
- 2, 148, 149, 3, 2, 2, 2, 149, 38, 3, 2, 2, 2, 150, 151, 7, 80, 2, 2, 151,
- 152, 7, 99, 2, 2, 152, 171, 7, 80, 2, 2, 153, 154, 7, 47, 2, 2, 154, 155,
- 7, 75, 2, 2, 155, 156, 7, 112, 2, 2, 156, 157, 7, 104, 2, 2, 157, 158,
- 7, 107, 2, 2, 158, 159, 7, 112, 2, 2, 159, 160, 7, 107, 2, 2, 160, 161,
- 7, 118, 2, 2, 161, 171, 7, 123, 2, 2, 162, 163, 7, 75, 2, 2, 163, 164,
- 7, 112, 2, 2, 164, 165, 7, 104, 2, 2, 165, 166, 7, 107, 2, 2, 166, 167,
- 7, 112, 2, 2, 167, 168, 7, 107, 2, 2, 168, 169, 7, 118, 2, 2, 169, 171,
- 7, 123, 2, 2, 170, 150, 3, 2, 2, 2, 170, 153, 3, 2, 2, 2, 170, 162, 3,
- 2, 2, 2, 171, 40, 3, 2, 2, 2, 172, 174, 7, 47, 2, 2, 173, 172, 3, 2, 2,
- 2, 173, 174, 3, 2, 2, 2, 174, 175, 3, 2, 2, 2, 175, 182, 5, 43, 22, 2,
- 176, 178, 7, 48, 2, 2, 177, 179, 9, 5, 2, 2, 178, 177, 3, 2, 2, 2, 179,
- 180, 3, 2, 2, 2, 180, 178, 3, 2, 2, 2, 180, 181, 3, 2, 2, 2, 181, 183,
- 3, 2, 2, 2, 182, 176, 3, 2, 2, 2, 182, 183, 3, 2, 2, 2, 183, 185, 3, 2,
- 2, 2, 184, 186, 5, 45, 23, 2, 185, 184, 3, 2, 2, 2, 185, 186, 3, 2, 2,
- 2, 186, 187, 3, 2, 2, 2, 187, 188, 5, 21, 11, 2, 188, 42, 3, 2, 2, 2, 189,
- 198, 7, 50, 2, 2, 190, 194, 9, 6, 2, 2, 191, 193, 9, 5, 2, 2, 192, 191,
- 3, 2, 2, 2, 193, 196, 3, 2, 2, 2, 194, 192, 3, 2, 2, 2, 194, 195, 3, 2,
- 2, 2, 195, 198, 3, 2, 2, 2, 196, 194, 3, 2, 2, 2, 197, 189, 3, 2, 2, 2,
- 197, 190, 3, 2, 2, 2, 198, 44, 3, 2, 2, 2, 199, 201, 9, 7, 2, 2, 200, 202,
- 9, 8, 2, 2, 201, 200, 3, 2, 2, 2, 201, 202, 3, 2, 2, 2, 202, 203, 3, 2,
- 2, 2, 203, 204, 5, 43, 22, 2, 204, 46, 3, 2, 2, 2, 205, 207, 9, 9, 2, 2,
- 206, 205, 3, 2, 2, 2, 207, 208, 3, 2, 2, 2, 208, 206, 3, 2, 2, 2, 208,
- 209, 3, 2, 2, 2, 209, 210, 3, 2, 2, 2, 210, 211, 8, 24, 2, 2, 211, 48,
- 3, 2, 2, 2, 20, 2, 97, 99, 113, 123, 136, 143, 145, 148, 170, 173, 180,
- 182, 185, 194, 197, 201, 208, 3, 8, 2, 2,
-}
-
-var lexerChannelNames = []string{
- "DEFAULT_TOKEN_CHANNEL", "HIDDEN",
-}
-
-var lexerModeNames = []string{
- "DEFAULT_MODE",
-}
-
-var lexerLiteralNames = []string{
- "", "'['", "','", "']'", "'{'", "'}'", "':'", "'::vertex'", "'::edge'",
- "'::path'", "'::numeric'", "", "", "'null'",
-}
-
-var lexerSymbolicNames = []string{
- "", "", "", "", "", "", "", "KW_VERTEX", "KW_EDGE", "KW_PATH", "KW_NUMERIC",
- "STRING", "BOOL", "NULL", "NUMBER", "FLOAT_EXPR", "NUMERIC", "WS",
-}
-
-var lexerRuleNames = []string{
- "T__0", "T__1", "T__2", "T__3", "T__4", "T__5", "KW_VERTEX", "KW_EDGE",
- "KW_PATH", "KW_NUMERIC", "STRING", "BOOL", "NULL", "ESC", "UNICODE", "HEX",
- "SAFECODEPOINT", "NUMBER", "FLOAT_EXPR", "NUMERIC", "INT", "EXP", "WS",
-}
-
-type AgeLexer struct {
- *antlr.BaseLexer
- channelNames []string
- modeNames []string
- // TODO: EOF string
-}
-
-// NewAgeLexer produces a new lexer instance for the optional input antlr.CharStream.
-//
-// The *AgeLexer instance produced may be reused by calling the SetInputStream method.
-// The initial lexer configuration is expensive to construct, and the object is not thread-safe;
-// however, if used within a Golang sync.Pool, the construction cost amortizes well and the
-// objects can be used in a thread-safe manner.
-func NewAgeLexer(input antlr.CharStream) *AgeLexer {
- l := new(AgeLexer)
- lexerDeserializer := antlr.NewATNDeserializer(nil)
- lexerAtn := lexerDeserializer.DeserializeFromUInt16(serializedLexerAtn)
- lexerDecisionToDFA := make([]*antlr.DFA, len(lexerAtn.DecisionToState))
- for index, ds := range lexerAtn.DecisionToState {
- lexerDecisionToDFA[index] = antlr.NewDFA(ds, index)
- }
- l.BaseLexer = antlr.NewBaseLexer(input)
- l.Interpreter = antlr.NewLexerATNSimulator(l, lexerAtn, lexerDecisionToDFA, antlr.NewPredictionContextCache())
-
- l.channelNames = lexerChannelNames
- l.modeNames = lexerModeNames
- l.RuleNames = lexerRuleNames
- l.LiteralNames = lexerLiteralNames
- l.SymbolicNames = lexerSymbolicNames
- l.GrammarFileName = "Age.g4"
- // TODO: l.EOF = antlr.TokenEOF
-
- return l
-}
-
-// AgeLexer tokens.
-const (
- AgeLexerT__0 = 1
- AgeLexerT__1 = 2
- AgeLexerT__2 = 3
- AgeLexerT__3 = 4
- AgeLexerT__4 = 5
- AgeLexerT__5 = 6
- AgeLexerKW_VERTEX = 7
- AgeLexerKW_EDGE = 8
- AgeLexerKW_PATH = 9
- AgeLexerKW_NUMERIC = 10
- AgeLexerSTRING = 11
- AgeLexerBOOL = 12
- AgeLexerNULL = 13
- AgeLexerNUMBER = 14
- AgeLexerFLOAT_EXPR = 15
- AgeLexerNUMERIC = 16
- AgeLexerWS = 17
-)
diff --git a/drivers/golang/parser/age_listener.go b/drivers/golang/parser/age_listener.go
deleted file mode 100644
index fdb06ce60..000000000
--- a/drivers/golang/parser/age_listener.go
+++ /dev/null
@@ -1,76 +0,0 @@
-/*
- * 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.
- */
-// Code generated from Age.g4 by ANTLR 4.9.2. DO NOT EDIT.
-
-package parser // Age
-
-import "github.com/antlr/antlr4/runtime/Go/antlr"
-
-// AgeListener is a complete listener for a parse tree produced by AgeParser.
-type AgeListener interface {
- antlr.ParseTreeListener
-
- // EnterAgeout is called when entering the ageout production.
- EnterAgeout(c *AgeoutContext)
-
- // EnterVertex is called when entering the vertex production.
- EnterVertex(c *VertexContext)
-
- // EnterEdge is called when entering the edge production.
- EnterEdge(c *EdgeContext)
-
- // EnterPath is called when entering the path production.
- EnterPath(c *PathContext)
-
- // EnterValue is called when entering the value production.
- EnterValue(c *ValueContext)
-
- // EnterProperties is called when entering the properties production.
- EnterProperties(c *PropertiesContext)
-
- // EnterPair is called when entering the pair production.
- EnterPair(c *PairContext)
-
- // EnterArr is called when entering the arr production.
- EnterArr(c *ArrContext)
-
- // ExitAgeout is called when exiting the ageout production.
- ExitAgeout(c *AgeoutContext)
-
- // ExitVertex is called when exiting the vertex production.
- ExitVertex(c *VertexContext)
-
- // ExitEdge is called when exiting the edge production.
- ExitEdge(c *EdgeContext)
-
- // ExitPath is called when exiting the path production.
- ExitPath(c *PathContext)
-
- // ExitValue is called when exiting the value production.
- ExitValue(c *ValueContext)
-
- // ExitProperties is called when exiting the properties production.
- ExitProperties(c *PropertiesContext)
-
- // ExitPair is called when exiting the pair production.
- ExitPair(c *PairContext)
-
- // ExitArr is called when exiting the arr production.
- ExitArr(c *ArrContext)
-}
diff --git a/drivers/golang/parser/age_parser.go b/drivers/golang/parser/age_parser.go
deleted file mode 100644
index 4b8a91f3c..000000000
--- a/drivers/golang/parser/age_parser.go
+++ /dev/null
@@ -1,1375 +0,0 @@
-/*
- * 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.
- */
-// Code generated from Age.g4 by ANTLR 4.9.2. DO NOT EDIT.
-
-package parser // Age
-
-import (
- "fmt"
- "reflect"
- "strconv"
-
- "github.com/antlr/antlr4/runtime/Go/antlr"
-)
-
-// Suppress unused import errors
-var _ = fmt.Printf
-var _ = reflect.Copy
-var _ = strconv.Itoa
-
-var parserATN = []uint16{
- 3, 24715, 42794, 33075, 47597, 16764, 15335, 30598, 22884, 3, 19, 90, 4,
- 2, 9, 2, 4, 3, 9, 3, 4, 4, 9, 4, 4, 5, 9, 5, 4, 6, 9, 6, 4, 7, 9, 7, 4,
- 8, 9, 8, 4, 9, 9, 9, 3, 2, 3, 2, 3, 2, 3, 2, 5, 2, 23, 10, 2, 3, 3, 3,
- 3, 3, 3, 3, 4, 3, 4, 3, 4, 3, 5, 3, 5, 3, 5, 3, 5, 3, 5, 3, 5, 3, 5, 7,
- 5, 38, 10, 5, 12, 5, 14, 5, 41, 11, 5, 3, 5, 3, 5, 3, 5, 3, 6, 3, 6, 3,
- 6, 3, 6, 3, 6, 3, 6, 3, 6, 3, 6, 5, 6, 54, 10, 6, 3, 7, 3, 7, 3, 7, 3,
- 7, 7, 7, 60, 10, 7, 12, 7, 14, 7, 63, 11, 7, 3, 7, 3, 7, 3, 7, 3, 7, 5,
- 7, 69, 10, 7, 3, 8, 3, 8, 3, 8, 3, 8, 3, 9, 3, 9, 3, 9, 3, 9, 7, 9, 79,
- 10, 9, 12, 9, 14, 9, 82, 11, 9, 3, 9, 3, 9, 3, 9, 3, 9, 5, 9, 88, 10, 9,
- 3, 9, 2, 2, 10, 2, 4, 6, 8, 10, 12, 14, 16, 2, 2, 2, 96, 2, 22, 3, 2, 2,
- 2, 4, 24, 3, 2, 2, 2, 6, 27, 3, 2, 2, 2, 8, 30, 3, 2, 2, 2, 10, 53, 3,
- 2, 2, 2, 12, 68, 3, 2, 2, 2, 14, 70, 3, 2, 2, 2, 16, 87, 3, 2, 2, 2, 18,
- 23, 5, 10, 6, 2, 19, 23, 5, 4, 3, 2, 20, 23, 5, 6, 4, 2, 21, 23, 5, 8,
- 5, 2, 22, 18, 3, 2, 2, 2, 22, 19, 3, 2, 2, 2, 22, 20, 3, 2, 2, 2, 22, 21,
- 3, 2, 2, 2, 23, 3, 3, 2, 2, 2, 24, 25, 5, 12, 7, 2, 25, 26, 7, 9, 2, 2,
- 26, 5, 3, 2, 2, 2, 27, 28, 5, 12, 7, 2, 28, 29, 7, 10, 2, 2, 29, 7, 3,
- 2, 2, 2, 30, 31, 7, 3, 2, 2, 31, 39, 5, 4, 3, 2, 32, 33, 7, 4, 2, 2, 33,
- 34, 5, 6, 4, 2, 34, 35, 7, 4, 2, 2, 35, 36, 5, 4, 3, 2, 36, 38, 3, 2, 2,
- 2, 37, 32, 3, 2, 2, 2, 38, 41, 3, 2, 2, 2, 39, 37, 3, 2, 2, 2, 39, 40,
- 3, 2, 2, 2, 40, 42, 3, 2, 2, 2, 41, 39, 3, 2, 2, 2, 42, 43, 7, 5, 2, 2,
- 43, 44, 7, 11, 2, 2, 44, 9, 3, 2, 2, 2, 45, 54, 7, 13, 2, 2, 46, 54, 7,
- 16, 2, 2, 47, 54, 7, 18, 2, 2, 48, 54, 7, 17, 2, 2, 49, 54, 7, 14, 2, 2,
- 50, 54, 7, 15, 2, 2, 51, 54, 5, 12, 7, 2, 52, 54, 5, 16, 9, 2, 53, 45,
- 3, 2, 2, 2, 53, 46, 3, 2, 2, 2, 53, 47, 3, 2, 2, 2, 53, 48, 3, 2, 2, 2,
- 53, 49, 3, 2, 2, 2, 53, 50, 3, 2, 2, 2, 53, 51, 3, 2, 2, 2, 53, 52, 3,
- 2, 2, 2, 54, 11, 3, 2, 2, 2, 55, 56, 7, 6, 2, 2, 56, 61, 5, 14, 8, 2, 57,
- 58, 7, 4, 2, 2, 58, 60, 5, 14, 8, 2, 59, 57, 3, 2, 2, 2, 60, 63, 3, 2,
- 2, 2, 61, 59, 3, 2, 2, 2, 61, 62, 3, 2, 2, 2, 62, 64, 3, 2, 2, 2, 63, 61,
- 3, 2, 2, 2, 64, 65, 7, 7, 2, 2, 65, 69, 3, 2, 2, 2, 66, 67, 7, 6, 2, 2,
- 67, 69, 7, 7, 2, 2, 68, 55, 3, 2, 2, 2, 68, 66, 3, 2, 2, 2, 69, 13, 3,
- 2, 2, 2, 70, 71, 7, 13, 2, 2, 71, 72, 7, 8, 2, 2, 72, 73, 5, 10, 6, 2,
- 73, 15, 3, 2, 2, 2, 74, 75, 7, 3, 2, 2, 75, 80, 5, 10, 6, 2, 76, 77, 7,
- 4, 2, 2, 77, 79, 5, 10, 6, 2, 78, 76, 3, 2, 2, 2, 79, 82, 3, 2, 2, 2, 80,
- 78, 3, 2, 2, 2, 80, 81, 3, 2, 2, 2, 81, 83, 3, 2, 2, 2, 82, 80, 3, 2, 2,
- 2, 83, 84, 7, 5, 2, 2, 84, 88, 3, 2, 2, 2, 85, 86, 7, 3, 2, 2, 86, 88,
- 7, 5, 2, 2, 87, 74, 3, 2, 2, 2, 87, 85, 3, 2, 2, 2, 88, 17, 3, 2, 2, 2,
- 9, 22, 39, 53, 61, 68, 80, 87,
-}
-var literalNames = []string{
- "", "'['", "','", "']'", "'{'", "'}'", "':'", "'::vertex'", "'::edge'",
- "'::path'", "'::numeric'", "", "", "'null'",
-}
-var symbolicNames = []string{
- "", "", "", "", "", "", "", "KW_VERTEX", "KW_EDGE", "KW_PATH", "KW_NUMERIC",
- "STRING", "BOOL", "NULL", "NUMBER", "FLOAT_EXPR", "NUMERIC", "WS",
-}
-
-var ruleNames = []string{
- "ageout", "vertex", "edge", "path", "value", "properties", "pair", "arr",
-}
-
-type AgeParser struct {
- *antlr.BaseParser
-}
-
-// NewAgeParser produces a new parser instance for the optional input antlr.TokenStream.
-//
-// The *AgeParser instance produced may be reused by calling the SetInputStream method.
-// The initial parser configuration is expensive to construct, and the object is not thread-safe;
-// however, if used within a Golang sync.Pool, the construction cost amortizes well and the
-// objects can be used in a thread-safe manner.
-func NewAgeParser(input antlr.TokenStream) *AgeParser {
- this := new(AgeParser)
- deserializer := antlr.NewATNDeserializer(nil)
- deserializedATN := deserializer.DeserializeFromUInt16(parserATN)
- decisionToDFA := make([]*antlr.DFA, len(deserializedATN.DecisionToState))
- for index, ds := range deserializedATN.DecisionToState {
- decisionToDFA[index] = antlr.NewDFA(ds, index)
- }
- this.BaseParser = antlr.NewBaseParser(input)
-
- this.Interpreter = antlr.NewParserATNSimulator(this, deserializedATN, decisionToDFA, antlr.NewPredictionContextCache())
- this.RuleNames = ruleNames
- this.LiteralNames = literalNames
- this.SymbolicNames = symbolicNames
- this.GrammarFileName = "Age.g4"
-
- return this
-}
-
-// AgeParser tokens.
-const (
- AgeParserEOF = antlr.TokenEOF
- AgeParserT__0 = 1
- AgeParserT__1 = 2
- AgeParserT__2 = 3
- AgeParserT__3 = 4
- AgeParserT__4 = 5
- AgeParserT__5 = 6
- AgeParserKW_VERTEX = 7
- AgeParserKW_EDGE = 8
- AgeParserKW_PATH = 9
- AgeParserKW_NUMERIC = 10
- AgeParserSTRING = 11
- AgeParserBOOL = 12
- AgeParserNULL = 13
- AgeParserNUMBER = 14
- AgeParserFLOAT_EXPR = 15
- AgeParserNUMERIC = 16
- AgeParserWS = 17
-)
-
-// AgeParser rules.
-const (
- AgeParserRULE_ageout = 0
- AgeParserRULE_vertex = 1
- AgeParserRULE_edge = 2
- AgeParserRULE_path = 3
- AgeParserRULE_value = 4
- AgeParserRULE_properties = 5
- AgeParserRULE_pair = 6
- AgeParserRULE_arr = 7
-)
-
-// IAgeoutContext is an interface to support dynamic dispatch.
-type IAgeoutContext interface {
- antlr.ParserRuleContext
-
- // GetParser returns the parser.
- GetParser() antlr.Parser
-
- // IsAgeoutContext differentiates from other interfaces.
- IsAgeoutContext()
-}
-
-type AgeoutContext struct {
- *antlr.BaseParserRuleContext
- parser antlr.Parser
-}
-
-func NewEmptyAgeoutContext() *AgeoutContext {
- var p = new(AgeoutContext)
- p.BaseParserRuleContext = antlr.NewBaseParserRuleContext(nil, -1)
- p.RuleIndex = AgeParserRULE_ageout
- return p
-}
-
-func (*AgeoutContext) IsAgeoutContext() {}
-
-func NewAgeoutContext(parser antlr.Parser, parent antlr.ParserRuleContext, invokingState int) *AgeoutContext {
- var p = new(AgeoutContext)
-
- p.BaseParserRuleContext = antlr.NewBaseParserRuleContext(parent, invokingState)
-
- p.parser = parser
- p.RuleIndex = AgeParserRULE_ageout
-
- return p
-}
-
-func (s *AgeoutContext) GetParser() antlr.Parser { return s.parser }
-
-func (s *AgeoutContext) Value() IValueContext {
- var t = s.GetTypedRuleContext(reflect.TypeOf((*IValueContext)(nil)).Elem(), 0)
-
- if t == nil {
- return nil
- }
-
- return t.(IValueContext)
-}
-
-func (s *AgeoutContext) Vertex() IVertexContext {
- var t = s.GetTypedRuleContext(reflect.TypeOf((*IVertexContext)(nil)).Elem(), 0)
-
- if t == nil {
- return nil
- }
-
- return t.(IVertexContext)
-}
-
-func (s *AgeoutContext) Edge() IEdgeContext {
- var t = s.GetTypedRuleContext(reflect.TypeOf((*IEdgeContext)(nil)).Elem(), 0)
-
- if t == nil {
- return nil
- }
-
- return t.(IEdgeContext)
-}
-
-func (s *AgeoutContext) Path() IPathContext {
- var t = s.GetTypedRuleContext(reflect.TypeOf((*IPathContext)(nil)).Elem(), 0)
-
- if t == nil {
- return nil
- }
-
- return t.(IPathContext)
-}
-
-func (s *AgeoutContext) GetRuleContext() antlr.RuleContext {
- return s
-}
-
-func (s *AgeoutContext) ToStringTree(ruleNames []string, recog antlr.Recognizer) string {
- return antlr.TreesStringTree(s, ruleNames, recog)
-}
-
-func (s *AgeoutContext) EnterRule(listener antlr.ParseTreeListener) {
- if listenerT, ok := listener.(AgeListener); ok {
- listenerT.EnterAgeout(s)
- }
-}
-
-func (s *AgeoutContext) ExitRule(listener antlr.ParseTreeListener) {
- if listenerT, ok := listener.(AgeListener); ok {
- listenerT.ExitAgeout(s)
- }
-}
-
-func (s *AgeoutContext) Accept(visitor antlr.ParseTreeVisitor) interface{} {
- switch t := visitor.(type) {
- case AgeVisitor:
- return t.VisitAgeout(s)
-
- default:
- return t.VisitChildren(s)
- }
-}
-
-func (p *AgeParser) Ageout() (localctx IAgeoutContext) {
- localctx = NewAgeoutContext(p, p.GetParserRuleContext(), p.GetState())
- p.EnterRule(localctx, 0, AgeParserRULE_ageout)
-
- defer func() {
- p.ExitRule()
- }()
-
- defer func() {
- if err := recover(); err != nil {
- if v, ok := err.(antlr.RecognitionException); ok {
- localctx.SetException(v)
- p.GetErrorHandler().ReportError(p, v)
- p.GetErrorHandler().Recover(p, v)
- } else {
- panic(err)
- }
- }
- }()
-
- p.SetState(20)
- p.GetErrorHandler().Sync(p)
- switch p.GetInterpreter().AdaptivePredict(p.GetTokenStream(), 0, p.GetParserRuleContext()) {
- case 1:
- p.EnterOuterAlt(localctx, 1)
- {
- p.SetState(16)
- p.Value()
- }
-
- case 2:
- p.EnterOuterAlt(localctx, 2)
- {
- p.SetState(17)
- p.Vertex()
- }
-
- case 3:
- p.EnterOuterAlt(localctx, 3)
- {
- p.SetState(18)
- p.Edge()
- }
-
- case 4:
- p.EnterOuterAlt(localctx, 4)
- {
- p.SetState(19)
- p.Path()
- }
-
- }
-
- return localctx
-}
-
-// IVertexContext is an interface to support dynamic dispatch.
-type IVertexContext interface {
- antlr.ParserRuleContext
-
- // GetParser returns the parser.
- GetParser() antlr.Parser
-
- // IsVertexContext differentiates from other interfaces.
- IsVertexContext()
-}
-
-type VertexContext struct {
- *antlr.BaseParserRuleContext
- parser antlr.Parser
-}
-
-func NewEmptyVertexContext() *VertexContext {
- var p = new(VertexContext)
- p.BaseParserRuleContext = antlr.NewBaseParserRuleContext(nil, -1)
- p.RuleIndex = AgeParserRULE_vertex
- return p
-}
-
-func (*VertexContext) IsVertexContext() {}
-
-func NewVertexContext(parser antlr.Parser, parent antlr.ParserRuleContext, invokingState int) *VertexContext {
- var p = new(VertexContext)
-
- p.BaseParserRuleContext = antlr.NewBaseParserRuleContext(parent, invokingState)
-
- p.parser = parser
- p.RuleIndex = AgeParserRULE_vertex
-
- return p
-}
-
-func (s *VertexContext) GetParser() antlr.Parser { return s.parser }
-
-func (s *VertexContext) Properties() IPropertiesContext {
- var t = s.GetTypedRuleContext(reflect.TypeOf((*IPropertiesContext)(nil)).Elem(), 0)
-
- if t == nil {
- return nil
- }
-
- return t.(IPropertiesContext)
-}
-
-func (s *VertexContext) KW_VERTEX() antlr.TerminalNode {
- return s.GetToken(AgeParserKW_VERTEX, 0)
-}
-
-func (s *VertexContext) GetRuleContext() antlr.RuleContext {
- return s
-}
-
-func (s *VertexContext) ToStringTree(ruleNames []string, recog antlr.Recognizer) string {
- return antlr.TreesStringTree(s, ruleNames, recog)
-}
-
-func (s *VertexContext) EnterRule(listener antlr.ParseTreeListener) {
- if listenerT, ok := listener.(AgeListener); ok {
- listenerT.EnterVertex(s)
- }
-}
-
-func (s *VertexContext) ExitRule(listener antlr.ParseTreeListener) {
- if listenerT, ok := listener.(AgeListener); ok {
- listenerT.ExitVertex(s)
- }
-}
-
-func (s *VertexContext) Accept(visitor antlr.ParseTreeVisitor) interface{} {
- switch t := visitor.(type) {
- case AgeVisitor:
- return t.VisitVertex(s)
-
- default:
- return t.VisitChildren(s)
- }
-}
-
-func (p *AgeParser) Vertex() (localctx IVertexContext) {
- localctx = NewVertexContext(p, p.GetParserRuleContext(), p.GetState())
- p.EnterRule(localctx, 2, AgeParserRULE_vertex)
-
- defer func() {
- p.ExitRule()
- }()
-
- defer func() {
- if err := recover(); err != nil {
- if v, ok := err.(antlr.RecognitionException); ok {
- localctx.SetException(v)
- p.GetErrorHandler().ReportError(p, v)
- p.GetErrorHandler().Recover(p, v)
- } else {
- panic(err)
- }
- }
- }()
-
- p.EnterOuterAlt(localctx, 1)
- {
- p.SetState(22)
- p.Properties()
- }
- {
- p.SetState(23)
- p.Match(AgeParserKW_VERTEX)
- }
-
- return localctx
-}
-
-// IEdgeContext is an interface to support dynamic dispatch.
-type IEdgeContext interface {
- antlr.ParserRuleContext
-
- // GetParser returns the parser.
- GetParser() antlr.Parser
-
- // IsEdgeContext differentiates from other interfaces.
- IsEdgeContext()
-}
-
-type EdgeContext struct {
- *antlr.BaseParserRuleContext
- parser antlr.Parser
-}
-
-func NewEmptyEdgeContext() *EdgeContext {
- var p = new(EdgeContext)
- p.BaseParserRuleContext = antlr.NewBaseParserRuleContext(nil, -1)
- p.RuleIndex = AgeParserRULE_edge
- return p
-}
-
-func (*EdgeContext) IsEdgeContext() {}
-
-func NewEdgeContext(parser antlr.Parser, parent antlr.ParserRuleContext, invokingState int) *EdgeContext {
- var p = new(EdgeContext)
-
- p.BaseParserRuleContext = antlr.NewBaseParserRuleContext(parent, invokingState)
-
- p.parser = parser
- p.RuleIndex = AgeParserRULE_edge
-
- return p
-}
-
-func (s *EdgeContext) GetParser() antlr.Parser { return s.parser }
-
-func (s *EdgeContext) Properties() IPropertiesContext {
- var t = s.GetTypedRuleContext(reflect.TypeOf((*IPropertiesContext)(nil)).Elem(), 0)
-
- if t == nil {
- return nil
- }
-
- return t.(IPropertiesContext)
-}
-
-func (s *EdgeContext) KW_EDGE() antlr.TerminalNode {
- return s.GetToken(AgeParserKW_EDGE, 0)
-}
-
-func (s *EdgeContext) GetRuleContext() antlr.RuleContext {
- return s
-}
-
-func (s *EdgeContext) ToStringTree(ruleNames []string, recog antlr.Recognizer) string {
- return antlr.TreesStringTree(s, ruleNames, recog)
-}
-
-func (s *EdgeContext) EnterRule(listener antlr.ParseTreeListener) {
- if listenerT, ok := listener.(AgeListener); ok {
- listenerT.EnterEdge(s)
- }
-}
-
-func (s *EdgeContext) ExitRule(listener antlr.ParseTreeListener) {
- if listenerT, ok := listener.(AgeListener); ok {
- listenerT.ExitEdge(s)
- }
-}
-
-func (s *EdgeContext) Accept(visitor antlr.ParseTreeVisitor) interface{} {
- switch t := visitor.(type) {
- case AgeVisitor:
- return t.VisitEdge(s)
-
- default:
- return t.VisitChildren(s)
- }
-}
-
-func (p *AgeParser) Edge() (localctx IEdgeContext) {
- localctx = NewEdgeContext(p, p.GetParserRuleContext(), p.GetState())
- p.EnterRule(localctx, 4, AgeParserRULE_edge)
-
- defer func() {
- p.ExitRule()
- }()
-
- defer func() {
- if err := recover(); err != nil {
- if v, ok := err.(antlr.RecognitionException); ok {
- localctx.SetException(v)
- p.GetErrorHandler().ReportError(p, v)
- p.GetErrorHandler().Recover(p, v)
- } else {
- panic(err)
- }
- }
- }()
-
- p.EnterOuterAlt(localctx, 1)
- {
- p.SetState(25)
- p.Properties()
- }
- {
- p.SetState(26)
- p.Match(AgeParserKW_EDGE)
- }
-
- return localctx
-}
-
-// IPathContext is an interface to support dynamic dispatch.
-type IPathContext interface {
- antlr.ParserRuleContext
-
- // GetParser returns the parser.
- GetParser() antlr.Parser
-
- // IsPathContext differentiates from other interfaces.
- IsPathContext()
-}
-
-type PathContext struct {
- *antlr.BaseParserRuleContext
- parser antlr.Parser
-}
-
-func NewEmptyPathContext() *PathContext {
- var p = new(PathContext)
- p.BaseParserRuleContext = antlr.NewBaseParserRuleContext(nil, -1)
- p.RuleIndex = AgeParserRULE_path
- return p
-}
-
-func (*PathContext) IsPathContext() {}
-
-func NewPathContext(parser antlr.Parser, parent antlr.ParserRuleContext, invokingState int) *PathContext {
- var p = new(PathContext)
-
- p.BaseParserRuleContext = antlr.NewBaseParserRuleContext(parent, invokingState)
-
- p.parser = parser
- p.RuleIndex = AgeParserRULE_path
-
- return p
-}
-
-func (s *PathContext) GetParser() antlr.Parser { return s.parser }
-
-func (s *PathContext) AllVertex() []IVertexContext {
- var ts = s.GetTypedRuleContexts(reflect.TypeOf((*IVertexContext)(nil)).Elem())
- var tst = make([]IVertexContext, len(ts))
-
- for i, t := range ts {
- if t != nil {
- tst[i] = t.(IVertexContext)
- }
- }
-
- return tst
-}
-
-func (s *PathContext) Vertex(i int) IVertexContext {
- var t = s.GetTypedRuleContext(reflect.TypeOf((*IVertexContext)(nil)).Elem(), i)
-
- if t == nil {
- return nil
- }
-
- return t.(IVertexContext)
-}
-
-func (s *PathContext) KW_PATH() antlr.TerminalNode {
- return s.GetToken(AgeParserKW_PATH, 0)
-}
-
-func (s *PathContext) AllEdge() []IEdgeContext {
- var ts = s.GetTypedRuleContexts(reflect.TypeOf((*IEdgeContext)(nil)).Elem())
- var tst = make([]IEdgeContext, len(ts))
-
- for i, t := range ts {
- if t != nil {
- tst[i] = t.(IEdgeContext)
- }
- }
-
- return tst
-}
-
-func (s *PathContext) Edge(i int) IEdgeContext {
- var t = s.GetTypedRuleContext(reflect.TypeOf((*IEdgeContext)(nil)).Elem(), i)
-
- if t == nil {
- return nil
- }
-
- return t.(IEdgeContext)
-}
-
-func (s *PathContext) GetRuleContext() antlr.RuleContext {
- return s
-}
-
-func (s *PathContext) ToStringTree(ruleNames []string, recog antlr.Recognizer) string {
- return antlr.TreesStringTree(s, ruleNames, recog)
-}
-
-func (s *PathContext) EnterRule(listener antlr.ParseTreeListener) {
- if listenerT, ok := listener.(AgeListener); ok {
- listenerT.EnterPath(s)
- }
-}
-
-func (s *PathContext) ExitRule(listener antlr.ParseTreeListener) {
- if listenerT, ok := listener.(AgeListener); ok {
- listenerT.ExitPath(s)
- }
-}
-
-func (s *PathContext) Accept(visitor antlr.ParseTreeVisitor) interface{} {
- switch t := visitor.(type) {
- case AgeVisitor:
- return t.VisitPath(s)
-
- default:
- return t.VisitChildren(s)
- }
-}
-
-func (p *AgeParser) Path() (localctx IPathContext) {
- localctx = NewPathContext(p, p.GetParserRuleContext(), p.GetState())
- p.EnterRule(localctx, 6, AgeParserRULE_path)
- var _la int
-
- defer func() {
- p.ExitRule()
- }()
-
- defer func() {
- if err := recover(); err != nil {
- if v, ok := err.(antlr.RecognitionException); ok {
- localctx.SetException(v)
- p.GetErrorHandler().ReportError(p, v)
- p.GetErrorHandler().Recover(p, v)
- } else {
- panic(err)
- }
- }
- }()
-
- p.EnterOuterAlt(localctx, 1)
- {
- p.SetState(28)
- p.Match(AgeParserT__0)
- }
- {
- p.SetState(29)
- p.Vertex()
- }
- p.SetState(37)
- p.GetErrorHandler().Sync(p)
- _la = p.GetTokenStream().LA(1)
-
- for _la == AgeParserT__1 {
- {
- p.SetState(30)
- p.Match(AgeParserT__1)
- }
- {
- p.SetState(31)
- p.Edge()
- }
- {
- p.SetState(32)
- p.Match(AgeParserT__1)
- }
- {
- p.SetState(33)
- p.Vertex()
- }
-
- p.SetState(39)
- p.GetErrorHandler().Sync(p)
- _la = p.GetTokenStream().LA(1)
- }
- {
- p.SetState(40)
- p.Match(AgeParserT__2)
- }
- {
- p.SetState(41)
- p.Match(AgeParserKW_PATH)
- }
-
- return localctx
-}
-
-// IValueContext is an interface to support dynamic dispatch.
-type IValueContext interface {
- antlr.ParserRuleContext
-
- // GetParser returns the parser.
- GetParser() antlr.Parser
-
- // IsValueContext differentiates from other interfaces.
- IsValueContext()
-}
-
-type ValueContext struct {
- *antlr.BaseParserRuleContext
- parser antlr.Parser
-}
-
-func NewEmptyValueContext() *ValueContext {
- var p = new(ValueContext)
- p.BaseParserRuleContext = antlr.NewBaseParserRuleContext(nil, -1)
- p.RuleIndex = AgeParserRULE_value
- return p
-}
-
-func (*ValueContext) IsValueContext() {}
-
-func NewValueContext(parser antlr.Parser, parent antlr.ParserRuleContext, invokingState int) *ValueContext {
- var p = new(ValueContext)
-
- p.BaseParserRuleContext = antlr.NewBaseParserRuleContext(parent, invokingState)
-
- p.parser = parser
- p.RuleIndex = AgeParserRULE_value
-
- return p
-}
-
-func (s *ValueContext) GetParser() antlr.Parser { return s.parser }
-
-func (s *ValueContext) STRING() antlr.TerminalNode {
- return s.GetToken(AgeParserSTRING, 0)
-}
-
-func (s *ValueContext) NUMBER() antlr.TerminalNode {
- return s.GetToken(AgeParserNUMBER, 0)
-}
-
-func (s *ValueContext) NUMERIC() antlr.TerminalNode {
- return s.GetToken(AgeParserNUMERIC, 0)
-}
-
-func (s *ValueContext) FLOAT_EXPR() antlr.TerminalNode {
- return s.GetToken(AgeParserFLOAT_EXPR, 0)
-}
-
-func (s *ValueContext) BOOL() antlr.TerminalNode {
- return s.GetToken(AgeParserBOOL, 0)
-}
-
-func (s *ValueContext) NULL() antlr.TerminalNode {
- return s.GetToken(AgeParserNULL, 0)
-}
-
-func (s *ValueContext) Properties() IPropertiesContext {
- var t = s.GetTypedRuleContext(reflect.TypeOf((*IPropertiesContext)(nil)).Elem(), 0)
-
- if t == nil {
- return nil
- }
-
- return t.(IPropertiesContext)
-}
-
-func (s *ValueContext) Arr() IArrContext {
- var t = s.GetTypedRuleContext(reflect.TypeOf((*IArrContext)(nil)).Elem(), 0)
-
- if t == nil {
- return nil
- }
-
- return t.(IArrContext)
-}
-
-func (s *ValueContext) GetRuleContext() antlr.RuleContext {
- return s
-}
-
-func (s *ValueContext) ToStringTree(ruleNames []string, recog antlr.Recognizer) string {
- return antlr.TreesStringTree(s, ruleNames, recog)
-}
-
-func (s *ValueContext) EnterRule(listener antlr.ParseTreeListener) {
- if listenerT, ok := listener.(AgeListener); ok {
- listenerT.EnterValue(s)
- }
-}
-
-func (s *ValueContext) ExitRule(listener antlr.ParseTreeListener) {
- if listenerT, ok := listener.(AgeListener); ok {
- listenerT.ExitValue(s)
- }
-}
-
-func (s *ValueContext) Accept(visitor antlr.ParseTreeVisitor) interface{} {
- switch t := visitor.(type) {
- case AgeVisitor:
- return t.VisitValue(s)
-
- default:
- return t.VisitChildren(s)
- }
-}
-
-func (p *AgeParser) Value() (localctx IValueContext) {
- localctx = NewValueContext(p, p.GetParserRuleContext(), p.GetState())
- p.EnterRule(localctx, 8, AgeParserRULE_value)
-
- defer func() {
- p.ExitRule()
- }()
-
- defer func() {
- if err := recover(); err != nil {
- if v, ok := err.(antlr.RecognitionException); ok {
- localctx.SetException(v)
- p.GetErrorHandler().ReportError(p, v)
- p.GetErrorHandler().Recover(p, v)
- } else {
- panic(err)
- }
- }
- }()
-
- p.SetState(51)
- p.GetErrorHandler().Sync(p)
-
- switch p.GetTokenStream().LA(1) {
- case AgeParserSTRING:
- p.EnterOuterAlt(localctx, 1)
- {
- p.SetState(43)
- p.Match(AgeParserSTRING)
- }
-
- case AgeParserNUMBER:
- p.EnterOuterAlt(localctx, 2)
- {
- p.SetState(44)
- p.Match(AgeParserNUMBER)
- }
-
- case AgeParserNUMERIC:
- p.EnterOuterAlt(localctx, 3)
- {
- p.SetState(45)
- p.Match(AgeParserNUMERIC)
- }
-
- case AgeParserFLOAT_EXPR:
- p.EnterOuterAlt(localctx, 4)
- {
- p.SetState(46)
- p.Match(AgeParserFLOAT_EXPR)
- }
-
- case AgeParserBOOL:
- p.EnterOuterAlt(localctx, 5)
- {
- p.SetState(47)
- p.Match(AgeParserBOOL)
- }
-
- case AgeParserNULL:
- p.EnterOuterAlt(localctx, 6)
- {
- p.SetState(48)
- p.Match(AgeParserNULL)
- }
-
- case AgeParserT__3:
- p.EnterOuterAlt(localctx, 7)
- {
- p.SetState(49)
- p.Properties()
- }
-
- case AgeParserT__0:
- p.EnterOuterAlt(localctx, 8)
- {
- p.SetState(50)
- p.Arr()
- }
-
- default:
- panic(antlr.NewNoViableAltException(p, nil, nil, nil, nil, nil))
- }
-
- return localctx
-}
-
-// IPropertiesContext is an interface to support dynamic dispatch.
-type IPropertiesContext interface {
- antlr.ParserRuleContext
-
- // GetParser returns the parser.
- GetParser() antlr.Parser
-
- // IsPropertiesContext differentiates from other interfaces.
- IsPropertiesContext()
-}
-
-type PropertiesContext struct {
- *antlr.BaseParserRuleContext
- parser antlr.Parser
-}
-
-func NewEmptyPropertiesContext() *PropertiesContext {
- var p = new(PropertiesContext)
- p.BaseParserRuleContext = antlr.NewBaseParserRuleContext(nil, -1)
- p.RuleIndex = AgeParserRULE_properties
- return p
-}
-
-func (*PropertiesContext) IsPropertiesContext() {}
-
-func NewPropertiesContext(parser antlr.Parser, parent antlr.ParserRuleContext, invokingState int) *PropertiesContext {
- var p = new(PropertiesContext)
-
- p.BaseParserRuleContext = antlr.NewBaseParserRuleContext(parent, invokingState)
-
- p.parser = parser
- p.RuleIndex = AgeParserRULE_properties
-
- return p
-}
-
-func (s *PropertiesContext) GetParser() antlr.Parser { return s.parser }
-
-func (s *PropertiesContext) AllPair() []IPairContext {
- var ts = s.GetTypedRuleContexts(reflect.TypeOf((*IPairContext)(nil)).Elem())
- var tst = make([]IPairContext, len(ts))
-
- for i, t := range ts {
- if t != nil {
- tst[i] = t.(IPairContext)
- }
- }
-
- return tst
-}
-
-func (s *PropertiesContext) Pair(i int) IPairContext {
- var t = s.GetTypedRuleContext(reflect.TypeOf((*IPairContext)(nil)).Elem(), i)
-
- if t == nil {
- return nil
- }
-
- return t.(IPairContext)
-}
-
-func (s *PropertiesContext) GetRuleContext() antlr.RuleContext {
- return s
-}
-
-func (s *PropertiesContext) ToStringTree(ruleNames []string, recog antlr.Recognizer) string {
- return antlr.TreesStringTree(s, ruleNames, recog)
-}
-
-func (s *PropertiesContext) EnterRule(listener antlr.ParseTreeListener) {
- if listenerT, ok := listener.(AgeListener); ok {
- listenerT.EnterProperties(s)
- }
-}
-
-func (s *PropertiesContext) ExitRule(listener antlr.ParseTreeListener) {
- if listenerT, ok := listener.(AgeListener); ok {
- listenerT.ExitProperties(s)
- }
-}
-
-func (s *PropertiesContext) Accept(visitor antlr.ParseTreeVisitor) interface{} {
- switch t := visitor.(type) {
- case AgeVisitor:
- return t.VisitProperties(s)
-
- default:
- return t.VisitChildren(s)
- }
-}
-
-func (p *AgeParser) Properties() (localctx IPropertiesContext) {
- localctx = NewPropertiesContext(p, p.GetParserRuleContext(), p.GetState())
- p.EnterRule(localctx, 10, AgeParserRULE_properties)
- var _la int
-
- defer func() {
- p.ExitRule()
- }()
-
- defer func() {
- if err := recover(); err != nil {
- if v, ok := err.(antlr.RecognitionException); ok {
- localctx.SetException(v)
- p.GetErrorHandler().ReportError(p, v)
- p.GetErrorHandler().Recover(p, v)
- } else {
- panic(err)
- }
- }
- }()
-
- p.SetState(66)
- p.GetErrorHandler().Sync(p)
- switch p.GetInterpreter().AdaptivePredict(p.GetTokenStream(), 4, p.GetParserRuleContext()) {
- case 1:
- p.EnterOuterAlt(localctx, 1)
- {
- p.SetState(53)
- p.Match(AgeParserT__3)
- }
- {
- p.SetState(54)
- p.Pair()
- }
- p.SetState(59)
- p.GetErrorHandler().Sync(p)
- _la = p.GetTokenStream().LA(1)
-
- for _la == AgeParserT__1 {
- {
- p.SetState(55)
- p.Match(AgeParserT__1)
- }
- {
- p.SetState(56)
- p.Pair()
- }
-
- p.SetState(61)
- p.GetErrorHandler().Sync(p)
- _la = p.GetTokenStream().LA(1)
- }
- {
- p.SetState(62)
- p.Match(AgeParserT__4)
- }
-
- case 2:
- p.EnterOuterAlt(localctx, 2)
- {
- p.SetState(64)
- p.Match(AgeParserT__3)
- }
- {
- p.SetState(65)
- p.Match(AgeParserT__4)
- }
-
- }
-
- return localctx
-}
-
-// IPairContext is an interface to support dynamic dispatch.
-type IPairContext interface {
- antlr.ParserRuleContext
-
- // GetParser returns the parser.
- GetParser() antlr.Parser
-
- // IsPairContext differentiates from other interfaces.
- IsPairContext()
-}
-
-type PairContext struct {
- *antlr.BaseParserRuleContext
- parser antlr.Parser
-}
-
-func NewEmptyPairContext() *PairContext {
- var p = new(PairContext)
- p.BaseParserRuleContext = antlr.NewBaseParserRuleContext(nil, -1)
- p.RuleIndex = AgeParserRULE_pair
- return p
-}
-
-func (*PairContext) IsPairContext() {}
-
-func NewPairContext(parser antlr.Parser, parent antlr.ParserRuleContext, invokingState int) *PairContext {
- var p = new(PairContext)
-
- p.BaseParserRuleContext = antlr.NewBaseParserRuleContext(parent, invokingState)
-
- p.parser = parser
- p.RuleIndex = AgeParserRULE_pair
-
- return p
-}
-
-func (s *PairContext) GetParser() antlr.Parser { return s.parser }
-
-func (s *PairContext) STRING() antlr.TerminalNode {
- return s.GetToken(AgeParserSTRING, 0)
-}
-
-func (s *PairContext) Value() IValueContext {
- var t = s.GetTypedRuleContext(reflect.TypeOf((*IValueContext)(nil)).Elem(), 0)
-
- if t == nil {
- return nil
- }
-
- return t.(IValueContext)
-}
-
-func (s *PairContext) GetRuleContext() antlr.RuleContext {
- return s
-}
-
-func (s *PairContext) ToStringTree(ruleNames []string, recog antlr.Recognizer) string {
- return antlr.TreesStringTree(s, ruleNames, recog)
-}
-
-func (s *PairContext) EnterRule(listener antlr.ParseTreeListener) {
- if listenerT, ok := listener.(AgeListener); ok {
- listenerT.EnterPair(s)
- }
-}
-
-func (s *PairContext) ExitRule(listener antlr.ParseTreeListener) {
- if listenerT, ok := listener.(AgeListener); ok {
- listenerT.ExitPair(s)
- }
-}
-
-func (s *PairContext) Accept(visitor antlr.ParseTreeVisitor) interface{} {
- switch t := visitor.(type) {
- case AgeVisitor:
- return t.VisitPair(s)
-
- default:
- return t.VisitChildren(s)
- }
-}
-
-func (p *AgeParser) Pair() (localctx IPairContext) {
- localctx = NewPairContext(p, p.GetParserRuleContext(), p.GetState())
- p.EnterRule(localctx, 12, AgeParserRULE_pair)
-
- defer func() {
- p.ExitRule()
- }()
-
- defer func() {
- if err := recover(); err != nil {
- if v, ok := err.(antlr.RecognitionException); ok {
- localctx.SetException(v)
- p.GetErrorHandler().ReportError(p, v)
- p.GetErrorHandler().Recover(p, v)
- } else {
- panic(err)
- }
- }
- }()
-
- p.EnterOuterAlt(localctx, 1)
- {
- p.SetState(68)
- p.Match(AgeParserSTRING)
- }
- {
- p.SetState(69)
- p.Match(AgeParserT__5)
- }
- {
- p.SetState(70)
- p.Value()
- }
-
- return localctx
-}
-
-// IArrContext is an interface to support dynamic dispatch.
-type IArrContext interface {
- antlr.ParserRuleContext
-
- // GetParser returns the parser.
- GetParser() antlr.Parser
-
- // IsArrContext differentiates from other interfaces.
- IsArrContext()
-}
-
-type ArrContext struct {
- *antlr.BaseParserRuleContext
- parser antlr.Parser
-}
-
-func NewEmptyArrContext() *ArrContext {
- var p = new(ArrContext)
- p.BaseParserRuleContext = antlr.NewBaseParserRuleContext(nil, -1)
- p.RuleIndex = AgeParserRULE_arr
- return p
-}
-
-func (*ArrContext) IsArrContext() {}
-
-func NewArrContext(parser antlr.Parser, parent antlr.ParserRuleContext, invokingState int) *ArrContext {
- var p = new(ArrContext)
-
- p.BaseParserRuleContext = antlr.NewBaseParserRuleContext(parent, invokingState)
-
- p.parser = parser
- p.RuleIndex = AgeParserRULE_arr
-
- return p
-}
-
-func (s *ArrContext) GetParser() antlr.Parser { return s.parser }
-
-func (s *ArrContext) AllValue() []IValueContext {
- var ts = s.GetTypedRuleContexts(reflect.TypeOf((*IValueContext)(nil)).Elem())
- var tst = make([]IValueContext, len(ts))
-
- for i, t := range ts {
- if t != nil {
- tst[i] = t.(IValueContext)
- }
- }
-
- return tst
-}
-
-func (s *ArrContext) Value(i int) IValueContext {
- var t = s.GetTypedRuleContext(reflect.TypeOf((*IValueContext)(nil)).Elem(), i)
-
- if t == nil {
- return nil
- }
-
- return t.(IValueContext)
-}
-
-func (s *ArrContext) GetRuleContext() antlr.RuleContext {
- return s
-}
-
-func (s *ArrContext) ToStringTree(ruleNames []string, recog antlr.Recognizer) string {
- return antlr.TreesStringTree(s, ruleNames, recog)
-}
-
-func (s *ArrContext) EnterRule(listener antlr.ParseTreeListener) {
- if listenerT, ok := listener.(AgeListener); ok {
- listenerT.EnterArr(s)
- }
-}
-
-func (s *ArrContext) ExitRule(listener antlr.ParseTreeListener) {
- if listenerT, ok := listener.(AgeListener); ok {
- listenerT.ExitArr(s)
- }
-}
-
-func (s *ArrContext) Accept(visitor antlr.ParseTreeVisitor) interface{} {
- switch t := visitor.(type) {
- case AgeVisitor:
- return t.VisitArr(s)
-
- default:
- return t.VisitChildren(s)
- }
-}
-
-func (p *AgeParser) Arr() (localctx IArrContext) {
- localctx = NewArrContext(p, p.GetParserRuleContext(), p.GetState())
- p.EnterRule(localctx, 14, AgeParserRULE_arr)
- var _la int
-
- defer func() {
- p.ExitRule()
- }()
-
- defer func() {
- if err := recover(); err != nil {
- if v, ok := err.(antlr.RecognitionException); ok {
- localctx.SetException(v)
- p.GetErrorHandler().ReportError(p, v)
- p.GetErrorHandler().Recover(p, v)
- } else {
- panic(err)
- }
- }
- }()
-
- p.SetState(85)
- p.GetErrorHandler().Sync(p)
- switch p.GetInterpreter().AdaptivePredict(p.GetTokenStream(), 6, p.GetParserRuleContext()) {
- case 1:
- p.EnterOuterAlt(localctx, 1)
- {
- p.SetState(72)
- p.Match(AgeParserT__0)
- }
- {
- p.SetState(73)
- p.Value()
- }
- p.SetState(78)
- p.GetErrorHandler().Sync(p)
- _la = p.GetTokenStream().LA(1)
-
- for _la == AgeParserT__1 {
- {
- p.SetState(74)
- p.Match(AgeParserT__1)
- }
- {
- p.SetState(75)
- p.Value()
- }
-
- p.SetState(80)
- p.GetErrorHandler().Sync(p)
- _la = p.GetTokenStream().LA(1)
- }
- {
- p.SetState(81)
- p.Match(AgeParserT__2)
- }
-
- case 2:
- p.EnterOuterAlt(localctx, 2)
- {
- p.SetState(83)
- p.Match(AgeParserT__0)
- }
- {
- p.SetState(84)
- p.Match(AgeParserT__2)
- }
-
- }
-
- return localctx
-}
diff --git a/drivers/golang/parser/age_visitor.go b/drivers/golang/parser/age_visitor.go
deleted file mode 100644
index 06bdc25ac..000000000
--- a/drivers/golang/parser/age_visitor.go
+++ /dev/null
@@ -1,52 +0,0 @@
-/*
- * 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.
- */
-// Code generated from Age.g4 by ANTLR 4.9.2. DO NOT EDIT.
-
-package parser // Age
-
-import "github.com/antlr/antlr4/runtime/Go/antlr"
-
-// A complete Visitor for a parse tree produced by AgeParser.
-type AgeVisitor interface {
- antlr.ParseTreeVisitor
-
- // Visit a parse tree produced by AgeParser#ageout.
- VisitAgeout(ctx *AgeoutContext) interface{}
-
- // Visit a parse tree produced by AgeParser#vertex.
- VisitVertex(ctx *VertexContext) interface{}
-
- // Visit a parse tree produced by AgeParser#edge.
- VisitEdge(ctx *EdgeContext) interface{}
-
- // Visit a parse tree produced by AgeParser#path.
- VisitPath(ctx *PathContext) interface{}
-
- // Visit a parse tree produced by AgeParser#value.
- VisitValue(ctx *ValueContext) interface{}
-
- // Visit a parse tree produced by AgeParser#properties.
- VisitProperties(ctx *PropertiesContext) interface{}
-
- // Visit a parse tree produced by AgeParser#pair.
- VisitPair(ctx *PairContext) interface{}
-
- // Visit a parse tree produced by AgeParser#arr.
- VisitArr(ctx *ArrContext) interface{}
-}
diff --git a/drivers/golang/parser/generate.go b/drivers/golang/parser/generate.go
new file mode 100644
index 000000000..9da7fbc6a
--- /dev/null
+++ b/drivers/golang/parser/generate.go
@@ -0,0 +1,3 @@
+package parser
+
+//go:generate ./generate.sh
diff --git a/drivers/golang/parser/generate.sh b/drivers/golang/parser/generate.sh
new file mode 100755
index 000000000..e45501b4e
--- /dev/null
+++ b/drivers/golang/parser/generate.sh
@@ -0,0 +1,12 @@
+#!/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
+
diff --git a/drivers/jdbc/README.md b/drivers/jdbc/README.md
index d22373030..c4369de0e 100644
--- a/drivers/jdbc/README.md
+++ b/drivers/jdbc/README.md
@@ -94,5 +94,5 @@ 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)
+- 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)
diff --git a/drivers/jdbc/lib/build.gradle.kts b/drivers/jdbc/lib/build.gradle.kts
index f363cfe2c..2ba529ec1 100644
--- a/drivers/jdbc/lib/build.gradle.kts
+++ b/drivers/jdbc/lib/build.gradle.kts
@@ -30,18 +30,18 @@ repositories {
}
dependencies {
- implementation("org.postgresql:postgresql:42.2.20")
- api("org.apache.commons:commons-text:1.9")
- antlr("org.antlr:antlr4:4.9.2")
+ 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.7.1")
+ testImplementation("org.junit.jupiter:junit-jupiter-api:5.9.3")
testRuntimeOnly("org.junit.jupiter:junit-jupiter-engine")
- testImplementation("org.testcontainers:testcontainers:1.15.3")
- testImplementation("org.postgresql:postgresql:42.2.20")
+ testImplementation("org.testcontainers:testcontainers:1.18.0")
+ testImplementation("org.postgresql:postgresql:42.6.0")
- testImplementation("org.slf4j:slf4j-api:1.7.5")
- testImplementation("org.slf4j:slf4j-simple:1.7.5")
+ testImplementation("org.slf4j:slf4j-api:2.0.7")
+ testImplementation("org.slf4j:slf4j-simple:2.0.7")
}
tasks.generateGrammarSource {
@@ -68,4 +68,4 @@ tasks.test {
showCauses = true
showStackTraces = true
}
-}
\ No newline at end of file
+}
diff --git a/drivers/jdbc/lib/src/test/java/org/apache/age/jdbc/AgtypeStatementTest.java b/drivers/jdbc/lib/src/test/java/org/apache/age/jdbc/AgtypeStatementTest.java
index e160ef394..97e424a70 100644
--- a/drivers/jdbc/lib/src/test/java/org/apache/age/jdbc/AgtypeStatementTest.java
+++ b/drivers/jdbc/lib/src/test/java/org/apache/age/jdbc/AgtypeStatementTest.java
@@ -29,11 +29,14 @@
import org.apache.age.jdbc.base.Agtype;
import org.apache.age.jdbc.base.AgtypeFactory;
import org.apache.age.jdbc.base.InvalidAgtypeException;
-import org.junit.jupiter.api.AfterEach;
-import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.AfterAll;
+import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.Test;
import org.postgresql.jdbc.PgConnection;
+import org.junit.jupiter.api.TestInstance;
+import org.junit.jupiter.api.TestInstance.Lifecycle;
+@TestInstance(Lifecycle.PER_CLASS)
/**
* Tests the different combinations that are possible when running Statements and Prepared
* Statements with the AgType and shows how the JDBC needs to be setup to convert values to the
@@ -43,12 +46,12 @@ class AgtypeStatementTest {
BaseDockerizedTest baseDockerizedTest = new BaseDockerizedTest();
- @BeforeEach
+ @BeforeAll
public void setup() throws Exception {
baseDockerizedTest.beforeAll();
}
- @AfterEach
+ @AfterAll
void tearDown() throws Exception {
baseDockerizedTest.afterAll();
}
diff --git a/drivers/jdbc/lib/src/test/java/org/apache/age/jdbc/BaseDockerizedTest.java b/drivers/jdbc/lib/src/test/java/org/apache/age/jdbc/BaseDockerizedTest.java
index 54f2b6491..b743258b0 100644
--- a/drivers/jdbc/lib/src/test/java/org/apache/age/jdbc/BaseDockerizedTest.java
+++ b/drivers/jdbc/lib/src/test/java/org/apache/age/jdbc/BaseDockerizedTest.java
@@ -50,6 +50,11 @@ public void afterAll() throws Exception {
@BeforeAll
public void beforeAll() throws Exception {
String CORRECT_DB_PASSWORDS = "postgres";
+ String imageTag = System.getenv("TAG");
+
+ if (imageTag == null) {
+ imageTag = "latest";
+ }
agensGraphContainer = new GenericContainer<>(DockerImageName
.parse("apache/age:PG12_latest"))
.withEnv("POSTGRES_PASSWORD", CORRECT_DB_PASSWORDS)
@@ -60,9 +65,13 @@ public void beforeAll() throws Exception {
String jdbcUrl = String
.format("jdbc:postgresql://%s:%d/%s", "localhost", mappedPort, "postgres");
- connection = DriverManager.getConnection(jdbcUrl, "postgres", CORRECT_DB_PASSWORDS)
- .unwrap(PgConnection.class);
- connection.addDataType("agtype", Agtype.class);
+ try {
+ this.connection = DriverManager.getConnection(jdbcUrl, "postgres", CORRECT_DB_PASSWORDS)
+ .unwrap(PgConnection.class);
+ this.connection.addDataType("agtype", Agtype.class);
+ } catch (Exception e) {
+ System.out.println(e);
+ }
try (Statement statement = connection.createStatement()) {
statement.execute("CREATE EXTENSION IF NOT EXISTS age;");
statement.execute("LOAD 'age'");
diff --git a/drivers/nodejs/README.md b/drivers/nodejs/README.md
index c15632200..283010cc6 100644
--- a/drivers/nodejs/README.md
+++ b/drivers/nodejs/README.md
@@ -48,5 +48,5 @@ const results: QueryResultRow = await client?.query(`
```
### For more information about [Apache AGE](https://age.apache.org/)
* Apache Age : https://age.apache.org/
-* Github : https://github.com/apache/age
+* GitHub : https://github.com/apache/age
* Document : https://age.apache.org/age-manual/master/index.html
diff --git a/drivers/python/README.md b/drivers/python/README.md
index f4eabfbcc..61430629d 100644
--- a/drivers/python/README.md
+++ b/drivers/python/README.md
@@ -1,9 +1,25 @@
+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.
+
+
# AGE AGType parser and driver support for Python
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 Psycopg2 PostreSQL driver (enables to use cypher queries directly)
+* Cypher query support for Psycopg2 PostgreSQL driver (enables to use cypher queries directly)
### Prerequisites
* over Python 3.9
@@ -11,18 +27,27 @@ AGType parser and driver support for [Apache AGE](https://age.apache.org/), grap
```
sudo apt-get update
sudo apt-get install python3-dev libpq-dev
-git clone https://github.com/apache/age.git
-cd age/drivers/python
-```
-
-### Install required packages
-```
-pip install -r requirements.txt
-```
-
+git clone https://github.com/apache/age.git
+cd age/drivers/python
+```
+
+### Install required packages
+```
+pip install -r requirements.txt
+```
+
### Test
```
-python -m unittest -v test_age_py.py
+python test_age_py.py \
+-host "127.0.0.1" \
+-db "postgres" \
+-u "postgres" \
+-pass "agens" \
+-port 5432 \
+-gn "test_graph"
+```
+
+```
python -m unittest -v test_agtypes.py
```
@@ -33,7 +58,7 @@ python setup.py install
### For more information about [Apache AGE](https://age.apache.org/)
* Apache Age : https://age.apache.org/
-* Github : https://github.com/apache/age
+* GitHub : https://github.com/apache/age
* Document : https://age.apache.org/age-manual/master/index.html
### Check AGE loaded on your PostgreSQL
diff --git a/drivers/python/age/age.py b/drivers/python/age/age.py
index e7cc88d42..40ad0c21e 100644
--- a/drivers/python/age/age.py
+++ b/drivers/python/age/age.py
@@ -113,7 +113,7 @@ def execCypher(conn:ext.connection, graphName:str, cypherStmt:str, cols:list=Non
raise _EXCEPTION_NoConnection
cursor = conn.cursor()
- #clean up the string for mogrificiation
+ #clean up the string for mogrification
cypherStmt = cypherStmt.replace("\n", "")
cypherStmt = cypherStmt.replace("\t", "")
cypher = str(cursor.mogrify(cypherStmt, params))
@@ -146,7 +146,7 @@ def execCypher(conn:ext.connection, graphName:str, cypherStmt:str, cols:list=Non
def cypher(cursor:ext.cursor, graphName:str, cypherStmt:str, cols:list=None, params:tuple=None) -> ext.cursor :
- #clean up the string for mogrificiation
+ #clean up the string for mogrification
cypherStmt = cypherStmt.replace("\n", "")
cypherStmt = cypherStmt.replace("\t", "")
cypher = str(cursor.mogrify(cypherStmt, params))
@@ -211,4 +211,4 @@ def cypher(self, cursor:ext.cursor, cypherStmt:str, cols:list=None, params:tuple
# return execCypherWithReturn(self.connection, self.graphName, cypherStmt, columns, params)
# def queryCypher(self, cypherStmt:str, columns:list=None , params:tuple=None) -> ext.cursor :
- # return queryCypher(self.connection, self.graphName, cypherStmt, columns, params)
\ No newline at end of file
+ # return queryCypher(self.connection, self.graphName, cypherStmt, columns, params)
diff --git a/drivers/python/age/exceptions.py b/drivers/python/age/exceptions.py
index d3ed67a34..8c46e8e99 100644
--- a/drivers/python/age/exceptions.py
+++ b/drivers/python/age/exceptions.py
@@ -65,4 +65,3 @@ def __init__(self, msg, cause):
self.msg = msg
self.cause = cause
super().__init__(msg, cause)
-
diff --git a/drivers/python/samples/apache-age-agtypes.ipynb b/drivers/python/samples/apache-age-agtypes.ipynb
index 232c4d7e4..141ff3f4b 100644
--- a/drivers/python/samples/apache-age-agtypes.ipynb
+++ b/drivers/python/samples/apache-age-agtypes.ipynb
@@ -62,7 +62,7 @@
"metadata": {},
"outputs": [],
"source": [
- " vertexExp = '''{\"id\": 2251799813685425, \"label\": \"Person\", \n",
+ "vertexExp = '''{\"id\": 2251799813685425, \"label\": \"Person\", \n",
" \"properties\": {\"name\": \"Smith\", \"numInt\":123, \"numFloat\": 384.23424, \n",
" \"bigInt\":123456789123456789123456789123456789123456789123456789123456789123456789123456789123456789123456789123456789::numeric, \n",
" \"bigFloat\":123456789123456789123456789123456789.12345::numeric, \n",
diff --git a/drivers/python/samples/apache-age-note.ipynb b/drivers/python/samples/apache-age-note.ipynb
index 44b15b43e..59c25bc5b 100644
--- a/drivers/python/samples/apache-age-note.ipynb
+++ b/drivers/python/samples/apache-age-note.ipynb
@@ -1,6 +1,7 @@
{
"cells": [
{
+ "attachments": {},
"cell_type": "markdown",
"id": "722ec93e-b87c-43d0-9c54-61b30933d892",
"metadata": {},
@@ -42,7 +43,7 @@
"outputs": [],
"source": [
"import age\n",
- "from age.gen.ageParser import *\n",
+ "from age.gen.AgtypeParser import *\n",
"\n",
"GRAPH_NAME = \"test_graph\"\n",
"DSN = \"host=172.17.0.2 port=5432 dbname=postgres user=postgres password=agens\"\n",
@@ -51,6 +52,7 @@
]
},
{
+ "attachments": {},
"cell_type": "markdown",
"id": "96bbaf49-e774-4939-8fe9-f179ac9addc9",
"metadata": {},
@@ -92,6 +94,7 @@
]
},
{
+ "attachments": {},
"cell_type": "markdown",
"id": "0f15bc37-4b19-4204-af93-757b07e7e9f9",
"metadata": {},
@@ -168,6 +171,7 @@
]
},
{
+ "attachments": {},
"cell_type": "markdown",
"id": "cf0f16c8-07d0-49b9-ba4f-3f9044cac9e7",
"metadata": {},
@@ -264,6 +268,7 @@
]
},
{
+ "attachments": {},
"cell_type": "markdown",
"id": "0a8606b5-8583-49f9-aa39-a1ac3e90d542",
"metadata": {},
@@ -347,6 +352,7 @@
]
},
{
+ "attachments": {},
"cell_type": "markdown",
"id": "2615465d-9cff-4e67-9935-21344df4574c",
"metadata": {},
@@ -571,6 +577,7 @@
]
},
{
+ "attachments": {},
"cell_type": "markdown",
"id": "22690e1d-f258-4f2f-87b2-4f5b0a7f4574",
"metadata": {},
@@ -624,6 +631,7 @@
]
},
{
+ "attachments": {},
"cell_type": "markdown",
"id": "ac4d3e0e-0101-40cd-bc9a-59386a1e4485",
"metadata": {},
@@ -695,6 +703,7 @@
]
},
{
+ "attachments": {},
"cell_type": "markdown",
"id": "f502e0db-a603-4eb9-90e8-dac2c9edd1d4",
"metadata": {},
@@ -758,6 +767,7 @@
]
},
{
+ "attachments": {},
"cell_type": "markdown",
"id": "8ce0a003-b038-4334-9de8-111569549040",
"metadata": {},
diff --git a/drivers/python/setup.py b/drivers/python/setup.py
index 4565dc5d5..0b48b8726 100644
--- a/drivers/python/setup.py
+++ b/drivers/python/setup.py
@@ -25,8 +25,8 @@
description = 'Python driver support for Apache AGE',
long_description=long_description,
long_description_content_type="text/markdown",
- author = 'rhizome',
- author_email = 'rhizome.ai@gmail.com',
+ author = 'Apache AGE',
+ author_email = 'dev-subscribe@age.apache.org',
url = 'https://github.com/apache/age',
license = 'Apache2.0',
install_requires = [ 'psycopg2', 'antlr4-python3-runtime==4.11.1'],
diff --git a/drivers/python/test_age_py.py b/drivers/python/test_age_py.py
index d319480c2..99c8d0857 100644
--- a/drivers/python/test_age_py.py
+++ b/drivers/python/test_age_py.py
@@ -17,19 +17,20 @@
import unittest
import decimal
import age
+import argparse
DSN = "host=127.0.0.1 port=5432 dbname=postgres user=postgres password=agens"
-TEST_HOST = "127.0.0.1"
-TEST_PORT = 5432
-TEST_DB = "postgres"
-TEST_USER = "postgres"
-TEST_PASSWORD = "agens"
-TEST_GRAPH_NAME = "test_graph"
class TestAgeBasic(unittest.TestCase):
ag = None
def setUp(self):
print("Connecting to Test Graph.....")
+ TEST_DB = self.args.database
+ TEST_USER = self.args.user
+ TEST_PASSWORD = self.args.password
+ TEST_PORT = self.args.port
+ TEST_HOST = self.args.host
+ TEST_GRAPH_NAME = self.args.graphName
self.ag = age.connect(graph=TEST_GRAPH_NAME, host=TEST_HOST, port=TEST_PORT, dbname=TEST_DB, user=TEST_USER, password=TEST_PASSWORD)
@@ -40,35 +41,47 @@ def tearDown(self):
self.ag.close()
def testExec(self):
+
+ print("\n---------------------------------------------------")
+ print("Test 1: Checking single and multi column Returns.....")
+ print("---------------------------------------------------\n")
+
ag = self.ag
# Create and Return single column
cursor = ag.execCypher("CREATE (n:Person {name: %s, title: 'Developer'}) RETURN n", params=('Andy',))
for row in cursor:
- print(Vertex, type(row[0]))
+ print("Vertex: %s , Type: %s " % (Vertex, type(row[0])))
# Create and Return multi columns
cursor = ag.execCypher("CREATE (n:Person {name: %s, title: %s}) RETURN id(n), n.name", cols=['id','name'], params=('Jack','Manager'))
row = cursor.fetchone()
- print(row[0], row[1])
+ print("Id: %s , Name: %s" % (row[0], row[1]))
self.assertEqual(int, type(row[0]))
ag.commit()
+ print("\nTest 1 Successful....")
+
def testQuery(self):
+
+ print("\n--------------------------------------------------")
+ print("Test 2: Testing CREATE and query relationships....." )
+ print("--------------------------------------------------\n")
+
ag = self.ag
ag.execCypher("CREATE (n:Person {name: %s}) ", params=('Jack',))
ag.execCypher("CREATE (n:Person {name: %s}) ", params=('Andy',))
ag.execCypher("CREATE (n:Person {name: %s}) ", params=('Smith',))
- ag.execCypher("MATCH (a:Person), (b:Person) WHERE a.name = 'Andy' AND b.name = 'Jack' CREATE (a)-[r:workWith {weight: 3}]->(b)")
+ ag.execCypher("MATCH (a:Person), (b:Person) WHERE a.name = 'Andy' AND b.name = 'Jack' CREATE (a)-[r:worksWith {weight: 3}]->(b)")
ag.execCypher("""MATCH (a:Person), (b:Person)
WHERE a.name = %s AND b.name = %s
- CREATE p=((a)-[r:workWith]->(b)) """, params=('Jack', 'Smith',))
+ CREATE p=((a)-[r:worksWith]->(b)) """, params=('Jack', 'Smith',))
ag.commit()
- cursor = ag.execCypher("MATCH p=()-[:workWith]-() RETURN p")
+ cursor = ag.execCypher("MATCH p=()-[:worksWith]-() RETURN p")
for row in cursor:
path = row[0]
print("START:", path[0])
@@ -81,10 +94,18 @@ def testQuery(self):
edgel = row[1]
edgew = row[2]
end = row[3]
- print(start["name"] , edgel, edgew, end["name"])
-
+ print("Relationship: %s %s %s. Edge weight: %s" % (start["name"] , edgel,end["name"], edgew))
+ #Assert that the weight of the edge is greater than 2
+ self.assertEqual(edgew > 2, True)
+ print("\nTest 2 Successful...")
+
def testChangeData(self):
+
+ print("\n-------------------------------------------------------")
+ print("Test 3: Testing changes in data using SET and REMOVE.....")
+ print("-------------------------------------------------------\n")
+
ag = self.ag
# Create Vertices
# Commit automatically
@@ -115,7 +136,7 @@ def testChangeData(self):
row = cursor.fetchone()
vertex = row[0]
for row in cursor:
- print("SET bigNum: ", vertex)
+ print("SET bigNum: ", vertex['bigNum'])
bigNum1 = vertex["bigNum"]
@@ -133,17 +154,25 @@ def testChangeData(self):
cursor = ag.execCypher("MATCH (n:Person {name: %s}) REMOVE n.title RETURN n", params=('Smith',))
for row in cursor:
print("REMOVE Prop title: ", row[0])
+ #Assert that the title property is removed
+ self.assertIsNone(row[0].properties.get('title'))
+ print("\nTest 3 Successful....")
# You must commit explicitly
ag.commit()
def testCypher(self):
+
+ print("\n--------------------------")
+ print("Test 4: Testing Cypher.....")
+ print("--------------------------\n")
+
ag = self.ag
with ag.connection.cursor() as cursor:
try :
- ag.cypher(cursor, "CREATE (n:Person {name: %s}) ", params=('Jone',))
+ ag.cypher(cursor, "CREATE (n:Person {name: %s}) ", params=('Joe',))
ag.cypher(cursor, "CREATE (n:Person {name: %s}) ", params=('Jack',))
ag.cypher(cursor, "CREATE (n:Person {name: %s}) ", params=('Andy',))
ag.cypher(cursor, "CREATE (n:Person {name: %s}) ", params=('Smith',))
@@ -157,9 +186,9 @@ def testCypher(self):
with ag.connection.cursor() as cursor:
try :# Create Edges
- ag.cypher(cursor,"MATCH (a:Person), (b:Person) WHERE a.name = 'Joe' AND b.name = 'Smith' CREATE (a)-[r:workWith {weight: 3}]->(b)")
- ag.cypher(cursor,"MATCH (a:Person), (b:Person) WHERE a.name = 'Andy' AND b.name = 'Tom' CREATE (a)-[r:workWith {weight: 1}]->(b)")
- ag.cypher(cursor,"MATCH (a:Person {name: 'Jack'}), (b:Person {name: 'Andy'}) CREATE (a)-[r:workWith {weight: 5}]->(b)")
+ ag.cypher(cursor,"MATCH (a:Person), (b:Person) WHERE a.name = 'Joe' AND b.name = 'Smith' CREATE (a)-[r:worksWith {weight: 3}]->(b)")
+ ag.cypher(cursor,"MATCH (a:Person), (b:Person) WHERE a.name = 'Andy' AND b.name = 'Tom' CREATE (a)-[r:worksWith {weight: 1}]->(b)")
+ ag.cypher(cursor,"MATCH (a:Person {name: 'Jack'}), (b:Person {name: 'Andy'}) CREATE (a)-[r:worksWith {weight: 5}]->(b)")
# You must commit explicitly
ag.commit()
@@ -171,22 +200,30 @@ def testCypher(self):
# With Params
cursor = ag.execCypher("""MATCH (a:Person), (b:Person)
WHERE a.name = %s AND b.name = %s
- CREATE p=((a)-[r:workWith]->(b)) RETURN p""",
+ CREATE p=((a)-[r:worksWith]->(b)) RETURN p""",
params=('Andy', 'Smith',))
for row in cursor:
- print(row[0])
+ print("CREATED EDGE: %s" % row[0])
cursor = ag.execCypher("""MATCH (a:Person {name: 'Joe'}), (b:Person {name: 'Jack'})
- CREATE p=((a)-[r:workWith {weight: 5}]->(b))
+ CREATE p=((a)-[r:worksWith {weight: 5}]->(b))
RETURN p """)
for row in cursor:
- print(row[0])
+ print("CREATED EDGE WITH PROPERTIES: %s" % row[0])
+ self.assertEqual(row[0][1].properties['weight'], 5)
+
+ print("\nTest 4 Successful...")
def testMultipleEdges(self):
+
+ print("\n------------------------------------")
+ print("Test 5: Testing Multiple Edges.....")
+ print("------------------------------------\n")
+
ag = self.ag
with ag.connection.cursor() as cursor:
try :
@@ -218,23 +255,33 @@ def testMultipleEdges(self):
RETURN p""")
count = 0
+ output = []
for row in cursor:
path = row[0]
- indent = ""
for e in path:
if e.gtype == age.TP_VERTEX:
- print(indent, e.label, e["name"])
+ output.append(e.label + " " + e["name"])
elif e.gtype == age.TP_EDGE:
- print(indent, e.label, e["value"], e["unit"])
+ output.append("---- (distance " + str(e["value"]) + " " + e["unit"] + ") --->")
else:
- print(indent, "Unknown element.", e)
+ output.append("Unknown element. " + str(e))
count += 1
- indent += " >"
+ formatted_output = " ".join(output)
+ print("PATH WITH MULTIPLE EDGES: %s" % formatted_output)
self.assertEqual(5,count)
+ print("\nTest 5 Successful...")
+
+
+
def testCollect(self):
+
+ print("\n--------------------------")
+ print("Test 6: Testing COLLECT.....")
+ print("--------------------------\n")
+
ag = self.ag
with ag.connection.cursor() as cursor:
@@ -253,9 +300,9 @@ def testCollect(self):
with ag.connection.cursor() as cursor:
try :# Create Edges
- ag.cypher(cursor,"MATCH (a:Person), (b:Person) WHERE a.name = 'Joe' AND b.name = 'Smith' CREATE (a)-[r:workWith {weight: 3}]->(b)")
- ag.cypher(cursor,"MATCH (a:Person), (b:Person) WHERE a.name = 'Joe' AND b.name = 'Tom' CREATE (a)-[r:workWith {weight: 1}]->(b)")
- ag.cypher(cursor,"MATCH (a:Person {name: 'Joe'}), (b:Person {name: 'Andy'}) CREATE (a)-[r:workWith {weight: 5}]->(b)")
+ ag.cypher(cursor,"MATCH (a:Person), (b:Person) WHERE a.name = 'Joe' AND b.name = 'Smith' CREATE (a)-[r:worksWith {weight: 3}]->(b)")
+ ag.cypher(cursor,"MATCH (a:Person), (b:Person) WHERE a.name = 'Joe' AND b.name = 'Tom' CREATE (a)-[r:worksWith {weight: 1}]->(b)")
+ ag.cypher(cursor,"MATCH (a:Person {name: 'Joe'}), (b:Person {name: 'Andy'}) CREATE (a)-[r:worksWith {weight: 5}]->(b)")
# You must commit explicitly
ag.commit()
@@ -263,22 +310,59 @@ def testCollect(self):
print(ex)
ag.rollback()
- print(" - COLLECT 1 --------")
+ print(" -------- TESTING COLLECT #1 --------")
with ag.connection.cursor() as cursor:
- ag.cypher(cursor, "MATCH (a)-[:workWith]->(c) WITH a as V, COLLECT(c) as CV RETURN V.name, CV", cols=["V","CV"])
+ ag.cypher(cursor, "MATCH (a)-[:worksWith]->(c) WITH a as V, COLLECT(c) as CV RETURN V.name, CV", cols=["V","CV"])
for row in cursor:
nm = row[0]
collected = row[1]
- print(nm, "workWith", [i["name"] for i in collected])
+ print(nm, "worksWith", [i["name"] for i in collected])
self.assertEqual(3,len(collected))
- print(" - COLLECT 2 --------")
- for row in ag.execCypher("MATCH (a)-[:workWith]->(c) WITH a as V, COLLECT(c) as CV RETURN V.name, CV", cols=["V1","CV"]):
+ print(" -------- TESTING COLLECT #2 --------")
+ for row in ag.execCypher("MATCH (a)-[:worksWith]->(c) WITH a as V, COLLECT(c) as CV RETURN V.name, CV", cols=["V1","CV"]):
nm = row[0]
collected = row[1]
- print(nm, "workWith", [i["name"] for i in collected])
+ print(nm, "worksWith", [i["name"] for i in collected])
self.assertEqual(3,len(collected))
+ print("\nTest 6 Successful...")
if __name__ == '__main__':
- unittest.main()
+ parser = argparse.ArgumentParser()
+
+ parser.add_argument('-host',
+ '--host',
+ help='Optional Host Name. Default Host is "127.0.0.1" ',
+ default="127.0.0.1")
+ parser.add_argument('-port',
+ '--port',
+ help='Optional Port Number. Default port no is 5432',
+ default=5432)
+ parser.add_argument('-db',
+ '--database',
+ help='Required Database Name',
+ required=True)
+ parser.add_argument('-u',
+ '--user',
+ help='Required Username Name',
+ required=True)
+ parser.add_argument('-pass',
+ '--password',
+ help='Required Password for authentication',
+ required=True)
+ parser.add_argument('-gn',
+ '--graphName',
+ help='Optional Graph Name to be created. Default graphName is "test_graph"',
+ default="test_graph")
+
+ args = parser.parse_args()
+ suite = unittest.TestSuite()
+ suite.addTest(TestAgeBasic('testExec'))
+ suite.addTest(TestAgeBasic('testQuery'))
+ suite.addTest(TestAgeBasic('testChangeData'))
+ suite.addTest(TestAgeBasic('testCypher'))
+ suite.addTest(TestAgeBasic('testMultipleEdges'))
+ suite.addTest(TestAgeBasic('testCollect'))
+ TestAgeBasic.args = args
+ unittest.TextTestRunner().run(suite)
diff --git a/drivers/python/test_agtypes.py b/drivers/python/test_agtypes.py
index e6a6d2cc4..69bbbc298 100644
--- a/drivers/python/test_agtypes.py
+++ b/drivers/python/test_agtypes.py
@@ -15,8 +15,9 @@
import unittest
from decimal import Decimal
-import math
-import age
+import math
+import age
+
class TestAgtype(unittest.TestCase):
resultHandler = None
@@ -24,21 +25,23 @@ class TestAgtype(unittest.TestCase):
def __init__(self, methodName: str) -> None:
super().__init__(methodName=methodName)
self.resultHandler = age.newResultHandler()
-
+
def parse(self, exp):
- return self.resultHandler.parse(exp)
+ return self.resultHandler.parse(exp)
def test_scalar(self):
- mapStr = '{"name": "Smith", "num":123, "yn":true, "bigInt":123456789123456789123456789123456789::numeric}'
- arrStr = '["name", "Smith", "num", 123, "yn", true, 123456789123456789123456789123456789.8888::numeric]'
- strStr = '"abcd"'
- intStr = '1234'
- floatStr = '1234.56789'
- floatStr2 = '6.45161290322581e+46'
- numericStr1 = '12345678901234567890123456789123456789.789::numeric'
- numericStr2 = '12345678901234567890123456789123456789::numeric'
- boolStr = 'true'
- nullStr = ''
+ print("\nTesting Scalar Value Parsing. Result : ", end='')
+
+ mapStr = '{"name": "Smith", "num":123, "yn":true, "bigInt":123456789123456789123456789123456789::numeric}'
+ arrStr = '["name", "Smith", "num", 123, "yn", true, 123456789123456789123456789123456789.8888::numeric]'
+ strStr = '"abcd"'
+ intStr = '1234'
+ floatStr = '1234.56789'
+ floatStr2 = '6.45161290322581e+46'
+ numericStr1 = '12345678901234567890123456789123456789.789::numeric'
+ numericStr2 = '12345678901234567890123456789123456789::numeric'
+ boolStr = 'true'
+ nullStr = ''
nanStr = "NaN"
infpStr = "Infinity"
infnStr = "-Infinity"
@@ -57,34 +60,27 @@ def test_scalar(self):
infpVal = self.parse(infpStr)
infnVal = self.parse(infnStr)
- print("map", type(mapVal), mapVal)
- print("arr", type(arrVal), arrVal)
- print("str", type(str), str)
- print("intVal", type(intVal), intVal)
- print("floatVal", type(floatVal), floatVal)
- print("floatVal", type(floatVal2), floatVal2)
- print("bigFloat", type(bigFloat), bigFloat)
- print("bigInt", type(bigInt), bigInt)
- print("bool", type(boolVal), boolVal)
- print("null", type(nullVal), nullVal)
- print("nanVal", type(nanVal), nanVal)
- print("infpVal", type(infpVal), infpVal)
- print("infnVal", type(infnVal), infnVal)
-
- self.assertEqual(mapVal, {'name': 'Smith', 'num': 123, 'yn': True, 'bigInt': Decimal('123456789123456789123456789123456789')})
- self.assertEqual(arrVal, ["name", "Smith", "num", 123, "yn", True, Decimal("123456789123456789123456789123456789.8888")] )
+ self.assertEqual(mapVal, {'name': 'Smith', 'num': 123, 'yn': True, 'bigInt': Decimal(
+ '123456789123456789123456789123456789')})
+ self.assertEqual(arrVal, ["name", "Smith", "num", 123, "yn", True, Decimal(
+ "123456789123456789123456789123456789.8888")])
self.assertEqual(str, "abcd")
self.assertEqual(intVal, 1234)
self.assertEqual(floatVal, 1234.56789)
self.assertEqual(floatVal2, 6.45161290322581e+46)
- self.assertEqual(bigFloat, Decimal("12345678901234567890123456789123456789.789"))
- self.assertEqual(bigInt, Decimal("12345678901234567890123456789123456789"))
+ self.assertEqual(bigFloat, Decimal(
+ "12345678901234567890123456789123456789.789"))
+ self.assertEqual(bigInt, Decimal(
+ "12345678901234567890123456789123456789"))
self.assertEqual(boolVal, True)
self.assertTrue(math.isnan(nanVal))
self.assertTrue(math.isinf(infpVal))
self.assertTrue(math.isinf(infnVal))
def test_vertex(self):
+
+ print("\nTesting vertex Parsing. Result : ", end='')
+
vertexExp = '''{"id": 2251799813685425, "label": "Person",
"properties": {"name": "Smith", "numInt":123, "numFloat": 384.23424,
"bigInt":123456789123456789123456789123456789123456789123456789123456789123456789123456789123456789123456789123456789::numeric,
@@ -97,12 +93,17 @@ def test_vertex(self):
self.assertEqual(vertex["name"], "Smith")
self.assertEqual(vertex["numInt"], 123)
self.assertEqual(vertex["numFloat"], 384.23424)
- self.assertEqual(vertex["bigInt"], Decimal("123456789123456789123456789123456789123456789123456789123456789123456789123456789123456789123456789123456789"))
- self.assertEqual(vertex["bigFloat"], Decimal("123456789123456789123456789123456789.12345"))
+ self.assertEqual(vertex["bigInt"], Decimal(
+ "123456789123456789123456789123456789123456789123456789123456789123456789123456789123456789123456789123456789"))
+ self.assertEqual(vertex["bigFloat"], Decimal(
+ "123456789123456789123456789123456789.12345"))
self.assertEqual(vertex["yn"], True)
self.assertEqual(vertex["nullVal"], None)
def test_path(self):
+
+ print("\nTesting Path Parsing. Result : ", end='')
+
pathExp = '''[{"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,
@@ -119,7 +120,8 @@ def test_path(self):
self.assertEqual(edge.id, 2533274790396576)
self.assertEqual(edge.label, "workWith")
self.assertEqual(edge["weight"], 3)
- self.assertEqual(edge["bigFloat"], Decimal("123456789123456789123456789.12345"))
+ self.assertEqual(edge["bigFloat"], Decimal(
+ "123456789123456789123456789.12345"))
self.assertEqual(vertexEnd.id, 2251799813685424)
self.assertEqual(vertexEnd.label, "Person")
@@ -127,4 +129,4 @@ def test_path(self):
if __name__ == '__main__':
- unittest.main()
\ No newline at end of file
+ unittest.main()
diff --git a/regress/expected/age_global_graph.out b/regress/expected/age_global_graph.out
index b98a370d9..73377b314 100644
--- a/regress/expected/age_global_graph.out
+++ b/regress/expected/age_global_graph.out
@@ -93,7 +93,7 @@ SELECT * FROM cypher('ag_graph_1', $$ RETURN delete_global_graphs('ag_graph_1')
(1 row)
-- delete ag_graph_3's context
--- should return true(succeed) beacuse the previous commands should not delete the 3rd graph's context
+-- should return true(succeed) because the previous commands should not delete the 3rd graph's context
SELECT * FROM cypher('ag_graph_3', $$ RETURN delete_global_graphs('ag_graph_3') $$) AS (result agtype);
result
--------
@@ -120,7 +120,7 @@ SELECT * FROM cypher('ag_graph_3', $$ RETURN delete_global_graphs('ag_graph_3')
false
(1 row)
---- delete unitialized graph context
+--- delete uninitialized graph context
--- should throw exception graph "ag_graph_4" does not exist
SELECT * FROM cypher('ag_graph_4', $$ RETURN delete_global_graphs('ag_graph_4') $$) AS (result agtype);
ERROR: graph "ag_graph_4" does not exist
diff --git a/regress/expected/agtype.out b/regress/expected/agtype.out
index f4f98b17b..65cca0e4c 100644
--- a/regress/expected/agtype.out
+++ b/regress/expected/agtype.out
@@ -1524,7 +1524,7 @@ SELECT agtype_in('{"bool":true}') < agtype_in('{"bool":true, "null": null}');
(1 row)
-- Comparisons between types
--- Object < List < String < Boolean < Integer = Float = Numeric < Null
+-- Path < Edge < Vertex < Object < List < String < Boolean < Integer = Float = Numeric < Null
SELECT agtype_in('1') < agtype_in('null');
?column?
----------
@@ -1591,6 +1591,24 @@ SELECT agtype_in('{"bool":true, "integer":1}') < agtype_in('{"bool":true, "integ
t
(1 row)
+SELECT agtype_in('{"id":0, "label": "v", "properties":{"i":0}}::vertex') < agtype_in('{"bool":true, "i":0}');
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT agtype_in('{"id":2, "start_id":0, "end_id":1, "label": "e", "properties":{"i":0}}::edge') < agtype_in('{"id":0, "label": "v", "properties":{"i":0}}::vertex');
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT agtype_in('[{"id": 0, "label": "v", "properties": {"i": 0}}::vertex, {"id": 2, "start_id": 0, "end_id": 1, "label": "e", "properties": {"i": 0}}::edge, {"id": 1, "label": "v", "properties": {"i": 0}}::vertex]::path') < agtype_in('{"id":2, "start_id":0, "end_id":1, "label": "e", "properties":{"i":0}}::edge');
+ ?column?
+----------
+ t
+(1 row)
+
SELECT agtype_in('1::numeric') < agtype_in('null');
?column?
----------
@@ -1603,6 +1621,66 @@ SELECT agtype_in('true') < agtype_in('1::numeric');
t
(1 row)
+-- Testing orderability between types
+SELECT * FROM create_graph('orderability_graph');
+NOTICE: graph "orderability_graph" has been created
+ create_graph
+--------------
+
+(1 row)
+
+SELECT * FROM cypher('orderability_graph', $$ CREATE (:vertex {prop: null}), (:vertex {prop: 1}), (:vertex {prop: 1.01}),(:vertex {prop: true}), (:vertex {prop:"string"}),(:vertex {prop:"string_2"}), (:vertex {prop:[1, 2, 3]}), (:vertex {prop:[1, 2, 3, 4, 5]}), (:vertex {prop:{bool:true, i:0}}), (:vertex {prop:{bool:true, i:null}}), (:vertex {prop: {id:0, label: "v", properties:{i:0}}::vertex}), (:vertex {prop: {id: 2, start_id: 0, end_id: 1, label: "e", properties: {i: 0}}::edge}), (:vertex {prop: [{id: 0, label: "v", properties: {i: 0}}::vertex, {id: 2, start_id: 0, end_id: 1, label: "e", properties: {i: 0}}::edge, {id: 1, label: "v", properties: {i: 0}}::vertex]::path}) $$) AS (x agtype);
+ x
+---
+(0 rows)
+
+SELECT * FROM cypher('orderability_graph', $$ MATCH (n) RETURN n ORDER BY n.prop $$) AS (sorted agtype);
+ sorted
+-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
+ {"id": 844424930131981, "label": "vertex", "properties": {"prop": [{"id": 0, "label": "v", "properties": {"i": 0}}::vertex, {"id": 2, "label": "e", "end_id": 1, "start_id": 0, "properties": {"i": 0}}::edge, {"id": 1, "label": "v", "properties": {"i": 0}}::vertex]::path}}::vertex
+ {"id": 844424930131980, "label": "vertex", "properties": {"prop": {"id": 2, "label": "e", "end_id": 1, "start_id": 0, "properties": {"i": 0}}::edge}}::vertex
+ {"id": 844424930131979, "label": "vertex", "properties": {"prop": {"id": 0, "label": "v", "properties": {"i": 0}}::vertex}}::vertex
+ {"id": 844424930131978, "label": "vertex", "properties": {"prop": {"bool": true}}}::vertex
+ {"id": 844424930131977, "label": "vertex", "properties": {"prop": {"i": 0, "bool": true}}}::vertex
+ {"id": 844424930131975, "label": "vertex", "properties": {"prop": [1, 2, 3]}}::vertex
+ {"id": 844424930131976, "label": "vertex", "properties": {"prop": [1, 2, 3, 4, 5]}}::vertex
+ {"id": 844424930131973, "label": "vertex", "properties": {"prop": "string"}}::vertex
+ {"id": 844424930131974, "label": "vertex", "properties": {"prop": "string_2"}}::vertex
+ {"id": 844424930131972, "label": "vertex", "properties": {"prop": true}}::vertex
+ {"id": 844424930131970, "label": "vertex", "properties": {"prop": 1}}::vertex
+ {"id": 844424930131971, "label": "vertex", "properties": {"prop": 1.01}}::vertex
+ {"id": 844424930131969, "label": "vertex", "properties": {}}::vertex
+(13 rows)
+
+SELECT * FROM cypher('orderability_graph', $$ MATCH (n) RETURN n ORDER BY n.prop DESC $$) AS (sorted agtype);
+ sorted
+-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
+ {"id": 844424930131969, "label": "vertex", "properties": {}}::vertex
+ {"id": 844424930131971, "label": "vertex", "properties": {"prop": 1.01}}::vertex
+ {"id": 844424930131970, "label": "vertex", "properties": {"prop": 1}}::vertex
+ {"id": 844424930131972, "label": "vertex", "properties": {"prop": true}}::vertex
+ {"id": 844424930131974, "label": "vertex", "properties": {"prop": "string_2"}}::vertex
+ {"id": 844424930131973, "label": "vertex", "properties": {"prop": "string"}}::vertex
+ {"id": 844424930131976, "label": "vertex", "properties": {"prop": [1, 2, 3, 4, 5]}}::vertex
+ {"id": 844424930131975, "label": "vertex", "properties": {"prop": [1, 2, 3]}}::vertex
+ {"id": 844424930131977, "label": "vertex", "properties": {"prop": {"i": 0, "bool": true}}}::vertex
+ {"id": 844424930131978, "label": "vertex", "properties": {"prop": {"bool": true}}}::vertex
+ {"id": 844424930131979, "label": "vertex", "properties": {"prop": {"id": 0, "label": "v", "properties": {"i": 0}}::vertex}}::vertex
+ {"id": 844424930131980, "label": "vertex", "properties": {"prop": {"id": 2, "label": "e", "end_id": 1, "start_id": 0, "properties": {"i": 0}}::edge}}::vertex
+ {"id": 844424930131981, "label": "vertex", "properties": {"prop": [{"id": 0, "label": "v", "properties": {"i": 0}}::vertex, {"id": 2, "label": "e", "end_id": 1, "start_id": 0, "properties": {"i": 0}}::edge, {"id": 1, "label": "v", "properties": {"i": 0}}::vertex]::path}}::vertex
+(13 rows)
+
+SELECT * FROM drop_graph('orderability_graph', true);
+NOTICE: drop cascades to 3 other objects
+DETAIL: drop cascades to table orderability_graph._ag_label_vertex
+drop cascades to table orderability_graph._ag_label_edge
+drop cascades to table orderability_graph.vertex
+NOTICE: graph "orderability_graph" has been dropped
+ drop_graph
+------------
+
+(1 row)
+
--
-- Test overloaded agytype any comparison operators =, <>, <, >, <=, >=,
--
@@ -2079,10 +2157,10 @@ SELECT agtype_to_bool(agtype_in('false'));
(1 row)
-- These should all fail
-SELECT agtype_to_bool(agtype_in('null'));
-ERROR: cannot cast agtype null to type boolean
SELECT agtype_to_bool(agtype_in('1'));
ERROR: cannot cast agtype integer to type boolean
+SELECT agtype_to_bool(agtype_in('null'));
+ERROR: cannot cast agtype null to type boolean
SELECT agtype_to_bool(agtype_in('1.0'));
ERROR: cannot cast agtype float to type boolean
SELECT agtype_to_bool(agtype_in('"string"'));
@@ -2124,6 +2202,70 @@ SELECT bool_to_agtype(true) <> bool_to_agtype(false);
t
(1 row)
+--
+-- Test boolean to pg_bigint cast
+--
+SELECT agtype_to_int8(agtype_in('true'));
+ agtype_to_int8
+----------------
+ 1
+(1 row)
+
+SELECT agtype_to_int8(agtype_in('false'));
+ agtype_to_int8
+----------------
+ 0
+(1 row)
+
+--
+-- Test boolean to integer cast
+--
+SELECT agtype_to_int4(agtype_in('true'));
+ agtype_to_int4
+----------------
+ 1
+(1 row)
+
+SELECT agtype_to_int4(agtype_in('false'));
+ agtype_to_int4
+----------------
+ 0
+(1 row)
+
+SELECT agtype_to_int4(agtype_in('null'));
+ agtype_to_int4
+----------------
+
+(1 row)
+
+--
+-- Test agtype to integer cast
+--
+SELECT agtype_to_int4(agtype_in('1'));
+ agtype_to_int4
+----------------
+ 1
+(1 row)
+
+SELECT agtype_to_int4(agtype_in('1.45'));
+ agtype_to_int4
+----------------
+ 1
+(1 row)
+
+SELECT agtype_to_int4(agtype_in('1.444::numeric'));
+ agtype_to_int4
+----------------
+ 1
+(1 row)
+
+-- These should all fail
+SELECT agtype_to_int4(agtype_in('"string"'));
+ERROR: invalid input syntax for type integer: "string"
+SELECT agtype_to_int4(agtype_in('[1, 2, 3]'));
+ERROR: cannot cast agtype array to type int
+SELECT agtype_to_int4(agtype_in('{"int":1}'));
+ERROR: cannot cast agtype object to type int
--
-- Test agtype to int[]
--
diff --git a/regress/expected/catalog.out b/regress/expected/catalog.out
index c7d5cc3ae..bc7b9434a 100644
--- a/regress/expected/catalog.out
+++ b/regress/expected/catalog.out
@@ -28,10 +28,10 @@ NOTICE: graph "graph" has been created
(1 row)
-SELECT * FROM ag_graph WHERE name = 'graph';
- graphid | name | namespace
----------+-------+-----------
- 17595 | graph | graph
+SELECT name, namespace FROM ag_graph WHERE name = 'graph';
+ name | namespace
+-------+-----------
+ graph | graph
(1 row)
-- create a label to test drop_label()
diff --git a/regress/expected/cypher_call.out b/regress/expected/cypher_call.out
index a7862b5d0..8c5c5fca7 100644
--- a/regress/expected/cypher_call.out
+++ b/regress/expected/cypher_call.out
@@ -41,8 +41,8 @@ CREATE FUNCTION call_stmt_test.add_agtype(agtype, agtype) RETURNS agtype
LANGUAGE SQL
IMMUTABLE
RETURNS NULL ON NULL INPUT;
-/*
- * CALL (solo)
+/*
+ * CALL (solo)
*/
SELECT * FROM cypher('cypher_call', $$CALL sqrt(64)$$) as (sqrt agtype);
sqrt
@@ -75,7 +75,7 @@ SELECT * FROM cypher('cypher_call', $$CALL call_stmt_test.add_agtype(1,2)$$) as
3
(1 row)
-/* non-existent schema should fail */
+/* nonexistent schema should fail */
SELECT * FROM cypher('cypher_call', $$CALL ag_catalog.add_agtype(1,2)$$) as (sqrt agtype);
ERROR: function ag_catalog.add_agtype(agtype, agtype) does not exist
LINE 2: ...cypher('cypher_call', $$CALL ag_catalog.add_agtype(1,2)$$) a...
diff --git a/regress/expected/cypher_create.out b/regress/expected/cypher_create.out
index 7cac75e35..9e5b1a240 100644
--- a/regress/expected/cypher_create.out
+++ b/regress/expected/cypher_create.out
@@ -499,8 +499,8 @@ SELECT * FROM cypher('cypher_create', $$
CREATE (a {test:1})-[:e_var]->()
$$) as (a agtype);
ERROR: previously declared nodes in a create clause cannot have properties
-LINE 1: SELECT * FROM cypher('cypher_create', $$
- ^
+LINE 4: CREATE (a {test:1})-[:e_var]->()
+ ^
-- Var 'a' cannot change labels
SELECT * FROM cypher('cypher_create', $$
MATCH (a:n_var)
@@ -508,16 +508,16 @@ SELECT * FROM cypher('cypher_create', $$
CREATE (a:new_label)-[:e_var]->()
$$) as (a agtype);
ERROR: previously declared variables cannot have a label
-LINE 1: SELECT * FROM cypher('cypher_create', $$
- ^
+LINE 4: CREATE (a:new_label)-[:e_var]->()
+ ^
SELECT * FROM cypher('cypher_create', $$
MATCH (a:n_var)-[b]-()
WHERE a.name = 'Node A'
CREATE (a)-[b:e_var]->()
$$) as (a agtype);
ERROR: variable b already exists
-LINE 1: SELECT * FROM cypher('cypher_create', $$
- ^
+LINE 4: CREATE (a)-[b:e_var]->()
+ ^
-- A valid single vertex path
SELECT * FROM cypher('cypher_create', $$
CREATE p=(a)
@@ -538,7 +538,7 @@ cypher('cypher_create', $$
CREATE (b)
RETURN b
$$) as t(b agtype);
-ERROR: cypher create clause cannot be rescaned
+ERROR: cypher create clause cannot be rescanned
HINT: its unsafe to use joins in a query with a Cypher CREATE clause
-- column definition list for CREATE clause must contain a single agtype
-- attribute
@@ -578,7 +578,7 @@ SELECT * FROM cypher('cypher_create', $$
$$) as (a agtype);
ERROR: label existing_elabel is for edges, not vertices
LINE 2: CREATE (a:existing_elabel { id: 5})
- ^
+ ^
--
-- check the cypher CREATE clause inside an INSERT INTO
--
@@ -641,12 +641,137 @@ SELECT * FROM cypher('cypher_create', $$ MATCH (a:Part) RETURN a $$) as (a agtyp
END;
--
+-- variable reuse
+--
+-- Valid variable reuse
+SELECT * FROM cypher('cypher_create', $$
+ CREATE (p)-[a:new]->(p)
+ RETURN p,a,p
+$$) as (n1 agtype, e agtype, n2 agtype);
+ n1 | e | n2
+----------------------------------------------------------------+--------------------------------------------------------------------------------------------------------------------------+----------------------------------------------------------------
+ {"id": 281474976710681, "label": "", "properties": {}}::vertex | {"id": 3940649673949185, "label": "new", "end_id": 281474976710681, "start_id": 281474976710681, "properties": {}}::edge | {"id": 281474976710681, "label": "", "properties": {}}::vertex
+(1 row)
+
+SELECT * FROM cypher('cypher_create', $$
+ CREATE (p:node)-[e:new]->(p)
+ RETURN p,e,p
+$$) as (n1 agtype, e agtype, n2 agtype);
+ n1 | e | n2
+---------------------------------------------------------------------+----------------------------------------------------------------------------------------------------------------------------+---------------------------------------------------------------------
+ {"id": 4222124650659841, "label": "node", "properties": {}}::vertex | {"id": 3940649673949186, "label": "new", "end_id": 4222124650659841, "start_id": 4222124650659841, "properties": {}}::edge | {"id": 4222124650659841, "label": "node", "properties": {}}::vertex
+(1 row)
+
+SELECT * FROM cypher('cypher_create', $$
+ CREATE (p)
+ CREATE (p)-[a:new]->(p)
+ RETURN p,a,p
+$$) as (n1 agtype, e agtype, n2 agtype);
+ n1 | e | n2
+----------------------------------------------------------------+--------------------------------------------------------------------------------------------------------------------------+----------------------------------------------------------------
+ {"id": 281474976710682, "label": "", "properties": {}}::vertex | {"id": 3940649673949187, "label": "new", "end_id": 281474976710682, "start_id": 281474976710682, "properties": {}}::edge | {"id": 281474976710682, "label": "", "properties": {}}::vertex
+(1 row)
+
+SELECT * FROM cypher('cypher_create', $$
+ CREATE (p:n1)
+ CREATE (p)-[a:new]->(p)
+ RETURN p,a,p
+$$) as (n1 agtype, e agtype, n2 agtype);
+ n1 | e | n2
+-------------------------------------------------------------------+----------------------------------------------------------------------------------------------------------------------------+-------------------------------------------------------------------
+ {"id": 4503599627370497, "label": "n1", "properties": {}}::vertex | {"id": 3940649673949188, "label": "new", "end_id": 4503599627370497, "start_id": 4503599627370497, "properties": {}}::edge | {"id": 4503599627370497, "label": "n1", "properties": {}}::vertex
+(1 row)
+
+SELECT * FROM cypher('cypher_create', $$
+ MATCH (p:node)
+ CREATE (p)-[a:new]->(p)
+ RETURN p,a,p
+$$) as (n1 agtype, e agtype, n2 agtype);
+ n1 | e | n2
+---------------------------------------------------------------------+----------------------------------------------------------------------------------------------------------------------------+---------------------------------------------------------------------
+ {"id": 4222124650659841, "label": "node", "properties": {}}::vertex | {"id": 3940649673949189, "label": "new", "end_id": 4222124650659841, "start_id": 4222124650659841, "properties": {}}::edge | {"id": 4222124650659841, "label": "node", "properties": {}}::vertex
+(1 row)
+
+-- Invalid variable reuse
+SELECT * FROM cypher('cypher_create', $$
+ CREATE (p)-[a:new]->(p {n0:'n1'})
+$$) as (a agtype);
+ERROR: previously declared nodes in a create clause cannot have properties
+LINE 2: CREATE (p)-[a:new]->(p {n0:'n1'})
+ ^
+SELECT * FROM cypher('cypher_create', $$
+ CREATE (p:n0)-[a:new]->(p:n1)
+$$) as (a agtype);
+ERROR: previously declared variables cannot have a label
+LINE 2: CREATE (p:n0)-[a:new]->(p:n1)
+ ^
+SELECT * FROM cypher('cypher_create', $$
+ CREATE p=(p)
+$$) as (a agtype);
+ERROR: variable "p" already exists
+LINE 2: CREATE p=(p)
+ ^
+SELECT * FROM cypher('cypher_create', $$
+ CREATE p=() CREATE (p)
+$$) as (a agtype);
+ERROR: agtype must resolve to a vertex
+SELECT * FROM cypher('cypher_create', $$
+ CREATE p=(a)-[p:b]->(a)
+$$) as (a agtype);
+ERROR: variable "p" already exists
+LINE 2: CREATE p=(a)-[p:b]->(a)
+ ^
+SELECT * FROM cypher('cypher_create', $$
+ CREATE p=(a)-[:new]->(p)
+$$) as (a agtype);
+ERROR: variable "p" already exists
+LINE 2: CREATE p=(a)-[:new]->(p)
+ ^
+SELECT * FROM cypher('cypher_create', $$
+ MATCH (p) CREATE p=()
+$$) as (a agtype);
+ERROR: variable "p" already exists
+LINE 2: MATCH (p) CREATE p=()
+ ^
+SELECT * FROM cypher('cypher_create', $$
+ MATCH (p) CREATE p=(p)
+$$) as (a agtype);
+ERROR: variable "p" already exists
+LINE 2: MATCH (p) CREATE p=(p)
+ ^
+SELECT * FROM cypher('cypher_create', $$
+ MATCH (p) CREATE (a)-[p:b]->(a)
+$$) as (a agtype);
+ERROR: variable p already exists
+LINE 2: MATCH (p) CREATE (a)-[p:b]->(a)
+ ^
+SELECT * FROM cypher('cypher_create', $$
+ CREATE (a)-[e:new]->(p)-[e]->(a)
+$$) as (a agtype);
+ERROR: relationships must be specify a label in CREATE.
+LINE 2: CREATE (a)-[e:new]->(p)-[e]->(a)
+ ^
+SELECT * FROM cypher('cypher_create', $$
+ CREATE (a)-[e:new]->(p)
+ CREATE (p)-[e:new]->(a)
+$$) as (a agtype);
+ERROR: variable e already exists
+LINE 3: CREATE (p)-[e:new]->(a)
+ ^
+SELECT * FROM cypher('cypher_create', $$
+ MATCH (a)-[e:new]->(p)
+ CREATE (p)-[e:new]->(a)
+$$) as (a agtype);
+ERROR: variable e already exists
+LINE 3: CREATE (p)-[e:new]->(a)
+ ^
+--
-- Clean up
--
DROP TABLE simple_path;
DROP FUNCTION create_test;
SELECT drop_graph('cypher_create', true);
-NOTICE: drop cascades to 13 other objects
+NOTICE: drop cascades to 16 other objects
DETAIL: drop cascades to table cypher_create._ag_label_vertex
drop cascades to table cypher_create._ag_label_edge
drop cascades to table cypher_create.v
@@ -660,6 +785,9 @@ drop cascades to table cypher_create.existing_vlabel
drop cascades to table cypher_create.existing_elabel
drop cascades to table cypher_create.knows
drop cascades to table cypher_create."Part"
+drop cascades to table cypher_create.new
+drop cascades to table cypher_create.node
+drop cascades to table cypher_create.n1
NOTICE: graph "cypher_create" has been dropped
drop_graph
------------
diff --git a/regress/expected/cypher_delete.out b/regress/expected/cypher_delete.out
index 8760a4881..d7d198261 100644
--- a/regress/expected/cypher_delete.out
+++ b/regress/expected/cypher_delete.out
@@ -84,7 +84,7 @@ SELECT * FROM cypher('cypher_delete', $$MATCH(n) DELETE n RETURN n$$) AS (a agty
{"id": 844424930131973, "label": "v", "properties": {}}::vertex
(2 rows)
---Test 4: DETACH DELECT a vertex
+--Test 4: DETACH DELETE a vertex
SELECT * FROM cypher('cypher_delete', $$CREATE (:v)-[:e]->(:v)$$) AS (a agtype);
a
---
@@ -103,7 +103,7 @@ SELECT * FROM cypher('cypher_delete', $$MATCH(n) RETURN n$$) AS (a agtype);
{"id": 844424930131975, "label": "v", "properties": {}}::vertex
(1 row)
---Test 4: DETACH DELECT two vertices tied to the same edge
+--Test 4: DETACH DELETE two vertices tied to the same edge
SELECT * FROM cypher('cypher_delete', $$CREATE (:v)-[:e]->(:v)$$) AS (a agtype);
a
---
@@ -115,7 +115,7 @@ SELECT * FROM cypher('cypher_delete', $$MATCH(n1)-[e]->(n2) DETACH DELETE n1, n2
{"id": 1125899906842627, "label": "e", "end_id": 844424930131977, "start_id": 844424930131976, "properties": {}}::edge
(1 row)
---Test 4: DETACH DELECT a vertex
+--Test 4: DETACH DELETE a vertex
SELECT * FROM cypher('cypher_delete', $$CREATE (:v)-[:e]->(:v)$$) AS (a agtype);
a
---
diff --git a/regress/expected/cypher_match.out b/regress/expected/cypher_match.out
index d1c58c1f2..a207cabff 100644
--- a/regress/expected/cypher_match.out
+++ b/regress/expected/cypher_match.out
@@ -508,38 +508,319 @@ SELECT * FROM cypher('cypher_match', $$
$$) AS (a agtype);
ERROR: multiple labels for variable 'a' are not supported
LINE 2: MATCH (a)-[]-()-[]-(a:v1) RETURN a
- ^
+ ^
SELECT * FROM cypher('cypher_match', $$
- MATCH (a)-[]-(a:v2)-[]-(a) RETURN a
+ MATCH (a)-[]-(a:v2)-[]-(a) RETURN a
$$) AS (a agtype);
ERROR: multiple labels for variable 'a' are not supported
-LINE 2: MATCH (a)-[]-(a:v2)-[]-(a) RETURN a
- ^
+LINE 2: MATCH (a)-[]-(a:v2)-[]-(a) RETURN a
+ ^
SELECT * FROM cypher('cypher_match', $$
- MATCH (a)-[]-(a:v1) RETURN a
+ MATCH (a)-[]-(a:v1) RETURN a
$$) AS (a agtype);
ERROR: multiple labels for variable 'a' are not supported
-LINE 2: MATCH (a)-[]-(a:v1) RETURN a
- ^
+LINE 2: MATCH (a)-[]-(a:v1) RETURN a
+ ^
SELECT * FROM cypher('cypher_match', $$
- MATCH (a)-[]-(a)-[]-(a:v1) RETURN a
+ MATCH (a)-[]-(a)-[]-(a:v1) RETURN a
$$) AS (a agtype);
ERROR: multiple labels for variable 'a' are not supported
-LINE 2: MATCH (a)-[]-(a)-[]-(a:v1) RETURN a
- ^
+LINE 2: MATCH (a)-[]-(a)-[]-(a:v1) RETURN a
+ ^
SELECT * FROM cypher('cypher_match', $$
- MATCH (a)-[]-(a)-[]-(a:invalid_label) RETURN a
+ MATCH (a)-[]-(a)-[]-(a:invalid_label) RETURN a
$$) AS (a agtype);
ERROR: multiple labels for variable 'a' are not supported
-LINE 2: MATCH (a)-[]-(a)-[]-(a:invalid_label) RETURN a
- ^
---Valid variable reuse, although why would you want to do it this way?
+LINE 2: MATCH (a)-[]-(a)-[]-(a:invalid_label) RETURN a
+ ^
+SELECT * FROM cypher('cypher_match', $$
+ MATCH (a) MATCH (a:v1) RETURN a
+$$) AS (a agtype);
+ERROR: multiple labels for variable 'a' are not supported
+LINE 2: MATCH (a) MATCH (a:v1) RETURN a
+ ^
+SELECT * FROM cypher('cypher_match', $$
+ MATCH (a) MATCH (a:invalid_label) RETURN a
+$$) AS (a agtype);
+ERROR: multiple labels for variable 'a' are not supported
+LINE 2: MATCH (a) MATCH (a:invalid_label) RETURN a
+ ^
SELECT * FROM cypher('cypher_match', $$
MATCH (a:v1)-[]-()-[a]-() RETURN a
$$) AS (a agtype);
ERROR: variable 'a' is for a vertex
LINE 2: MATCH (a:v1)-[]-()-[a]-() RETURN a
^
+-- valid variable reuse for edge labels across clauses
+SELECT * FROM cypher('cypher_match', $$
+ MATCH ()-[r0]->() MATCH ()-[r0]->() RETURN r0
+$$) AS (r0 agtype);
+ r0
+---------------------------------------------------------------------------------------------------------------------------
+ {"id": 1407374883553281, "label": "e1", "end_id": 1125899906842627, "start_id": 1125899906842626, "properties": {}}::edge
+ {"id": 1407374883553282, "label": "e1", "end_id": 1125899906842626, "start_id": 1125899906842625, "properties": {}}::edge
+ {"id": 1970324836974593, "label": "e2", "end_id": 1688849860263939, "start_id": 1688849860263938, "properties": {}}::edge
+ {"id": 1970324836974594, "label": "e2", "end_id": 1688849860263937, "start_id": 1688849860263938, "properties": {}}::edge
+ {"id": 2533274790395905, "label": "e3", "end_id": 2251799813685250, "start_id": 2251799813685251, "properties": {}}::edge
+ {"id": 2533274790395906, "label": "e3", "end_id": 2251799813685250, "start_id": 2251799813685249, "properties": {}}::edge
+(6 rows)
+
+SELECT * FROM cypher('cypher_match', $$
+ MATCH ()-[r0:e1]->() MATCH ()-[r0:e1]->() RETURN r0
+$$) AS (r0 agtype);
+ r0
+---------------------------------------------------------------------------------------------------------------------------
+ {"id": 1407374883553281, "label": "e1", "end_id": 1125899906842627, "start_id": 1125899906842626, "properties": {}}::edge
+ {"id": 1407374883553282, "label": "e1", "end_id": 1125899906842626, "start_id": 1125899906842625, "properties": {}}::edge
+(2 rows)
+
+SELECT * FROM cypher('cypher_match', $$
+ MATCH ()-[r0:e2]->() MATCH ()-[r0:e2]->() RETURN r0
+$$) AS (r0 agtype);
+ r0
+---------------------------------------------------------------------------------------------------------------------------
+ {"id": 1970324836974593, "label": "e2", "end_id": 1688849860263939, "start_id": 1688849860263938, "properties": {}}::edge
+ {"id": 1970324836974594, "label": "e2", "end_id": 1688849860263937, "start_id": 1688849860263938, "properties": {}}::edge
+(2 rows)
+
+SELECT * FROM cypher('cypher_match', $$
+ MATCH ()-[r0:e1]->()-[r1]->() RETURN r0,r1
+$$) AS (r0 agtype, r1 agtype);
+ r0 | r1
+---------------------------------------------------------------------------------------------------------------------------+---------------------------------------------------------------------------------------------------------------------------
+ {"id": 1407374883553282, "label": "e1", "end_id": 1125899906842626, "start_id": 1125899906842625, "properties": {}}::edge | {"id": 1407374883553281, "label": "e1", "end_id": 1125899906842627, "start_id": 1125899906842626, "properties": {}}::edge
+(1 row)
+
+SELECT * FROM cypher('cypher_match', $$
+ MATCH p0=()-[:e1]->() MATCH p1=()-[:e2]->() RETURN p1
+$$) AS (p1 agtype);
+ p1
+------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
+ [{"id": 1688849860263938, "label": "v2", "properties": {"id": "middle"}}::vertex, {"id": 1970324836974594, "label": "e2", "end_id": 1688849860263937, "start_id": 1688849860263938, "properties": {}}::edge, {"id": 1688849860263937, "label": "v2", "properties": {"id": "initial"}}::vertex]::path
+ [{"id": 1688849860263938, "label": "v2", "properties": {"id": "middle"}}::vertex, {"id": 1970324836974594, "label": "e2", "end_id": 1688849860263937, "start_id": 1688849860263938, "properties": {}}::edge, {"id": 1688849860263937, "label": "v2", "properties": {"id": "initial"}}::vertex]::path
+ [{"id": 1688849860263938, "label": "v2", "properties": {"id": "middle"}}::vertex, {"id": 1970324836974593, "label": "e2", "end_id": 1688849860263939, "start_id": 1688849860263938, "properties": {}}::edge, {"id": 1688849860263939, "label": "v2", "properties": {"id": "end"}}::vertex]::path
+ [{"id": 1688849860263938, "label": "v2", "properties": {"id": "middle"}}::vertex, {"id": 1970324836974593, "label": "e2", "end_id": 1688849860263939, "start_id": 1688849860263938, "properties": {}}::edge, {"id": 1688849860263939, "label": "v2", "properties": {"id": "end"}}::vertex]::path
+(4 rows)
+
+SELECT * FROM cypher('cypher_match', $$
+ MATCH ()-[r0:e1]->()-[r1]->() MATCH ()-[r0:e1]->()-[r1]->() RETURN r0,r1
+$$) AS (r0 agtype, r1 agtype);
+ r0 | r1
+---------------------------------------------------------------------------------------------------------------------------+---------------------------------------------------------------------------------------------------------------------------
+ {"id": 1407374883553282, "label": "e1", "end_id": 1125899906842626, "start_id": 1125899906842625, "properties": {}}::edge | {"id": 1407374883553281, "label": "e1", "end_id": 1125899906842627, "start_id": 1125899906842626, "properties": {}}::edge
+(1 row)
+
+SELECT * FROM cypher('cypher_match', $$
+ MATCH ()-[]->() MATCH ()-[r1:e2]->() RETURN r1
+$$) AS (r1 agtype);
+ r1
+---------------------------------------------------------------------------------------------------------------------------
+ {"id": 1970324836974593, "label": "e2", "end_id": 1688849860263939, "start_id": 1688849860263938, "properties": {}}::edge
+ {"id": 1970324836974594, "label": "e2", "end_id": 1688849860263937, "start_id": 1688849860263938, "properties": {}}::edge
+ {"id": 1970324836974593, "label": "e2", "end_id": 1688849860263939, "start_id": 1688849860263938, "properties": {}}::edge
+ {"id": 1970324836974594, "label": "e2", "end_id": 1688849860263937, "start_id": 1688849860263938, "properties": {}}::edge
+ {"id": 1970324836974593, "label": "e2", "end_id": 1688849860263939, "start_id": 1688849860263938, "properties": {}}::edge
+ {"id": 1970324836974594, "label": "e2", "end_id": 1688849860263937, "start_id": 1688849860263938, "properties": {}}::edge
+ {"id": 1970324836974593, "label": "e2", "end_id": 1688849860263939, "start_id": 1688849860263938, "properties": {}}::edge
+ {"id": 1970324836974594, "label": "e2", "end_id": 1688849860263937, "start_id": 1688849860263938, "properties": {}}::edge
+ {"id": 1970324836974593, "label": "e2", "end_id": 1688849860263939, "start_id": 1688849860263938, "properties": {}}::edge
+ {"id": 1970324836974594, "label": "e2", "end_id": 1688849860263937, "start_id": 1688849860263938, "properties": {}}::edge
+ {"id": 1970324836974593, "label": "e2", "end_id": 1688849860263939, "start_id": 1688849860263938, "properties": {}}::edge
+ {"id": 1970324836974594, "label": "e2", "end_id": 1688849860263937, "start_id": 1688849860263938, "properties": {}}::edge
+(12 rows)
+
+SELECT * FROM cypher('cypher_match', $$
+ MATCH ()-[r0:e1]->() MATCH ()-[r1:e2]->() RETURN r0,r1
+$$) AS (r0 agtype, r1 agtype);
+ r0 | r1
+---------------------------------------------------------------------------------------------------------------------------+---------------------------------------------------------------------------------------------------------------------------
+ {"id": 1407374883553281, "label": "e1", "end_id": 1125899906842627, "start_id": 1125899906842626, "properties": {}}::edge | {"id": 1970324836974593, "label": "e2", "end_id": 1688849860263939, "start_id": 1688849860263938, "properties": {}}::edge
+ {"id": 1407374883553281, "label": "e1", "end_id": 1125899906842627, "start_id": 1125899906842626, "properties": {}}::edge | {"id": 1970324836974594, "label": "e2", "end_id": 1688849860263937, "start_id": 1688849860263938, "properties": {}}::edge
+ {"id": 1407374883553282, "label": "e1", "end_id": 1125899906842626, "start_id": 1125899906842625, "properties": {}}::edge | {"id": 1970324836974593, "label": "e2", "end_id": 1688849860263939, "start_id": 1688849860263938, "properties": {}}::edge
+ {"id": 1407374883553282, "label": "e1", "end_id": 1125899906842626, "start_id": 1125899906842625, "properties": {}}::edge | {"id": 1970324836974594, "label": "e2", "end_id": 1688849860263937, "start_id": 1688849860263938, "properties": {}}::edge
+(4 rows)
+
+-- valid variable reuse for vertex labels across clauses
+SELECT * FROM cypher('cypher_match', $$
+ MATCH (r1:invalid), (r1:invalid) return r1
+$$) AS (r1 agtype);
+ r1
+----
+(0 rows)
+
+SELECT * FROM cypher('cypher_match', $$
+ MATCH (r1), (r1) return r1
+$$) AS (r1 agtype);
+ r1
+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------
+ {"id": 281474976710657, "label": "", "properties": {"int_key": 1, "map_key": {"key": "value"}, "list_key": [1, 2, 3], "float_key": 3.14, "string_key": "test"}}::vertex
+ {"id": 281474976710658, "label": "", "properties": {"lst": [1, null, 3.14, "string", {"key": "value"}, []]}}::vertex
+ {"id": 844424930131969, "label": "v", "properties": {}}::vertex
+ {"id": 844424930131970, "label": "v", "properties": {"i": 0}}::vertex
+ {"id": 844424930131971, "label": "v", "properties": {"i": 1}}::vertex
+ {"id": 1125899906842625, "label": "v1", "properties": {"id": "initial"}}::vertex
+ {"id": 1125899906842626, "label": "v1", "properties": {"id": "middle"}}::vertex
+ {"id": 1125899906842627, "label": "v1", "properties": {"id": "end"}}::vertex
+ {"id": 1688849860263937, "label": "v2", "properties": {"id": "initial"}}::vertex
+ {"id": 1688849860263938, "label": "v2", "properties": {"id": "middle"}}::vertex
+ {"id": 1688849860263939, "label": "v2", "properties": {"id": "end"}}::vertex
+ {"id": 2251799813685249, "label": "v3", "properties": {"id": "initial"}}::vertex
+ {"id": 2251799813685250, "label": "v3", "properties": {"id": "middle"}}::vertex
+ {"id": 2251799813685251, "label": "v3", "properties": {"id": "end"}}::vertex
+(14 rows)
+
+SELECT * FROM cypher('cypher_match', $$
+ MATCH (r1:invalid), (r1) return r1
+$$) AS (r1 agtype);
+ r1
+----
+(0 rows)
+
+SELECT * FROM cypher('cypher_match', $$
+ MATCH (r1:invalid), (r1), (r1), (r1:invalid) return r1
+$$) AS (r1 agtype);
+ r1
+----
+(0 rows)
+
+SELECT * FROM cypher('cypher_match', $$
+ MATCH (r1:invalid)-[]->(r1)-[]->(r1:invalid)-[]->(r1) return r1
+$$) AS (r1 agtype);
+ r1
+----
+(0 rows)
+
+SELECT * FROM cypher('cypher_match', $$
+ MATCH (r1:invalid)-[]->()-[]->()-[]->(r1:invalid) return r1
+$$) AS (r1 agtype);
+ r1
+----
+(0 rows)
+
+SELECT * FROM cypher('cypher_match', $$
+ MATCH ()-[r0:e1]->()-[r1]->() MATCH ()-[r0:e1]->()-[r0]->() RETURN r0
+$$) AS (r0 agtype);
+ r0
+----
+(0 rows)
+
+SELECT * FROM cypher('cypher_match', $$
+ MATCH ()-[r0:e1]->() MATCH ()-[r0]->() RETURN r0
+$$) AS (r0 agtype);
+ r0
+---------------------------------------------------------------------------------------------------------------------------
+ {"id": 1407374883553281, "label": "e1", "end_id": 1125899906842627, "start_id": 1125899906842626, "properties": {}}::edge
+ {"id": 1407374883553282, "label": "e1", "end_id": 1125899906842626, "start_id": 1125899906842625, "properties": {}}::edge
+(2 rows)
+
+-- invalid variable reuse for vertex
+SELECT * FROM cypher('cypher_match', $$
+ MATCH (r1:invalid)-[]->(r1)-[]->(r1)-[]->(r1:invalids) return r1
+$$) AS (r1 agtype);
+ERROR: multiple labels for variable 'r1' are not supported
+LINE 2: ... MATCH (r1:invalid)-[]->(r1)-[]->(r1)-[]->(r1:invalid...
+ ^
+SELECT * FROM cypher('cypher_match', $$
+ MATCH (r1:invalid)-[]->(r1)-[]->(r1)-[]->(r1)-[r1]->() return r1
+$$) AS (r1 agtype);
+ERROR: variable 'r1' is for a vertex
+LINE 2: ... MATCH (r1:invalid)-[]->(r1)-[]->(r1)-[]->(r1)-[r1]->() r...
+ ^
+-- invalid variable reuse for labels across clauses
+SELECT * FROM cypher('cypher_match', $$
+ MATCH (r1:e1), (r1:e2) return r1
+$$) AS (r1 agtype);
+ERROR: multiple labels for variable 'r1' are not supported
+LINE 2: MATCH (r1:e1), (r1:e2) return r1
+ ^
+SELECT * FROM cypher('cypher_match', $$
+ MATCH (r1:invalid), (r1:e2) return r1
+$$) AS (r1 agtype);
+ERROR: multiple labels for variable 'r1' are not supported
+LINE 2: MATCH (r1:invalid), (r1:e2) return r1
+ ^
+SELECT * FROM cypher('cypher_match', $$
+ MATCH (r1:e1), (r1:invalid) return r1
+$$) AS (r1 agtype);
+ERROR: multiple labels for variable 'r1' are not supported
+LINE 2: MATCH (r1:e1), (r1:invalid) return r1
+ ^
+SELECT * FROM cypher('cypher_match', $$
+ MATCH (r1:e1), (r1), (r1:invalid) return r1
+$$) AS (r1 agtype);
+ERROR: multiple labels for variable 'r1' are not supported
+LINE 2: MATCH (r1:e1), (r1), (r1:invalid) return r1
+ ^
+SELECT * FROM cypher('cypher_match', $$
+ MATCH (r0)-[r0]->() MATCH ()-[]->() RETURN r0
+$$) AS (r0 agtype);
+ERROR: variable 'r0' is for a vertex
+LINE 2: MATCH (r0)-[r0]->() MATCH ()-[]->() RETURN r0
+ ^
+SELECT * FROM cypher('cypher_match', $$
+ MATCH (r0)-[]->() MATCH ()-[r0]->() RETURN r0
+$$) AS (r0 agtype);
+ERROR: variable 'r0' is for a vertex
+LINE 2: MATCH (r0)-[]->() MATCH ()-[r0]->() RETURN r0
+ ^
+SELECT * FROM cypher('cypher_match', $$
+ MATCH ()-[r0]->() MATCH ()-[]->(r0) RETURN r0
+$$) AS (r0 agtype);
+ERROR: variable 'r0' is for an edge
+LINE 2: MATCH ()-[r0]->() MATCH ()-[]->(r0) RETURN r0
+ ^
+SELECT * FROM cypher('cypher_match', $$
+ MATCH ()-[r0:e1]->() MATCH ()-[r0:e2]->() RETURN r0
+$$) AS (r0 agtype);
+ERROR: multiple labels for variable 'r0' are not supported
+LINE 2: MATCH ()-[r0:e1]->() MATCH ()-[r0:e2]->() RETURN r0
+ ^
+SELECT * FROM cypher('cypher_match', $$
+ MATCH ()-[r0]->() MATCH ()-[r0:e2]->() RETURN r0
+$$) AS (r0 agtype);
+ERROR: multiple labels for variable 'r0' are not supported
+LINE 2: MATCH ()-[r0]->() MATCH ()-[r0:e2]->() RETURN r0
+ ^
+SELECT * FROM cypher('cypher_match', $$
+ MATCH ()-[r0:e1]->() MATCH ()-[r0:e2]->() RETURN r0
+$$) AS (r0 agtype);
+ERROR: multiple labels for variable 'r0' are not supported
+LINE 2: MATCH ()-[r0:e1]->() MATCH ()-[r0:e2]->() RETURN r0
+ ^
+SELECT * FROM cypher('cypher_match', $$
+ MATCH ()-[r0:e1]->()-[r0]->() MATCH ()-[r0:e2]->() RETURN r0
+$$) AS (r0 agtype);
+ERROR: duplicate edge variable 'r0' within a clause
+LINE 2: MATCH ()-[r0:e1]->()-[r0]->() MATCH ()-[r0:e2]->() R...
+ ^
+SELECT * FROM cypher('cypher_match', $$
+ MATCH ()-[r0:e1]->()-[r1]->() MATCH ()-[r1:e1]->()-[r0]->() RETURN r0
+$$) AS (r0 agtype);
+ERROR: multiple labels for variable 'r1' are not supported
+LINE 2: MATCH ()-[r0:e1]->()-[r1]->() MATCH ()-[r1:e1]->()-[...
+ ^
+-- Labels that don't exist but do match
+SELECT * FROM cypher('cypher_match', $$
+ MATCH (r0)-[r1:related]->() MATCH ()-[r1:related]->() RETURN r0
+$$) AS (r0 agtype);
+ r0
+----
+(0 rows)
+
+-- Labels that don't exist and don't match
+SELECT * FROM cypher('cypher_match', $$
+ MATCH (r0)-[r1]->() MATCH ()-[r1:related]->() RETURN r0
+$$) AS (r0 agtype);
+ERROR: multiple labels for variable 'r1' are not supported
+LINE 2: MATCH (r0)-[r1]->() MATCH ()-[r1:related]->() RETURN...
+ ^
+SELECT * FROM cypher('cypher_match', $$
+ MATCH (r0)-[r1:related]->() MATCH ()-[r1:relateds]->() RETURN r0
+$$) AS (r0 agtype);
+ERROR: multiple labels for variable 'r1' are not supported
+LINE 2: MATCH (r0)-[r1:related]->() MATCH ()-[r1:relateds]->...
+ ^
+--Valid variable reuse, although why would you want to do it this way?
SELECT * FROM cypher('cypher_match', $$
MATCH (a:v1)-[]-()-[]-(a {id:'will_not_fail'}) RETURN a
$$) AS (a agtype);
@@ -714,15 +995,6 @@ AS (u agtype, e agtype, v agtype);
{"id": 2814749767106561, "label": "loop", "properties": {"id": "initial"}}::vertex | {"id": 3096224743817217, "label": "self", "end_id": 2814749767106561, "start_id": 2814749767106561, "properties": {}}::edge | {"id": 2814749767106561, "label": "loop", "properties": {"id": "initial"}}::vertex
(1 row)
--- Exists checks for a loop. There should be none because of edge uniqueness
--- requirement.
-SELECT * FROM cypher('cypher_match',
- $$MATCH (u)-[e]->(v) WHERE EXISTS((u)-[e]->(u)-[e]->(u)) RETURN u, e, v $$)
-AS (u agtype, e agtype, v agtype);
- u | e | v
----+---+---
-(0 rows)
-
-- Multiple exists
SELECT * FROM cypher('cypher_match',
$$MATCH (u)-[e]->(v) WHERE EXISTS((u)) AND EXISTS((v)) RETURN u, e, v $$)
@@ -811,7 +1083,7 @@ SELECT * FROM cypher('cypher_match',
AS (u agtype, e agtype, v agtype);
ERROR: variable `x` does not exist
LINE 2: $$MATCH (u)-[e]->(v) WHERE EXISTS((u)-[e]->(x)) RETURN u, e...
- ^
+ ^
--
-- Tests for EXISTS(property)
--
@@ -1659,7 +1931,9 @@ SELECT * FROM cypher('cypher_match', $$
-----------------------------------------------------------------------------------------------------------------------------------------------------------------
{"id": 4785074604081153, "label": "knows", "end_id": 281474976710666, "start_id": 281474976710661, "properties": {"years": 3, "relationship": "friends"}}::edge
{"id": 4785074604081153, "label": "knows", "end_id": 281474976710666, "start_id": 281474976710661, "properties": {"years": 3, "relationship": "friends"}}::edge
-(2 rows)
+ {"id": 4785074604081153, "label": "knows", "end_id": 281474976710666, "start_id": 281474976710661, "properties": {"years": 3, "relationship": "friends"}}::edge
+ {"id": 4785074604081153, "label": "knows", "end_id": 281474976710666, "start_id": 281474976710661, "properties": {"years": 3, "relationship": "friends"}}::edge
+(4 rows)
SELECT * FROM cypher('cypher_match', $$
MATCH ()-[r {years:3, relationship: "friends"}]-() RETURN r $$) as (r agtype);
@@ -1675,7 +1949,9 @@ SELECT * FROM cypher('cypher_match', $$
-----------------------------------------------------------------------------------------------------------------------------------------------------------------
{"id": 4785074604081153, "label": "knows", "end_id": 281474976710666, "start_id": 281474976710661, "properties": {"years": 3, "relationship": "friends"}}::edge
{"id": 4785074604081153, "label": "knows", "end_id": 281474976710666, "start_id": 281474976710661, "properties": {"years": 3, "relationship": "friends"}}::edge
-(2 rows)
+ {"id": 4785074604081153, "label": "knows", "end_id": 281474976710666, "start_id": 281474976710661, "properties": {"years": 3, "relationship": "friends"}}::edge
+ {"id": 4785074604081153, "label": "knows", "end_id": 281474976710666, "start_id": 281474976710661, "properties": {"years": 3, "relationship": "friends"}}::edge
+(4 rows)
--mismatch year #, should return nothing
SELECT * FROM cypher('cypher_match', $$
@@ -1690,7 +1966,9 @@ SELECT * FROM cypher('cypher_match', $$
-----------------------------------------------------------------------------------------------------------------------------------------------------------------
{"id": 4785074604081154, "label": "knows", "end_id": 281474976710666, "start_id": 281474976710659, "properties": {"years": 4, "relationship": "enemies"}}::edge
{"id": 4785074604081154, "label": "knows", "end_id": 281474976710666, "start_id": 281474976710659, "properties": {"years": 4, "relationship": "enemies"}}::edge
-(2 rows)
+ {"id": 4785074604081154, "label": "knows", "end_id": 281474976710666, "start_id": 281474976710659, "properties": {"years": 4, "relationship": "enemies"}}::edge
+ {"id": 4785074604081154, "label": "knows", "end_id": 281474976710666, "start_id": 281474976710659, "properties": {"years": 4, "relationship": "enemies"}}::edge
+(4 rows)
SELECT * FROM cypher('cypher_match', $$
MATCH ()-[r {relationship:"enemies"}]-() MATCH ()-[r {relationship:"friends"}]-() RETURN r $$) as (r agtype);
@@ -1758,14 +2036,14 @@ SELECT * FROM cypher('cypher_match', $$ MATCH p=(a {name: "John"})-[]->()-[]->(a
(1 row)
-- these are illegal and should fail
-SELECT * FROM cypher('cypher_match', $$ MATCH p=(a)-[b]->()-[b]->(a) RETURN p $$)as (p agtype);
-ERROR: duplicate edge variable 'b' within a clause
-LINE 1: ...ROM cypher('cypher_match', $$ MATCH p=(a)-[b]->()-[b]->(a) R...
- ^
SELECT * FROM cypher('cypher_match', $$ MATCH p=(a)-[b]->()-[b:knows]->(a) RETURN p $$)as (p agtype);
ERROR: duplicate edge variable 'b' within a clause
LINE 1: ...ROM cypher('cypher_match', $$ MATCH p=(a)-[b]->()-[b:knows]-...
^
+SELECT * FROM cypher('cypher_match', $$ MATCH p=(a)-[b]->()-[b]->(a) RETURN p $$)as (p agtype);
+ERROR: duplicate edge variable 'b' within a clause
+LINE 1: ...ROM cypher('cypher_match', $$ MATCH p=(a)-[b]->()-[b]->(a) R...
+ ^
SELECT * FROM cypher('cypher_match', $$ MATCH p=(a)-[b:knows]->()-[b:knows]->(a) RETURN p $$)as (p agtype);
ERROR: duplicate edge variable 'b' within a clause
LINE 1: ...pher('cypher_match', $$ MATCH p=(a)-[b:knows]->()-[b:knows]-...
@@ -1774,6 +2052,189 @@ SELECT * FROM cypher('cypher_match', $$ MATCH p=(a)-[b:knows]->()-[b]->(a) RETUR
ERROR: duplicate edge variable 'b' within a clause
LINE 1: ...pher('cypher_match', $$ MATCH p=(a)-[b:knows]->()-[b]->(a) R...
^
+SELECT * FROM cypher('cypher_match', $$ MATCH p=(p) RETURN p $$)as (p agtype);
+ERROR: variable "p" is for a path
+LINE 1: SELECT * FROM cypher('cypher_match', $$ MATCH p=(p) RETURN p...
+ ^
+SELECT * FROM cypher('cypher_match', $$ MATCH p=(p)-[]->() RETURN p $$)as (p agtype);
+ERROR: variable "p" is for a path
+LINE 1: SELECT * FROM cypher('cypher_match', $$ MATCH p=(p)-[]->() R...
+ ^
+SELECT * FROM cypher('cypher_match', $$ MATCH p=()-[p]->() RETURN p $$)as (p agtype);
+ERROR: variable "p" is for a path
+LINE 1: ...ELECT * FROM cypher('cypher_match', $$ MATCH p=()-[p]->() RE...
+ ^
+SELECT * FROM cypher('cypher_match', $$ MATCH p=() MATCH (p) RETURN p $$)as (p agtype);
+ERROR: variable 'p' already exists
+LINE 1: ... FROM cypher('cypher_match', $$ MATCH p=() MATCH (p) RETURN ...
+ ^
+SELECT * FROM cypher('cypher_match', $$ MATCH p=() MATCH (p)-[]->() RETURN p $$)as (p agtype);
+ERROR: variable 'p' already exists
+LINE 1: ... FROM cypher('cypher_match', $$ MATCH p=() MATCH (p)-[]->() ...
+ ^
+SELECT * FROM cypher('cypher_match', $$ MATCH p=() MATCH ()-[p]->() RETURN p $$)as (p agtype);
+ERROR: variable 'p' already exists
+LINE 1: ...ROM cypher('cypher_match', $$ MATCH p=() MATCH ()-[p]->() RE...
+ ^
+SELECT * FROM cypher('cypher_match', $$ MATCH (p) MATCH p=() RETURN p $$)as (p agtype);
+ERROR: variable "p" already exists
+LINE 1: ... * FROM cypher('cypher_match', $$ MATCH (p) MATCH p=() RETUR...
+ ^
+SELECT * FROM cypher('cypher_match', $$ MATCH ()-[p]->() MATCH p=() RETURN p $$)as (p agtype);
+ERROR: variable "p" already exists
+LINE 1: ... cypher('cypher_match', $$ MATCH ()-[p]->() MATCH p=() RETUR...
+ ^
+SELECT * FROM cypher('cypher_match', $$ MATCH ()-[p *]-()-[p]-() RETURN 0 $$)as (p agtype);
+ERROR: variable 'p' is for a VLE edge
+LINE 1: ... FROM cypher('cypher_match', $$ MATCH ()-[p *]-()-[p]-() RET...
+ ^
+SELECT * FROM cypher('cypher_match', $$ CREATE (p) WITH p MATCH p=() RETURN p $$)as (p agtype);
+ERROR: variable "p" already exists
+LINE 1: ...cypher('cypher_match', $$ CREATE (p) WITH p MATCH p=() RETUR...
+ ^
+SELECT * FROM cypher('cypher_match', $$ CREATE p=() WITH p MATCH (p) RETURN p $$)as (p agtype);
+ERROR: variable 'p' already exists
+LINE 1: ...pher('cypher_match', $$ CREATE p=() WITH p MATCH (p) RETURN ...
+ ^
+SELECT * FROM cypher('cypher_match', $$ CREATE ()-[p:knows]->() WITH p MATCH p=() RETURN p $$)as (p agtype);
+ERROR: variable "p" already exists
+LINE 1: ...r_match', $$ CREATE ()-[p:knows]->() WITH p MATCH p=() RETUR...
+ ^
+SELECT * FROM cypher('cypher_match', $$ CREATE p=() WITH p MATCH ()-[p]->() RETURN p $$)as (p agtype);
+ERROR: variable 'p' already exists
+LINE 1: ...er('cypher_match', $$ CREATE p=() WITH p MATCH ()-[p]->() RE...
+ ^
+--
+-- Default alias check (issue #883)
+--
+SELECT * FROM cypher('cypher_match', $$ MATCH (_) RETURN _ $$) as (a agtype);
+ a
+------------------------------------------------------------------------------------------
+ {"id": 281474976710662, "label": "", "properties": {"i": 1, "j": 2, "k": 3}}::vertex
+ {"id": 281474976710663, "label": "", "properties": {"i": 1, "j": 3}}::vertex
+ {"id": 281474976710664, "label": "", "properties": {"i": 2, "k": 3}}::vertex
+ {"id": 281474976710665, "label": "", "properties": {"age": 4}}::vertex
+ {"id": 281474976710666, "label": "", "properties": {"age": 6}}::vertex
+ {"id": 281474976710660, "label": "", "properties": {"age": 4, "name": "F"}}::vertex
+ {"id": 281474976710661, "label": "", "properties": {"age": 4, "name": "T"}}::vertex
+ {"id": 281474976710659, "label": "", "properties": {"age": 3, "name": "orphan"}}::vertex
+ {"id": 281474976710667, "label": "", "properties": {"name": "Dave"}}::vertex
+ {"id": 281474976710668, "label": "", "properties": {"name": "John"}}::vertex
+(10 rows)
+
+SELECT * FROM cypher('cypher_match', $$ MATCH () MATCH (_{name: "Dave"}) RETURN 0 $$) as (a agtype);
+ a
+---
+ 0
+(1 row)
+
+SELECT * FROM cypher('cypher_match', $$ MATCH () MATCH (_{name: "Dave"}) RETURN _ $$) as (a agtype);
+ a
+------------------------------------------------------------------------------
+ {"id": 281474976710667, "label": "", "properties": {"name": "Dave"}}::vertex
+(1 row)
+
+SELECT * FROM cypher('cypher_match', $$ MATCH (my_age_default_{name: "Dave"}) RETURN my_age_default_$$) as (a agtype);
+ a
+------------------------------------------------------------------------------
+ {"id": 281474976710667, "label": "", "properties": {"name": "Dave"}}::vertex
+(1 row)
+
+SELECT * FROM cypher('cypher_match', $$ MATCH () MATCH (my_age_default_{name: "Dave"}) RETURN my_age_default_$$) as (a agtype);
+ a
+------------------------------------------------------------------------------
+ {"id": 281474976710667, "label": "", "properties": {"name": "Dave"}}::vertex
+(1 row)
+
+-- these should fail as they are prefixed with _age_default_ which is only for internal use
+SELECT * FROM cypher('cypher_match', $$ MATCH (_age_default_) RETURN _age_default_ $$) as (a agtype);
+ERROR: _age_default_ is only for internal use
+LINE 1: SELECT * FROM cypher('cypher_match', $$ MATCH (_age_default_...
+ ^
+SELECT * FROM cypher('cypher_match', $$ MATCH (_age_default_a) RETURN _age_default_a $$) as (a agtype);
+ERROR: _age_default_ is only for internal use
+LINE 1: SELECT * FROM cypher('cypher_match', $$ MATCH (_age_default_...
+ ^
+SELECT * FROM cypher('cypher_match', $$ MATCH (_age_default_whatever) RETURN 0 $$) as (a agtype);
+ERROR: _age_default_ is only for internal use
+LINE 1: SELECT * FROM cypher('cypher_match', $$ MATCH (_age_default_...
+ ^
+-- issue 876
+SELECT * FROM cypher('cypher_match', $$ MATCH ({name: "Dave"}) MATCH ({name: "Dave"}) MATCH ({name: "Dave"}) RETURN 0 $$) as (a agtype);
+ a
+---
+ 0
+(1 row)
+
+SELECT * FROM cypher('cypher_match', $$MATCH ({n0:0}) MATCH ()-[]->() MATCH ({n1:0})-[]-() RETURN 0 AS n2$$) as (a agtype);
+ a
+---
+(0 rows)
+
+--
+-- self referencing property constraints (issue #898)
+--
+SELECT * FROM cypher('cypher_match', $$ MATCH (a {name:a.name}) RETURN a $$) as (a agtype);
+ a
+------------------------------------------------------------------------------------------
+ {"id": 281474976710660, "label": "", "properties": {"age": 4, "name": "F"}}::vertex
+ {"id": 281474976710661, "label": "", "properties": {"age": 4, "name": "T"}}::vertex
+ {"id": 281474976710659, "label": "", "properties": {"age": 3, "name": "orphan"}}::vertex
+ {"id": 281474976710667, "label": "", "properties": {"name": "Dave"}}::vertex
+ {"id": 281474976710668, "label": "", "properties": {"name": "John"}}::vertex
+(5 rows)
+
+SELECT * FROM cypher('cypher_match', $$ MATCH (a {name:a.name, age:a.age}) RETURN a $$) as (a agtype);
+ a
+------------------------------------------------------------------------------------------
+ {"id": 281474976710660, "label": "", "properties": {"age": 4, "name": "F"}}::vertex
+ {"id": 281474976710661, "label": "", "properties": {"age": 4, "name": "T"}}::vertex
+ {"id": 281474976710659, "label": "", "properties": {"age": 3, "name": "orphan"}}::vertex
+(3 rows)
+
+SELECT * FROM cypher('cypher_match', $$ MATCH (a {name:a.name}) MATCH (a {age:a.age}) RETURN a $$) as (a agtype);
+ a
+------------------------------------------------------------------------------------------
+ {"id": 281474976710660, "label": "", "properties": {"age": 4, "name": "F"}}::vertex
+ {"id": 281474976710661, "label": "", "properties": {"age": 4, "name": "T"}}::vertex
+ {"id": 281474976710659, "label": "", "properties": {"age": 3, "name": "orphan"}}::vertex
+(3 rows)
+
+SELECT * FROM cypher('cypher_match', $$ MATCH p=(a)-[u {relationship: u.relationship}]->(b) RETURN p $$) as (a agtype);
+ a
+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
+ [{"id": 281474976710661, "label": "", "properties": {"age": 4, "name": "T"}}::vertex, {"id": 4785074604081153, "label": "knows", "end_id": 281474976710666, "start_id": 281474976710661, "properties": {"years": 3, "relationship": "friends"}}::edge, {"id": 281474976710666, "label": "", "properties": {"age": 6}}::vertex]::path
+ [{"id": 281474976710659, "label": "", "properties": {"age": 3, "name": "orphan"}}::vertex, {"id": 4785074604081154, "label": "knows", "end_id": 281474976710666, "start_id": 281474976710659, "properties": {"years": 4, "relationship": "enemies"}}::edge, {"id": 281474976710666, "label": "", "properties": {"age": 6}}::vertex]::path
+(2 rows)
+
+SELECT * FROM cypher('cypher_match', $$ MATCH p=(a)-[u {relationship: u.relationship, years: u.years}]->(b) RETURN p $$) as (a agtype);
+ a
+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
+ [{"id": 281474976710661, "label": "", "properties": {"age": 4, "name": "T"}}::vertex, {"id": 4785074604081153, "label": "knows", "end_id": 281474976710666, "start_id": 281474976710661, "properties": {"years": 3, "relationship": "friends"}}::edge, {"id": 281474976710666, "label": "", "properties": {"age": 6}}::vertex]::path
+ [{"id": 281474976710659, "label": "", "properties": {"age": 3, "name": "orphan"}}::vertex, {"id": 4785074604081154, "label": "knows", "end_id": 281474976710666, "start_id": 281474976710659, "properties": {"years": 4, "relationship": "enemies"}}::edge, {"id": 281474976710666, "label": "", "properties": {"age": 6}}::vertex]::path
+(2 rows)
+
+SELECT * FROM cypher('cypher_match', $$ MATCH p=(a {name:a.name})-[u {relationship: u.relationship}]->(b {age:b.age}) RETURN p $$) as (a agtype);
+ a
+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
+ [{"id": 281474976710661, "label": "", "properties": {"age": 4, "name": "T"}}::vertex, {"id": 4785074604081153, "label": "knows", "end_id": 281474976710666, "start_id": 281474976710661, "properties": {"years": 3, "relationship": "friends"}}::edge, {"id": 281474976710666, "label": "", "properties": {"age": 6}}::vertex]::path
+ [{"id": 281474976710659, "label": "", "properties": {"age": 3, "name": "orphan"}}::vertex, {"id": 4785074604081154, "label": "knows", "end_id": 281474976710666, "start_id": 281474976710659, "properties": {"years": 4, "relationship": "enemies"}}::edge, {"id": 281474976710666, "label": "", "properties": {"age": 6}}::vertex]::path
+(2 rows)
+
+SELECT * FROM cypher('cypher_match', $$ CREATE () WITH * MATCH (x{n0:x.n1}) RETURN 0 $$) as (a agtype);
+ a
+---
+(0 rows)
+
+-- these should fail due to multiple labels for a variable
+SELECT * FROM cypher('cypher_match', $$ MATCH p=(x)-[]->(x:R) RETURN p, x $$) AS (p agtype, x agtype);
+ERROR: multiple labels for variable 'x' are not supported
+LINE 1: ...* FROM cypher('cypher_match', $$ MATCH p=(x)-[]->(x:R) RETUR...
+ ^
+SELECT * FROM cypher('cypher_match', $$ MATCH p=(x:r)-[]->(x:R) RETURN p, x $$) AS (p agtype, x agtype);
+ERROR: multiple labels for variable 'x' are not supported
+LINE 1: ...FROM cypher('cypher_match', $$ MATCH p=(x:r)-[]->(x:R) RETUR...
+ ^
--
-- Clean up
--
diff --git a/regress/expected/cypher_merge.out b/regress/expected/cypher_merge.out
index 988dff3c2..4bd6e1bb8 100644
--- a/regress/expected/cypher_merge.out
+++ b/regress/expected/cypher_merge.out
@@ -32,16 +32,16 @@ NOTICE: graph "cypher_merge" has been created
* test 1: Single MERGE Clause, path doesn't exist
*/
--test query
-SELECT * FROM cypher('cypher_merge', $$MERGE (n {i: "Hello Merge"})$$) AS (a agtype);
+SELECT * FROM cypher('cypher_merge', $$MERGE (n {i: "Hello Merge", j: (null IS NULL), k: (null IS NOT NULL)})$$) AS (a agtype);
a
---
(0 rows)
--validate
SELECT * FROM cypher('cypher_merge', $$MATCH (n) RETURN n$$) AS (n agtype);
- n
-----------------------------------------------------------------------------------
- {"id": 281474976710657, "label": "", "properties": {"i": "Hello Merge"}}::vertex
+ n
+---------------------------------------------------------------------------------------------------------
+ {"id": 281474976710657, "label": "", "properties": {"i": "Hello Merge", "j": true, "k": false}}::vertex
(1 row)
--clean up
@@ -54,7 +54,7 @@ SELECT * FROM cypher('cypher_merge', $$MATCH (n) DETACH DELETE n $$) AS (a agtyp
* test 2: Single MERGE Clause, path exists
*/
--data setup
-SELECT * FROM cypher('cypher_merge', $$CREATE ({i: "Hello Merge"}) $$) AS (a agtype);
+SELECT * FROM cypher('cypher_merge', $$CREATE ({i: "Hello Merge", j: (null IS NULL)}) $$) AS (a agtype);
a
---
(0 rows)
@@ -65,11 +65,16 @@ SELECT * FROM cypher('cypher_merge', $$MERGE ({i: "Hello Merge"})$$) AS (a agtyp
---
(0 rows)
+SELECT * FROM cypher('cypher_merge', $$MERGE ({j: (null IS NULL)})$$) AS (a agtype);
+ a
+---
+(0 rows)
+
--validate
SELECT * FROM cypher('cypher_merge', $$MATCH (n) RETURN n$$) AS (n agtype);
- n
-----------------------------------------------------------------------------------
- {"id": 281474976710658, "label": "", "properties": {"i": "Hello Merge"}}::vertex
+ n
+---------------------------------------------------------------------------------------------
+ {"id": 281474976710658, "label": "", "properties": {"i": "Hello Merge", "j": true}}::vertex
(1 row)
--clean up
@@ -127,7 +132,7 @@ SELECT * FROM cypher('cypher_merge', $$MATCH (n) DETACH DELETE n $$) AS (a agtyp
(0 rows)
/*
- * test 5: Prev clause has results, path does not exist (differnt property name)
+ * test 5: Prev clause has results, path does not exist (different property name)
*/
--data setup
SELECT * FROM cypher('cypher_merge', $$CREATE ({i: "Hello Merge"}) $$) AS (a agtype);
@@ -366,12 +371,19 @@ SELECT * FROM cypher('cypher_merge', $$MERGE ()-[:e]->()-[:e]->()$$) AS (a agtyp
--validate created correctly
--Returns 3. One for the data setup and 2 for the longer path in MERGE
-SELECT count(*) FROM cypher('cypher_merge', $$MATCH p=()-[e:e]->() RETURN p$$) AS (p agtype)
+SELECT count(*) FROM cypher('cypher_merge', $$MATCH p=()-[e:e]->() RETURN p$$) AS (p agtype);
+ count
+-------
+ 3
+(1 row)
+
-- Returns 1, the path created in MERGE
SELECT count(*) FROM cypher('cypher_merge', $$MATCH p=()-[:e]->()-[]->() RETURN p$$) AS (p agtype);
-ERROR: syntax error at or near "SELECT"
-LINE 3: SELECT count(*) FROM cypher('cypher_merge', $$MATCH p=()-[:e...
- ^
+ count
+-------
+ 1
+(1 row)
+
-- 5 vertices total should have been created
SELECT count(*) FROM cypher('cypher_merge', $$MATCH (n) RETURN n$$) AS (n agtype);
count
@@ -386,7 +398,7 @@ SELECT * FROM cypher('cypher_merge', $$MATCH (n) DETACH DELETE n $$) AS (a agtyp
(0 rows)
/*
- * test 13: edge doesn't exists (differnt label), using MATCH
+ * test 13: edge doesn't exists (different label), using MATCH
*/
-- setup
SELECT * FROM cypher('cypher_merge', $$CREATE ()-[:e]->() $$) AS (a agtype);
@@ -491,17 +503,20 @@ SELECT * FROM cypher('cypher_merge', $$MATCH (n) DETACH DELETE n $$) AS (a agtyp
/*
* test 17:
- * XXX: Incorrect Output. To FIX
*/
--test query
SELECT * FROM cypher('cypher_merge', $$CREATE (n) MERGE (n)-[:e]->() $$) AS (a agtype);
-ERROR: end_id() argument must resolve to a scalar value
---validate created correctly
-SELECT * FROM cypher('cypher_merge', $$MATCH p=()-[:e]->() RETURN p$$) AS (p agtype);
- p
+ a
---
(0 rows)
+--validate created correctly
+SELECT * FROM cypher('cypher_merge', $$MATCH p=()-[:e]->() RETURN p$$) AS (p agtype);
+ p
+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
+ [{"id": 281474976710689, "label": "", "properties": {}}::vertex, {"id": 844424930131982, "label": "e", "end_id": 281474976710690, "start_id": 281474976710689, "properties": {}}::edge, {"id": 281474976710690, "label": "", "properties": {}}::vertex]::path
+(1 row)
+
--clean up
SELECT * FROM cypher('cypher_merge', $$MATCH (n) DETACH DELETE n $$) AS (a agtype);
a
@@ -521,7 +536,7 @@ SELECT * FROM cypher('cypher_merge', $$CREATE (n {i : 1}) SET n.i = 2 MERGE ({i:
SELECT * FROM cypher('cypher_merge', $$MATCH (a) RETURN a$$) AS (a agtype);
a
----------------------------------------------------------------------
- {"id": 281474976710690, "label": "", "properties": {"i": 2}}::vertex
+ {"id": 281474976710691, "label": "", "properties": {"i": 2}}::vertex
(1 row)
--clean up
@@ -543,7 +558,7 @@ SELECT * FROM cypher('cypher_merge', $$CREATE (n {i : 1}) SET n.i = 2 WITH n as
SELECT * FROM cypher('cypher_merge', $$MATCH (a) RETURN a$$) AS (a agtype);
a
----------------------------------------------------------------------
- {"id": 281474976710691, "label": "", "properties": {"i": 2}}::vertex
+ {"id": 281474976710692, "label": "", "properties": {"i": 2}}::vertex
(1 row)
--clean up
@@ -571,7 +586,7 @@ SELECT * FROM cypher('cypher_merge', $$MATCH (n {i : 1}) SET n.i = 2 WITH n as a
SELECT * FROM cypher('cypher_merge', $$MATCH (a) RETURN a$$) AS (a agtype);
a
----------------------------------------------------------------------
- {"id": 281474976710692, "label": "", "properties": {"i": 2}}::vertex
+ {"id": 281474976710693, "label": "", "properties": {"i": 2}}::vertex
(1 row)
--clean up
@@ -596,7 +611,7 @@ ERROR: vertex assigned to variable n was deleted
SELECT * FROM cypher('cypher_merge', $$MATCH (a) RETURN a$$) AS (a agtype);
a
----------------------------------------------------------------------
- {"id": 281474976710693, "label": "", "properties": {"i": 1}}::vertex
+ {"id": 281474976710694, "label": "", "properties": {"i": 1}}::vertex
(1 row)
--clean up
@@ -674,7 +689,7 @@ SELECT * FROM cypher('cypher_merge', $$MERGE ()-[:e]-()$$) AS (a agtype);
SELECT * FROM cypher('cypher_merge', $$MATCH p=()-[]->() RETURN p$$) AS (a agtype);
a
---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
- [{"id": 281474976710694, "label": "", "properties": {}}::vertex, {"id": 844424930131982, "label": "e", "end_id": 281474976710695, "start_id": 281474976710694, "properties": {}}::edge, {"id": 281474976710695, "label": "", "properties": {}}::vertex]::path
+ [{"id": 281474976710695, "label": "", "properties": {}}::vertex, {"id": 844424930131983, "label": "e", "end_id": 281474976710696, "start_id": 281474976710695, "properties": {}}::edge, {"id": 281474976710696, "label": "", "properties": {}}::vertex]::path
(1 row)
--clean up
@@ -689,14 +704,14 @@ SELECT * FROM cypher('cypher_merge', $$MATCH (n) DETACH DELETE n $$) AS (a agtyp
SELECT * FROM cypher('cypher_merge', $$MERGE (a) RETURN a$$) AS (a agtype);
a
----------------------------------------------------------------
- {"id": 281474976710696, "label": "", "properties": {}}::vertex
+ {"id": 281474976710697, "label": "", "properties": {}}::vertex
(1 row)
--validate
SELECT * FROM cypher('cypher_merge', $$MATCH (a) RETURN a$$) AS (a agtype);
a
----------------------------------------------------------------
- {"id": 281474976710696, "label": "", "properties": {}}::vertex
+ {"id": 281474976710697, "label": "", "properties": {}}::vertex
(1 row)
--clean up
@@ -711,14 +726,14 @@ SELECT * FROM cypher('cypher_merge', $$MATCH (n) DETACH DELETE n $$) AS (a agtyp
SELECT * FROM cypher('cypher_merge', $$MERGE p=()-[:e]-() RETURN p$$) AS (a agtype);
a
---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
- [{"id": 281474976710697, "label": "", "properties": {}}::vertex, {"id": 844424930131983, "label": "e", "end_id": 281474976710698, "start_id": 281474976710697, "properties": {}}::edge, {"id": 281474976710698, "label": "", "properties": {}}::vertex]::path
+ [{"id": 281474976710698, "label": "", "properties": {}}::vertex, {"id": 844424930131984, "label": "e", "end_id": 281474976710699, "start_id": 281474976710698, "properties": {}}::edge, {"id": 281474976710699, "label": "", "properties": {}}::vertex]::path
(1 row)
--validate
SELECT * FROM cypher('cypher_merge', $$MATCH p=()-[]->() RETURN p$$) AS (a agtype);
a
---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
- [{"id": 281474976710697, "label": "", "properties": {}}::vertex, {"id": 844424930131983, "label": "e", "end_id": 281474976710698, "start_id": 281474976710697, "properties": {}}::edge, {"id": 281474976710698, "label": "", "properties": {}}::vertex]::path
+ [{"id": 281474976710698, "label": "", "properties": {}}::vertex, {"id": 844424930131984, "label": "e", "end_id": 281474976710699, "start_id": 281474976710698, "properties": {}}::edge, {"id": 281474976710699, "label": "", "properties": {}}::vertex]::path
(1 row)
--clean up
@@ -733,14 +748,14 @@ SELECT * FROM cypher('cypher_merge', $$MATCH (n) DETACH DELETE n $$) AS (a agtyp
SELECT * FROM cypher('cypher_merge', $$MERGE (a)-[:e]-(b) RETURN a$$) AS (a agtype);
a
----------------------------------------------------------------
- {"id": 281474976710699, "label": "", "properties": {}}::vertex
+ {"id": 281474976710700, "label": "", "properties": {}}::vertex
(1 row)
--validate
SELECT * FROM cypher('cypher_merge', $$MATCH p=()-[]->() RETURN p$$) AS (a agtype);
a
---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
- [{"id": 281474976710699, "label": "", "properties": {}}::vertex, {"id": 844424930131984, "label": "e", "end_id": 281474976710700, "start_id": 281474976710699, "properties": {}}::edge, {"id": 281474976710700, "label": "", "properties": {}}::vertex]::path
+ [{"id": 281474976710700, "label": "", "properties": {}}::vertex, {"id": 844424930131985, "label": "e", "end_id": 281474976710701, "start_id": 281474976710700, "properties": {}}::edge, {"id": 281474976710701, "label": "", "properties": {}}::vertex]::path
(1 row)
--clean up
@@ -755,14 +770,14 @@ SELECT * FROM cypher('cypher_merge', $$MATCH (n) DETACH DELETE n $$) AS (a agtyp
SELECT * FROM cypher('cypher_merge', $$CREATE p=()-[:e]->() RETURN p$$) AS (a agtype);
a
---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
- [{"id": 281474976710701, "label": "", "properties": {}}::vertex, {"id": 844424930131985, "label": "e", "end_id": 281474976710702, "start_id": 281474976710701, "properties": {}}::edge, {"id": 281474976710702, "label": "", "properties": {}}::vertex]::path
+ [{"id": 281474976710702, "label": "", "properties": {}}::vertex, {"id": 844424930131986, "label": "e", "end_id": 281474976710703, "start_id": 281474976710702, "properties": {}}::edge, {"id": 281474976710703, "label": "", "properties": {}}::vertex]::path
(1 row)
SELECT * FROM cypher('cypher_merge', $$MERGE p=()-[:e]-() RETURN p$$) AS (a agtype);
a
---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
- [{"id": 281474976710701, "label": "", "properties": {}}::vertex, {"id": 844424930131985, "label": "e", "end_id": 281474976710702, "start_id": 281474976710701, "properties": {}}::edge, {"id": 281474976710702, "label": "", "properties": {}}::vertex]::path
- [{"id": 281474976710702, "label": "", "properties": {}}::vertex, {"id": 844424930131985, "label": "e", "end_id": 281474976710702, "start_id": 281474976710701, "properties": {}}::edge, {"id": 281474976710701, "label": "", "properties": {}}::vertex]::path
+ [{"id": 281474976710702, "label": "", "properties": {}}::vertex, {"id": 844424930131986, "label": "e", "end_id": 281474976710703, "start_id": 281474976710702, "properties": {}}::edge, {"id": 281474976710703, "label": "", "properties": {}}::vertex]::path
+ [{"id": 281474976710703, "label": "", "properties": {}}::vertex, {"id": 844424930131986, "label": "e", "end_id": 281474976710703, "start_id": 281474976710702, "properties": {}}::edge, {"id": 281474976710702, "label": "", "properties": {}}::vertex]::path
(2 rows)
--clean up
@@ -807,7 +822,7 @@ ERROR: Existing variable m cannot be NULL in MERGE clause
SELECT * FROM cypher('cypher_merge', $$MATCH (n) RETURN n$$) AS (a agtype);
a
----------------------------------------------------------------
- {"id": 281474976710703, "label": "", "properties": {}}::vertex
+ {"id": 281474976710704, "label": "", "properties": {}}::vertex
(1 row)
--
@@ -886,6 +901,177 @@ SELECT * FROM cypher('cypher_merge', $$ MATCH (n:node) RETURN n $$) AS (n agtype
{"id": 2533274790395907, "label": "node", "properties": {"age": 23, "name": "Lisa", "gender": "Female"}}::vertex
(2 rows)
+--
+-- Complex MERGE w/wo RETURN values
+--
+-- The first one should create a path, the others should just return parts of it.
+SELECT * FROM cypher('cypher_merge', $$ MERGE ()-[:B]->(x:C)-[:E]->(x:C)<-[f:F]-(y:I) $$) AS (x agtype);
+ x
+---
+(0 rows)
+
+SELECT * FROM cypher('cypher_merge', $$ MERGE ()-[:B]->(x:C)-[:E]->(x:C)<-[f:F]-(y:I) RETURN x $$) AS (x agtype);
+ x
+------------------------------------------------------------------
+ {"id": 3096224743817217, "label": "C", "properties": {}}::vertex
+(1 row)
+
+SELECT * FROM cypher('cypher_merge', $$ MERGE p=()-[:B]->(x:C)-[:E]->(x:C)<-[f:F]-(y:I) $$) AS (p agtype);
+ p
+---
+(0 rows)
+
+SELECT * FROM cypher('cypher_merge', $$ MERGE p=()-[:B]->(x:C)-[:E]->(x:C)<-[f:F]-(y:I) RETURN p $$) AS (p agtype);
+ p
+-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
+ [{"id": 281474976710705, "label": "", "properties": {}}::vertex, {"id": 2814749767106561, "label": "B", "end_id": 3096224743817217, "start_id": 281474976710705, "properties": {}}::edge, {"id": 3096224743817217, "label": "C", "properties": {}}::vertex, {"id": 3377699720527873, "label": "E", "end_id": 3096224743817217, "start_id": 3096224743817217, "properties": {}}::edge, {"id": 3096224743817217, "label": "C", "properties": {}}::vertex, {"id": 3659174697238529, "label": "F", "end_id": 3096224743817217, "start_id": 3940649673949185, "properties": {}}::edge, {"id": 3940649673949185, "label": "I", "properties": {}}::vertex]::path
+(1 row)
+
+SELECT * FROM cypher('cypher_merge', $$ MERGE p=()-[:B]->(x:C)-[:E]->(x:C)<-[f:F]-(y:I) RETURN p $$) AS (p agtype);
+ p
+-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
+ [{"id": 281474976710705, "label": "", "properties": {}}::vertex, {"id": 2814749767106561, "label": "B", "end_id": 3096224743817217, "start_id": 281474976710705, "properties": {}}::edge, {"id": 3096224743817217, "label": "C", "properties": {}}::vertex, {"id": 3377699720527873, "label": "E", "end_id": 3096224743817217, "start_id": 3096224743817217, "properties": {}}::edge, {"id": 3096224743817217, "label": "C", "properties": {}}::vertex, {"id": 3659174697238529, "label": "F", "end_id": 3096224743817217, "start_id": 3940649673949185, "properties": {}}::edge, {"id": 3940649673949185, "label": "I", "properties": {}}::vertex]::path
+(1 row)
+
+-- This should only return 1 row, as the path should already exist.
+SELECT * FROM cypher('cypher_merge', $$ MATCH p=()-[:B]->(:C)-[:E]->(:C)<-[:F]-(:I) RETURN p $$) AS (p agtype);
+ p
+-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
+ [{"id": 281474976710705, "label": "", "properties": {}}::vertex, {"id": 2814749767106561, "label": "B", "end_id": 3096224743817217, "start_id": 281474976710705, "properties": {}}::edge, {"id": 3096224743817217, "label": "C", "properties": {}}::vertex, {"id": 3377699720527873, "label": "E", "end_id": 3096224743817217, "start_id": 3096224743817217, "properties": {}}::edge, {"id": 3096224743817217, "label": "C", "properties": {}}::vertex, {"id": 3659174697238529, "label": "F", "end_id": 3096224743817217, "start_id": 3940649673949185, "properties": {}}::edge, {"id": 3940649673949185, "label": "I", "properties": {}}::vertex]::path
+(1 row)
+
+-- test variable reuse in MERGE - the first MERGE of each group should create,
+-- the second MERGE shouldn't.
+SELECT * FROM cypher('cypher_merge', $$ MATCH p=(x:P)-[:E]->(x:P) RETURN p, x $$) AS (p agtype, x agtype);
+ p | x
+---+---
+(0 rows)
+
+SELECT * FROM cypher('cypher_merge', $$ MERGE (x:P)-[:E]->(x:P) $$) AS (x agtype);
+ x
+---
+(0 rows)
+
+SELECT * FROM cypher('cypher_merge', $$ MERGE (x:P)-[:E]->(x) $$) AS (x agtype);
+ x
+---
+(0 rows)
+
+SELECT * FROM cypher('cypher_merge', $$ MATCH p=(x:P)-[:E]->(x) RETURN p, x $$) AS (p agtype, x agtype);
+ p | x
+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+------------------------------------------------------------------
+ [{"id": 4222124650659841, "label": "P", "properties": {}}::vertex, {"id": 3377699720527874, "label": "E", "end_id": 4222124650659841, "start_id": 4222124650659841, "properties": {}}::edge, {"id": 4222124650659841, "label": "P", "properties": {}}::vertex]::path | {"id": 4222124650659841, "label": "P", "properties": {}}::vertex
+(1 row)
+
+SELECT * FROM cypher('cypher_merge', $$ MATCH p=(x:Q)-[:E]->(x:Q) RETURN p, x $$) AS (p agtype, x agtype);
+ p | x
+---+---
+(0 rows)
+
+SELECT * FROM cypher('cypher_merge', $$ MERGE (x:Q)-[:E]->(x) $$) AS (x agtype);
+ x
+---
+(0 rows)
+
+SELECT * FROM cypher('cypher_merge', $$ MERGE (x:Q)-[:E]->(x:Q) $$) AS (x agtype);
+ x
+---
+(0 rows)
+
+SELECT * FROM cypher('cypher_merge', $$ MATCH p=(x:Q)-[:E]->(x) RETURN p, x $$) AS (p agtype, x agtype);
+ p | x
+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+------------------------------------------------------------------
+ [{"id": 4503599627370497, "label": "Q", "properties": {}}::vertex, {"id": 3377699720527875, "label": "E", "end_id": 4503599627370497, "start_id": 4503599627370497, "properties": {}}::edge, {"id": 4503599627370497, "label": "Q", "properties": {}}::vertex]::path | {"id": 4503599627370497, "label": "Q", "properties": {}}::vertex
+(1 row)
+
+SELECT * FROM cypher('cypher_merge', $$ MATCH p=(x:R)-[:E]->(x) RETURN p, x $$) AS (p agtype, x agtype);
+ p | x
+---+---
+(0 rows)
+
+SELECT * FROM cypher('cypher_merge', $$ MERGE p=(x:R)-[:E]->(x) RETURN p, x $$) AS (p agtype, x agtype);
+ p | x
+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+------------------------------------------------------------------
+ [{"id": 4785074604081153, "label": "R", "properties": {}}::vertex, {"id": 3377699720527876, "label": "E", "end_id": 4785074604081153, "start_id": 4785074604081153, "properties": {}}::edge, {"id": 4785074604081153, "label": "R", "properties": {}}::vertex]::path | {"id": 4785074604081153, "label": "R", "properties": {}}::vertex
+(1 row)
+
+SELECT * FROM cypher('cypher_merge', $$ MERGE p=(x:R)-[:E]->(x) RETURN p, x $$) AS (p agtype, x agtype);
+ p | x
+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+------------------------------------------------------------------
+ [{"id": 4785074604081153, "label": "R", "properties": {}}::vertex, {"id": 3377699720527876, "label": "E", "end_id": 4785074604081153, "start_id": 4785074604081153, "properties": {}}::edge, {"id": 4785074604081153, "label": "R", "properties": {}}::vertex]::path | {"id": 4785074604081153, "label": "R", "properties": {}}::vertex
+(1 row)
+
+SELECT * FROM cypher('cypher_merge', $$ MATCH p=(x:R)-[:E]->(x) RETURN p, x $$) AS (p agtype, x agtype);
+ p | x
+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+------------------------------------------------------------------
+ [{"id": 4785074604081153, "label": "R", "properties": {}}::vertex, {"id": 3377699720527876, "label": "E", "end_id": 4785074604081153, "start_id": 4785074604081153, "properties": {}}::edge, {"id": 4785074604081153, "label": "R", "properties": {}}::vertex]::path | {"id": 4785074604081153, "label": "R", "properties": {}}::vertex
+(1 row)
+
+-- should return 4 rows
+SELECT * FROM cypher('cypher_merge', $$ MERGE p=(x)-[:E]->(x) RETURN p, x $$) AS (p agtype, x agtype);
+ p | x
+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+------------------------------------------------------------------
+ [{"id": 3096224743817217, "label": "C", "properties": {}}::vertex, {"id": 3377699720527873, "label": "E", "end_id": 3096224743817217, "start_id": 3096224743817217, "properties": {}}::edge, {"id": 3096224743817217, "label": "C", "properties": {}}::vertex]::path | {"id": 3096224743817217, "label": "C", "properties": {}}::vertex
+ [{"id": 4222124650659841, "label": "P", "properties": {}}::vertex, {"id": 3377699720527874, "label": "E", "end_id": 4222124650659841, "start_id": 4222124650659841, "properties": {}}::edge, {"id": 4222124650659841, "label": "P", "properties": {}}::vertex]::path | {"id": 4222124650659841, "label": "P", "properties": {}}::vertex
+ [{"id": 4503599627370497, "label": "Q", "properties": {}}::vertex, {"id": 3377699720527875, "label": "E", "end_id": 4503599627370497, "start_id": 4503599627370497, "properties": {}}::edge, {"id": 4503599627370497, "label": "Q", "properties": {}}::vertex]::path | {"id": 4503599627370497, "label": "Q", "properties": {}}::vertex
+ [{"id": 4785074604081153, "label": "R", "properties": {}}::vertex, {"id": 3377699720527876, "label": "E", "end_id": 4785074604081153, "start_id": 4785074604081153, "properties": {}}::edge, {"id": 4785074604081153, "label": "R", "properties": {}}::vertex]::path | {"id": 4785074604081153, "label": "R", "properties": {}}::vertex
+(4 rows)
+
+-- should create 1 row
+SELECT * FROM cypher('cypher_merge', $$ MERGE p=(x)-[:E1]->(x) RETURN p, x $$) AS (p agtype, x agtype);
+ p | x
+-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+----------------------------------------------------------------
+ [{"id": 281474976710706, "label": "", "properties": {}}::vertex, {"id": 5066549580791809, "label": "E1", "end_id": 281474976710706, "start_id": 281474976710706, "properties": {}}::edge, {"id": 281474976710706, "label": "", "properties": {}}::vertex]::path | {"id": 281474976710706, "label": "", "properties": {}}::vertex
+(1 row)
+
+SELECT * FROM cypher('cypher_merge', $$ MATCH p=(x)-[:E1]->(x) RETURN p, x $$) AS (p agtype, x agtype);
+ p | x
+-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+----------------------------------------------------------------
+ [{"id": 281474976710706, "label": "", "properties": {}}::vertex, {"id": 5066549580791809, "label": "E1", "end_id": 281474976710706, "start_id": 281474976710706, "properties": {}}::edge, {"id": 281474976710706, "label": "", "properties": {}}::vertex]::path | {"id": 281474976710706, "label": "", "properties": {}}::vertex
+(1 row)
+
+-- the following should fail due to multiple labels
+SELECT * FROM cypher('cypher_merge', $$ MERGE p=(x)-[:E]->(x:R) RETURN p, x $$) AS (p agtype, x agtype);
+ERROR: multiple labels for variable 'x' are not supported
+LINE 1: ...FROM cypher('cypher_merge', $$ MERGE p=(x)-[:E]->(x:R) RETUR...
+ ^
+SELECT * FROM cypher('cypher_merge', $$ MERGE p=(x:r)-[:E]->(x:R) RETURN p, x $$) AS (p agtype, x agtype);
+ERROR: multiple labels for variable 'x' are not supported
+LINE 1: ...OM cypher('cypher_merge', $$ MERGE p=(x:r)-[:E]->(x:R) RETUR...
+ ^
+SELECT * FROM cypher('cypher_merge', $$ MERGE (x)-[:E]->(x:R) $$) AS (x agtype);
+ERROR: multiple labels for variable 'x' are not supported
+LINE 1: ...* FROM cypher('cypher_merge', $$ MERGE (x)-[:E]->(x:R) $$) A...
+ ^
+SELECT * FROM cypher('cypher_merge', $$ MERGE (x:r)-[:E]->(x:R) $$) AS (x agtype);
+ERROR: multiple labels for variable 'x' are not supported
+LINE 1: ...FROM cypher('cypher_merge', $$ MERGE (x:r)-[:E]->(x:R) $$) A...
+ ^
+-- the following should fail due to reuse issues
+SELECT * FROM cypher('cypher_merge', $$ MERGE (x:r)-[y:E]->(x)-[y]->(x) $$) AS (x agtype);
+ERROR: a duplicate edge variable "y" is not permitted within a path
+LINE 1: ... cypher('cypher_merge', $$ MERGE (x:r)-[y:E]->(x)-[y]->(x) $...
+ ^
+SELECT * FROM cypher('cypher_merge', $$ MERGE (x:r)-[y:E]->(x)-[x]->(y) $$) AS (x agtype);
+ERROR: variable "x" is for a vertex
+LINE 1: ... cypher('cypher_merge', $$ MERGE (x:r)-[y:E]->(x)-[x]->(y) $...
+ ^
+SELECT * FROM cypher('cypher_merge', $$ MERGE (x:r)-[y:E]->(x)-[z:E]->(y) $$) AS (x agtype);
+ERROR: variable "y" is for an edge
+LINE 1: ...'cypher_merge', $$ MERGE (x:r)-[y:E]->(x)-[z:E]->(y) $$) AS ...
+ ^
+SELECT * FROM cypher('cypher_merge', $$ MERGE p=(x:r)-[y:E]->(x)-[p]->(x) $$) AS (x agtype);
+ERROR: variable "p" is for a path
+LINE 1: ...ypher('cypher_merge', $$ MERGE p=(x:r)-[y:E]->(x)-[p]->(x) $...
+ ^
+SELECT * FROM cypher('cypher_merge', $$ MERGE p=(x:r)-[y:E]->(x)-[p:E]->(x) $$) AS (x agtype);
+ERROR: variable "p" is for a path
+LINE 1: ...ypher('cypher_merge', $$ MERGE p=(x:r)-[y:E]->(x)-[p:E]->(x)...
+ ^
+SELECT * FROM cypher('cypher_merge', $$ MERGE p=(x:r)-[y:E]->(p)-[x]->(y) $$) AS (x agtype);
+ERROR: variable "p" is for a path
+LINE 1: ...M cypher('cypher_merge', $$ MERGE p=(x:r)-[y:E]->(p)-[x]->(y...
+ ^
--clean up
SELECT * FROM cypher('cypher_merge', $$MATCH (n) DETACH DELETE n $$) AS (a agtype);
a
@@ -896,7 +1082,7 @@ SELECT * FROM cypher('cypher_merge', $$MATCH (n) DETACH DELETE n $$) AS (a agtyp
* Clean up graph
*/
SELECT drop_graph('cypher_merge', true);
-NOTICE: drop cascades to 9 other objects
+NOTICE: drop cascades to 18 other objects
DETAIL: drop cascades to table cypher_merge._ag_label_vertex
drop cascades to table cypher_merge._ag_label_edge
drop cascades to table cypher_merge.e
@@ -906,6 +1092,15 @@ drop cascades to table cypher_merge."Person"
drop cascades to table cypher_merge."City"
drop cascades to table cypher_merge."BORN_IN"
drop cascades to table cypher_merge.node
+drop cascades to table cypher_merge."B"
+drop cascades to table cypher_merge."C"
+drop cascades to table cypher_merge."E"
+drop cascades to table cypher_merge."F"
+drop cascades to table cypher_merge."I"
+drop cascades to table cypher_merge."P"
+drop cascades to table cypher_merge."Q"
+drop cascades to table cypher_merge."R"
+drop cascades to table cypher_merge."E1"
NOTICE: graph "cypher_merge" has been dropped
drop_graph
------------
diff --git a/regress/expected/cypher_set.out b/regress/expected/cypher_set.out
index c6eaa6932..0310ae015 100644
--- a/regress/expected/cypher_set.out
+++ b/regress/expected/cypher_set.out
@@ -92,6 +92,23 @@ SELECT * FROM cypher('cypher_set', $$MATCH (n) RETURN n$$) AS (a agtype);
{"id": 844424930131970, "label": "v", "properties": {"a": 0, "i": 3, "j": 5}}::vertex
(3 rows)
+--Test assigning properties to rand() and pi()
+SELECT * FROM cypher('cypher_set', $$MATCH (n) SET n.i = rand() RETURN n.i < 1 AND n.i >= 0$$) AS (a agtype);
+ a
+------
+ true
+ true
+ true
+(3 rows)
+
+SELECT * FROM cypher('cypher_set', $$MATCH (n) SET n.i = pi() RETURN n$$) AS (a agtype);
+ a
+-------------------------------------------------------------------------------------------------------
+ {"id": 844424930131969, "label": "v", "properties": {"i": 3.141592653589793}}::vertex
+ {"id": 844424930131971, "label": "v", "properties": {"i": 3.141592653589793}}::vertex
+ {"id": 844424930131970, "label": "v", "properties": {"a": 0, "i": 3.141592653589793, "j": 5}}::vertex
+(3 rows)
+
--Handle Inheritance
SELECT * FROM cypher('cypher_set', $$CREATE ()$$) AS (a agtype);
a
@@ -404,7 +421,7 @@ SELECT set_test();
(10 rows)
--
--- Updating multiple fieds
+-- Updating multiple fields
--
SELECT * FROM cypher('cypher_set', $$MATCH (n) SET n.i = 3, n.j = 5 RETURN n $$) AS (a agtype);
a
@@ -814,7 +831,7 @@ $$) AS (p agtype);
{"id": 1970324836974593, "label": "Juan", "properties": {}}::vertex
(1 row)
--- test assigning non-map to an enitity
+-- test assigning non-map to an entity
SELECT * FROM cypher('cypher_set_1', $$
MATCH (p {name: 'Peter'})
SET p = "Peter"
@@ -852,6 +869,26 @@ $$) AS (p agtype);
{"id": 2251799813685249, "label": "Robert", "properties": {"age": 47, "city": "London", "name": "Rob"}}::vertex
(1 row)
+--
+-- Check passing mismatched types with SET
+-- Issue 899
+--
+SELECT * FROM cypher('cypher_set_1', $$
+ CREATE (x) SET x.n0 = (true OR true) RETURN x
+$$) AS (p agtype);
+ p
+--------------------------------------------------------------------------
+ {"id": 281474976710657, "label": "", "properties": {"n0": true}}::vertex
+(1 row)
+
+SELECT * FROM cypher('cypher_set_1', $$
+ CREATE (x) SET x.n0 = (true OR false), x.n1 = (false AND false), x.n2 = (false = false) RETURN x
+$$) AS (p agtype);
+ p
+---------------------------------------------------------------------------------------------------
+ {"id": 281474976710658, "label": "", "properties": {"n0": true, "n1": false, "n2": true}}::vertex
+(1 row)
+
--
-- Clean up
--
diff --git a/regress/expected/cypher_vle.out b/regress/expected/cypher_vle.out
index 5f8f922c2..e153ce972 100644
--- a/regress/expected/cypher_vle.out
+++ b/regress/expected/cypher_vle.out
@@ -761,6 +761,59 @@ NOTICE: graph "mygraph" has been dropped
(1 row)
+-- invalid reuse of VLE
+SELECT * FROM cypher('cypher_vle', $$ MATCH ()-[p]-()-[p *]-() RETURN p $$)as (p agtype);
+ERROR: variable 'p' is for an edge
+LINE 1: ...CT * FROM cypher('cypher_vle', $$ MATCH ()-[p]-()-[p *]-() R...
+ ^
+SELECT * FROM cypher('cypher_vle', $$ MATCH ()-[p *]-()-[p]-() RETURN p $$)as (p agtype);
+ERROR: variable 'p' is for a VLE edge
+LINE 1: ... * FROM cypher('cypher_vle', $$ MATCH ()-[p *]-()-[p]-() RET...
+ ^
+SELECT * FROM cypher('cypher_vle', $$ MATCH (p)-[p *]-() RETURN p $$)as (p agtype);
+ERROR: variable 'p' is for a vertex
+LINE 1: SELECT * FROM cypher('cypher_vle', $$ MATCH (p)-[p *]-() RET...
+ ^
+SELECT * FROM cypher('cypher_vle', $$ MATCH ()-[p *]-(p) RETURN p $$)as (p agtype);
+ERROR: variable 'p' is for a VLE edge
+LINE 1: ...CT * FROM cypher('cypher_vle', $$ MATCH ()-[p *]-(p) RETURN ...
+ ^
+SELECT * FROM cypher('cypher_vle', $$ MATCH p=()-[p *]-() RETURN p $$)as (p agtype);
+ERROR: variable "p" is for a path
+LINE 1: SELECT * FROM cypher('cypher_vle', $$ MATCH p=()-[p *]-() RE...
+ ^
+SELECT * FROM cypher('cypher_vle', $$ MATCH ()-[p *]-()-[p *]-() RETURN p $$)as (p agtype);
+ERROR: duplicate variable 'p'
+LINE 1: ... * FROM cypher('cypher_vle', $$ MATCH ()-[p *]-()-[p *]-() R...
+ ^
+SELECT * FROM cypher('cypher_vle', $$ MATCH ()-[p]-() MATCH ()-[p *]-() RETURN p $$)as (p agtype);
+ERROR: variable 'p' is for an edge
+LINE 1: ... cypher('cypher_vle', $$ MATCH ()-[p]-() MATCH ()-[p *]-() R...
+ ^
+SELECT * FROM cypher('cypher_vle', $$ MATCH ()-[p *]-() MATCH ()-[p]-() RETURN p $$)as (p agtype);
+ERROR: variable 'p' is for a VLE edge
+LINE 1: ...ypher('cypher_vle', $$ MATCH ()-[p *]-() MATCH ()-[p]-() RET...
+ ^
+SELECT * FROM cypher('cypher_vle', $$ MATCH ()-[p *]-() MATCH ()-[p *]-() RETURN p $$)as (p agtype);
+ERROR: duplicate variable 'p'
+LINE 1: ...ypher('cypher_vle', $$ MATCH ()-[p *]-() MATCH ()-[p *]-() R...
+ ^
+SELECT * FROM cypher('cypher_vle', $$ MATCH (p) MATCH ()-[p *]-() RETURN p $$)as (p agtype);
+ERROR: variable 'p' is for a vertex
+LINE 1: ...* FROM cypher('cypher_vle', $$ MATCH (p) MATCH ()-[p *]-() R...
+ ^
+SELECT * FROM cypher('cypher_vle', $$ MATCH ()-[p *]-() MATCH (p) RETURN p $$)as (p agtype);
+ERROR: variable 'p' is for a VLE edge
+LINE 1: ... cypher('cypher_vle', $$ MATCH ()-[p *]-() MATCH (p) RETURN ...
+ ^
+SELECT * FROM cypher('cypher_vle', $$ MATCH p=() MATCH ()-[p *]-() RETURN p $$)as (p agtype);
+ERROR: variable 'p' already exists
+LINE 1: ... FROM cypher('cypher_vle', $$ MATCH p=() MATCH ()-[p *]-() R...
+ ^
+SELECT * FROM cypher('cypher_vle', $$ MATCH ()-[p *]-() MATCH p=() RETURN p $$)as (p agtype);
+ERROR: variable "p" already exists
+LINE 1: ...M cypher('cypher_vle', $$ MATCH ()-[p *]-() MATCH p=() RETUR...
+ ^
--
-- Clean up
--
diff --git a/regress/expected/expr.out b/regress/expected/expr.out
index aac308cb0..37c7849b0 100644
--- a/regress/expected/expr.out
+++ b/regress/expected/expr.out
@@ -899,6 +899,79 @@ $$) AS r(result boolean);
f
(1 row)
+SELECT * FROM cypher('expr', $$
+RETURN false OR 1::bool
+$$) AS (result boolean);
+ result
+--------
+ t
+(1 row)
+
+SELECT * FROM cypher('expr', $$
+RETURN false AND NOT 1::bool
+$$) AS (result boolean);
+ result
+--------
+ f
+(1 row)
+
+SELECT * FROM cypher('expr', $$
+RETURN NOT 1::bool::int::bool
+$$) AS (result boolean);
+ result
+--------
+ f
+(1 row)
+
+-- Invalid operands for AND, OR, NOT, XOR
+SELECT * FROM cypher('expr', $$
+RETURN 1 AND true
+$$) AS r(result boolean);
+ERROR: cannot cast agtype integer to type boolean
+SELECT * FROM cypher('expr', $$
+RETURN true AND 0
+$$) AS r(result boolean);
+ERROR: cannot cast agtype integer to type boolean
+SELECT * FROM cypher('expr', $$
+RETURN false OR 1
+$$) AS r(result boolean);
+ERROR: cannot cast agtype integer to type boolean
+SELECT * FROM cypher('expr', $$
+RETURN 0 OR true
+$$) AS r(result boolean);
+ERROR: cannot cast agtype integer to type boolean
+SELECT * FROM cypher('expr', $$
+RETURN NOT 1
+$$) AS r(result boolean);
+ERROR: cannot cast agtype integer to type boolean
+SELECT * FROM cypher('expr', $$
+RETURN true XOR 0
+$$) AS r(result boolean);
+ERROR: cannot cast agtype integer to type boolean
+SELECT * FROM cypher('expr', $$
+RETURN 1 XOR 0
+$$) AS r(result boolean);
+ERROR: cannot cast agtype integer to type boolean
+SELECT * FROM cypher('expr', $$
+RETURN NOT ((1 OR 0) AND (0 OR 1))
+$$) AS r(result boolean);
+ERROR: cannot cast agtype integer to type boolean
+SELECT * FROM cypher('expr', $$
+RETURN 1.0 AND true
+$$) AS (result agtype);
+ERROR: cannot cast agtype float to type boolean
+SELECT * FROM cypher('expr', $$
+RETURN false OR 'string'
+$$) AS (result agtype);
+ERROR: cannot cast agtype string to type boolean
+SELECT * FROM cypher('expr', $$
+RETURN false XOR 1::numeric
+$$) AS (result agtype);
+ERROR: cannot cast agtype numeric to type boolean
+SELECT * FROM cypher('expr', $$
+RETURN false OR 1::bool::int
+$$) AS (result boolean);
+ERROR: cannot cast agtype integer to type boolean
--
-- Test indirection transform logic for object.property, object["property"],
-- and array[element]
@@ -1075,7 +1148,7 @@ $$) AS r(result agtype);
(3 rows)
--
---Coearce to Postgres 3 int types (smallint, int, bigint)
+--Coerce to Postgres 3 int types (smallint, int, bigint)
--
SELECT create_graph('type_coercion');
NOTICE: graph "type_coercion" has been created
@@ -1140,6 +1213,22 @@ $$) AS (i bigint);
1
(1 row)
+SELECT * FROM cypher('type_coercion', $$
+ RETURN true
+$$) AS (i bigint);
+ i
+---
+ 1
+(1 row)
+
+SELECT * FROM cypher('type_coercion', $$
+ RETURN true
+$$) AS (i int);
+ i
+---
+ 1
+(1 row)
+
--Invalid String Format
SELECT * FROM cypher('type_coercion', $$
RETURN '1.0'
@@ -1155,10 +1244,6 @@ SELECT * FROM cypher('type_coercion', $$
$$) AS (i int);
ERROR: integer out of range
--Invalid types
-SELECT * FROM cypher('type_coercion', $$
- RETURN true
-$$) AS (i bigint);
-ERROR: cannot cast agtype boolean to type int
SELECT * FROM cypher('type_coercion', $$
RETURN {key: 1}
$$) AS (i bigint);
@@ -1167,6 +1252,10 @@ SELECT * FROM cypher('type_coercion', $$
RETURN [1]
$$) AS (i bigint);
ERROR: cannot cast agtype array to type int
+SELECT * FROM cypher('type_coercion', $$
+ RETURN 1
+$$) AS (i bool);
+ERROR: cannot cast agtype integer to type boolean
SELECT * FROM cypher('type_coercion', $$CREATE ()-[:edge]->()$$) AS (result agtype);
result
--------
@@ -1249,6 +1338,22 @@ $$) AS r(result agtype);
3
(1 row)
+SELECT * FROM cypher('expr', $$
+RETURN true::int
+$$) AS r(result agtype);
+ result
+--------
+ 1
+(1 row)
+
+SELECT * FROM cypher('expr', $$
+RETURN false::int
+$$) AS r(result agtype);
+ result
+--------
+ 0
+(1 row)
+
SELECT * FROM cypher('expr', $$
RETURN ([0, {one: 1.0, pie: 3.1415927, e: 2::numeric}, 2, null][1].one)::int
$$) AS r(result agtype);
@@ -1332,6 +1437,38 @@ SELECT * FROM cypher('expr', $$
RETURN 'infinity'::float::int
$$) AS r(result agtype);
ERROR: bigint out of range
+SELECT * FROM cypher('expr', $$
+RETURN ''::int
+$$) AS r(result agtype);
+ERROR: invalid input syntax for type bigint: ""
+SELECT * FROM cypher('expr', $$
+RETURN 'false_'::int
+$$) AS r(result agtype);
+ERROR: invalid input syntax for type bigint: "false_"
+--
+-- Test from an agtype value to agtype int
+--
+SELECT * FROM cypher('expr', $$
+RETURN 0::bool
+$$) AS r(result agtype);
+ result
+--------
+ false
+(1 row)
+
+-- these should fail
+SELECT * FROM cypher('expr', $$
+RETURN 1.23::bool
+$$) AS r(result agtype);
+ERROR: typecast expression must be an integer or a boolean
+SELECT * FROM cypher('expr', $$
+RETURN ''::bool
+$$) AS r(result agtype);
+ERROR: typecast expression must be an integer or a boolean
+SELECT * FROM cypher('expr', $$
+RETURN 'false_'::bool
+$$) AS r(result agtype);
+ERROR: typecast expression must be an integer or a boolean
-- Test from an agtype value to an agtype numeric
--
SELECT * FROM cypher('expr', $$
@@ -1664,6 +1801,143 @@ SELECT agtype_in('[NaN, {"e": 2.718281::numeric, "one": Infinity, "pie": 3.14159
[NaN, {"e": 2.718281::numeric, "one": Infinity, "pie": 3.1415927}, 2::numeric, null]
(1 row)
+--
+-- Test typecast ::pg_float8
+--
+SELECT * FROM cypher('expr', $$
+RETURN 0::pg_float8
+$$) AS r(result agtype);
+ result
+--------
+ 0.0
+(1 row)
+
+SELECT * FROM cypher('expr', $$
+RETURN '2.71'::pg_float8
+$$) AS r(result agtype);
+ result
+--------
+ 2.71
+(1 row)
+
+SELECT * FROM cypher('expr', $$
+RETURN 2.71::pg_float8
+$$) AS r(result agtype);
+ result
+--------
+ 2.71
+(1 row)
+
+SELECT * FROM cypher('expr', $$
+RETURN ([0, {one: 1, pie: 3.1415927, e: 2::numeric}, 2, null][1].one)::pg_float8
+$$) AS r(result agtype);
+ result
+--------
+ 1.0
+(1 row)
+
+SELECT * FROM cypher('expr', $$
+RETURN ([0, {one: 1::pg_float8, pie: 3.1415927, e: 2.718281::numeric}, 2, null][1].one)
+$$) AS r(result agtype);
+ result
+--------
+ 1.0
+(1 row)
+
+SELECT * FROM cypher('expr', $$
+RETURN ([0, {one: 1::pg_float8, pie: 3.1415927, e: 2.718281::numeric}, 2, null][1].one)::float
+$$) AS r(result agtype);
+ result
+--------
+ 1.0
+(1 row)
+
+SELECT * FROM cypher('expr', $$
+RETURN ([0, {one: 1, pie: 3.1415927, e: 2.718281::numeric}, 2, null][3])::pg_float8
+$$) AS r(result agtype);
+ result
+--------
+
+(1 row)
+
+SELECT * FROM cypher('expr', $$
+RETURN (['NaN'::pg_float8, {one: 'inf'::pg_float8, pie: 3.1415927, e: 2.718281::numeric}, 2::numeric, null])
+$$) AS r(result agtype);
+ result
+--------------------------------------------------------------------------------------
+ [NaN, {"e": 2.718281::numeric, "one": Infinity, "pie": 3.1415927}, 2::numeric, null]
+(1 row)
+
+SELECT * FROM cypher('expr', $$
+RETURN ([0, {one: 1, pie: 3.1415927, e: 2.718281::numeric}, 2, null][1].e)::pg_float8
+$$) AS r(result agtype);
+ result
+----------
+ 2.718281
+(1 row)
+
+-- test NaN, Infinity and -Infinity
+SELECT * FROM cypher('expr', $$
+RETURN 'NaN'::pg_float8
+$$) AS r(result agtype);
+ result
+--------
+ NaN
+(1 row)
+
+SELECT * FROM cypher('expr', $$
+RETURN 'inf'::pg_float8
+$$) AS r(result agtype);
+ result
+----------
+ Infinity
+(1 row)
+
+SELECT * FROM cypher('expr', $$
+RETURN '-inf'::pg_float8
+$$) AS r(result agtype);
+ result
+-----------
+ -Infinity
+(1 row)
+
+SELECT * FROM cypher('expr', $$
+RETURN 'infinity'::pg_float8
+$$) AS r(result agtype);
+ result
+----------
+ Infinity
+(1 row)
+
+SELECT * FROM cypher('expr', $$
+RETURN '-infinity'::pg_float8
+$$) AS r(result agtype);
+ result
+-----------
+ -Infinity
+(1 row)
+
+SELECT * FROM cypher('expr', $$
+RETURN null::pg_float8
+$$) AS r(result agtype);
+ result
+--------
+
+(1 row)
+
+-- these should fail
+SELECT * FROM cypher('expr', $$
+RETURN ''::pg_float8
+$$) AS r(result agtype);
+ERROR: invalid input syntax for type double precision: ""
+SELECT * FROM cypher('expr', $$
+RETURN '2:71'::pg_float8
+$$) AS r(result agtype);
+ERROR: invalid input syntax for type double precision: "2:71"
+SELECT * FROM cypher('expr', $$
+RETURN 'infi'::pg_float8
+$$) AS r(result agtype);
+ERROR: invalid input syntax for type double precision: "infi"
--
-- Test typecast :: transform and execution logic for object (vertex & edge)
--
@@ -2518,7 +2792,7 @@ $$) AS (toBoolean agtype);
-- should return null
SELECT * FROM cypher('expr', $$
- RETURN toBoolean("falze")
+ RETURN toBoolean("false_")
$$) AS (toBoolean agtype);
toboolean
-----------
@@ -2588,7 +2862,7 @@ $$) AS (toFloat agtype);
-- should return null
SELECT * FROM cypher('expr', $$
- RETURN toFloat("falze")
+ RETURN toFloat("false_")
$$) AS (toFloat agtype);
tofloat
---------
@@ -2658,7 +2932,7 @@ $$) AS (toInteger agtype);
-- should return null
SELECT * FROM cypher('expr', $$
- RETURN toInteger("falze")
+ RETURN toInteger("false_")
$$) AS (toInteger agtype);
tointeger
-----------
@@ -4321,14 +4595,14 @@ $$) AS (results agtype);
SELECT * FROM cypher('expr', $$
RETURN pi(null)
$$) AS (results agtype);
-ERROR: function pg_catalog.pi(agtype) does not exist
+ERROR: function ag_catalog.age_pi(agtype) does not exist
LINE 2: RETURN pi(null)
^
HINT: No function matches the given name and argument types. You might need to add explicit type casts.
SELECT * FROM cypher('expr', $$
RETURN pi(1)
$$) AS (results agtype);
-ERROR: function pg_catalog.pi(agtype) does not exist
+ERROR: function ag_catalog.age_pi(agtype) does not exist
LINE 2: RETURN pi(1)
^
HINT: No function matches the given name and argument types. You might need to add explicit type casts.
@@ -4996,6 +5270,14 @@ $$) as (result agtype);
5.0
(1 row)
+SELECT * from cypher('expr', $$
+ RETURN pg_catalog.sqrt("25"::pg_float8)
+$$) as (result agtype);
+ result
+--------
+ 5.0
+(1 row)
+
SELECT * from cypher('expr', $$
RETURN ag_catalog.age_sqrt(25)
$$) as (result agtype);
@@ -5021,10 +5303,6 @@ ERROR: function pg_catalog.sqrt() does not exist
LINE 2: RETURN pg_catalog.sqrt()
^
HINT: No function matches the given name and argument types. You might need to add explicit type casts.
-SELECT * from cypher('expr', $$
- RETURN pg_catalog.sqrt("1"::pg_float8)
-$$) as (result agtype);
-ERROR: cannot cast agtype string to type float
SELECT * from cypher('expr', $$
RETURN pg_catalog.sqrt(-1::pg_float8)
$$) as (result agtype);
@@ -5402,7 +5680,7 @@ SELECT * FROM cypher('UCSC', $$ RETURN collect(5) $$) AS (result agtype);
[5]
(1 row)
--- should return an empty aray
+-- should return an empty array
SELECT * FROM cypher('UCSC', $$ RETURN collect(NULL) $$) AS (empty agtype);
empty
-------
@@ -5697,6 +5975,41 @@ SELECT * FROM cypher('case_statement', $$CREATE ({i: {}, j: {i:1}})$$) AS (resul
--------
(0 rows)
+--standalone case & edge cases
+--base case
+SELECT * FROM cypher('case_statement', $$ RETURN (CASE WHEN true THEN true END) $$) as (a agtype);
+ a
+------
+ true
+(1 row)
+
+--should return 1 empty row
+SELECT * FROM cypher('case_statement', $$ RETURN (CASE WHEN false THEN true END) $$) as (a agtype);
+ a
+---
+
+(1 row)
+
+--should return 'false'
+SELECT * FROM cypher('case_statement', $$ RETURN (CASE WHEN true THEN false END) $$) as (a agtype);
+ a
+-------
+ false
+(1 row)
+
+--invalid case (WHEN should be boolean)
+SELECT * FROM cypher('case_statement', $$ RETURN (CASE WHEN 1 THEN 'fail' END) $$) as (a agtype);
+ERROR: cannot cast agtype integer to type boolean
+-- booleans + logic gates
+SELECT * FROM cypher('case_statement', $$ RETURN (CASE WHEN true THEN (true AND true) END) $$) as (a agtype);
+ a
+------
+ true
+(1 row)
+
+-- invalid mixed logic gate
+SELECT * FROM cypher('case_statement', $$ RETURN (CASE WHEN true THEN (true AND 1) END) $$) as (a agtype);
+ERROR: cannot cast agtype integer to type boolean
--CASE WHEN condition THEN result END
SELECT * FROM cypher('case_statement', $$
MATCH (n)
@@ -5817,6 +6130,22 @@ SELECT * FROM cypher('opt_forms', $$MATCH (u)-->()<--(v) RETURN *$$) AS (col1 ag
(2 rows)
-- Added typecasts ::pg_bigint and ::pg_float8
+SELECT * FROM cypher('expr', $$
+RETURN true::pg_bigint
+$$) AS (result agtype);
+ result
+--------
+ 1
+(1 row)
+
+SELECT * FROM cypher('expr', $$
+RETURN "1.0"::pg_float8
+$$) AS (result agtype);
+ result
+--------
+ 1.0
+(1 row)
+
SELECT * from cypher('expr', $$
RETURN pg_catalog.sqrt(pg_catalog.sqrt(pg_catalog.sqrt(256::pg_bigint)))
$$) as (result agtype);
@@ -6200,6 +6529,161 @@ SELECT * from cypher('list', $$RETURN labels(NULL)$$) as (Labels agtype);
-- should return an error
SELECT * from cypher('list', $$RETURN labels("string")$$) as (Labels agtype);
ERROR: labels() argument must be a vertex
+-- Issue 989: Impossible to create an object with an array field of more than
+-- 100 elements.
+SELECT * FROM cypher('list', $$ CREATE (any_vertex: test_label { `largeArray`: [] }) RETURN any_vertex $$) AS (u agtype);
+ u
+-------------------------------------------------------------------------------------------
+ {"id": 1688849860263937, "label": "test_label", "properties": {"largeArray": []}}::vertex
+(1 row)
+
+SELECT * FROM cypher('list', $$ CREATE (any_vertex: test_label { `largeArray`: [0] }) RETURN any_vertex $$) AS (u agtype);
+ u
+--------------------------------------------------------------------------------------------
+ {"id": 1688849860263938, "label": "test_label", "properties": {"largeArray": [0]}}::vertex
+(1 row)
+
+SELECT * FROM cypher('list', $$ CREATE (any_vertex: test_label { `largeArray`: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99] }) RETURN any_vertex $$) AS (u agtype);
+ u
+-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
+ {"id": 1688849860263939, "label": "test_label", "properties": {"largeArray": [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99]}}::vertex
+(1 row)
+
+SELECT * FROM cypher('list', $$ CREATE (any_vertex: test_label { `largeArray`: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, 100] }) RETURN any_vertex $$) AS (u agtype);
+ u
+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
+ {"id": 1688849860263940, "label": "test_label", "properties": {"largeArray": [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, 100]}}::vertex
+(1 row)
+
+SELECT * FROM cypher('list', $$ CREATE (any_vertex: test_label { `largeArray`: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, 100, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99] }) RETURN any_vertex $$) AS (u agtype);
+ u
+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
+ {"id": 1688849860263941, "label": "test_label", "properties": {"largeArray": [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, 100, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99]}}::vertex
+(1 row)
+
+SELECT * FROM cypher('list', $$ CREATE (any_vertex: test_label { `largeArray`: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, 100, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, 100, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, 100, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99] }) RETURN any_vertex $$) AS (u agtype);
+ u
+-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
+ {"id": 1688849860263942, "label": "test_label", "properties": {"largeArray": [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, 100, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, 100, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, 100, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99]}}::vertex
+(1 row)
+
+SELECT * FROM cypher('list', $$ CREATE (any_vertex: test_label { `largeArray`: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, 100, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, 100, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, 100, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, 100, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, 100, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, 100, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, 100, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99] }) RETURN any_vertex $$) AS (u agtype);
+ u
+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
+ {"id": 1688849860263943, "label": "test_label", "properties": {"largeArray": [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, 100, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, 100, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, 100, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, 100, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, 100, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, 100, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, 100, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99]}}::vertex
+(1 row)
+
+-- should return 7 rows with counts: 0, 1, 100, 101, 200, 400, 800
+SELECT * FROM cypher('list', $$ MATCH (u:test_label) RETURN size(u.largeArray) $$) AS (u agtype);
+ u
+-----
+ 0
+ 1
+ 100
+ 101
+ 200
+ 400
+ 800
+(7 rows)
+
+-- nested cases
+SELECT * FROM cypher('list',$$ CREATE (n:xyz {array:[0, 1, 2, 3, 4, 5, 6, 7, 8, 9,
+ 10, 11, 12, 13, 14, 15, 16, 17, 18, 19,
+ 20,21, 22, 23, 24, 25, 26, 27, 28, 29,
+ 30, 31, 32, 33, 34, 35, 36, 37, 38, 39,
+ 40, 41, 42, 43, 44, 45, 46, 47, 48, 49,
+ 50, 51, 52, 53, 54, 55, 56, 57, 58, 59,
+ 60, 61, 62, 63, 64, 65, 66, 67, 68, 69,
+ 70, 71, 72, 73, 74, 75, 76, 77, 78, 79,
+ 80, 81, 82, 83, 84, 85, 86, 87, 88, 89,
+ 90, 91, 92, 93, 94, 95, 96, 97, 98, 99,
+ [0, 1, 2, 3, 4, 5, 6, 7, 8, 9,
+ 10, 11, 12, 13, 14, 15, 16, 17, 18, 19,
+ 20,21, 22, 23, 24, 25, 26, 27, 28, 29,
+ 30, 31, 32, 33, 34, 35, 36, 37, 38, 39,
+ 40, 41, 42, 43, 44, 45, 46, 47, 48, 49,
+ 50, 51, 52, 53, 54, 55, 56, 57, 58, 59,
+ 60, 61, 62, 63, 64, 65, 66, 67, 68, 69,
+ 70, 71, 72, 73, 74, 75, 76, 77, 78, 79,
+ 80, 81, 82, 83, 84, 85, 86, 87, 88, 89,
+ 90, 91, 92, 93, 94, 95, 96, 97, 98, 99,
+ 100], 100]}) return n $$) as (a agtype);
+ a
+-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
+ {"id": 1970324836974593, "label": "xyz", "properties": {"array": [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, 100], 100]}}::vertex
+(1 row)
+
+SELECT * FROM cypher('list',$$ MATCH (n:xyz) CREATE (m:xyz {array:[0,1,2,3,n.array,5,6,7,8,9,
+ 10, 11, 12, 13, 14, 15, 16, 17, 18, 19,
+ 20,21, 22, 23, 24, 25, 26, 27, 28, 29,
+ 30, 31, 32, 33, 34, 35, 36, 37, 38, 39,
+ 40, 41, 42, 43, 44, 45, 46, 47, 48, 49,
+ 50, 51, 52, 53, 54, 55, 56, 57, 58, 59,
+ 60, 61, 62, 63, 64, 65, 66, 67, 68, 69,
+ 70, 71, 72, 73, 74, 75, 76, 77, 78, 79,
+ 80, 81, 82, 83, 84, 85, 86, 87, 88, 89,
+ 90, 91, 92, 93, 94, 95, 96, 97, 98, 99,
+ 100]}) return m $$) as (a agtype);
+ a
+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
+ {"id": 1970324836974594, "label": "xyz", "properties": {"array": [0, 1, 2, 3, [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, 100], 100], 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, 100]}}::vertex
+(1 row)
+
+SELECT * FROM cypher('list',$$ MATCH (n:xyz) CREATE (m:xyz {array:[n.array,[n.array,[n.array]]]}) return m $$) as (a agtype);
+ a
+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
+ {"id": 1970324836974595, "label": "xyz", "properties": {"array": [[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, 100], 100], [[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, 100], 100], [[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, 100], 100]]]]}}::vertex
+ {"id": 1970324836974596, "label": "xyz", "properties": {"array": [[0, 1, 2, 3, [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, 100], 100], 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, 100], [[0, 1, 2, 3, [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, 100], 100], 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, 100], [[0, 1, 2, 3, [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, 100], 100], 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, 100]]]]}}::vertex
+(2 rows)
+
+-- SET
+SELECT * FROM cypher('list',$$ CREATE (n:xyz)-[e:KNOWS {array:[0, 1, 2, 3, 4, 5, 6, 7, 8, 9,
+ 10, 11, 12, 13, 14, 15, 16, 17, 18, 19,
+ 20,21, 22, 23, 24, 25, 26, 27, 28, 29,
+ 30, 31, 32, 33, 34, 35, 36, 37, 38, 39,
+ 40, 41, 42, 43, 44, 45, 46, 47, 48, 49,
+ 50, 51, 52, 53, 54, 55, 56, 57, 58, 59,
+ 60, 61, 62, 63, 64, 65, 66, 67, 68, 69,
+ 70, 71, 72, 73, 74, 75, 76, 77, 78, 79,
+ 80, 81, 82, 83, 84, 85, 86, 87, 88, 89,
+ 90, 91, 92, 93, 94, 95, 96, 97, 98, 99,
+ 100]}]->(m:xyz) $$) as (a agtype);
+ a
+---
+(0 rows)
+
+SELECT * FROM cypher('list',$$ MATCH p=(n:xyz)-[e]->() SET n.array=[0, 1, 2, 3, 4, 5, 6, 7, 8, 9,
+ 10, 11, 12, 13, 14, 15, 16, 17, 18, 19,
+ 20,21, 22, 23, 24, 25, 26, 27, 28, 29,
+ 30, 31, 32, 33, 34, 35, 36, 37, 38, 39,
+ 40, 41, 42, 43, 44, 45, 46, 47, 48, 49,
+ 50, 51, 52, 53, 54, 55, 56, 57, 58, 59,
+ 60, 61, 62, 63, 64, 65, 66, 67, 68, 69,
+ 70, 71, 72, 73, 74, 75, 76, 77, 78, 79,
+ 80, 81, 82, 83, 84, 85, 86, 87, 88, 89,
+ 90, 91, 92, 93, 94, 95, 96, 97, 98, 99,
+ 100] return n,e $$) as (a agtype, b agtype);
+ a | b
+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
+ {"id": 1970324836974597, "label": "xyz", "properties": {"array": [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, 100]}}::vertex | {"id": 2251799813685249, "label": "KNOWS", "end_id": 1970324836974598, "start_id": 1970324836974597, "properties": {"array": [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, 100]}}::edge
+(1 row)
+
+SELECT * FROM cypher('list',$$ MATCH p=(n:xyz)-[e]->() SET n.array=[0, 1, 2, 3, 4, 5, 6, 7, 8, 9,
+ 10, 11, 12, 13, 14, 15, 16, 17, 18, 19,
+ 20,21, 22, 23, 24, 25, 26, 27, 28, 29,
+ 30, 31, 32, 33, 34, 35, 36, 37, 38, 39,
+ 40, 41, 42, 43, 44, 45, 46, 47, 48, 49,
+ 50, 51, 52, 53, 54, 55, 56, 57, 58, 59,
+ 60, 61, 62, 63, 64, 65, 66, 67, 68, 69,
+ 70, 71, 72, 73, 74, 75, 76, 77, 78, 79,
+ 80, 81, 82, 83, 84, 85, 86, 87, 88, 89,
+ 90, 91, 92, 93, 94, 95, 96, 97, 98, 99,
+ e.array, 100] return n,e $$) as (a agtype, b agtype);
+ a | b
+-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
+ {"id": 1970324836974597, "label": "xyz", "properties": {"array": [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, 100], 100]}}::vertex | {"id": 2251799813685249, "label": "KNOWS", "end_id": 1970324836974598, "start_id": 1970324836974597, "properties": {"array": [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, 100]}}::edge
+(1 row)
+
--
-- Cleanup
--
@@ -6334,12 +6818,15 @@ NOTICE: graph "keys" has been dropped
(1 row)
SELECT * FROM drop_graph('list', true);
-NOTICE: drop cascades to 5 other objects
+NOTICE: drop cascades to 8 other objects
DETAIL: drop cascades to table list._ag_label_vertex
drop cascades to table list._ag_label_edge
drop cascades to table list.knows
drop cascades to table list."People"
drop cascades to table list."Cars"
+drop cascades to table list.test_label
+drop cascades to table list.xyz
+drop cascades to table list."KNOWS"
NOTICE: graph "list" has been dropped
drop_graph
------------
diff --git a/regress/expected/index.out b/regress/expected/index.out
index c4f9012b3..f911900ab 100644
--- a/regress/expected/index.out
+++ b/regress/expected/index.out
@@ -254,7 +254,7 @@ SELECT * FROM cypher('cypher_index', $$
(us)<-[:has_city]-(:City {city_id: 3, name:"Los Angeles", west_coast: true, country_code:"US"}),
(us)<-[:has_city]-(:City {city_id: 4, name:"Seattle", west_coast: true, country_code:"US"}),
(ca)<-[:has_city]-(:City {city_id: 5, name:"Vancouver", west_coast: true, country_code:"CA"}),
- (ca)<-[:has_city]-(:City {city_id: 6, name:"Toroto", west_coast: false, country_code:"CA"}),
+ (ca)<-[:has_city]-(:City {city_id: 6, name:"Toronto", west_coast: false, country_code:"CA"}),
(ca)<-[:has_city]-(:City {city_id: 7, name:"Montreal", west_coast: false, country_code:"CA"}),
(mx)<-[:has_city]-(:City {city_id: 8, name:"Mexico City", west_coast: false, country_code:"MX"}),
(mx)<-[:has_city]-(:City {city_id: 9, name:"Monterrey", west_coast: false, country_code:"MX"}),
diff --git a/regress/sql/age_global_graph.sql b/regress/sql/age_global_graph.sql
index 13b81a9ed..badc0b5b2 100644
--- a/regress/sql/age_global_graph.sql
+++ b/regress/sql/age_global_graph.sql
@@ -37,7 +37,7 @@ SELECT * FROM cypher('ag_graph_2', $$ RETURN delete_global_graphs('ag_graph_2')
SELECT * FROM cypher('ag_graph_1', $$ RETURN delete_global_graphs('ag_graph_1') $$) AS (result agtype);
-- delete ag_graph_3's context
--- should return true(succeed) beacuse the previous commands should not delete the 3rd graph's context
+-- should return true(succeed) because the previous commands should not delete the 3rd graph's context
SELECT * FROM cypher('ag_graph_3', $$ RETURN delete_global_graphs('ag_graph_3') $$) AS (result agtype);
-- delete all graphs' context again
@@ -46,7 +46,7 @@ SELECT * FROM cypher('ag_graph_2', $$ RETURN delete_global_graphs('ag_graph_2')
SELECT * FROM cypher('ag_graph_1', $$ RETURN delete_global_graphs('ag_graph_1') $$) AS (result agtype);
SELECT * FROM cypher('ag_graph_3', $$ RETURN delete_global_graphs('ag_graph_3') $$) AS (result agtype);
---- delete unitialized graph context
+--- delete uninitialized graph context
--- should throw exception graph "ag_graph_4" does not exist
SELECT * FROM cypher('ag_graph_4', $$ RETURN delete_global_graphs('ag_graph_4') $$) AS (result agtype);
diff --git a/regress/sql/agtype.sql b/regress/sql/agtype.sql
index fa43bb80a..3fb81d01e 100644
--- a/regress/sql/agtype.sql
+++ b/regress/sql/agtype.sql
@@ -404,7 +404,7 @@ SELECT agtype_in('{"bool":true, "null": null}') = agtype_in('{"null":null, "bool
SELECT agtype_in('{"bool":true}') < agtype_in('{"bool":true, "null": null}');
-- Comparisons between types
--- Object < List < String < Boolean < Integer = Float = Numeric < Null
+-- Path < Edge < Vertex < Object < List < String < Boolean < Integer = Float = Numeric < Null
SELECT agtype_in('1') < agtype_in('null');
SELECT agtype_in('NaN') < agtype_in('null');
SELECT agtype_in('Infinity') < agtype_in('null');
@@ -416,8 +416,17 @@ SELECT agtype_in('[1,3,5,7,9,11]') < agtype_in('"string"');
SELECT agtype_in('{"bool":true, "integer":1}') < agtype_in('[1,3,5,7,9,11]');
SELECT agtype_in('[1, "string"]') < agtype_in('[1, 1]');
SELECT agtype_in('{"bool":true, "integer":1}') < agtype_in('{"bool":true, "integer":null}');
+SELECT agtype_in('{"id":0, "label": "v", "properties":{"i":0}}::vertex') < agtype_in('{"bool":true, "i":0}');
+SELECT agtype_in('{"id":2, "start_id":0, "end_id":1, "label": "e", "properties":{"i":0}}::edge') < agtype_in('{"id":0, "label": "v", "properties":{"i":0}}::vertex');
+SELECT agtype_in('[{"id": 0, "label": "v", "properties": {"i": 0}}::vertex, {"id": 2, "start_id": 0, "end_id": 1, "label": "e", "properties": {"i": 0}}::edge, {"id": 1, "label": "v", "properties": {"i": 0}}::vertex]::path') < agtype_in('{"id":2, "start_id":0, "end_id":1, "label": "e", "properties":{"i":0}}::edge');
SELECT agtype_in('1::numeric') < agtype_in('null');
SELECT agtype_in('true') < agtype_in('1::numeric');
+-- Testing orderability between types
+SELECT * FROM create_graph('orderability_graph');
+SELECT * FROM cypher('orderability_graph', $$ CREATE (:vertex {prop: null}), (:vertex {prop: 1}), (:vertex {prop: 1.01}),(:vertex {prop: true}), (:vertex {prop:"string"}),(:vertex {prop:"string_2"}), (:vertex {prop:[1, 2, 3]}), (:vertex {prop:[1, 2, 3, 4, 5]}), (:vertex {prop:{bool:true, i:0}}), (:vertex {prop:{bool:true, i:null}}), (:vertex {prop: {id:0, label: "v", properties:{i:0}}::vertex}), (:vertex {prop: {id: 2, start_id: 0, end_id: 1, label: "e", properties: {i: 0}}::edge}), (:vertex {prop: [{id: 0, label: "v", properties: {i: 0}}::vertex, {id: 2, start_id: 0, end_id: 1, label: "e", properties: {i: 0}}::edge, {id: 1, label: "v", properties: {i: 0}}::vertex]::path}) $$) AS (x agtype);
+SELECT * FROM cypher('orderability_graph', $$ MATCH (n) RETURN n ORDER BY n.prop $$) AS (sorted agtype);
+SELECT * FROM cypher('orderability_graph', $$ MATCH (n) RETURN n ORDER BY n.prop DESC $$) AS (sorted agtype);
+SELECT * FROM drop_graph('orderability_graph', true);
--
-- Test overloaded agytype any comparison operators =, <>, <, >, <=, >=,
@@ -525,8 +534,8 @@ SELECT agtype_in('true') < '1::numeric';
SELECT agtype_to_bool(agtype_in('true'));
SELECT agtype_to_bool(agtype_in('false'));
-- These should all fail
-SELECT agtype_to_bool(agtype_in('null'));
SELECT agtype_to_bool(agtype_in('1'));
+SELECT agtype_to_bool(agtype_in('null'));
SELECT agtype_to_bool(agtype_in('1.0'));
SELECT agtype_to_bool(agtype_in('"string"'));
SELECT agtype_to_bool(agtype_in('[1,2,3]'));
@@ -541,6 +550,30 @@ SELECT bool_to_agtype(null);
SELECT bool_to_agtype(true) = bool_to_agtype(true);
SELECT bool_to_agtype(true) <> bool_to_agtype(false);
+--
+-- Test boolean to pg_bigint cast
+--
+SELECT agtype_to_int8(agtype_in('true'));
+SELECT agtype_to_int8(agtype_in('false'));
+
+--
+-- Test boolean to integer cast
+--
+SELECT agtype_to_int4(agtype_in('true'));
+SELECT agtype_to_int4(agtype_in('false'));
+SELECT agtype_to_int4(agtype_in('null'));
+
+--
+-- Test agtype to integer cast
+--
+SELECT agtype_to_int4(agtype_in('1'));
+SELECT agtype_to_int4(agtype_in('1.45'));
+SELECT agtype_to_int4(agtype_in('1.444::numeric'));
+-- These should all fail
+SELECT agtype_to_int4(agtype_in('"string"'));
+SELECT agtype_to_int4(agtype_in('[1, 2, 3]'));
+SELECT agtype_to_int4(agtype_in('{"int":1}'));
+
--
-- Test agtype to int[]
--
diff --git a/regress/sql/catalog.sql b/regress/sql/catalog.sql
index 5ae04fccd..96c63e47a 100644
--- a/regress/sql/catalog.sql
+++ b/regress/sql/catalog.sql
@@ -25,7 +25,7 @@ SET search_path TO ag_catalog;
--
SELECT create_graph('graph');
-SELECT * FROM ag_graph WHERE name = 'graph';
+SELECT name, namespace FROM ag_graph WHERE name = 'graph';
-- create a label to test drop_label()
SELECT * FROM cypher('graph', $$CREATE (:l)$$) AS r(a agtype);
diff --git a/regress/sql/cypher_call.sql b/regress/sql/cypher_call.sql
index 9f600a96f..236b0e7ae 100644
--- a/regress/sql/cypher_call.sql
+++ b/regress/sql/cypher_call.sql
@@ -35,8 +35,8 @@ CREATE FUNCTION call_stmt_test.add_agtype(agtype, agtype) RETURNS agtype
IMMUTABLE
RETURNS NULL ON NULL INPUT;
-/*
- * CALL (solo)
+/*
+ * CALL (solo)
*/
SELECT * FROM cypher('cypher_call', $$CALL sqrt(64)$$) as (sqrt agtype);
@@ -49,7 +49,7 @@ SELECT * FROM cypher('cypher_call', $$CALL sqrt(64) YIELD squirt$$) as (sqrt agt
/* qualified name */
SELECT * FROM cypher('cypher_call', $$CALL call_stmt_test.add_agtype(1,2)$$) as (sqrt agtype);
-/* non-existent schema should fail */
+/* nonexistent schema should fail */
SELECT * FROM cypher('cypher_call', $$CALL ag_catalog.add_agtype(1,2)$$) as (sqrt agtype);
/* CALL YIELD WHERE, should fail */
@@ -106,4 +106,4 @@ SELECT * FROM cypher('cypher_call', $$ CALL sqrt(64) YIELD sqrt AS sqrt1 CALL sq
SELECT * FROM cypher('cypher_call', $$ CALL sqrt(64) YIELD sqrt CALL agtype_sum(2,2) YIELD agtype_sum AS sqrt RETURN sqrt, sqrt $$) as (a agtype, b agtype);
DROP SCHEMA call_stmt_test CASCADE;
-SELECT drop_graph('cypher_call', true);
\ No newline at end of file
+SELECT drop_graph('cypher_call', true);
diff --git a/regress/sql/cypher_create.sql b/regress/sql/cypher_create.sql
index 5934dcb18..dd9506982 100644
--- a/regress/sql/cypher_create.sql
+++ b/regress/sql/cypher_create.sql
@@ -299,6 +299,92 @@ SELECT * FROM cypher('cypher_create', $$ CREATE (a:Part {part_num: '673'}) $$) a
SELECT * FROM cypher('cypher_create', $$ MATCH (a:Part) RETURN a $$) as (a agtype);
END;
+--
+-- variable reuse
+--
+
+-- Valid variable reuse
+
+SELECT * FROM cypher('cypher_create', $$
+ CREATE (p)-[a:new]->(p)
+ RETURN p,a,p
+$$) as (n1 agtype, e agtype, n2 agtype);
+
+SELECT * FROM cypher('cypher_create', $$
+ CREATE (p:node)-[e:new]->(p)
+ RETURN p,e,p
+$$) as (n1 agtype, e agtype, n2 agtype);
+
+SELECT * FROM cypher('cypher_create', $$
+ CREATE (p)
+ CREATE (p)-[a:new]->(p)
+ RETURN p,a,p
+$$) as (n1 agtype, e agtype, n2 agtype);
+
+SELECT * FROM cypher('cypher_create', $$
+ CREATE (p:n1)
+ CREATE (p)-[a:new]->(p)
+ RETURN p,a,p
+$$) as (n1 agtype, e agtype, n2 agtype);
+
+SELECT * FROM cypher('cypher_create', $$
+ MATCH (p:node)
+ CREATE (p)-[a:new]->(p)
+ RETURN p,a,p
+$$) as (n1 agtype, e agtype, n2 agtype);
+
+-- Invalid variable reuse
+
+SELECT * FROM cypher('cypher_create', $$
+ CREATE (p)-[a:new]->(p {n0:'n1'})
+$$) as (a agtype);
+
+SELECT * FROM cypher('cypher_create', $$
+ CREATE (p:n0)-[a:new]->(p:n1)
+$$) as (a agtype);
+
+SELECT * FROM cypher('cypher_create', $$
+ CREATE p=(p)
+$$) as (a agtype);
+
+SELECT * FROM cypher('cypher_create', $$
+ CREATE p=() CREATE (p)
+$$) as (a agtype);
+
+SELECT * FROM cypher('cypher_create', $$
+ CREATE p=(a)-[p:b]->(a)
+$$) as (a agtype);
+
+SELECT * FROM cypher('cypher_create', $$
+ CREATE p=(a)-[:new]->(p)
+$$) as (a agtype);
+
+SELECT * FROM cypher('cypher_create', $$
+ MATCH (p) CREATE p=()
+$$) as (a agtype);
+
+SELECT * FROM cypher('cypher_create', $$
+ MATCH (p) CREATE p=(p)
+$$) as (a agtype);
+
+SELECT * FROM cypher('cypher_create', $$
+ MATCH (p) CREATE (a)-[p:b]->(a)
+$$) as (a agtype);
+
+SELECT * FROM cypher('cypher_create', $$
+ CREATE (a)-[e:new]->(p)-[e]->(a)
+$$) as (a agtype);
+
+SELECT * FROM cypher('cypher_create', $$
+ CREATE (a)-[e:new]->(p)
+ CREATE (p)-[e:new]->(a)
+$$) as (a agtype);
+
+SELECT * FROM cypher('cypher_create', $$
+ MATCH (a)-[e:new]->(p)
+ CREATE (p)-[e:new]->(a)
+$$) as (a agtype);
+
--
-- Clean up
--
diff --git a/regress/sql/cypher_delete.sql b/regress/sql/cypher_delete.sql
index 05accc5b2..38d911683 100644
--- a/regress/sql/cypher_delete.sql
+++ b/regress/sql/cypher_delete.sql
@@ -43,18 +43,18 @@ SELECT * FROM cypher('cypher_delete', $$MATCH()-[e]->() DELETE e RETURN e$$) AS
--Cleanup
SELECT * FROM cypher('cypher_delete', $$MATCH(n) DELETE n RETURN n$$) AS (a agtype);
---Test 4: DETACH DELECT a vertex
+--Test 4: DETACH DELETE a vertex
SELECT * FROM cypher('cypher_delete', $$CREATE (:v)-[:e]->(:v)$$) AS (a agtype);
SELECT * FROM cypher('cypher_delete', $$MATCH(n1)-[e]->(n2) DETACH DELETE n1 RETURN e$$) AS (a agtype);
--Cleanup
SELECT * FROM cypher('cypher_delete', $$MATCH(n) RETURN n$$) AS (a agtype);
---Test 4: DETACH DELECT two vertices tied to the same edge
+--Test 4: DETACH DELETE two vertices tied to the same edge
SELECT * FROM cypher('cypher_delete', $$CREATE (:v)-[:e]->(:v)$$) AS (a agtype);
SELECT * FROM cypher('cypher_delete', $$MATCH(n1)-[e]->(n2) DETACH DELETE n1, n2 RETURN e$$) AS (a agtype);
---Test 4: DETACH DELECT a vertex
+--Test 4: DETACH DELETE a vertex
SELECT * FROM cypher('cypher_delete', $$CREATE (:v)-[:e]->(:v)$$) AS (a agtype);
SELECT * FROM cypher('cypher_delete', $$MATCH(n1)-[e]->(n2) DETACH DELETE n1, n2 RETURN e$$) AS (a agtype);
diff --git a/regress/sql/cypher_match.sql b/regress/sql/cypher_match.sql
index 2d9c5758e..fb6508423 100644
--- a/regress/sql/cypher_match.sql
+++ b/regress/sql/cypher_match.sql
@@ -276,23 +276,139 @@ SELECT * FROM cypher('cypher_match', $$
MATCH (a)-[]-()-[]-(a:v1) RETURN a
$$) AS (a agtype);
SELECT * FROM cypher('cypher_match', $$
- MATCH (a)-[]-(a:v2)-[]-(a) RETURN a
+ MATCH (a)-[]-(a:v2)-[]-(a) RETURN a
$$) AS (a agtype);
SELECT * FROM cypher('cypher_match', $$
- MATCH (a)-[]-(a:v1) RETURN a
+ MATCH (a)-[]-(a:v1) RETURN a
$$) AS (a agtype);
SELECT * FROM cypher('cypher_match', $$
- MATCH (a)-[]-(a)-[]-(a:v1) RETURN a
+ MATCH (a)-[]-(a)-[]-(a:v1) RETURN a
$$) AS (a agtype);
SELECT * FROM cypher('cypher_match', $$
- MATCH (a)-[]-(a)-[]-(a:invalid_label) RETURN a
+ MATCH (a)-[]-(a)-[]-(a:invalid_label) RETURN a
+$$) AS (a agtype);
+SELECT * FROM cypher('cypher_match', $$
+ MATCH (a) MATCH (a:v1) RETURN a
+$$) AS (a agtype);
+SELECT * FROM cypher('cypher_match', $$
+ MATCH (a) MATCH (a:invalid_label) RETURN a
$$) AS (a agtype);
-
---Valid variable reuse, although why would you want to do it this way?
SELECT * FROM cypher('cypher_match', $$
MATCH (a:v1)-[]-()-[a]-() RETURN a
$$) AS (a agtype);
+-- valid variable reuse for edge labels across clauses
+SELECT * FROM cypher('cypher_match', $$
+ MATCH ()-[r0]->() MATCH ()-[r0]->() RETURN r0
+$$) AS (r0 agtype);
+SELECT * FROM cypher('cypher_match', $$
+ MATCH ()-[r0:e1]->() MATCH ()-[r0:e1]->() RETURN r0
+$$) AS (r0 agtype);
+SELECT * FROM cypher('cypher_match', $$
+ MATCH ()-[r0:e2]->() MATCH ()-[r0:e2]->() RETURN r0
+$$) AS (r0 agtype);
+SELECT * FROM cypher('cypher_match', $$
+ MATCH ()-[r0:e1]->()-[r1]->() RETURN r0,r1
+$$) AS (r0 agtype, r1 agtype);
+SELECT * FROM cypher('cypher_match', $$
+ MATCH p0=()-[:e1]->() MATCH p1=()-[:e2]->() RETURN p1
+$$) AS (p1 agtype);
+SELECT * FROM cypher('cypher_match', $$
+ MATCH ()-[r0:e1]->()-[r1]->() MATCH ()-[r0:e1]->()-[r1]->() RETURN r0,r1
+$$) AS (r0 agtype, r1 agtype);
+SELECT * FROM cypher('cypher_match', $$
+ MATCH ()-[]->() MATCH ()-[r1:e2]->() RETURN r1
+$$) AS (r1 agtype);
+SELECT * FROM cypher('cypher_match', $$
+ MATCH ()-[r0:e1]->() MATCH ()-[r1:e2]->() RETURN r0,r1
+$$) AS (r0 agtype, r1 agtype);
+
+-- valid variable reuse for vertex labels across clauses
+SELECT * FROM cypher('cypher_match', $$
+ MATCH (r1:invalid), (r1:invalid) return r1
+$$) AS (r1 agtype);
+SELECT * FROM cypher('cypher_match', $$
+ MATCH (r1), (r1) return r1
+$$) AS (r1 agtype);
+SELECT * FROM cypher('cypher_match', $$
+ MATCH (r1:invalid), (r1) return r1
+$$) AS (r1 agtype);
+SELECT * FROM cypher('cypher_match', $$
+ MATCH (r1:invalid), (r1), (r1), (r1:invalid) return r1
+$$) AS (r1 agtype);
+SELECT * FROM cypher('cypher_match', $$
+ MATCH (r1:invalid)-[]->(r1)-[]->(r1:invalid)-[]->(r1) return r1
+$$) AS (r1 agtype);
+SELECT * FROM cypher('cypher_match', $$
+ MATCH (r1:invalid)-[]->()-[]->()-[]->(r1:invalid) return r1
+$$) AS (r1 agtype);
+SELECT * FROM cypher('cypher_match', $$
+ MATCH ()-[r0:e1]->()-[r1]->() MATCH ()-[r0:e1]->()-[r0]->() RETURN r0
+$$) AS (r0 agtype);
+SELECT * FROM cypher('cypher_match', $$
+ MATCH ()-[r0:e1]->() MATCH ()-[r0]->() RETURN r0
+$$) AS (r0 agtype);
+
+-- invalid variable reuse for vertex
+SELECT * FROM cypher('cypher_match', $$
+ MATCH (r1:invalid)-[]->(r1)-[]->(r1)-[]->(r1:invalids) return r1
+$$) AS (r1 agtype);
+SELECT * FROM cypher('cypher_match', $$
+ MATCH (r1:invalid)-[]->(r1)-[]->(r1)-[]->(r1)-[r1]->() return r1
+$$) AS (r1 agtype);
+
+-- invalid variable reuse for labels across clauses
+SELECT * FROM cypher('cypher_match', $$
+ MATCH (r1:e1), (r1:e2) return r1
+$$) AS (r1 agtype);
+SELECT * FROM cypher('cypher_match', $$
+ MATCH (r1:invalid), (r1:e2) return r1
+$$) AS (r1 agtype);
+SELECT * FROM cypher('cypher_match', $$
+ MATCH (r1:e1), (r1:invalid) return r1
+$$) AS (r1 agtype);
+SELECT * FROM cypher('cypher_match', $$
+ MATCH (r1:e1), (r1), (r1:invalid) return r1
+$$) AS (r1 agtype);
+SELECT * FROM cypher('cypher_match', $$
+ MATCH (r0)-[r0]->() MATCH ()-[]->() RETURN r0
+$$) AS (r0 agtype);
+SELECT * FROM cypher('cypher_match', $$
+ MATCH (r0)-[]->() MATCH ()-[r0]->() RETURN r0
+$$) AS (r0 agtype);
+SELECT * FROM cypher('cypher_match', $$
+ MATCH ()-[r0]->() MATCH ()-[]->(r0) RETURN r0
+$$) AS (r0 agtype);
+SELECT * FROM cypher('cypher_match', $$
+ MATCH ()-[r0:e1]->() MATCH ()-[r0:e2]->() RETURN r0
+$$) AS (r0 agtype);
+SELECT * FROM cypher('cypher_match', $$
+ MATCH ()-[r0]->() MATCH ()-[r0:e2]->() RETURN r0
+$$) AS (r0 agtype);
+SELECT * FROM cypher('cypher_match', $$
+ MATCH ()-[r0:e1]->() MATCH ()-[r0:e2]->() RETURN r0
+$$) AS (r0 agtype);
+SELECT * FROM cypher('cypher_match', $$
+ MATCH ()-[r0:e1]->()-[r0]->() MATCH ()-[r0:e2]->() RETURN r0
+$$) AS (r0 agtype);
+SELECT * FROM cypher('cypher_match', $$
+ MATCH ()-[r0:e1]->()-[r1]->() MATCH ()-[r1:e1]->()-[r0]->() RETURN r0
+$$) AS (r0 agtype);
+
+-- Labels that don't exist but do match
+SELECT * FROM cypher('cypher_match', $$
+ MATCH (r0)-[r1:related]->() MATCH ()-[r1:related]->() RETURN r0
+$$) AS (r0 agtype);
+
+-- Labels that don't exist and don't match
+SELECT * FROM cypher('cypher_match', $$
+ MATCH (r0)-[r1]->() MATCH ()-[r1:related]->() RETURN r0
+$$) AS (r0 agtype);
+SELECT * FROM cypher('cypher_match', $$
+ MATCH (r0)-[r1:related]->() MATCH ()-[r1:relateds]->() RETURN r0
+$$) AS (r0 agtype);
+
+--Valid variable reuse, although why would you want to do it this way?
SELECT * FROM cypher('cypher_match', $$
MATCH (a:v1)-[]-()-[]-(a {id:'will_not_fail'}) RETURN a
$$) AS (a agtype);
@@ -370,12 +486,6 @@ SELECT * FROM cypher('cypher_match',
$$MATCH (u)-[e]->(v) WHERE EXISTS((v)-[e]->(v)) RETURN u, e, v $$)
AS (u agtype, e agtype, v agtype);
--- Exists checks for a loop. There should be none because of edge uniqueness
--- requirement.
-SELECT * FROM cypher('cypher_match',
- $$MATCH (u)-[e]->(v) WHERE EXISTS((u)-[e]->(u)-[e]->(u)) RETURN u, e, v $$)
-AS (u agtype, e agtype, v agtype);
-
-- Multiple exists
SELECT * FROM cypher('cypher_match',
$$MATCH (u)-[e]->(v) WHERE EXISTS((u)) AND EXISTS((v)) RETURN u, e, v $$)
@@ -831,10 +941,60 @@ SELECT * FROM cypher('cypher_match', $$ MATCH p=(a {name: "Dave"})-[]->()-[]->(a
SELECT * FROM cypher('cypher_match', $$ MATCH p=(a {name: "John"})-[]->()-[]->(a) RETURN p $$)as (p agtype);
-- these are illegal and should fail
-SELECT * FROM cypher('cypher_match', $$ MATCH p=(a)-[b]->()-[b]->(a) RETURN p $$)as (p agtype);
SELECT * FROM cypher('cypher_match', $$ MATCH p=(a)-[b]->()-[b:knows]->(a) RETURN p $$)as (p agtype);
+SELECT * FROM cypher('cypher_match', $$ MATCH p=(a)-[b]->()-[b]->(a) RETURN p $$)as (p agtype);
SELECT * FROM cypher('cypher_match', $$ MATCH p=(a)-[b:knows]->()-[b:knows]->(a) RETURN p $$)as (p agtype);
SELECT * FROM cypher('cypher_match', $$ MATCH p=(a)-[b:knows]->()-[b]->(a) RETURN p $$)as (p agtype);
+SELECT * FROM cypher('cypher_match', $$ MATCH p=(p) RETURN p $$)as (p agtype);
+SELECT * FROM cypher('cypher_match', $$ MATCH p=(p)-[]->() RETURN p $$)as (p agtype);
+SELECT * FROM cypher('cypher_match', $$ MATCH p=()-[p]->() RETURN p $$)as (p agtype);
+SELECT * FROM cypher('cypher_match', $$ MATCH p=() MATCH (p) RETURN p $$)as (p agtype);
+SELECT * FROM cypher('cypher_match', $$ MATCH p=() MATCH (p)-[]->() RETURN p $$)as (p agtype);
+SELECT * FROM cypher('cypher_match', $$ MATCH p=() MATCH ()-[p]->() RETURN p $$)as (p agtype);
+SELECT * FROM cypher('cypher_match', $$ MATCH (p) MATCH p=() RETURN p $$)as (p agtype);
+SELECT * FROM cypher('cypher_match', $$ MATCH ()-[p]->() MATCH p=() RETURN p $$)as (p agtype);
+SELECT * FROM cypher('cypher_match', $$ MATCH ()-[p *]-()-[p]-() RETURN 0 $$)as (p agtype);
+SELECT * FROM cypher('cypher_match', $$ CREATE (p) WITH p MATCH p=() RETURN p $$)as (p agtype);
+SELECT * FROM cypher('cypher_match', $$ CREATE p=() WITH p MATCH (p) RETURN p $$)as (p agtype);
+SELECT * FROM cypher('cypher_match', $$ CREATE ()-[p:knows]->() WITH p MATCH p=() RETURN p $$)as (p agtype);
+SELECT * FROM cypher('cypher_match', $$ CREATE p=() WITH p MATCH ()-[p]->() RETURN p $$)as (p agtype);
+
+
+
+--
+-- Default alias check (issue #883)
+--
+SELECT * FROM cypher('cypher_match', $$ MATCH (_) RETURN _ $$) as (a agtype);
+SELECT * FROM cypher('cypher_match', $$ MATCH () MATCH (_{name: "Dave"}) RETURN 0 $$) as (a agtype);
+SELECT * FROM cypher('cypher_match', $$ MATCH () MATCH (_{name: "Dave"}) RETURN _ $$) as (a agtype);
+SELECT * FROM cypher('cypher_match', $$ MATCH (my_age_default_{name: "Dave"}) RETURN my_age_default_$$) as (a agtype);
+SELECT * FROM cypher('cypher_match', $$ MATCH () MATCH (my_age_default_{name: "Dave"}) RETURN my_age_default_$$) as (a agtype);
+
+-- these should fail as they are prefixed with _age_default_ which is only for internal use
+SELECT * FROM cypher('cypher_match', $$ MATCH (_age_default_) RETURN _age_default_ $$) as (a agtype);
+SELECT * FROM cypher('cypher_match', $$ MATCH (_age_default_a) RETURN _age_default_a $$) as (a agtype);
+SELECT * FROM cypher('cypher_match', $$ MATCH (_age_default_whatever) RETURN 0 $$) as (a agtype);
+
+-- issue 876
+SELECT * FROM cypher('cypher_match', $$ MATCH ({name: "Dave"}) MATCH ({name: "Dave"}) MATCH ({name: "Dave"}) RETURN 0 $$) as (a agtype);
+SELECT * FROM cypher('cypher_match', $$MATCH ({n0:0}) MATCH ()-[]->() MATCH ({n1:0})-[]-() RETURN 0 AS n2$$) as (a agtype);
+
+--
+-- self referencing property constraints (issue #898)
+--
+SELECT * FROM cypher('cypher_match', $$ MATCH (a {name:a.name}) RETURN a $$) as (a agtype);
+SELECT * FROM cypher('cypher_match', $$ MATCH (a {name:a.name, age:a.age}) RETURN a $$) as (a agtype);
+SELECT * FROM cypher('cypher_match', $$ MATCH (a {name:a.name}) MATCH (a {age:a.age}) RETURN a $$) as (a agtype);
+
+SELECT * FROM cypher('cypher_match', $$ MATCH p=(a)-[u {relationship: u.relationship}]->(b) RETURN p $$) as (a agtype);
+SELECT * FROM cypher('cypher_match', $$ MATCH p=(a)-[u {relationship: u.relationship, years: u.years}]->(b) RETURN p $$) as (a agtype);
+SELECT * FROM cypher('cypher_match', $$ MATCH p=(a {name:a.name})-[u {relationship: u.relationship}]->(b {age:b.age}) RETURN p $$) as (a agtype);
+
+SELECT * FROM cypher('cypher_match', $$ CREATE () WITH * MATCH (x{n0:x.n1}) RETURN 0 $$) as (a agtype);
+
+-- these should fail due to multiple labels for a variable
+SELECT * FROM cypher('cypher_match', $$ MATCH p=(x)-[]->(x:R) RETURN p, x $$) AS (p agtype, x agtype);
+SELECT * FROM cypher('cypher_match', $$ MATCH p=(x:r)-[]->(x:R) RETURN p, x $$) AS (p agtype, x agtype);
--
-- Clean up
diff --git a/regress/sql/cypher_merge.sql b/regress/sql/cypher_merge.sql
index 6cd4207ea..9d2a11d67 100644
--- a/regress/sql/cypher_merge.sql
+++ b/regress/sql/cypher_merge.sql
@@ -30,7 +30,7 @@ SELECT create_graph('cypher_merge');
* test 1: Single MERGE Clause, path doesn't exist
*/
--test query
-SELECT * FROM cypher('cypher_merge', $$MERGE (n {i: "Hello Merge"})$$) AS (a agtype);
+SELECT * FROM cypher('cypher_merge', $$MERGE (n {i: "Hello Merge", j: (null IS NULL), k: (null IS NOT NULL)})$$) AS (a agtype);
--validate
SELECT * FROM cypher('cypher_merge', $$MATCH (n) RETURN n$$) AS (n agtype);
@@ -42,10 +42,11 @@ SELECT * FROM cypher('cypher_merge', $$MATCH (n) DETACH DELETE n $$) AS (a agtyp
* test 2: Single MERGE Clause, path exists
*/
--data setup
-SELECT * FROM cypher('cypher_merge', $$CREATE ({i: "Hello Merge"}) $$) AS (a agtype);
+SELECT * FROM cypher('cypher_merge', $$CREATE ({i: "Hello Merge", j: (null IS NULL)}) $$) AS (a agtype);
--test_query
SELECT * FROM cypher('cypher_merge', $$MERGE ({i: "Hello Merge"})$$) AS (a agtype);
+SELECT * FROM cypher('cypher_merge', $$MERGE ({j: (null IS NULL)})$$) AS (a agtype);
--validate
SELECT * FROM cypher('cypher_merge', $$MATCH (n) RETURN n$$) AS (n agtype);
@@ -79,7 +80,7 @@ SELECT * FROM cypher('cypher_merge', $$MATCH (n) RETURN n$$) AS (n agtype);
SELECT * FROM cypher('cypher_merge', $$MATCH (n) DETACH DELETE n $$) AS (a agtype);
/*
- * test 5: Prev clause has results, path does not exist (differnt property name)
+ * test 5: Prev clause has results, path does not exist (different property name)
*/
--data setup
SELECT * FROM cypher('cypher_merge', $$CREATE ({i: "Hello Merge"}) $$) AS (a agtype);
@@ -203,7 +204,7 @@ SELECT * FROM cypher('cypher_merge', $$MERGE ()-[:e]->()-[:e]->()$$) AS (a agtyp
--validate created correctly
--Returns 3. One for the data setup and 2 for the longer path in MERGE
-SELECT count(*) FROM cypher('cypher_merge', $$MATCH p=()-[e:e]->() RETURN p$$) AS (p agtype)
+SELECT count(*) FROM cypher('cypher_merge', $$MATCH p=()-[e:e]->() RETURN p$$) AS (p agtype);
-- Returns 1, the path created in MERGE
SELECT count(*) FROM cypher('cypher_merge', $$MATCH p=()-[:e]->()-[]->() RETURN p$$) AS (p agtype);
@@ -215,7 +216,7 @@ SELECT count(*) FROM cypher('cypher_merge', $$MATCH (n) RETURN n$$) AS (n agtype
SELECT * FROM cypher('cypher_merge', $$MATCH (n) DETACH DELETE n $$) AS (a agtype);
/*
- * test 13: edge doesn't exists (differnt label), using MATCH
+ * test 13: edge doesn't exists (different label), using MATCH
*/
-- setup
SELECT * FROM cypher('cypher_merge', $$CREATE ()-[:e]->() $$) AS (a agtype);
@@ -277,7 +278,6 @@ SELECT * FROM cypher('cypher_merge', $$MATCH (n) DETACH DELETE n $$) AS (a agtyp
/*
* test 17:
- * XXX: Incorrect Output. To FIX
*/
--test query
@@ -479,6 +479,51 @@ SELECT * FROM cypher('cypher_merge', $$ MATCH (n:node) RETURN n $$) AS (n agtype
SELECT * FROM cypher('cypher_merge', $$ MERGE (n:node {name: 'Jason'}) SET n.name = 'Lisa', n.age = 23, n.gender = 'Female' RETURN n $$) AS (n agtype);
SELECT * FROM cypher('cypher_merge', $$ MATCH (n:node) RETURN n $$) AS (n agtype);
+--
+-- Complex MERGE w/wo RETURN values
+--
+-- The first one should create a path, the others should just return parts of it.
+SELECT * FROM cypher('cypher_merge', $$ MERGE ()-[:B]->(x:C)-[:E]->(x:C)<-[f:F]-(y:I) $$) AS (x agtype);
+SELECT * FROM cypher('cypher_merge', $$ MERGE ()-[:B]->(x:C)-[:E]->(x:C)<-[f:F]-(y:I) RETURN x $$) AS (x agtype);
+SELECT * FROM cypher('cypher_merge', $$ MERGE p=()-[:B]->(x:C)-[:E]->(x:C)<-[f:F]-(y:I) $$) AS (p agtype);
+SELECT * FROM cypher('cypher_merge', $$ MERGE p=()-[:B]->(x:C)-[:E]->(x:C)<-[f:F]-(y:I) RETURN p $$) AS (p agtype);
+SELECT * FROM cypher('cypher_merge', $$ MERGE p=()-[:B]->(x:C)-[:E]->(x:C)<-[f:F]-(y:I) RETURN p $$) AS (p agtype);
+
+-- This should only return 1 row, as the path should already exist.
+SELECT * FROM cypher('cypher_merge', $$ MATCH p=()-[:B]->(:C)-[:E]->(:C)<-[:F]-(:I) RETURN p $$) AS (p agtype);
+
+-- test variable reuse in MERGE - the first MERGE of each group should create,
+-- the second MERGE shouldn't.
+SELECT * FROM cypher('cypher_merge', $$ MATCH p=(x:P)-[:E]->(x:P) RETURN p, x $$) AS (p agtype, x agtype);
+SELECT * FROM cypher('cypher_merge', $$ MERGE (x:P)-[:E]->(x:P) $$) AS (x agtype);
+SELECT * FROM cypher('cypher_merge', $$ MERGE (x:P)-[:E]->(x) $$) AS (x agtype);
+SELECT * FROM cypher('cypher_merge', $$ MATCH p=(x:P)-[:E]->(x) RETURN p, x $$) AS (p agtype, x agtype);
+SELECT * FROM cypher('cypher_merge', $$ MATCH p=(x:Q)-[:E]->(x:Q) RETURN p, x $$) AS (p agtype, x agtype);
+SELECT * FROM cypher('cypher_merge', $$ MERGE (x:Q)-[:E]->(x) $$) AS (x agtype);
+SELECT * FROM cypher('cypher_merge', $$ MERGE (x:Q)-[:E]->(x:Q) $$) AS (x agtype);
+SELECT * FROM cypher('cypher_merge', $$ MATCH p=(x:Q)-[:E]->(x) RETURN p, x $$) AS (p agtype, x agtype);
+SELECT * FROM cypher('cypher_merge', $$ MATCH p=(x:R)-[:E]->(x) RETURN p, x $$) AS (p agtype, x agtype);
+SELECT * FROM cypher('cypher_merge', $$ MERGE p=(x:R)-[:E]->(x) RETURN p, x $$) AS (p agtype, x agtype);
+SELECT * FROM cypher('cypher_merge', $$ MERGE p=(x:R)-[:E]->(x) RETURN p, x $$) AS (p agtype, x agtype);
+SELECT * FROM cypher('cypher_merge', $$ MATCH p=(x:R)-[:E]->(x) RETURN p, x $$) AS (p agtype, x agtype);
+-- should return 4 rows
+SELECT * FROM cypher('cypher_merge', $$ MERGE p=(x)-[:E]->(x) RETURN p, x $$) AS (p agtype, x agtype);
+-- should create 1 row
+SELECT * FROM cypher('cypher_merge', $$ MERGE p=(x)-[:E1]->(x) RETURN p, x $$) AS (p agtype, x agtype);
+SELECT * FROM cypher('cypher_merge', $$ MATCH p=(x)-[:E1]->(x) RETURN p, x $$) AS (p agtype, x agtype);
+-- the following should fail due to multiple labels
+SELECT * FROM cypher('cypher_merge', $$ MERGE p=(x)-[:E]->(x:R) RETURN p, x $$) AS (p agtype, x agtype);
+SELECT * FROM cypher('cypher_merge', $$ MERGE p=(x:r)-[:E]->(x:R) RETURN p, x $$) AS (p agtype, x agtype);
+SELECT * FROM cypher('cypher_merge', $$ MERGE (x)-[:E]->(x:R) $$) AS (x agtype);
+SELECT * FROM cypher('cypher_merge', $$ MERGE (x:r)-[:E]->(x:R) $$) AS (x agtype);
+-- the following should fail due to reuse issues
+SELECT * FROM cypher('cypher_merge', $$ MERGE (x:r)-[y:E]->(x)-[y]->(x) $$) AS (x agtype);
+SELECT * FROM cypher('cypher_merge', $$ MERGE (x:r)-[y:E]->(x)-[x]->(y) $$) AS (x agtype);
+SELECT * FROM cypher('cypher_merge', $$ MERGE (x:r)-[y:E]->(x)-[z:E]->(y) $$) AS (x agtype);
+SELECT * FROM cypher('cypher_merge', $$ MERGE p=(x:r)-[y:E]->(x)-[p]->(x) $$) AS (x agtype);
+SELECT * FROM cypher('cypher_merge', $$ MERGE p=(x:r)-[y:E]->(x)-[p:E]->(x) $$) AS (x agtype);
+SELECT * FROM cypher('cypher_merge', $$ MERGE p=(x:r)-[y:E]->(p)-[x]->(y) $$) AS (x agtype);
+
--clean up
SELECT * FROM cypher('cypher_merge', $$MATCH (n) DETACH DELETE n $$) AS (a agtype);
diff --git a/regress/sql/cypher_set.sql b/regress/sql/cypher_set.sql
index 2b1354edb..484cbff7d 100644
--- a/regress/sql/cypher_set.sql
+++ b/regress/sql/cypher_set.sql
@@ -38,6 +38,10 @@ SELECT * FROM cypher('cypher_set', $$MATCH (n) RETURN n$$) AS (a agtype);
SELECT * FROM cypher('cypher_set', $$MATCH (n) SET n.i = 3 RETURN n$$) AS (a agtype);
SELECT * FROM cypher('cypher_set', $$MATCH (n) RETURN n$$) AS (a agtype);
+--Test assigning properties to rand() and pi()
+SELECT * FROM cypher('cypher_set', $$MATCH (n) SET n.i = rand() RETURN n.i < 1 AND n.i >= 0$$) AS (a agtype);
+SELECT * FROM cypher('cypher_set', $$MATCH (n) SET n.i = pi() RETURN n$$) AS (a agtype);
+
--Handle Inheritance
SELECT * FROM cypher('cypher_set', $$CREATE ()$$) AS (a agtype);
SELECT * FROM cypher('cypher_set', $$MATCH (n) SET n.i = 3 RETURN n$$) AS (a agtype);
@@ -163,7 +167,7 @@ SELECT set_test();
SELECT set_test();
--
--- Updating multiple fieds
+-- Updating multiple fields
--
SELECT * FROM cypher('cypher_set', $$MATCH (n) SET n.i = 3, n.j = 5 RETURN n $$) AS (a agtype);
@@ -284,7 +288,7 @@ SELECT * FROM cypher('cypher_set_1', $$
RETURN p
$$) AS (p agtype);
--- test assigning non-map to an enitity
+-- test assigning non-map to an entity
SELECT * FROM cypher('cypher_set_1', $$
MATCH (p {name: 'Peter'})
SET p = "Peter"
@@ -312,6 +316,18 @@ SELECT * FROM cypher('cypher_set_1', $$
RETURN p
$$) AS (p agtype);
+--
+-- Check passing mismatched types with SET
+-- Issue 899
+--
+SELECT * FROM cypher('cypher_set_1', $$
+ CREATE (x) SET x.n0 = (true OR true) RETURN x
+$$) AS (p agtype);
+
+SELECT * FROM cypher('cypher_set_1', $$
+ CREATE (x) SET x.n0 = (true OR false), x.n1 = (false AND false), x.n2 = (false = false) RETURN x
+$$) AS (p agtype);
+
--
-- Clean up
--
diff --git a/regress/sql/cypher_vle.sql b/regress/sql/cypher_vle.sql
index 468135eeb..13cf05944 100644
--- a/regress/sql/cypher_vle.sql
+++ b/regress/sql/cypher_vle.sql
@@ -290,6 +290,21 @@ DROP FUNCTION show_list_use_vle;
SELECT drop_graph('mygraph', true);
+-- invalid reuse of VLE
+SELECT * FROM cypher('cypher_vle', $$ MATCH ()-[p]-()-[p *]-() RETURN p $$)as (p agtype);
+SELECT * FROM cypher('cypher_vle', $$ MATCH ()-[p *]-()-[p]-() RETURN p $$)as (p agtype);
+SELECT * FROM cypher('cypher_vle', $$ MATCH (p)-[p *]-() RETURN p $$)as (p agtype);
+SELECT * FROM cypher('cypher_vle', $$ MATCH ()-[p *]-(p) RETURN p $$)as (p agtype);
+SELECT * FROM cypher('cypher_vle', $$ MATCH p=()-[p *]-() RETURN p $$)as (p agtype);
+SELECT * FROM cypher('cypher_vle', $$ MATCH ()-[p *]-()-[p *]-() RETURN p $$)as (p agtype);
+SELECT * FROM cypher('cypher_vle', $$ MATCH ()-[p]-() MATCH ()-[p *]-() RETURN p $$)as (p agtype);
+SELECT * FROM cypher('cypher_vle', $$ MATCH ()-[p *]-() MATCH ()-[p]-() RETURN p $$)as (p agtype);
+SELECT * FROM cypher('cypher_vle', $$ MATCH ()-[p *]-() MATCH ()-[p *]-() RETURN p $$)as (p agtype);
+SELECT * FROM cypher('cypher_vle', $$ MATCH (p) MATCH ()-[p *]-() RETURN p $$)as (p agtype);
+SELECT * FROM cypher('cypher_vle', $$ MATCH ()-[p *]-() MATCH (p) RETURN p $$)as (p agtype);
+SELECT * FROM cypher('cypher_vle', $$ MATCH p=() MATCH ()-[p *]-() RETURN p $$)as (p agtype);
+SELECT * FROM cypher('cypher_vle', $$ MATCH ()-[p *]-() MATCH p=() RETURN p $$)as (p agtype);
+
--
-- Clean up
--
diff --git a/regress/sql/expr.sql b/regress/sql/expr.sql
index 8553b3c3c..c9547e332 100644
--- a/regress/sql/expr.sql
+++ b/regress/sql/expr.sql
@@ -394,6 +394,66 @@ SELECT * FROM cypher('expr', $$
RETURN false XOR false
$$) AS r(result boolean);
+SELECT * FROM cypher('expr', $$
+RETURN false OR 1::bool
+$$) AS (result boolean);
+
+SELECT * FROM cypher('expr', $$
+RETURN false AND NOT 1::bool
+$$) AS (result boolean);
+
+SELECT * FROM cypher('expr', $$
+RETURN NOT 1::bool::int::bool
+$$) AS (result boolean);
+
+-- Invalid operands for AND, OR, NOT, XOR
+SELECT * FROM cypher('expr', $$
+RETURN 1 AND true
+$$) AS r(result boolean);
+
+SELECT * FROM cypher('expr', $$
+RETURN true AND 0
+$$) AS r(result boolean);
+
+SELECT * FROM cypher('expr', $$
+RETURN false OR 1
+$$) AS r(result boolean);
+
+SELECT * FROM cypher('expr', $$
+RETURN 0 OR true
+$$) AS r(result boolean);
+
+SELECT * FROM cypher('expr', $$
+RETURN NOT 1
+$$) AS r(result boolean);
+
+SELECT * FROM cypher('expr', $$
+RETURN true XOR 0
+$$) AS r(result boolean);
+
+SELECT * FROM cypher('expr', $$
+RETURN 1 XOR 0
+$$) AS r(result boolean);
+
+SELECT * FROM cypher('expr', $$
+RETURN NOT ((1 OR 0) AND (0 OR 1))
+$$) AS r(result boolean);
+
+SELECT * FROM cypher('expr', $$
+RETURN 1.0 AND true
+$$) AS (result agtype);
+
+SELECT * FROM cypher('expr', $$
+RETURN false OR 'string'
+$$) AS (result agtype);
+
+SELECT * FROM cypher('expr', $$
+RETURN false XOR 1::numeric
+$$) AS (result agtype);
+
+SELECT * FROM cypher('expr', $$
+RETURN false OR 1::bool::int
+$$) AS (result boolean);
--
-- Test indirection transform logic for object.property, object["property"],
-- and array[element]
@@ -484,7 +544,7 @@ MATCH (n:Person) WHERE n.name =~ 'J.*' RETURN n
$$) AS r(result agtype);
--
---Coearce to Postgres 3 int types (smallint, int, bigint)
+--Coerce to Postgres 3 int types (smallint, int, bigint)
--
SELECT create_graph('type_coercion');
SELECT * FROM cypher('type_coercion', $$
@@ -515,6 +575,14 @@ SELECT * FROM cypher('type_coercion', $$
RETURN '1'
$$) AS (i bigint);
+SELECT * FROM cypher('type_coercion', $$
+ RETURN true
+$$) AS (i bigint);
+
+SELECT * FROM cypher('type_coercion', $$
+ RETURN true
+$$) AS (i int);
+
--Invalid String Format
SELECT * FROM cypher('type_coercion', $$
RETURN '1.0'
@@ -530,9 +598,6 @@ SELECT * FROM cypher('type_coercion', $$
$$) AS (i int);
--Invalid types
-SELECT * FROM cypher('type_coercion', $$
- RETURN true
-$$) AS (i bigint);
SELECT * FROM cypher('type_coercion', $$
RETURN {key: 1}
@@ -542,6 +607,10 @@ SELECT * FROM cypher('type_coercion', $$
RETURN [1]
$$) AS (i bigint);
+SELECT * FROM cypher('type_coercion', $$
+ RETURN 1
+$$) AS (i bool);
+
SELECT * FROM cypher('type_coercion', $$CREATE ()-[:edge]->()$$) AS (result agtype);
SELECT * FROM cypher('type_coercion', $$
MATCH (v)
@@ -585,6 +654,12 @@ SELECT * FROM cypher('expr', $$
RETURN 2.71::numeric::int
$$) AS r(result agtype);
SELECT * FROM cypher('expr', $$
+RETURN true::int
+$$) AS r(result agtype);
+SELECT * FROM cypher('expr', $$
+RETURN false::int
+$$) AS r(result agtype);
+SELECT * FROM cypher('expr', $$
RETURN ([0, {one: 1.0, pie: 3.1415927, e: 2::numeric}, 2, null][1].one)::int
$$) AS r(result agtype);
SELECT * FROM cypher('expr', $$
@@ -620,7 +695,29 @@ $$) AS r(result agtype);
SELECT * FROM cypher('expr', $$
RETURN 'infinity'::float::int
$$) AS r(result agtype);
+SELECT * FROM cypher('expr', $$
+RETURN ''::int
+$$) AS r(result agtype);
+SELECT * FROM cypher('expr', $$
+RETURN 'false_'::int
+$$) AS r(result agtype);
+--
+-- Test from an agtype value to agtype int
+--
+SELECT * FROM cypher('expr', $$
+RETURN 0::bool
+$$) AS r(result agtype);
+-- these should fail
+SELECT * FROM cypher('expr', $$
+RETURN 1.23::bool
+$$) AS r(result agtype);
+SELECT * FROM cypher('expr', $$
+RETURN ''::bool
+$$) AS r(result agtype);
+SELECT * FROM cypher('expr', $$
+RETURN 'false_'::bool
+$$) AS r(result agtype);
-- Test from an agtype value to an agtype numeric
--
SELECT * FROM cypher('expr', $$
@@ -750,6 +847,66 @@ RETURN (['NaN'::float, {one: 'inf'::float, pie: 3.1415927, e: 2.718281::numeric}
$$) AS r(result agtype);
SELECT agtype_in('[NaN, {"e": 2.718281::numeric, "one": Infinity, "pie": 3.1415927}, 2::numeric, null]');
+--
+-- Test typecast ::pg_float8
+--
+SELECT * FROM cypher('expr', $$
+RETURN 0::pg_float8
+$$) AS r(result agtype);
+SELECT * FROM cypher('expr', $$
+RETURN '2.71'::pg_float8
+$$) AS r(result agtype);
+SELECT * FROM cypher('expr', $$
+RETURN 2.71::pg_float8
+$$) AS r(result agtype);
+SELECT * FROM cypher('expr', $$
+RETURN ([0, {one: 1, pie: 3.1415927, e: 2::numeric}, 2, null][1].one)::pg_float8
+$$) AS r(result agtype);
+SELECT * FROM cypher('expr', $$
+RETURN ([0, {one: 1::pg_float8, pie: 3.1415927, e: 2.718281::numeric}, 2, null][1].one)
+$$) AS r(result agtype);
+SELECT * FROM cypher('expr', $$
+RETURN ([0, {one: 1::pg_float8, pie: 3.1415927, e: 2.718281::numeric}, 2, null][1].one)::float
+$$) AS r(result agtype);
+SELECT * FROM cypher('expr', $$
+RETURN ([0, {one: 1, pie: 3.1415927, e: 2.718281::numeric}, 2, null][3])::pg_float8
+$$) AS r(result agtype);
+SELECT * FROM cypher('expr', $$
+RETURN (['NaN'::pg_float8, {one: 'inf'::pg_float8, pie: 3.1415927, e: 2.718281::numeric}, 2::numeric, null])
+$$) AS r(result agtype);
+SELECT * FROM cypher('expr', $$
+RETURN ([0, {one: 1, pie: 3.1415927, e: 2.718281::numeric}, 2, null][1].e)::pg_float8
+$$) AS r(result agtype);
+-- test NaN, Infinity and -Infinity
+SELECT * FROM cypher('expr', $$
+RETURN 'NaN'::pg_float8
+$$) AS r(result agtype);
+SELECT * FROM cypher('expr', $$
+RETURN 'inf'::pg_float8
+$$) AS r(result agtype);
+SELECT * FROM cypher('expr', $$
+RETURN '-inf'::pg_float8
+$$) AS r(result agtype);
+SELECT * FROM cypher('expr', $$
+RETURN 'infinity'::pg_float8
+$$) AS r(result agtype);
+SELECT * FROM cypher('expr', $$
+RETURN '-infinity'::pg_float8
+$$) AS r(result agtype);
+SELECT * FROM cypher('expr', $$
+RETURN null::pg_float8
+$$) AS r(result agtype);
+-- these should fail
+SELECT * FROM cypher('expr', $$
+RETURN ''::pg_float8
+$$) AS r(result agtype);
+SELECT * FROM cypher('expr', $$
+RETURN '2:71'::pg_float8
+$$) AS r(result agtype);
+SELECT * FROM cypher('expr', $$
+RETURN 'infi'::pg_float8
+$$) AS r(result agtype);
+
--
-- Test typecast :: transform and execution logic for object (vertex & edge)
--
@@ -1100,7 +1257,7 @@ SELECT * FROM cypher('expr', $$
$$) AS (toBoolean agtype);
-- should return null
SELECT * FROM cypher('expr', $$
- RETURN toBoolean("falze")
+ RETURN toBoolean("false_")
$$) AS (toBoolean agtype);
SELECT * FROM cypher('expr', $$
RETURN toBoolean(null)
@@ -1130,7 +1287,7 @@ SELECT * FROM cypher('expr', $$
$$) AS (toFloat agtype);
-- should return null
SELECT * FROM cypher('expr', $$
- RETURN toFloat("falze")
+ RETURN toFloat("false_")
$$) AS (toFloat agtype);
SELECT * FROM cypher('expr', $$
RETURN toFloat(null)
@@ -1160,7 +1317,7 @@ SELECT * FROM cypher('expr', $$
$$) AS (toInteger agtype);
-- should return null
SELECT * FROM cypher('expr', $$
- RETURN toInteger("falze")
+ RETURN toInteger("false_")
$$) AS (toInteger agtype);
SELECT * FROM cypher('expr', $$
RETURN toInteger(null)
@@ -2100,6 +2257,9 @@ $$) as (result agtype);
SELECT * from cypher('expr', $$
RETURN pg_catalog.sqrt(25::pg_float8)
$$) as (result agtype);
+SELECT * from cypher('expr', $$
+ RETURN pg_catalog.sqrt("25"::pg_float8)
+$$) as (result agtype);
SELECT * from cypher('expr', $$
RETURN ag_catalog.age_sqrt(25)
$$) as (result agtype);
@@ -2111,9 +2271,6 @@ $$) as (result agtype);
SELECT * from cypher('expr', $$
RETURN pg_catalog.sqrt()
$$) as (result agtype);
-SELECT * from cypher('expr', $$
- RETURN pg_catalog.sqrt("1"::pg_float8)
-$$) as (result agtype);
SELECT * from cypher('expr', $$
RETURN pg_catalog.sqrt(-1::pg_float8)
$$) as (result agtype);
@@ -2229,7 +2386,7 @@ AS (gpa1 agtype, gpa2 agtype);
SELECT * FROM cypher('UCSC', $$ MATCH (u) RETURN collect(u.zip), collect(u.zip) $$)
AS (zip1 agtype, zip2 agtype);
SELECT * FROM cypher('UCSC', $$ RETURN collect(5) $$) AS (result agtype);
--- should return an empty aray
+-- should return an empty array
SELECT * FROM cypher('UCSC', $$ RETURN collect(NULL) $$) AS (empty agtype);
SELECT * FROM cypher('UCSC', $$ MATCH (u) WHERE u.name =~ "doesn't exist" RETURN collect(u.name) $$) AS (name agtype);
@@ -2306,6 +2463,23 @@ SELECT * FROM cypher('case_statement', $$CREATE ({i: true, j: false})$$) AS (res
SELECT * FROM cypher('case_statement', $$CREATE ({i: [], j: [0,1,2]})$$) AS (result agtype);
SELECT * FROM cypher('case_statement', $$CREATE ({i: {}, j: {i:1}})$$) AS (result agtype);
+--standalone case & edge cases
+--base case
+SELECT * FROM cypher('case_statement', $$ RETURN (CASE WHEN true THEN true END) $$) as (a agtype);
+--should return 1 empty row
+SELECT * FROM cypher('case_statement', $$ RETURN (CASE WHEN false THEN true END) $$) as (a agtype);
+--should return 'false'
+SELECT * FROM cypher('case_statement', $$ RETURN (CASE WHEN true THEN false END) $$) as (a agtype);
+--invalid case (WHEN should be boolean)
+SELECT * FROM cypher('case_statement', $$ RETURN (CASE WHEN 1 THEN 'fail' END) $$) as (a agtype);
+
+-- booleans + logic gates
+SELECT * FROM cypher('case_statement', $$ RETURN (CASE WHEN true THEN (true AND true) END) $$) as (a agtype);
+-- invalid mixed logic gate
+SELECT * FROM cypher('case_statement', $$ RETURN (CASE WHEN true THEN (true AND 1) END) $$) as (a agtype);
+
+
+
--CASE WHEN condition THEN result END
SELECT * FROM cypher('case_statement', $$
MATCH (n)
@@ -2346,6 +2520,12 @@ SELECT * FROM cypher('opt_forms', $$MATCH (u) CREATE (u)-[:edge]->() RETURN *$$)
SELECT * FROM cypher('opt_forms', $$MATCH (u)-->()<--(v) RETURN *$$) AS (col1 agtype, col2 agtype);
-- Added typecasts ::pg_bigint and ::pg_float8
+SELECT * FROM cypher('expr', $$
+RETURN true::pg_bigint
+$$) AS (result agtype);
+SELECT * FROM cypher('expr', $$
+RETURN "1.0"::pg_float8
+$$) AS (result agtype);
SELECT * from cypher('expr', $$
RETURN pg_catalog.sqrt(pg_catalog.sqrt(pg_catalog.sqrt(256::pg_bigint)))
$$) as (result agtype);
@@ -2448,6 +2628,87 @@ SELECT * from cypher('list', $$MATCH (u) RETURN labels(u), u$$) as (Labels agtyp
SELECT * from cypher('list', $$RETURN labels(NULL)$$) as (Labels agtype);
-- should return an error
SELECT * from cypher('list', $$RETURN labels("string")$$) as (Labels agtype);
+
+-- Issue 989: Impossible to create an object with an array field of more than
+-- 100 elements.
+SELECT * FROM cypher('list', $$ CREATE (any_vertex: test_label { `largeArray`: [] }) RETURN any_vertex $$) AS (u agtype);
+SELECT * FROM cypher('list', $$ CREATE (any_vertex: test_label { `largeArray`: [0] }) RETURN any_vertex $$) AS (u agtype);
+SELECT * FROM cypher('list', $$ CREATE (any_vertex: test_label { `largeArray`: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99] }) RETURN any_vertex $$) AS (u agtype);
+SELECT * FROM cypher('list', $$ CREATE (any_vertex: test_label { `largeArray`: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, 100] }) RETURN any_vertex $$) AS (u agtype);
+SELECT * FROM cypher('list', $$ CREATE (any_vertex: test_label { `largeArray`: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, 100, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99] }) RETURN any_vertex $$) AS (u agtype);
+SELECT * FROM cypher('list', $$ CREATE (any_vertex: test_label { `largeArray`: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, 100, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, 100, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, 100, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99] }) RETURN any_vertex $$) AS (u agtype);
+SELECT * FROM cypher('list', $$ CREATE (any_vertex: test_label { `largeArray`: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, 100, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, 100, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, 100, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, 100, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, 100, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, 100, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, 100, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99] }) RETURN any_vertex $$) AS (u agtype);
+-- should return 7 rows with counts: 0, 1, 100, 101, 200, 400, 800
+SELECT * FROM cypher('list', $$ MATCH (u:test_label) RETURN size(u.largeArray) $$) AS (u agtype);
+-- nested cases
+SELECT * FROM cypher('list',$$ CREATE (n:xyz {array:[0, 1, 2, 3, 4, 5, 6, 7, 8, 9,
+ 10, 11, 12, 13, 14, 15, 16, 17, 18, 19,
+ 20,21, 22, 23, 24, 25, 26, 27, 28, 29,
+ 30, 31, 32, 33, 34, 35, 36, 37, 38, 39,
+ 40, 41, 42, 43, 44, 45, 46, 47, 48, 49,
+ 50, 51, 52, 53, 54, 55, 56, 57, 58, 59,
+ 60, 61, 62, 63, 64, 65, 66, 67, 68, 69,
+ 70, 71, 72, 73, 74, 75, 76, 77, 78, 79,
+ 80, 81, 82, 83, 84, 85, 86, 87, 88, 89,
+ 90, 91, 92, 93, 94, 95, 96, 97, 98, 99,
+ [0, 1, 2, 3, 4, 5, 6, 7, 8, 9,
+ 10, 11, 12, 13, 14, 15, 16, 17, 18, 19,
+ 20,21, 22, 23, 24, 25, 26, 27, 28, 29,
+ 30, 31, 32, 33, 34, 35, 36, 37, 38, 39,
+ 40, 41, 42, 43, 44, 45, 46, 47, 48, 49,
+ 50, 51, 52, 53, 54, 55, 56, 57, 58, 59,
+ 60, 61, 62, 63, 64, 65, 66, 67, 68, 69,
+ 70, 71, 72, 73, 74, 75, 76, 77, 78, 79,
+ 80, 81, 82, 83, 84, 85, 86, 87, 88, 89,
+ 90, 91, 92, 93, 94, 95, 96, 97, 98, 99,
+ 100], 100]}) return n $$) as (a agtype);
+SELECT * FROM cypher('list',$$ MATCH (n:xyz) CREATE (m:xyz {array:[0,1,2,3,n.array,5,6,7,8,9,
+ 10, 11, 12, 13, 14, 15, 16, 17, 18, 19,
+ 20,21, 22, 23, 24, 25, 26, 27, 28, 29,
+ 30, 31, 32, 33, 34, 35, 36, 37, 38, 39,
+ 40, 41, 42, 43, 44, 45, 46, 47, 48, 49,
+ 50, 51, 52, 53, 54, 55, 56, 57, 58, 59,
+ 60, 61, 62, 63, 64, 65, 66, 67, 68, 69,
+ 70, 71, 72, 73, 74, 75, 76, 77, 78, 79,
+ 80, 81, 82, 83, 84, 85, 86, 87, 88, 89,
+ 90, 91, 92, 93, 94, 95, 96, 97, 98, 99,
+ 100]}) return m $$) as (a agtype);
+SELECT * FROM cypher('list',$$ MATCH (n:xyz) CREATE (m:xyz {array:[n.array,[n.array,[n.array]]]}) return m $$) as (a agtype);
+-- SET
+SELECT * FROM cypher('list',$$ CREATE (n:xyz)-[e:KNOWS {array:[0, 1, 2, 3, 4, 5, 6, 7, 8, 9,
+ 10, 11, 12, 13, 14, 15, 16, 17, 18, 19,
+ 20,21, 22, 23, 24, 25, 26, 27, 28, 29,
+ 30, 31, 32, 33, 34, 35, 36, 37, 38, 39,
+ 40, 41, 42, 43, 44, 45, 46, 47, 48, 49,
+ 50, 51, 52, 53, 54, 55, 56, 57, 58, 59,
+ 60, 61, 62, 63, 64, 65, 66, 67, 68, 69,
+ 70, 71, 72, 73, 74, 75, 76, 77, 78, 79,
+ 80, 81, 82, 83, 84, 85, 86, 87, 88, 89,
+ 90, 91, 92, 93, 94, 95, 96, 97, 98, 99,
+ 100]}]->(m:xyz) $$) as (a agtype);
+SELECT * FROM cypher('list',$$ MATCH p=(n:xyz)-[e]->() SET n.array=[0, 1, 2, 3, 4, 5, 6, 7, 8, 9,
+ 10, 11, 12, 13, 14, 15, 16, 17, 18, 19,
+ 20,21, 22, 23, 24, 25, 26, 27, 28, 29,
+ 30, 31, 32, 33, 34, 35, 36, 37, 38, 39,
+ 40, 41, 42, 43, 44, 45, 46, 47, 48, 49,
+ 50, 51, 52, 53, 54, 55, 56, 57, 58, 59,
+ 60, 61, 62, 63, 64, 65, 66, 67, 68, 69,
+ 70, 71, 72, 73, 74, 75, 76, 77, 78, 79,
+ 80, 81, 82, 83, 84, 85, 86, 87, 88, 89,
+ 90, 91, 92, 93, 94, 95, 96, 97, 98, 99,
+ 100] return n,e $$) as (a agtype, b agtype);
+SELECT * FROM cypher('list',$$ MATCH p=(n:xyz)-[e]->() SET n.array=[0, 1, 2, 3, 4, 5, 6, 7, 8, 9,
+ 10, 11, 12, 13, 14, 15, 16, 17, 18, 19,
+ 20,21, 22, 23, 24, 25, 26, 27, 28, 29,
+ 30, 31, 32, 33, 34, 35, 36, 37, 38, 39,
+ 40, 41, 42, 43, 44, 45, 46, 47, 48, 49,
+ 50, 51, 52, 53, 54, 55, 56, 57, 58, 59,
+ 60, 61, 62, 63, 64, 65, 66, 67, 68, 69,
+ 70, 71, 72, 73, 74, 75, 76, 77, 78, 79,
+ 80, 81, 82, 83, 84, 85, 86, 87, 88, 89,
+ 90, 91, 92, 93, 94, 95, 96, 97, 98, 99,
+ e.array, 100] return n,e $$) as (a agtype, b agtype);
+
--
-- Cleanup
--
diff --git a/regress/sql/index.sql b/regress/sql/index.sql
index 95322082d..aac1dc40e 100644
--- a/regress/sql/index.sql
+++ b/regress/sql/index.sql
@@ -159,7 +159,7 @@ SELECT * FROM cypher('cypher_index', $$
(us)<-[:has_city]-(:City {city_id: 3, name:"Los Angeles", west_coast: true, country_code:"US"}),
(us)<-[:has_city]-(:City {city_id: 4, name:"Seattle", west_coast: true, country_code:"US"}),
(ca)<-[:has_city]-(:City {city_id: 5, name:"Vancouver", west_coast: true, country_code:"CA"}),
- (ca)<-[:has_city]-(:City {city_id: 6, name:"Toroto", west_coast: false, country_code:"CA"}),
+ (ca)<-[:has_city]-(:City {city_id: 6, name:"Toronto", west_coast: false, country_code:"CA"}),
(ca)<-[:has_city]-(:City {city_id: 7, name:"Montreal", west_coast: false, country_code:"CA"}),
(mx)<-[:has_city]-(:City {city_id: 8, name:"Mexico City", west_coast: false, country_code:"MX"}),
(mx)<-[:has_city]-(:City {city_id: 9, name:"Monterrey", west_coast: false, country_code:"MX"}),
diff --git a/src/backend/catalog/ag_graph.c b/src/backend/catalog/ag_graph.c
index a344a6a37..f4a0d7213 100644
--- a/src/backend/catalog/ag_graph.c
+++ b/src/backend/catalog/ag_graph.c
@@ -157,9 +157,13 @@ Oid get_graph_oid(const char *graph_name)
cache_data = search_graph_name_cache(graph_name);
if (cache_data)
+ {
return cache_data->oid;
+ }
else
+ {
return InvalidOid;
+ }
}
static Oid get_graph_namespace(const char *graph_name)
diff --git a/src/backend/commands/label_commands.c b/src/backend/commands/label_commands.c
index 3ae24418b..39e9dce37 100644
--- a/src/backend/commands/label_commands.c
+++ b/src/backend/commands/label_commands.c
@@ -279,6 +279,12 @@ void create_label(char *graph_name, char *label_name, char label_type,
errmsg("label name is invalid")));
}
+ if (!is_valid_label(label_name, label_type))
+ {
+ ereport(ERROR, (errcode(ERRCODE_UNDEFINED_SCHEMA),
+ errmsg("label name is invalid")));
+ }
+
cache_data = search_graph_name_cache(graph_name);
if (!cache_data)
{
@@ -535,7 +541,7 @@ static FuncCall *build_id_default_func_expr(char *graph_name, char *label_name,
nextval_func = makeFuncCall(nextval_func_name, nextval_func_args, -1);
/*
- * Build a node that contructs the graphid from the label id function
+ * Build a node that constructs the graphid from the label id function
* and the next val function for the given sequence.
*/
graphid_func_name = list_make2(makeString("ag_catalog"),
@@ -687,7 +693,9 @@ static int32 get_new_label_id(Oid graph_oid, Oid nsp_id)
label_id = (int32) nextval_internal(seq_id, true);
Assert(label_id_is_valid(label_id));
if (!label_id_exists(graph_oid, label_id))
- return (int32)label_id;
+ {
+ return (int32) label_id;
+ }
}
ereport(ERROR, (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
@@ -802,7 +810,7 @@ static void remove_relation(List *qname)
rel->schemaname, rel->relname)));
}
- // concurent is false
+ // concurrent is false
ObjectAddressSet(address, RelationRelationId, rel_oid);
diff --git a/src/backend/executor/cypher_create.c b/src/backend/executor/cypher_create.c
index ca4f1777c..4b4810924 100644
--- a/src/backend/executor/cypher_create.c
+++ b/src/backend/executor/cypher_create.c
@@ -106,7 +106,7 @@ static void begin_cypher_create(CustomScanState *node, EState *estate,
if (!CYPHER_TARGET_NODE_INSERT_ENTITY(cypher_node->flags))
continue;
- // Open relation and aquire a row exclusive lock.
+ // Open relation and acquire a row exclusive lock.
rel = table_open(cypher_node->relid, RowExclusiveLock);
// Initialize resultRelInfo for the vertex
@@ -127,6 +127,12 @@ static void begin_cypher_create(CustomScanState *node, EState *estate,
cypher_node->id_expr_state =
ExecInitExpr(cypher_node->id_expr, (PlanState *)node);
}
+
+ if (cypher_node->prop_expr != NULL)
+ {
+ cypher_node->prop_expr_state = ExecInitExpr(cypher_node->prop_expr,
+ (PlanState *)node);
+ }
}
}
@@ -138,7 +144,9 @@ static void begin_cypher_create(CustomScanState *node, EState *estate,
* that have modified the command id.
*/
if (estate->es_output_cid == 0)
+ {
estate->es_output_cid = estate->es_snapshot->curcid;
+ }
Increment_Estate_CommandId(estate);
}
@@ -208,15 +216,19 @@ static TupleTableSlot *exec_cypher_create(CustomScanState *node)
Decrement_Estate_CommandId(estate)
slot = ExecProcNode(node->ss.ps.lefttree);
Increment_Estate_CommandId(estate)
+
/* break when there are no tuples */
if (TupIsNull(slot))
{
break;
}
+
/* setup the scantuple that the process_pattern needs */
econtext->ecxt_scantuple =
node->ss.ps.lefttree->ps_ProjInfo->pi_exprContext->ecxt_scantuple;
+
process_pattern(css);
+
/*
* This may not be necessary. If we have an empty pattern, nothing was
* inserted and the current command Id was not used. So, only flag it
@@ -228,6 +240,7 @@ static TupleTableSlot *exec_cypher_create(CustomScanState *node)
used = true;
}
} while (terminal);
+
/*
* If the current command Id wasn't used, nothing was inserted and we're
* done.
@@ -236,8 +249,10 @@ static TupleTableSlot *exec_cypher_create(CustomScanState *node)
{
return NULL;
}
+
/* update the current command Id */
CommandCounterIncrement();
+
/* if this was a terminal CREATE just return NULL */
if (terminal)
{
@@ -254,19 +269,25 @@ static void end_cypher_create(CustomScanState *node)
(cypher_create_custom_scan_state *)node;
ListCell *lc;
+ // increment the command counter
+ CommandCounterIncrement();
+
ExecEndNode(node->ss.ps.lefttree);
foreach (lc, css->pattern)
{
cypher_create_path *path = lfirst(lc);
ListCell *lc2;
+
foreach (lc2, path->target_nodes)
{
cypher_target_node *cypher_node =
(cypher_target_node *)lfirst(lc2);
if (!CYPHER_TARGET_NODE_INSERT_ENTITY(cypher_node->flags))
+ {
continue;
+ }
// close all indices for the node
ExecCloseIndices(cypher_node->resultRelInfo);
@@ -281,7 +302,7 @@ static void end_cypher_create(CustomScanState *node)
static void rescan_cypher_create(CustomScanState *node)
{
ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
- errmsg("cypher create clause cannot be rescaned"),
+ errmsg("cypher create clause cannot be rescanned"),
errhint("its unsafe to use joins in a query with a Cypher CREATE clause")));
}
@@ -505,8 +526,7 @@ static Datum create_vertex(cypher_create_custom_scan_state *css,
scantuple = ps->ps_ExprContext->ecxt_scantuple;
// make the vertex agtype
- result = make_vertex(
- id, CStringGetDatum(node->label_name),
+ result = make_vertex(id, CStringGetDatum(node->label_name),
PointerGetDatum(scanTupleSlot->tts_values[node->prop_attr_num]));
// append to the path list
@@ -545,9 +565,11 @@ static Datum create_vertex(cypher_create_custom_scan_state *css,
v = get_ith_agtype_value_from_container(&a->root, 0);
if (v->type != AGTV_VERTEX)
+ {
ereport(ERROR,
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
errmsg("agtype must resolve to a vertex")));
+ }
// extract the id agtype field
id_value = GET_AGTYPE_VALUE_OBJECT_VALUE(v, "id");
@@ -569,10 +591,12 @@ static Datum create_vertex(cypher_create_custom_scan_state *css,
if (!SAFE_TO_SKIP_EXISTENCE_CHECK(node->flags))
{
if (!entity_exists(estate, css->graph_oid, DATUM_GET_GRAPHID(id)))
+ {
ereport(ERROR,
(errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
errmsg("vertex assigned to variable %s was deleted",
node->variable_name)));
+ }
}
if (CYPHER_TARGET_NODE_IN_PATH(node->flags))
diff --git a/src/backend/executor/cypher_delete.c b/src/backend/executor/cypher_delete.c
index 134e6fe24..a83003607 100644
--- a/src/backend/executor/cypher_delete.c
+++ b/src/backend/executor/cypher_delete.c
@@ -209,7 +209,7 @@ static void rescan_cypher_delete(CustomScanState *node)
{
ereport(ERROR,
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
- errmsg("cypher DELETE clause cannot be rescaned"),
+ errmsg("cypher DELETE clause cannot be rescanned"),
errhint("its unsafe to use joins in a query with a Cypher DELETE clause")));
}
@@ -483,7 +483,7 @@ static void find_connected_edges(CustomScanState *node, char *graph_name,
* any edges attached to it.
*
* XXX: If we implement an on-disc graph storage system. Such as
- * an adjacency matrix, the performace of this check can be massively
+ * an adjacency matrix, the performance of this check can be massively
* improved. However, right now we have to scan every edge to see if
* one has this vertex as a start or end vertex.
*/
diff --git a/src/backend/executor/cypher_merge.c b/src/backend/executor/cypher_merge.c
index 9a8144d36..c386e6524 100644
--- a/src/backend/executor/cypher_merge.c
+++ b/src/backend/executor/cypher_merge.c
@@ -57,17 +57,11 @@ const CustomExecMethods cypher_merge_exec_methods = {MERGE_SCAN_STATE_NAME,
exec_cypher_merge,
end_cypher_merge,
rescan_cypher_merge,
- NULL,
- NULL,
- NULL,
- NULL,
- NULL,
- NULL,
- NULL,
- NULL};
+ NULL, NULL, NULL, NULL,
+ NULL, NULL, NULL, NULL};
/*
- * Initializes the MERGE Execution Node at the begginning of the execution
+ * Initializes the MERGE Execution Node at the beginning of the execution
* phase.
*/
static void begin_cypher_merge(CustomScanState *node, EState *estate,
@@ -75,8 +69,8 @@ static void begin_cypher_merge(CustomScanState *node, EState *estate,
{
cypher_merge_custom_scan_state *css =
(cypher_merge_custom_scan_state *)node;
- ListCell *lc;
- Plan *subplan;
+ ListCell *lc = NULL;
+ Plan *subplan = NULL;
Assert(list_length(css->cs->custom_plans) == 1);
@@ -84,6 +78,7 @@ static void begin_cypher_merge(CustomScanState *node, EState *estate,
subplan = linitial(css->cs->custom_plans);
node->ss.ps.lefttree = ExecInitNode(subplan, estate, eflags);
+ /* TODO is this necessary? Removing it seems to not have an impact */
ExecAssignExprContext(estate, &node->ss.ps);
ExecInitScanTupleSlot(estate, &node->ss,
@@ -109,7 +104,7 @@ static void begin_cypher_merge(CustomScanState *node, EState *estate,
{
cypher_target_node *cypher_node =
(cypher_target_node *)lfirst(lc);
- Relation rel;
+ Relation rel = NULL;
/*
* This entity is references an entity that is already declared. Either
@@ -122,7 +117,7 @@ static void begin_cypher_merge(CustomScanState *node, EState *estate,
continue;
}
- // Open relation and aquire a row exclusive lock.
+ // Open relation and acquire a row exclusive lock.
rel = table_open(cypher_node->relid, RowExclusiveLock);
// Initialize resultRelInfo for the vertex
@@ -148,8 +143,8 @@ static void begin_cypher_merge(CustomScanState *node, EState *estate,
if (cypher_node->prop_expr != NULL)
{
- cypher_node->prop_expr_state =
- ExecInitExpr(cypher_node->prop_expr, (PlanState *)node);
+ cypher_node->prop_expr_state = ExecInitExpr(cypher_node->prop_expr,
+ (PlanState *)node);
}
}
@@ -180,7 +175,7 @@ static bool check_path(cypher_merge_custom_scan_state *css,
TupleTableSlot *slot)
{
cypher_create_path *path = css->path;
- ListCell *lc;
+ ListCell *lc = NULL;
foreach(lc, path->target_nodes)
{
@@ -202,27 +197,25 @@ static bool check_path(cypher_merge_custom_scan_state *css,
*/
if (slot->tts_isnull[node->tuple_position - 1])
{
+
return true;
}
}
-
}
-
return false;
}
static void process_path(cypher_merge_custom_scan_state *css)
{
cypher_create_path *path = css->path;
-
ListCell *lc = list_head(path->target_nodes);
/*
* Create the first vertex. The create_vertex function will
* create the rest of the path, if necessary.
*/
- merge_vertex(css, lfirst(lc), lnext(lc));
+ merge_vertex(css, lfirst(lc), lnext(lc));
/*
* If this path is a variable, take the list that was accumulated
@@ -234,11 +227,27 @@ static void process_path(cypher_merge_custom_scan_state *css)
ExprContext *econtext = css->css.ss.ps.ps_ExprContext;
TupleTableSlot *scantuple = econtext->ecxt_scantuple;
Datum result;
+ int tuple_position = path->path_attr_num - 1;
+ bool debug_flag = false;
- result = make_path(css->path_values);
+ /*
+ * We need to make sure that the tuple_position is within the
+ * boundaries of the tuple's number of attributes. Otherwise, it
+ * will corrupt memory. The cases where it doesn't fit within are
+ * usually due to a variable that is specified but there isn't a RETURN
+ * clause. In these cases we just don't bother to store the
+ * value.
+ */
+ if (!debug_flag &&
+ (tuple_position < scantuple->tts_tupleDescriptor->natts ||
+ scantuple->tts_tupleDescriptor->natts != 1))
+ {
+ result = make_path(css->path_values);
- scantuple->tts_values[path->path_attr_num - 1] = result;
- scantuple->tts_isnull[path->path_attr_num - 1] = false;
+ /* store the result */
+ scantuple->tts_values[tuple_position] = result;
+ scantuple->tts_isnull[tuple_position] = false;
+ }
}
}
@@ -250,7 +259,7 @@ static void process_simple_merge(CustomScanState *node)
cypher_merge_custom_scan_state *css =
(cypher_merge_custom_scan_state *)node;
EState *estate = css->css.ss.ps.state;
- TupleTableSlot *slot;
+ TupleTableSlot *slot = NULL;
/*Process the subtree first */
Decrement_Estate_CommandId(estate)
@@ -260,9 +269,13 @@ static void process_simple_merge(CustomScanState *node)
if (TupIsNull(slot))
{
ExprContext *econtext = node->ss.ps.ps_ExprContext;
+ SubqueryScanState *sss = (SubqueryScanState *)node->ss.ps.lefttree;
+
+ /* our child execution node should be a subquery */
+ Assert(IsA(sss, SubqueryScanState));
/* setup the scantuple that the process_path needs */
- econtext->ecxt_scantuple = node->ss.ps.lefttree->ps_ResultTupleSlot;
+ econtext->ecxt_scantuple = sss->ss.ss_ScanTupleSlot;
process_path(css);
}
@@ -293,7 +306,7 @@ static void mark_tts_isnull(TupleTableSlot *slot)
/*
* Function that is called mid-execution. This function will call
* its subtree in the execution tree, and depending on the results
- * create the new path, and depending on the the context of the MERGE
+ * create the new path, and depending on the context of the MERGE
* within the query pass data to the parent execution node.
*
* Returns a TupleTableSlot with the next tuple to it parent or
@@ -305,7 +318,7 @@ static TupleTableSlot *exec_cypher_merge(CustomScanState *node)
(cypher_merge_custom_scan_state *)node;
EState *estate = css->css.ss.ps.state;
ExprContext *econtext = css->css.ss.ps.ps_ExprContext;
- TupleTableSlot *slot;
+ TupleTableSlot *slot = NULL;
bool terminal = CYPHER_CLAUSE_IS_TERMINAL(css->flags);
/*
@@ -321,7 +334,7 @@ static TupleTableSlot *exec_cypher_merge(CustomScanState *node)
/*
* Case 1: MERGE is not the first clause in the cypher query.
*
- * For this case, we need to process all tuples give to us by the
+ * For this case, we need to process all tuples given to us by the
* previous clause. When we receive a tuple from the previous clause:
* check to see if the left lateral join found the pattern already. If
* it did, we don't need to create the pattern. If the lateral join did
@@ -364,7 +377,6 @@ static TupleTableSlot *exec_cypher_merge(CustomScanState *node)
return NULL;
}
- //return ExecProject(node->ss.ps.ps_ProjInfo);
econtext->ecxt_scantuple = ExecProject(node->ss.ps.lefttree->ps_ProjInfo);
return ExecProject(node->ss.ps.ps_ProjInfo);
@@ -476,7 +488,7 @@ static TupleTableSlot *exec_cypher_merge(CustomScanState *node)
Assert(css->found_a_path == false);
/*
- * This block of sub-case 1 should only be executued once. To
+ * This block of sub-case 1 should only be executed once. To
* create the single path if the path does not exist. If we find
* ourselves here again, the internal state of the MERGE execution
* node was incorrectly altered.
@@ -538,7 +550,7 @@ static void end_cypher_merge(CustomScanState *node)
cypher_merge_custom_scan_state *css =
(cypher_merge_custom_scan_state *)node;
cypher_create_path *path = css->path;
- ListCell *lc;
+ ListCell *lc = NULL;
// increment the command counter
CommandCounterIncrement();
@@ -547,11 +559,12 @@ static void end_cypher_merge(CustomScanState *node)
foreach (lc, path->target_nodes)
{
- cypher_target_node *cypher_node =
- (cypher_target_node *)lfirst(lc);
+ cypher_target_node *cypher_node = (cypher_target_node *)lfirst(lc);
if (!CYPHER_TARGET_NODE_INSERT_ENTITY(cypher_node->flags))
+ {
continue;
+ }
// close all indices for the node
ExecCloseIndices(cypher_node->resultRelInfo);
@@ -569,9 +582,10 @@ static void end_cypher_merge(CustomScanState *node)
*/
static void rescan_cypher_merge(CustomScanState *node)
{
- ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
- errmsg("cypher merge clause cannot be rescaned"),
- errhint("its unsafe to use joins in a query with a Cypher MERGE clause")));
+ ereport(ERROR,
+ (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("cypher merge clause cannot be rescanned"),
+ errhint("its unsafe to use joins in a query with a Cypher MERGE clause")));
}
/*
@@ -584,8 +598,8 @@ Node *create_cypher_merge_plan_state(CustomScan *cscan)
cypher_merge_custom_scan_state *cypher_css =
palloc0(sizeof(cypher_merge_custom_scan_state));
cypher_merge_information *merge_information;
- char *serialized_data;
- Const *c;
+ char *serialized_data = NULL;
+ Const *c = NULL;
cypher_css->cs = cscan;
@@ -636,6 +650,7 @@ static Datum merge_vertex(cypher_merge_custom_scan_state *css,
{
ResultRelInfo *old_estate_es_result_relation_info = NULL;
Datum prop;
+
/*
* Set estate's result relation to the vertex's result
* relation.
@@ -716,8 +731,7 @@ static Datum merge_vertex(cypher_merge_custom_scan_state *css,
Datum result;
/* make the vertex agtype */
- result = make_vertex(
- id, CStringGetDatum(node->label_name), prop);
+ result = make_vertex(id, CStringGetDatum(node->label_name), prop);
/* append to the path list */
if (CYPHER_TARGET_NODE_IN_PATH(node->flags))
@@ -732,42 +746,57 @@ static Datum merge_vertex(cypher_merge_custom_scan_state *css,
*/
if (CYPHER_TARGET_NODE_IS_VARIABLE(node->flags))
{
- scanTupleSlot->tts_values[node->tuple_position - 1] = result;
- scanTupleSlot->tts_isnull[node->tuple_position - 1] = false;
+ bool debug_flag = false;
+ int tuple_position = node->tuple_position - 1;
+
+ /*
+ * We need to make sure that the tuple_position is within the
+ * boundaries of the tuple's number of attributes. Otherwise, it
+ * will corrupt memory. The cases where it doesn't fall within
+ * are usually due to a variable that is specified but there
+ * isn't a RETURN clause. In these cases we just don't bother to
+ * store the value.
+ */
+ if (!debug_flag &&
+ (tuple_position < scanTupleSlot->tts_tupleDescriptor->natts ||
+ scanTupleSlot->tts_tupleDescriptor->natts != 1))
+ {
+ /* store the result */
+ scanTupleSlot->tts_values[tuple_position] = result;
+ scanTupleSlot->tts_isnull[tuple_position] = false;
+ }
}
}
}
else
{
- agtype *a;
+ agtype *a = NULL;
Datum d;
- agtype_value *v;
- agtype_value *id_value;
- TupleTableSlot *scantuple;
- PlanState *ps;
-
- ps = css->css.ss.ps.lefttree;
- scantuple = ps->ps_ExprContext->ecxt_scantuple;
+ agtype_value *v = NULL;
+ agtype_value *id_value = NULL;
- if (scantuple->tts_isnull[node->tuple_position - 1])
+ /* check that the variable isn't NULL */
+ if (scanTupleSlot->tts_isnull[node->tuple_position - 1])
{
ereport(ERROR,
- (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
- errmsg("Existing variable %s cannot be NULL in MERGE clause",
- node->variable_name)));
+ (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("Existing variable %s cannot be NULL in MERGE clause",
+ node->variable_name)));
}
/* get the vertex agtype in the scanTupleSlot */
- d = scantuple->tts_values[node->tuple_position - 1];
+ d = scanTupleSlot->tts_values[node->tuple_position - 1];
a = DATUM_GET_AGTYPE_P(d);
/* Convert to an agtype value */
v = get_ith_agtype_value_from_container(&a->root, 0);
if (v->type != AGTV_VERTEX)
+ {
ereport(ERROR,
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
errmsg("agtype must resolve to a vertex")));
+ }
/* extract the id agtype field */
id_value = GET_AGTYPE_VALUE_OBJECT_VALUE(v, "id");
@@ -915,8 +944,8 @@ static void merge_edge(cypher_merge_custom_scan_state *css,
{
Datum result;
- result = make_edge(
- id, start_id, end_id, CStringGetDatum(node->label_name), prop);
+ result = make_edge(id, start_id, end_id,
+ CStringGetDatum(node->label_name), prop);
// add the Datum to the list of entities for creating the path variable
if (CYPHER_TARGET_NODE_IN_PATH(node->flags))
@@ -929,9 +958,25 @@ static void merge_edge(cypher_merge_custom_scan_state *css,
if (CYPHER_TARGET_NODE_IS_VARIABLE(node->flags))
{
TupleTableSlot *scantuple = econtext->ecxt_scantuple;
+ bool debug_flag = false;
+ int tuple_position = node->tuple_position - 1;
- scantuple->tts_values[node->tuple_position - 1] = result;
- scantuple->tts_isnull[node->tuple_position - 1] = false;
+ /*
+ * We need to make sure that the tuple_position is within the
+ * boundaries of the tuple's number of attributes. Otherwise, it
+ * will corrupt memory. The cases where it doesn't fall within are
+ * usually due to a variable that is specified but there isn't a
+ * RETURN clause. In these cases we just don't bother to store the
+ * value.
+ */
+ if (!debug_flag &&
+ (tuple_position < scantuple->tts_tupleDescriptor->natts ||
+ scantuple->tts_tupleDescriptor->natts != 1))
+ {
+ /* store the result */
+ scantuple->tts_values[tuple_position] = result;
+ scantuple->tts_isnull[tuple_position] = false;
+ }
}
}
}
diff --git a/src/backend/executor/cypher_set.c b/src/backend/executor/cypher_set.c
index 3fb98602a..7a8ec9b30 100644
--- a/src/backend/executor/cypher_set.c
+++ b/src/backend/executor/cypher_set.c
@@ -113,7 +113,6 @@ static HeapTuple update_entity_tuple(ResultRelInfo *resultRelInfo,
Buffer buffer;
bool update_indexes;
TM_Result result;
-
CommandId cid = GetCurrentCommandId(true);
ResultRelInfo *saved_resultRelInfo = estate->es_result_relation_info;
estate->es_result_relation_info = resultRelInfo;
@@ -140,9 +139,7 @@ static HeapTuple update_entity_tuple(ResultRelInfo *resultRelInfo,
result = table_tuple_update(resultRelInfo->ri_RelationDesc,
&tuple->t_self, elemTupleSlot,
- cid,
- //estate->es_output_cid,
- estate->es_snapshot,// NULL,
+ cid, estate->es_snapshot,
estate->es_crosscheck_snapshot,
true /* wait for commit */ ,
&hufd, &lockmode, &update_indexes);
@@ -273,7 +270,7 @@ static agtype_value *replace_entity_in_path(agtype_value *path,
elem = &path->val.array.elems[i];
- // something unexpected happended, throw an error.
+ // something unexpected happened, throw an error.
if (elem->type != AGTV_VERTEX && elem->type != AGTV_EDGE)
{
ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
@@ -671,7 +668,7 @@ static void rescan_cypher_set(CustomScanState *node)
ereport(ERROR,
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
- errmsg("cypher %s clause cannot be rescaned",
+ errmsg("cypher %s clause cannot be rescanned",
clause_name),
errhint("its unsafe to use joins in a query with a Cypher %s clause", clause_name)));
}
diff --git a/src/backend/nodes/cypher_outfuncs.c b/src/backend/nodes/cypher_outfuncs.c
index d63090410..4053a399c 100644
--- a/src/backend/nodes/cypher_outfuncs.c
+++ b/src/backend/nodes/cypher_outfuncs.c
@@ -204,6 +204,7 @@ void out_cypher_node(StringInfo str, const ExtensibleNode *node)
WRITE_STRING_FIELD(name);
WRITE_STRING_FIELD(label);
+ WRITE_STRING_FIELD(parsed_label);
WRITE_NODE_FIELD(props);
WRITE_LOCATION_FIELD(location);
}
@@ -215,6 +216,7 @@ void out_cypher_relationship(StringInfo str, const ExtensibleNode *node)
WRITE_STRING_FIELD(name);
WRITE_STRING_FIELD(label);
+ WRITE_STRING_FIELD(parsed_label);
WRITE_NODE_FIELD(props);
WRITE_NODE_FIELD(varlen);
WRITE_ENUM_FIELD(dir, cypher_rel_dir);
diff --git a/src/backend/nodes/cypher_readfuncs.c b/src/backend/nodes/cypher_readfuncs.c
index fe361df92..89cedd577 100644
--- a/src/backend/nodes/cypher_readfuncs.c
+++ b/src/backend/nodes/cypher_readfuncs.c
@@ -44,11 +44,11 @@
READ_TEMP_LOCALS()
/*
- * The READ_*_FIELD defines first skips the :fildname token (key) part of the string
+ * The READ_*_FIELD defines first skips the :fldname token (key) part of the string
* and then converts the next token (value) to the correct data type.
*
* pg_strtok will split the passed string by whitespace, skipping whitespace in
- * strings. We do not setup pg_strtok. That is for the the caller to do. By default
+ * strings. We do not setup pg_strtok. That is for the caller to do. By default
* that is the responsibility of Postgres' nodeRead function. We assume that was setup
* correctly.
*/
@@ -186,8 +186,8 @@ void read_cypher_create_target_nodes(struct ExtensibleNode *node)
READ_LOCALS(cypher_create_target_nodes);
READ_NODE_FIELD(paths);
- READ_INT_FIELD(flags);
- READ_INT_FIELD(graph_oid);
+ READ_UINT_FIELD(flags);
+ READ_UINT_FIELD(graph_oid);
}
/*
@@ -212,7 +212,7 @@ void read_cypher_target_node(struct ExtensibleNode *node)
READ_LOCALS(cypher_target_node);
READ_CHAR_FIELD(type);
- READ_INT_FIELD(flags);
+ READ_UINT_FIELD(flags);
READ_ENUM_FIELD(dir, cypher_rel_dir);
READ_NODE_FIELD(id_expr);
READ_NODE_FIELD(id_expr_state);
@@ -236,7 +236,7 @@ void read_cypher_update_information(struct ExtensibleNode *node)
READ_LOCALS(cypher_update_information);
READ_NODE_FIELD(set_items);
- READ_INT_FIELD(flags);
+ READ_UINT_FIELD(flags);
READ_INT_FIELD(tuple_position);
READ_STRING_FIELD(graph_name);
READ_STRING_FIELD(clause_name);
@@ -268,9 +268,9 @@ void read_cypher_delete_information(struct ExtensibleNode *node)
READ_LOCALS(cypher_delete_information);
READ_NODE_FIELD(delete_items);
- READ_INT_FIELD(flags);
+ READ_UINT_FIELD(flags);
READ_STRING_FIELD(graph_name);
- READ_INT_FIELD(graph_oid);
+ READ_UINT_FIELD(graph_oid);
READ_BOOL_FIELD(detach);
}
@@ -294,7 +294,7 @@ void read_cypher_merge_information(struct ExtensibleNode *node)
{
READ_LOCALS(cypher_merge_information);
- READ_INT_FIELD(flags);
+ READ_UINT_FIELD(flags);
READ_UINT_FIELD(graph_oid);
READ_INT_FIELD(merge_function_attr);
READ_NODE_FIELD(path);
diff --git a/src/backend/optimizer/cypher_paths.c b/src/backend/optimizer/cypher_paths.c
index 80d916127..151abfa43 100644
--- a/src/backend/optimizer/cypher_paths.c
+++ b/src/backend/optimizer/cypher_paths.c
@@ -145,7 +145,7 @@ static void handle_cypher_delete_clause(PlannerInfo *root, RelOptInfo *rel,
cp = create_cypher_delete_path(root, rel, custom_private);
- // Discard any pre-existing paths
+ // Discard any preexisting paths
rel->pathlist = NIL;
rel->partial_pathlist = NIL;
@@ -173,7 +173,7 @@ static void handle_cypher_create_clause(PlannerInfo *root, RelOptInfo *rel,
cp = create_cypher_create_path(root, rel, custom_private);
- // Discard any pre-existing paths, they should be under the cp path
+ // Discard any preexisting paths, they should be under the cp path
rel->pathlist = NIL;
rel->partial_pathlist = NIL;
@@ -198,7 +198,7 @@ static void handle_cypher_set_clause(PlannerInfo *root, RelOptInfo *rel,
cp = create_cypher_set_path(root, rel, custom_private);
- // Discard any pre-existing paths
+ // Discard any preexisting paths
rel->pathlist = NIL;
rel->partial_pathlist = NIL;
@@ -222,7 +222,7 @@ static void handle_cypher_merge_clause(PlannerInfo *root, RelOptInfo *rel,
cp = create_cypher_merge_path(root, rel, custom_private);
- // Discard any pre-existing paths
+ // Discard any preexisting paths
rel->pathlist = NIL;
rel->partial_pathlist = NIL;
diff --git a/src/backend/parser/cypher_analyze.c b/src/backend/parser/cypher_analyze.c
index c321f231a..1e881c3b7 100644
--- a/src/backend/parser/cypher_analyze.c
+++ b/src/backend/parser/cypher_analyze.c
@@ -72,9 +72,10 @@ static Query *analyze_cypher(List *stmt, ParseState *parent_pstate,
static Query *analyze_cypher_and_coerce(List *stmt, RangeTblFunction *rtfunc,
ParseState *parent_pstate,
const char *query_str, int query_loc,
- char *graph_name, uint32 graph_oid,
+ char *graph_name, Oid graph_oid,
Param *params);
+
void post_parse_analyze_init(void)
{
prev_post_parse_analyze_hook = post_parse_analyze_hook;
@@ -307,7 +308,7 @@ static void convert_cypher_to_subquery(RangeTblEntry *rte, ParseState *pstate)
Node *arg3 = NULL;
Name graph_name = NULL;
char *graph_name_str = NULL;
- uint32 graph_oid;
+ Oid graph_oid = InvalidOid;
const char *query_str = NULL;
int query_loc = -1;
Param *params = NULL;
@@ -359,7 +360,6 @@ static void convert_cypher_to_subquery(RangeTblEntry *rte, ParseState *pstate)
* may differ from what they are shown. This will confuse users.
* * In the case above, the error position may not be accurate.
*/
-
query_str = expr_get_const_cstring(arg2, pstate->p_sourcetext);
/*
@@ -470,7 +470,7 @@ static void convert_cypher_to_subquery(RangeTblEntry *rte, ParseState *pstate)
}
/*
- * Check to see if the cypher function had a third parameters passed to it,
+ * Check to see if the cypher function had a third parameter passed to it,
* if so make sure Postgres parsed the second argument to a Param node.
*/
if (list_length(funcexpr->args) == 3)
diff --git a/src/backend/parser/cypher_clause.c b/src/backend/parser/cypher_clause.c
index 0ef33131b..780644425 100644
--- a/src/backend/parser/cypher_clause.c
+++ b/src/backend/parser/cypher_clause.c
@@ -142,10 +142,8 @@ static Expr *transform_cypher_edge(cypher_parsestate *cpstate,
static Expr *transform_cypher_node(cypher_parsestate *cpstate,
cypher_node *node, List **target_list,
bool output_node, bool valid_label);
-static Node *make_vertex_expr(cypher_parsestate *cpstate, RangeTblEntry *rte,
- char *label);
-static Node *make_edge_expr(cypher_parsestate *cpstate, RangeTblEntry *rte,
- char *label);
+static Node *make_vertex_expr(cypher_parsestate *cpstate, RangeTblEntry *rte);
+static Node *make_edge_expr(cypher_parsestate *cpstate, RangeTblEntry *rte);
static Node *make_qual(cypher_parsestate *cpstate,
transform_entity *entity, char *name);
static TargetEntry *
@@ -204,6 +202,7 @@ static Expr *cypher_create_properties(cypher_parsestate *cpstate,
enum transform_entity_type type);
static Expr *add_volatile_wrapper(Expr *node);
static bool variable_exists(cypher_parsestate *cpstate, char *name);
+static void add_volatile_wrapper_to_target_entry(List *target_list, int resno);
static int get_target_entry_resno(List *target_list, char *name);
static void handle_prev_clause(cypher_parsestate *cpstate, Query *query,
cypher_clause *clause, bool first_rte);
@@ -274,16 +273,19 @@ static cypher_clause *convert_merge_to_match(cypher_merge *merge);
static void
transform_cypher_merge_mark_tuple_position(List *target_list,
cypher_create_path *path);
+static cypher_target_node *get_referenced_variable(ParseState *pstate,
+ Node *node,
+ List *transformed_path);
//call...[yield]
static Query *transform_cypher_call_stmt(cypher_parsestate *cpstate,
- cypher_clause *clause);
+ cypher_clause *clause);
static Query *transform_cypher_call_subquery(cypher_parsestate *cpstate,
- cypher_clause *clause);
+ cypher_clause *clause);
// transform
-#define PREV_CYPHER_CLAUSE_ALIAS "_"
-#define CYPHER_OPT_RIGHT_ALIAS "_R"
+#define PREV_CYPHER_CLAUSE_ALIAS AGE_DEFAULT_ALIAS_PREFIX"previous_cypher_clause"
+#define CYPHER_OPT_RIGHT_ALIAS AGE_DEFAULT_ALIAS_PREFIX"cypher_optional_right"
#define transform_prev_cypher_clause(cpstate, prev_clause, add_rte_to_query) \
transform_cypher_clause_as_subquery(cpstate, transform_cypher_clause, \
prev_clause, NULL, add_rte_to_query)
@@ -318,7 +320,6 @@ static ParseNamespaceItem *create_namespace_item(RangeTblEntry *rte, bool p_rel_
bool p_lateral_ok);
static List *make_target_list_from_join(ParseState *pstate,
RangeTblEntry *rte);
-static Expr *add_volatile_wrapper(Expr *node);
static FuncExpr *make_clause_func_expr(char *function_name,
Node *clause_information);
/* for VLE support */
@@ -813,7 +814,7 @@ transform_cypher_union_tree(cypher_parsestate *cpstate, cypher_clause *clause,
/*
* If we find ourselves processing a recursive CTE here something
- * went horribly wrong. That is an SQL contruct with no parallel in
+ * went horribly wrong. That is an SQL construct with no parallel in
* cypher.
*/
if (isTopLevel &&
@@ -1405,7 +1406,6 @@ static List *transform_cypher_delete_item_list(cypher_parsestate *cpstate,
}
resno = get_target_entry_resno(query->targetList, val->val.str);
-
if (resno == -1)
{
ereport(ERROR,
@@ -1415,6 +1415,8 @@ static List *transform_cypher_delete_item_list(cypher_parsestate *cpstate,
parser_errposition(pstate, col->location)));
}
+ add_volatile_wrapper_to_target_entry(query->targetList, resno);
+
pos = makeInteger(resno);
item->var_name = val->val.str;
@@ -1530,7 +1532,7 @@ cypher_update_information *transform_cypher_remove_item_list(
{
ereport(ERROR,
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
- errmsg("REMOVE clause does not support adding propereties from maps"),
+ errmsg("REMOVE clause does not support adding properties from maps"),
parser_errposition(pstate, set_item->location)));
}
set_item->is_add = false;
@@ -1563,9 +1565,9 @@ cypher_update_information *transform_cypher_remove_item_list(
variable_name = variable_node->val.str;
item->var_name = variable_name;
+
item->entity_position = get_target_entry_resno(query->targetList,
variable_name);
-
if (item->entity_position == -1)
{
ereport(ERROR,
@@ -1575,6 +1577,9 @@ cypher_update_information *transform_cypher_remove_item_list(
parser_errposition(pstate, set_item->location)));
}
+ add_volatile_wrapper_to_target_entry(query->targetList,
+ item->entity_position);
+
// extract property name
if (list_length(ind->indirection) != 1)
{
@@ -1708,8 +1713,7 @@ cypher_update_information *transform_cypher_set_item_list(
ereport(
ERROR,
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
- errmsg(
- "SET clause doesnt not support updating maps or lists in a property"),
+ errmsg("SET clause doesn't not support updating maps or lists in a property"),
parser_errposition(pstate, set_item->location)));
}
@@ -1738,9 +1742,9 @@ cypher_update_information *transform_cypher_set_item_list(
variable_name = variable_node->val.str;
item->var_name = variable_name;
+
item->entity_position = get_target_entry_resno(query->targetList,
variable_name);
-
if (item->entity_position == -1)
{
ereport(ERROR,
@@ -1750,6 +1754,9 @@ cypher_update_information *transform_cypher_set_item_list(
parser_errposition(pstate, set_item->location)));
}
+ add_volatile_wrapper_to_target_entry(query->targetList,
+ item->entity_position);
+
// set keep_null property
if (is_ag_node(set_item->expr, cypher_map))
{
@@ -2962,7 +2969,7 @@ static void transform_match_pattern(cypher_parsestate *cpstate, Query *query,
{
/*
* coerce the WHERE clause to a boolean before AND with the property
- * contraints, otherwise there could be evaluation issues.
+ * constraints, otherwise there could be evaluation issues.
*/
where_qual = (Expr *)coerce_to_boolean(pstate, (Node *)where_qual,
"WHERE");
@@ -3142,7 +3149,7 @@ static List *make_join_condition_for_edge(cypher_parsestate *cpstate,
* When the previous node is not in the join tree, but there is a vle
* edge before that join, then we need to compare this vle's start node
* against the previous vle's end node. No need to check the next edge,
- * because that would be redundent.
+ * because that would be redundant.
*/
if (!prev_node->in_join_tree &&
prev_edge != NULL &&
@@ -3501,9 +3508,9 @@ static A_Expr *filter_vertices_on_label_id(cypher_parsestate *cpstate,
}
/*
- * Creates the Contains operator to process property contraints for a vertex/
+ * Creates the Contains operator to process property constraints for a vertex/
* edge in a MATCH clause. creates the agtype @> with the entity's properties
- * on the right and the contraints in the MATCH clause on the left.
+ * on the right and the constraints in the MATCH clause on the left.
*/
static Node *create_property_constraints(cypher_parsestate *cpstate,
transform_entity *entity,
@@ -3560,6 +3567,7 @@ static Node *create_property_constraints(cypher_parsestate *cpstate,
static List *transform_match_path(cypher_parsestate *cpstate, Query *query,
cypher_path *path)
{
+ ParseState *pstate = (ParseState *)cpstate;
List *qual = NIL;
List *entities = NIL;
FuncCall *duplicate_edge_qual;
@@ -3573,6 +3581,15 @@ static List *transform_match_path(cypher_parsestate *cpstate, Query *query,
{
TargetEntry *path_te;
+ if (findTarget(query->targetList, path->var_name) != NULL)
+ {
+ ereport(ERROR,
+ (errcode(ERRCODE_DUPLICATE_ALIAS),
+ errmsg("variable \"%s\" already exists",
+ path->var_name),
+ parser_errposition(pstate, path->location)));
+ }
+
path_te = transform_match_create_path_variable(cpstate, path,
entities);
query->targetList = lappend(query->targetList, path_te);
@@ -3650,13 +3667,51 @@ static transform_entity *transform_VLE_edge_entity(cypher_parsestate *cpstate,
/*
* If we have a variable name (rel name), make the target entry. Otherwise,
- * there isn't a reason to create one.
+ * there isn't a reason to create one. Additionally, verify that it is not
+ * reused.
*/
if (rel->name != NULL)
{
FuncExpr *fexpr;
List *args = list_make1(var);
Oid func_oid = InvalidOid;
+ transform_entity *entity = NULL;
+
+ te = findTarget(query->targetList, rel->name);
+ entity = find_variable(cpstate, rel->name);
+
+ /* If the variable already exists, error out */
+ if (te && entity)
+ {
+ if (entity->type == ENT_VERTEX)
+ {
+ ereport(ERROR,
+ (errcode(ERRCODE_DUPLICATE_ALIAS),
+ errmsg("variable '%s' is for a vertex", rel->name),
+ parser_errposition(pstate, rel->location)));
+ }
+ else if (entity->type == ENT_EDGE)
+ {
+ ereport(ERROR,
+ (errcode(ERRCODE_DUPLICATE_ALIAS),
+ errmsg("variable '%s' is for an edge", rel->name),
+ parser_errposition(pstate, rel->location)));
+ }
+ else
+ {
+ ereport(ERROR,
+ (errcode(ERRCODE_DUPLICATE_ALIAS),
+ errmsg("duplicate variable '%s'", rel->name),
+ parser_errposition(pstate, rel->location)));
+ }
+ }
+ else if (te && !entity)
+ {
+ ereport(ERROR,
+ (errcode(ERRCODE_DUPLICATE_ALIAS),
+ errmsg("variable '%s' already exists", rel->name),
+ parser_errposition(pstate, rel->location)));
+ }
/*
* Get the oid for the materialize function that returns a list of
@@ -3804,11 +3859,22 @@ static List *transform_match_entities(cypher_parsestate *cpstate, Query *query,
*/
if (node->name != NULL)
{
+ Node *expr;
+
+ if (path->var_name && strcmp(node->name, path->var_name) == 0)
+ {
+ ereport(ERROR,
+ (errcode(ERRCODE_DUPLICATE_ALIAS),
+ errmsg("variable \"%s\" is for a path",
+ node->name),
+ parser_errposition(pstate, node->location)));
+ }
+
/*
* Checks the previous clauses to see if the variable already
* exists.
*/
- Node *expr = colNameToVar(pstate, node->name, false,
+ expr = colNameToVar(pstate, node->name, false,
node->location);
if (expr != NULL)
{
@@ -3828,6 +3894,13 @@ static List *transform_match_entities(cypher_parsestate *cpstate, Query *query,
entity = make_transform_entity(cpstate, ENT_VERTEX, (Node *)node,
expr);
+ /*
+ * We want to add transformed entity to entities before transforming props
+ * so that props referencing currently transformed entity can be resolved.
+ */
+ cpstate->entities = lappend(cpstate->entities, entity);
+ entities = lappend(entities, entity);
+
/* transform the properties if they exist */
if (node->props)
{
@@ -3895,9 +3968,6 @@ static List *transform_match_entities(cypher_parsestate *cpstate, Query *query,
lappend(cpstate->property_constraint_quals, n);
}
- cpstate->entities = lappend(cpstate->entities, entity);
- entities = lappend(entities, entity);
-
prev_entity = entity;
}
/* odd increments of i are edges */
@@ -3907,6 +3977,16 @@ static List *transform_match_entities(cypher_parsestate *cpstate, Query *query,
rel = lfirst(lc);
+ if (rel->name && path->var_name &&
+ strcmp(rel->name, path->var_name) == 0)
+ {
+ ereport(ERROR,
+ (errcode(ERRCODE_DUPLICATE_ALIAS),
+ errmsg("variable \"%s\" is for a path",
+ rel->name),
+ parser_errposition(pstate, rel->location)));
+ }
+
/*
* There are 2 edge cases - 1) a regular edge and 2) a VLE edge.
* A VLE edge is not added like a regular edge - it is a function.
@@ -3944,7 +4024,12 @@ static List *transform_match_entities(cypher_parsestate *cpstate, Query *query,
entity = make_transform_entity(cpstate, ENT_EDGE, (Node *)rel,
expr);
+ /*
+ * We want to add transformed entity to entities before transforming props
+ * so that props referencing currently transformed entity can be resolved.
+ */
cpstate->entities = lappend(cpstate->entities, entity);
+ entities = lappend(entities, entity);
if (rel->props)
{
@@ -4010,8 +4095,6 @@ static List *transform_match_entities(cypher_parsestate *cpstate, Query *query,
lappend(cpstate->property_constraint_quals, r);
}
- entities = lappend(entities, entity);
-
prev_entity = entity;
}
/* if we have a VLE edge */
@@ -4021,7 +4104,7 @@ static List *transform_match_entities(cypher_parsestate *cpstate, Query *query,
/*
* Check to see if the previous node was originally created
- * in a predecessing clause. If it was, then remove the id field
+ * in a preceding clause. If it was, then remove the id field
* from the column ref. Just reference the agtype vertex
* variable that the prev clause created and the vle will handle
* extracting the id.
@@ -4286,40 +4369,153 @@ static Expr *transform_cypher_edge(cypher_parsestate *cpstate,
bool valid_label)
{
ParseState *pstate = (ParseState *)cpstate;
- char *schema_name;
- char *rel_name;
- RangeVar *label_range_var;
- Alias *alias;
- RangeTblEntry *rte;
- int resno;
- TargetEntry *te;
- Expr *expr;
+ char *schema_name = NULL;
+ char *rel_name = NULL;
+ RangeVar *label_range_var = NULL;
+ Alias *alias = NULL;
+ RangeTblEntry *rte = NULL;
+ int resno = -1;
+ TargetEntry *te = NULL;
+ transform_entity *entity = NULL;
+ cypher_relationship *cr = NULL;
+ Node *expr = NULL;
+ bool refs_var = false;
+
+ /*
+ * If we have an edge name, get any potential variable or column
+ * references. Additionally, verify that they are for edges.
+ */
+ if (rel->name != NULL)
+ {
+ te = findTarget(*target_list, rel->name);
+ entity = find_variable(cpstate, rel->name);
+ expr = colNameToVar(pstate, rel->name, false, rel->location);
+
+ /*
+ * If we have a valid entity and te for this rel name, go ahead and get
+ * the cypher relationship as we will need this for later and flag that
+ * we have a variable reference.
+ */
+ if (te != NULL && entity != NULL)
+ {
+ cr = (cypher_relationship *)entity->entity.rel;
+ refs_var = true;
+ }
+
+ /* If the variable already exists, verify that it is for an edge */
+ if (refs_var)
+ {
+ if (entity->type == ENT_VERTEX)
+ {
+ ereport(ERROR,
+ (errcode(ERRCODE_DUPLICATE_ALIAS),
+ errmsg("variable '%s' is for a vertex", rel->name),
+ parser_errposition(pstate, rel->location)));
+ }
+ else if (entity->type == ENT_VLE_EDGE)
+ {
+ ereport(ERROR,
+ (errcode(ERRCODE_DUPLICATE_ALIAS),
+ errmsg("variable '%s' is for a VLE edge", rel->name),
+ parser_errposition(pstate, rel->location)));
+ }
+ }
- if (!rel->label)
+ else if (te && !entity)
+ {
+ ereport(ERROR,
+ (errcode(ERRCODE_DUPLICATE_ALIAS),
+ errmsg("variable '%s' already exists", rel->name),
+ parser_errposition(pstate, rel->location)));
+ }
+ }
+
+ /*
+ * If we do not have a label for this edge, we either need to find one
+ * from a referenced variable or we need to set it to the default label.
+ */
+ if (rel->label == NULL)
{
- rel->label = AG_DEFAULT_LABEL_EDGE;
+ /* if there is a variable for this rel name */
+ if (refs_var)
+ {
+ /*
+ * If the referenced var has a non NULL label, copy it. This is
+ * usually the case when it uses a variable that is already defined.
+ * Fx -
+ *
+ * MATCH (u:people)-[e:knows]->(v:people), (v)-[e]->(u) RETURN e
+ * MATCH (u:people)-[]->()-[]->(u) RETURN u
+ *
+ * We copy it so that we know what label it is referencing.
+ */
+ if (cr->parsed_label != NULL)
+ {
+ rel->parsed_label = cr->parsed_label;
+ rel->label = cr->label;
+ }
+ else
+ {
+ rel->label = AG_DEFAULT_LABEL_EDGE;
+ }
+ }
+ /* otherwise, just give it the default label */
+ else
+ {
+ rel->label = AG_DEFAULT_LABEL_EDGE;
+ }
}
- else if(!valid_label)
+ /* if we do have a label, is it valid */
+ else if (!valid_label)
{
/*
- * XXX: Need to determine proper rules, for when label does not exist
- * or is for an edge. Maybe labels and edges should share names, like
+ * XXX: Need to determine proper rules, for when a label does not exist
+ * or is for a vertex. Maybe labels and edges should share names, like
* in openCypher. But these are stand in errors, to prevent
* segmentation faults, and other errors.
*
* Update: Nonexistent and mismatched labels now return a NULL value to
- * prevent segmentation faults, and other errors. We can also consider
+ * prevent segmentation faults, and other errors. We can also consider
* if an all-purpose label would be useful.
*/
rel->label = NULL;
+ }
+ /*
+ * Variables for edges are not allowed to be used multiple times within the
+ * same clause.
+ */
+ if (expr == NULL && refs_var)
+ {
+ ereport(ERROR,
+ (errcode(ERRCODE_DUPLICATE_ALIAS),
+ errmsg("duplicate edge variable '%s' within a clause",
+ rel->name),
+ parser_errposition(pstate, rel->location)));
}
- if (rel->name != NULL)
+ /*
+ * If this edge uses a variable that already exists, verify that the label
+ * names are the same.
+ */
+ if (refs_var &&
+ (cr->parsed_label != NULL || rel->parsed_label != NULL) &&
+ (cr->parsed_label == NULL || rel->parsed_label == NULL ||
+ (strcmp(cr->parsed_label, rel->parsed_label) != 0)))
{
- TargetEntry *te;
- Node *expr;
+ ereport(ERROR,
+ (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("multiple labels for variable '%s' are not supported",
+ rel->name),
+ parser_errposition(pstate, rel->location)));
+ }
+ /*
+ * Now we need to do a few checks and either return the existing var or
+ * or build a new edge.
+ */
+ if (rel->name != NULL)
+ {
/*
* If we are in a WHERE clause transform, we don't want to create new
* variables, we want to use the existing ones. So, error if otherwise.
@@ -4332,11 +4528,12 @@ static Expr *transform_cypher_edge(cypher_parsestate *cpstate,
* If expr_kind is WHERE, the expressions are in the parent's
* parent's parsestate, due to the way we transform sublinks.
*/
- transform_entity *entity = find_variable(parent_cpstate, rel->name);
+ transform_entity *tentity = find_variable(parent_cpstate,
+ rel->name);
- if (entity != NULL)
+ if (tentity != NULL)
{
- return get_relative_expr(entity, 2);
+ return get_relative_expr(tentity, 2);
}
else
{
@@ -4347,47 +4544,20 @@ static Expr *transform_cypher_edge(cypher_parsestate *cpstate,
}
}
- te = findTarget(*target_list, rel->name);
- /* also search for a variable from a previous transform */
- expr = colNameToVar(pstate, rel->name, false, rel->location);
-
- if (expr != NULL)
+ /* if this vertex is referencing an existing te var, return its expr */
+ if (refs_var)
{
- return (Expr*)expr;
+ return te->expr;
}
- if (te != NULL)
+ /* if this vertex is referencing an existing col var, return its expr */
+ if (expr != NULL)
{
- transform_entity *entity = find_variable(cpstate, rel->name);
-
- /*
- * If the variable already exists, verify that it is for an edge.
- * You cannot have the same edge repeated in a path.
- * You cannot have an variable that is for a vertex.
- */
- if (entity != NULL)
- {
- if (entity->type == ENT_EDGE)
- {
- ereport(ERROR,
- (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
- errmsg("duplicate edge variable '%s' within a clause",
- rel->name),
- parser_errposition(pstate, rel->location)));
- }
- if (entity->type == ENT_VERTEX)
- {
- ereport(ERROR,
- (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
- errmsg("variable '%s' is for a vertex", rel->name),
- parser_errposition(pstate, rel->location)));
- }
- }
-
- return te->expr;
+ return (Expr *)expr;
}
}
+ /* if we aren't using a variable, build the edge */
if (!rel->name)
{
rel->name = get_next_default_alias(cpstate);
@@ -4419,20 +4589,20 @@ static Expr *transform_cypher_edge(cypher_parsestate *cpstate,
if (valid_label)
{
- expr = (Expr *)make_edge_expr(cpstate, rte, rel->label);
+ expr = make_edge_expr(cpstate, rte);
}
else
{
- expr = (Expr*)makeNullConst(AGTYPEOID, -1, InvalidOid);
+ expr = (Node *)makeNullConst(AGTYPEOID, -1, InvalidOid);
}
if (rel->name)
{
- te = makeTargetEntry(expr, resno, rel->name, false);
+ te = makeTargetEntry((Expr *)expr, resno, rel->name, false);
*target_list = lappend(*target_list, te);
}
- return expr;
+ return (Expr *)expr;
}
static Expr *transform_cypher_node(cypher_parsestate *cpstate,
@@ -4440,19 +4610,101 @@ static Expr *transform_cypher_node(cypher_parsestate *cpstate,
bool output_node, bool valid_label)
{
ParseState *pstate = (ParseState *)cpstate;
- char *schema_name;
- char *rel_name;
- RangeVar *label_range_var;
- Alias *alias;
- RangeTblEntry *rte;
- int resno;
- TargetEntry *te;
- Expr *expr;
+ char *schema_name = NULL;
+ char *rel_name = NULL;
+ RangeVar *label_range_var = NULL;
+ Alias *alias = NULL;
+ RangeTblEntry *rte = NULL;
+ int resno = -1;
+ TargetEntry *te = NULL;
+ Expr *expr = NULL;
+ transform_entity *entity = NULL;
+ cypher_node *cn = NULL;
+ bool refs_var = false;
- if (!node->label)
+ /* if we have a node name, get any potential variable references */
+ if (node->name != NULL)
{
- node->label = AG_DEFAULT_LABEL_VERTEX;
+ te = findTarget(*target_list, node->name);
+ entity = find_variable(cpstate, node->name);
+
+ /*
+ * If we have a valid entity and te for this rel name, go ahead and get
+ * the cypher relationship as we will need this for later and flag that
+ * we have a variable reference.
+ */
+ if (te != NULL && entity != NULL)
+ {
+ cn = (cypher_node *)entity->entity.node;
+ refs_var = true;
+ }
+
+ /* If the variable already exists, verify that it is for a vertex */
+ if (refs_var)
+ {
+ if (entity->type == ENT_EDGE)
+ {
+ ereport(ERROR,
+ (errcode(ERRCODE_DUPLICATE_ALIAS),
+ errmsg("variable '%s' is for an edge", node->name),
+ parser_errposition(pstate, node->location)));
+ }
+ else if (entity->type == ENT_VLE_EDGE)
+ {
+ ereport(ERROR,
+ (errcode(ERRCODE_DUPLICATE_ALIAS),
+ errmsg("variable '%s' is for a VLE edge", node->name),
+ parser_errposition(pstate, node->location)));
+ }
+ }
+
+ /* If their is a te but no entity, it implies that their is
+ * some variable that exists but not an edge,vle or a vertex
+ */
+ else if (te && !entity)
+ {
+ ereport(ERROR,
+ (errcode(ERRCODE_DUPLICATE_ALIAS),
+ errmsg("variable '%s' already exists", node->name),
+ parser_errposition(pstate, node->location)));
+ }
}
+
+ /*
+ * If we do not have a label for this vertex, we either need to find one
+ * from a referenced variable or we need to set it to the default label.
+ */
+ if (node->label == NULL)
+ {
+ if (refs_var)
+ {
+ /*
+ * If the referenced var has a non NULL label, copy it. This is
+ * usually the case when it uses a variable that is already defined.
+ * Fx -
+ *
+ * MATCH (u:people)-[e:knows]->(v:people), (v)-[e]->(u) RETURN e
+ * MATCH (u:people)-[]->()-[]->(u) RETURN u
+ *
+ * We copy it so that we know what label it is referencing.
+ */
+ if (cn->parsed_label != NULL)
+ {
+ node->parsed_label = cn->parsed_label;
+ node->label = cn->label;
+ }
+ else
+ {
+ node->label = AG_DEFAULT_LABEL_VERTEX;
+ }
+ }
+ /* otherwise, just give it the default label */
+ else
+ {
+ node->label = AG_DEFAULT_LABEL_VERTEX;
+ }
+ }
+ /* if we do have a label, is it valid */
else if (!valid_label)
{
/*
@@ -4466,18 +4718,37 @@ static Expr *transform_cypher_node(cypher_parsestate *cpstate,
* if an all-purpose label would be useful.
*/
node->label = NULL;
+ }
+ /*
+ * If this vertex uses a variable that already exists, verify that the label
+ * being used is of the same name.
+ */
+ if (refs_var &&
+ (cn->parsed_label != NULL || node->parsed_label != NULL) &&
+ (cn->parsed_label == NULL || node->parsed_label == NULL ||
+ (strcmp(cn->parsed_label, node->parsed_label) != 0)))
+ {
+ ereport(ERROR,
+ (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("multiple labels for variable '%s' are not supported",
+ node->name),
+ parser_errposition(pstate, node->location)));
}
+ /* if it is not an output node, just return null */
if (!output_node)
{
return NULL;
}
+ /*
+ * Now we need to do a few checks and either return the existing var or
+ * or build a new vertex.
+ */
if (node->name != NULL)
{
- TargetEntry *te;
- Node *expr;
+ Node *expr = NULL;
/*
* If we are in a WHERE clause transform, we don't want to create new
@@ -4491,11 +4762,12 @@ static Expr *transform_cypher_node(cypher_parsestate *cpstate,
* If expr_kind is WHERE, the expressions are in the parent's
* parent's parsestate, due to the way we transform sublinks.
*/
- transform_entity *entity = find_variable(parent_cpstate, node->name);
+ transform_entity *tentity = NULL;
- if (entity != NULL)
+ tentity = find_variable(parent_cpstate, node->name);
+ if (tentity != NULL)
{
- return get_relative_expr(entity, 2);
+ return get_relative_expr(tentity, 2);
}
else
{
@@ -4506,54 +4778,17 @@ static Expr *transform_cypher_node(cypher_parsestate *cpstate,
}
}
- te = findTarget(*target_list, node->name);
- /* also search for the variable from a previous transforms */
- expr = colNameToVar(pstate, node->name, false, node->location);
-
- if (expr != NULL)
+ /* if this vertex is referencing an existing te var, return its expr */
+ if (refs_var)
{
- return (Expr*)expr;
+ return te->expr;
}
- if (te != NULL)
+ /* if this vertex is referencing an existing col var, return its expr */
+ expr = colNameToVar(pstate, node->name, false, node->location);
+ if (expr != NULL)
{
- transform_entity *entity = find_variable(cpstate, node->name);
-
- /* If the variable already exists, verify that it is for a vertex */
- if (entity != NULL && (entity->type != ENT_VERTEX))
- {
- ereport(ERROR,
- (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
- errmsg("variable '%s' is for a edge", node->name),
- parser_errposition(pstate, node->location)));
- }
-
- /*
- * If the variable already exists, verify that any label specified
- * is of the same name or scope. Reject those that aren't.
- */
- if (entity != NULL)
- {
- cypher_node *cnode = (cypher_node *)entity->entity.node;
-
-
-
- if (!node->label ||
- (cnode != NULL &&
- node != NULL &&
- /* allow node using a default label against resolved var */
- pg_strcasecmp(node->label, AG_DEFAULT_LABEL_VERTEX) != 0 &&
- /* allow labels with the same name */
- pg_strcasecmp(cnode->label, node->label) != 0))
- {
- ereport(ERROR,
- (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
- errmsg("multiple labels for variable '%s' are not supported", node->name),
- parser_errposition(pstate, node->location)));
- }
- }
-
- return te->expr;
+ return (Expr*)expr;
}
}
else
@@ -4561,6 +4796,7 @@ static Expr *transform_cypher_node(cypher_parsestate *cpstate,
node->name = get_next_default_alias(cpstate);
}
+ /* now build a new vertex */
schema_name = get_graph_namespace_name(cpstate->graph_name);
if (valid_label)
@@ -4587,7 +4823,7 @@ static Expr *transform_cypher_node(cypher_parsestate *cpstate,
if (valid_label)
{
- expr = (Expr *)make_vertex_expr(cpstate, rte, node->label);
+ expr = (Expr *)make_vertex_expr(cpstate, rte);
}
else
{
@@ -4601,8 +4837,7 @@ static Expr *transform_cypher_node(cypher_parsestate *cpstate,
return expr;
}
-static Node *make_edge_expr(cypher_parsestate *cpstate, RangeTblEntry *rte,
- char *label)
+static Node *make_edge_expr(cypher_parsestate *cpstate, RangeTblEntry *rte)
{
ParseState *pstate = (ParseState *)cpstate;
Oid label_name_func_oid;
@@ -4650,8 +4885,7 @@ static Node *make_edge_expr(cypher_parsestate *cpstate, RangeTblEntry *rte,
return (Node *)func_expr;
}
-static Node *make_vertex_expr(cypher_parsestate *cpstate, RangeTblEntry *rte,
- char *label)
+static Node *make_vertex_expr(cypher_parsestate *cpstate, RangeTblEntry *rte)
{
ParseState *pstate = (ParseState *)cpstate;
Oid label_name_func_oid;
@@ -4786,6 +5020,18 @@ transform_cypher_create_path(cypher_parsestate *cpstate, List **target_list,
ccp->path_attr_num = InvalidAttrNumber;
+ if (in_path)
+ {
+ if (findTarget(*target_list, path->var_name) != NULL)
+ {
+ ereport(ERROR,
+ (errcode(ERRCODE_DUPLICATE_ALIAS),
+ errmsg("variable \"%s\" already exists",
+ path->var_name),
+ parser_errposition(pstate, path->location)));
+ }
+ }
+
foreach (lc, path->path)
{
if (is_ag_node(lfirst(lc), cypher_node))
@@ -4797,7 +5043,17 @@ transform_cypher_create_path(cypher_parsestate *cpstate, List **target_list,
transform_create_cypher_node(cpstate, target_list, node);
if (in_path)
+ {
+ if (node->name && strcmp(node->name, path->var_name) == 0)
+ {
+ ereport(ERROR,
+ (errcode(ERRCODE_DUPLICATE_ALIAS),
+ errmsg("variable \"%s\" already exists",
+ path->var_name),
+ parser_errposition(pstate, path->location)));
+ }
rel->flags |= CYPHER_TARGET_NODE_IN_PATH_VAR;
+ }
transformed_path = lappend(transformed_path, rel);
@@ -4815,7 +5071,17 @@ transform_cypher_create_path(cypher_parsestate *cpstate, List **target_list,
transform_create_cypher_edge(cpstate, target_list, edge);
if (in_path)
+ {
+ if (edge->name && strcmp(edge->name, path->var_name) == 0)
+ {
+ ereport(ERROR,
+ (errcode(ERRCODE_DUPLICATE_ALIAS),
+ errmsg("variable \"%s\" already exists",
+ path->var_name),
+ parser_errposition(pstate, path->location)));
+ }
rel->flags |= CYPHER_TARGET_NODE_IN_PATH_VAR;
+ }
transformed_path = lappend(transformed_path, rel);
@@ -4875,10 +5141,7 @@ transform_create_cypher_edge(cypher_parsestate *cpstate, List **target_list,
if (edge->label)
{
- label_cache_data *lcd =
- search_label_name_graph_cache(edge->label, cpstate->graph_oid);
-
- if (lcd && lcd->kind != LABEL_KIND_EDGE)
+ if (get_label_kind(edge->label, cpstate->graph_oid) == LABEL_KIND_VERTEX)
{
ereport(ERROR,
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
@@ -4898,11 +5161,16 @@ transform_create_cypher_edge(cypher_parsestate *cpstate, List **target_list,
* Variables can be declared in a CREATE clause, but not used if
* it already exists.
*/
- if (variable_exists(cpstate, edge->name))
+ transform_entity *entity;
+
+ entity = find_variable(cpstate, edge->name);
+
+ if ((entity && entity->type != ENT_EDGE) || variable_exists(cpstate, edge->name))
{
- ereport(ERROR,
- (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
- errmsg("variable %s already exists", edge->name)));
+ ereport(ERROR,
+ (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("variable %s already exists", edge->name),
+ parser_errposition(pstate, edge->location)));
}
rel->variable_name = edge->name;
@@ -5015,10 +5283,7 @@ transform_create_cypher_node(cypher_parsestate *cpstate, List **target_list,
if (node->label)
{
- label_cache_data *lcd =
- search_label_name_graph_cache(node->label, cpstate->graph_oid);
-
- if (lcd && lcd->kind != LABEL_KIND_VERTEX)
+ if (get_label_kind(node->label, cpstate->graph_oid) == LABEL_KIND_EDGE)
{
ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
errmsg("label %s is for edges, not vertices",
@@ -5035,9 +5300,16 @@ transform_create_cypher_node(cypher_parsestate *cpstate, List **target_list,
{
transform_entity *entity;
+ TargetEntry *te = findTarget(*target_list, node->name);
entity = find_variable(cpstate, node->name);
- if (entity)
+ /*
+ * If we find an entity as well as a target Entry with same name,
+ * that means that the variable is either for a vertex, edge or vle.
+ * But if we find a target entry but not an entity that means
+ * that the variable can be other than a vertex, edge or vle e.g path.
+ */
+ if (entity && te)
{
if (entity->type != ENT_VERTEX)
{
@@ -5050,6 +5322,16 @@ transform_create_cypher_node(cypher_parsestate *cpstate, List **target_list,
return transform_create_cypher_existing_node(cpstate, target_list,
entity->declared_in_current_clause, node);
}
+ else if (te)
+ {
+ /*
+ * Here we are not sure if the te is a vertex, path or something
+ * else. So we will let it pass and the execution stage will catch
+ * the error if variable was not vertex.
+ */
+ return transform_create_cypher_existing_node(cpstate, target_list,
+ te, node);
+ }
}
// otherwise transform the target node as a new node
@@ -5069,7 +5351,6 @@ static int get_target_entry_resno(List *target_list, char *name)
TargetEntry *te = (TargetEntry *)lfirst(lc);
if (!strcmp(te->resname, name))
{
- te->expr = add_volatile_wrapper(te->expr);
return te->resno;
}
}
@@ -5077,6 +5358,32 @@ static int get_target_entry_resno(List *target_list, char *name)
return -1;
}
+/* adds the volatile wrapper to the specified target entry */
+static void add_volatile_wrapper_to_target_entry(List *target_list, int resno)
+{
+ ListCell *lc;
+
+ Assert(target_list != NULL);
+ Assert(resno >= 0);
+
+ /* find the resource */
+ foreach (lc, target_list)
+ {
+ TargetEntry *te = (TargetEntry *)lfirst(lc);
+ if (te->resno == resno)
+ {
+ /* wrap it */
+ te->expr = add_volatile_wrapper(te->expr);
+ return;
+ }
+ }
+
+ /* if we didn't find anything, there was a problem */
+ ereport(ERROR,
+ (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+ errmsg("add_volatile_wrapper_to_target_entry: resno not found")));
+}
+
/*
* Transform logic for a previously declared variable in a CREATE clause.
* All we need from the variable node is its id, and whether we can skip
@@ -5087,24 +5394,26 @@ static cypher_target_node *transform_create_cypher_existing_node(
cypher_node *node)
{
cypher_target_node *rel = make_ag_node(cypher_target_node);
+ ParseState *pstate = (ParseState *)cpstate;
rel->type = LABEL_KIND_VERTEX;
rel->flags = CYPHER_TARGET_NODE_FLAG_NONE;
rel->resultRelInfo = NULL;
rel->variable_name = node->name;
-
if (node->props)
{
ereport(ERROR,
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
- errmsg("previously declared nodes in a create clause cannot have properties")));
+ errmsg("previously declared nodes in a create clause cannot have properties"),
+ parser_errposition(pstate, node->location)));
}
if (node->label)
{
ereport(ERROR,
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
- errmsg("previously declared variables cannot have a label")));
+ errmsg("previously declared variables cannot have a label"),
+ parser_errposition(pstate, node->location)));
}
/*
* When the variable is declared in the same clause this vertex is a part of
@@ -5121,6 +5430,8 @@ static cypher_target_node *transform_create_cypher_existing_node(
*/
rel->tuple_position = get_target_entry_resno(*target_list, node->name);
+ add_volatile_wrapper_to_target_entry(*target_list, rel->tuple_position);
+
return rel;
}
@@ -5456,13 +5767,25 @@ static TargetEntry *findTarget(List *targetList, char *resname)
/*
* Wrap the expression with a volatile function, to prevent the optimizer from
- * elimating the expression.
+ * eliminating the expression.
*/
static Expr *add_volatile_wrapper(Expr *node)
{
Oid oid;
- oid = get_ag_func_oid("agtype_volatile_wrapper", 1, AGTYPEOID);
+ /* if the passed Expr node is NULL it will cause a crash, so notify us */
+ if (node == NULL)
+ {
+ ereport(ERROR, (errmsg_internal("add_volatile_wrapper: NULL expr")));
+ }
+
+ oid = get_ag_func_oid("agtype_volatile_wrapper", 1, ANYOID);
+
+ /* if the passed Expr node is already wrapped, just return it */
+ if (IsA(node, FuncExpr) && oid == ((FuncExpr*)node)->funcid)
+ {
+ return node;
+ }
return (Expr *)makeFuncExpr(oid, AGTYPEOID, list_make1(node), InvalidOid,
InvalidOid, COERCE_EXPLICIT_CALL);
@@ -5473,10 +5796,10 @@ static Expr *add_volatile_wrapper(Expr *node)
* Modified entry point for recursively analyzing a sub-statement in union.
*/
Query *cypher_parse_sub_analyze_union(cypher_clause *clause,
- cypher_parsestate *cpstate,
- CommonTableExpr *parentCTE,
- bool locked_from_parent,
- bool resolve_unknowns)
+ cypher_parsestate *cpstate,
+ CommonTableExpr *parentCTE,
+ bool locked_from_parent,
+ bool resolve_unknowns)
{
cypher_parsestate *state = make_cypher_parsestate(cpstate);
Query *query;
@@ -5536,7 +5859,7 @@ Query *cypher_parse_sub_analyze(Node *parseTree,
* The second query will be for the path that this MERGE clause defines. The
* two subqueries will be joined together using a LATERAL LEFT JOIN with the
* previous query on the left and the MERGE path subquery on the right. Like
- * case 1 the targetList will have all the decalred variables and a FuncExpr
+ * case 1 the targetList will have all the declared variables and a FuncExpr
* that represents the MERGE clause with its needed metadata information, that
* will be caught in the planner phase and converted into a path.
*
@@ -5557,7 +5880,7 @@ Query *cypher_parse_sub_analyze(Node *parseTree,
* for one tuple the path exists (or there is multiple paths that exist and all
* paths must be emitted) and for another the path does not exist. This is
* similar to OPTIONAL MATCH, however with the added feature of creating the
- * path if not there, rather than just emiting NULL.
+ * path if not there, rather than just emitting NULL.
*/
static Query *transform_cypher_merge(cypher_parsestate *cpstate,
cypher_clause *clause)
@@ -5598,7 +5921,10 @@ static Query *transform_cypher_merge(cypher_parsestate *cpstate,
else
{
// make the merge node into a match node
- cypher_clause *merge_clause_as_match = convert_merge_to_match(self);
+
+ // TODO this is called above and appears redundant but needs to be
+ // looked into
+ //cypher_clause *merge_clause_as_match = convert_merge_to_match(self);
/*
* Create the metadata needed for creating missing paths.
@@ -5699,7 +6025,7 @@ transform_merge_make_lateral_join(cypher_parsestate *cpstate, Query *query,
* transform the previous clause
*/
j->larg = transform_clause_for_join(cpstate, clause->prev, &l_rte,
- &l_nsitem, l_alias);
+ &l_nsitem, l_alias);
pstate->p_namespace = lappend(pstate->p_namespace, l_nsitem);
/*
@@ -5827,6 +6153,134 @@ transform_cypher_merge_mark_tuple_position(List *target_list,
}
}
+/*
+ * Helper function to return a shallow copy of an existing, already transformed,
+ * matching variable. The copy returned will be flagged as such. If none are
+ * found, it will return NULL. If it finds a mismatched type, it will error
+ * stating that.
+ */
+static cypher_target_node *get_referenced_variable(ParseState *pstate,
+ Node *node,
+ List *transformed_path)
+{
+ ListCell *lc = NULL;
+ char *node_name = NULL;
+ char *node_label = NULL;
+ char node_type = 0;
+ int node_loc = -1;
+
+ /* passed node should only be a vertex or an edge */
+ Assert(is_ag_node(node, cypher_node) ||
+ is_ag_node(node, cypher_relationship));
+
+ /* set up our search based on our input type */
+ if (is_ag_node(node, cypher_node))
+ {
+ node_name = ((cypher_node *)node)->name;
+ node_label = ((cypher_node *)node)->label;
+ node_loc = ((cypher_node *)node)->location;
+ node_type = 'v';
+ }
+ else
+ {
+ node_name = ((cypher_relationship *)node)->name;
+ node_label = ((cypher_relationship *)node)->label;
+ node_loc = ((cypher_relationship *)node)->location;
+ node_type = 'e';
+ }
+
+ /* look through the list of previously transformed nodes and edges */
+ foreach (lc, transformed_path)
+ {
+ cypher_target_node *ctn = NULL;
+ bool is_name = false;
+ bool is_label = false;
+
+ /* list items should be of type cypher_target_node */
+ Assert(is_ag_node(lfirst(lc), cypher_target_node));
+ ctn = lfirst(lc);
+
+ /* do they have names? if so, do they match? */
+ is_name = (node_name == NULL || ctn->variable_name == NULL) ?
+ false : strcmp(node_name, ctn->variable_name) == 0;
+
+ /* do they have labels? if so, do they match? */
+ is_label = (ctn->label_name != NULL) ?
+ ((node_label == NULL) ? true : strcmp(ctn->label_name, node_label) == 0)
+ : false;
+
+ /* if the types don't match, error or skip */
+ if (node_type != ctn->type)
+ {
+ /* is the name a match, generate an error. otherwise, skip it. */
+ if (is_name)
+ {
+ if (node_type == 'v')
+ {
+ ereport(ERROR,
+ (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+ errmsg("variable \"%s\" is for an edge",
+ node_name),
+ parser_errposition(pstate, node_loc)));
+ }
+ else
+ {
+ ereport(ERROR,
+ (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+ errmsg("variable \"%s\" is for a vertex",
+ node_name),
+ parser_errposition(pstate, node_loc)));
+ }
+ }
+ else
+ {
+ continue;
+ }
+ }
+
+ if (is_name && !is_label)
+ {
+ ereport(ERROR,
+ (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+ errmsg("multiple labels for variable '%s' are not supported",
+ node_name),
+ parser_errposition(pstate, node_loc)));
+ }
+
+ /*
+ * If this is a match, make a shallow copy of it, modify the copy to be
+ * flagged as a previously declared variable, and then return it.
+ */
+ if (is_name && is_label)
+ {
+ cypher_target_node *_cpy = make_ag_node(cypher_target_node);
+
+ /* make a shallow copy */
+ _cpy->type = ctn->type;
+ _cpy->flags = ctn->flags;
+ _cpy->dir = ctn->dir;
+ _cpy->id_expr = ctn->id_expr;
+ _cpy->id_expr_state = ctn->id_expr_state;
+ _cpy->prop_expr = ctn->prop_expr;
+ _cpy->prop_expr_state = ctn->prop_expr_state;
+ _cpy->prop_attr_num = ctn->prop_attr_num;
+ _cpy->resultRelInfo = ctn->resultRelInfo;
+ _cpy->elemTupleSlot = ctn->elemTupleSlot;
+ _cpy->relid = ctn->relid;
+ _cpy->label_name = ctn->label_name;
+ _cpy->variable_name = ctn->variable_name;
+ _cpy->tuple_position = ctn->tuple_position;
+
+ /* set it to a declared variable */
+ _cpy->flags &= 0xfffffffe;
+ _cpy->flags |= EXISTING_VARIABLE_DECLARED_SAME_CLAUSE;
+
+ return _cpy;
+ }
+ }
+ return NULL;
+}
+
/*
* Creates the target nodes for a merge path. If MERGE has a path that doesn't
* exist then in the MERGE clause we act like a CREATE clause. This function
@@ -5836,6 +6290,7 @@ static cypher_create_path *
transform_cypher_merge_path(cypher_parsestate *cpstate, List **target_list,
cypher_path *path)
{
+ ParseState *pstate = (ParseState *)cpstate;
ListCell *lc;
List *transformed_path = NIL;
cypher_create_path *ccp = make_ag_node(cypher_create_path);
@@ -5848,9 +6303,30 @@ transform_cypher_merge_path(cypher_parsestate *cpstate, List **target_list,
if (is_ag_node(lfirst(lc), cypher_node))
{
cypher_node *node = lfirst(lc);
+ cypher_target_node *rel = NULL;
- cypher_target_node *rel =
- transform_merge_cypher_node(cpstate, target_list, node);
+ if (path->var_name != NULL && node->name != NULL &&
+ strcmp(path->var_name, node->name) == 0)
+ {
+ ereport(ERROR,
+ (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+ errmsg("variable \"%s\" is for a path", node->name),
+ parser_errposition(pstate, node->location)));
+ }
+
+ /*
+ * If the variable was already transformed, get a referenced copy of
+ * it. This copy will make sure the executor phase doesn't create a
+ * new node from it.
+ */
+ rel = get_referenced_variable(pstate, (Node *)node,
+ transformed_path);
+
+ /* if there wasn't a transformed variable, transform the node */
+ if (rel == NULL)
+ {
+ rel = transform_merge_cypher_node(cpstate, target_list, node);
+ }
if (in_path)
{
@@ -5861,10 +6337,37 @@ transform_cypher_merge_path(cypher_parsestate *cpstate, List **target_list,
}
else if (is_ag_node(lfirst(lc), cypher_relationship))
{
- cypher_relationship *edge = lfirst(lc);
+ cypher_relationship *edge = NULL;
+ cypher_target_node *rel = NULL;
- cypher_target_node *rel =
- transform_merge_cypher_edge(cpstate, target_list, edge);
+ edge = lfirst(lc);
+
+ if (path->var_name != NULL && edge->name != NULL &&
+ strcmp(path->var_name, edge->name) == 0)
+ {
+ ereport(ERROR,
+ (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+ errmsg("variable \"%s\" is for a path", edge->name),
+ parser_errposition(pstate, edge->location)));
+ }
+
+ /*
+ * Get a referenced edge variable. This should not happen as edges
+ * can not be duplicated within a path.
+ */
+ rel = get_referenced_variable(pstate, (Node *)edge,
+ transformed_path);
+ if (rel != NULL)
+ {
+ ereport(ERROR,
+ (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+ errmsg("a duplicate edge variable \"%s\" is not permitted within a path",
+ edge->name),
+ parser_errposition(pstate, edge->location)));
+ }
+
+ /* transform the edge */
+ rel = transform_merge_cypher_edge(cpstate, target_list, edge);
if (in_path)
{
@@ -5971,6 +6474,21 @@ transform_merge_cypher_edge(cypher_parsestate *cpstate, List **target_list,
rv = makeRangeVar(cpstate->graph_name, edge->label, -1);
label_relation = parserOpenTable(&cpstate->pstate, rv, RowExclusiveLock);
+ /*
+ * TODO
+ * It is possible for a vertex label to be retrieved, instead of an edge,
+ * due to the above logic. So, we need to check if it is a vertex label.
+ * This whole section needs to be fixed because it could be a relation that
+ * isn't either and has the correct number of columns. However, for now,
+ * we just check the number of columns.
+ */
+ if (label_relation->rd_att->natts == 2) // TODO temporarily hardcoded
+ {
+ ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("Expecting edge label, found existing vertex label"),
+ parser_errposition(&cpstate->pstate, edge->location)));
+ }
+
// Store the relid
rel->relid = RelationGetRelid(label_relation);
@@ -6006,10 +6524,8 @@ transform_merge_cypher_node(cypher_parsestate *cpstate, List **target_list,
if (node->name != NULL)
{
-
transform_entity *entity = find_transform_entity(cpstate, node->name,
ENT_VERTEX);
-
/*
* the vertex was previously declared, we do not need to do any setup
* to create the node.
@@ -6076,6 +6592,21 @@ transform_merge_cypher_node(cypher_parsestate *cpstate, List **target_list,
rv = makeRangeVar(cpstate->graph_name, node->label, -1);
label_relation = parserOpenTable(&cpstate->pstate, rv, RowExclusiveLock);
+ /*
+ * TODO
+ * It is possible for an edge label to be retrieved, instead of a vertex,
+ * due to the above logic. So, we need to check if it is an edge label.
+ * This whole section needs to be fixed because it could be a relation that
+ * isn't either and has the correct number of columns. However, for now,
+ * we just check the number of columns.
+ */
+ if (label_relation->rd_att->natts == 4) // TODO temporarily hardcoded
+ {
+ ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("Expecting vertex label, found existing edge label"),
+ parser_errposition(&cpstate->pstate, node->location)));
+ }
+
// Store the relid
rel->relid = RelationGetRelid(label_relation);
diff --git a/src/backend/parser/cypher_expr.c b/src/backend/parser/cypher_expr.c
index 00261dd1e..62f7efde2 100644
--- a/src/backend/parser/cypher_expr.c
+++ b/src/backend/parser/cypher_expr.c
@@ -63,6 +63,7 @@
#define FUNC_AGTYPE_TYPECAST_INT "agtype_typecast_int"
#define FUNC_AGTYPE_TYPECAST_PG_FLOAT8 "agtype_to_float8"
#define FUNC_AGTYPE_TYPECAST_PG_BIGINT "agtype_to_int8"
+#define FUNC_AGTYPE_TYPECAST_BOOL "agtype_typecast_bool"
static Node *transform_cypher_expr_recurse(cypher_parsestate *cpstate,
Node *expr);
@@ -153,12 +154,15 @@ static Node *transform_cypher_expr_recurse(cypher_parsestate *cpstate,
case T_NullTest:
{
NullTest *n = (NullTest *)expr;
+ NullTest *transformed_expr = makeNode(NullTest);
- n->arg = (Expr *)transform_cypher_expr_recurse(cpstate,
- (Node *)n->arg);
- n->argisrow = type_is_rowtype(exprType((Node *)n->arg));
+ transformed_expr->arg = (Expr *)transform_cypher_expr_recurse(cpstate,
+ (Node *)n->arg);
+ transformed_expr->nulltesttype = n->nulltesttype;
+ transformed_expr->argisrow = type_is_rowtype(exprType((Node *)transformed_expr->arg));
+ transformed_expr->location = n->location;
- return expr;
+ return (Node *) transformed_expr;
}
case T_CaseExpr:
return transform_CaseExpr(cpstate, (CaseExpr *) expr);
@@ -652,35 +656,108 @@ static Node *transform_cypher_map(cypher_parsestate *cpstate, cypher_map *cm)
return (Node *)fexpr;
}
+/*
+ * Helper function to transform a cypher list into an agtype list. The function
+ * will use agtype_add to concatenate lists when the number of parameters
+ * exceeds 100, a PG limitation.
+ */
static Node *transform_cypher_list(cypher_parsestate *cpstate, cypher_list *cl)
{
- List *newelems = NIL;
- ListCell *le;
- FuncExpr *fexpr;
- Oid func_oid;
-
- foreach (le, cl->elems)
+ List *abl_args = NIL;
+ ListCell *le = NULL;
+ FuncExpr *aa_lhs_arg = NULL;
+ FuncExpr *fexpr = NULL;
+ Oid abl_func_oid = InvalidOid;
+ Oid aa_func_oid = InvalidOid;
+ int nelems = 0;
+ int i = 0;
+
+ /* determine which build function we need */
+ nelems = list_length(cl->elems);
+ if (nelems == 0)
{
- Node *newv;
-
- newv = transform_cypher_expr_recurse(cpstate, lfirst(le));
-
- newelems = lappend(newelems, newv);
+ abl_func_oid = get_ag_func_oid("agtype_build_list", 0);
+ }
+ else
+ {
+ abl_func_oid = get_ag_func_oid("agtype_build_list", 1, ANYOID);
}
- if (list_length(newelems) == 0)
+ /* get the concat function oid, if necessary */
+ if (nelems > 100)
{
- func_oid = get_ag_func_oid("agtype_build_list", 0);
+ aa_func_oid = get_ag_func_oid("agtype_add", 2, AGTYPEOID, AGTYPEOID);
}
- else
+
+ /* iterate through the list of elements */
+ foreach (le, cl->elems)
{
- func_oid = get_ag_func_oid("agtype_build_list", 1, ANYOID);
+ Node *texpr = NULL;
+
+ /* transform the argument */
+ texpr = transform_cypher_expr_recurse(cpstate, lfirst(le));
+
+ /*
+ * If we have more than 100 elements we will need to add in the list
+ * concatenation function.
+ */
+ if (i >= 100)
+ {
+ /* build the list function node argument for concatenate */
+ fexpr = makeFuncExpr(abl_func_oid, AGTYPEOID, abl_args, InvalidOid,
+ InvalidOid, COERCE_EXPLICIT_CALL);
+ fexpr->location = cl->location;
+
+ /* initial case, set up for concatenating 2 lists */
+ if (aa_lhs_arg == NULL)
+ {
+ aa_lhs_arg = fexpr;
+ }
+ /*
+ * For every other case, concatenate the list on to the previous
+ * concatenate operation.
+ */
+ else
+ {
+ List *aa_args = list_make2(aa_lhs_arg, fexpr);
+
+ fexpr = makeFuncExpr(aa_func_oid, AGTYPEOID, aa_args,
+ InvalidOid, InvalidOid,
+ COERCE_EXPLICIT_CALL);
+ fexpr->location = cl->location;
+
+ /* set the lhs to the concatenation operation */
+ aa_lhs_arg = fexpr;
+ }
+
+ /* reset */
+ abl_args = NIL;
+ i = 0;
+ fexpr = NULL;
+ }
+
+ /* now add the latest transformed expression to the list */
+ abl_args = lappend(abl_args, texpr);
+ i++;
}
- fexpr = makeFuncExpr(func_oid, AGTYPEOID, newelems, InvalidOid, InvalidOid,
- COERCE_EXPLICIT_CALL);
+ /* now build the final list function */
+ fexpr = makeFuncExpr(abl_func_oid, AGTYPEOID, abl_args, InvalidOid,
+ InvalidOid, COERCE_EXPLICIT_CALL);
fexpr->location = cl->location;
+ /*
+ * If there was a previous concatenation or list function, build a final
+ * concatenation function node
+ */
+ if (aa_lhs_arg != NULL)
+ {
+ List *aa_args = list_make2(aa_lhs_arg, fexpr);
+
+ fexpr = makeFuncExpr(aa_func_oid, AGTYPEOID, aa_args, InvalidOid,
+ InvalidOid, COERCE_EXPLICIT_CALL);
+ }
+
return (Node *)fexpr;
}
@@ -953,6 +1030,11 @@ static Node *transform_cypher_typecast(cypher_parsestate *cpstate,
{
fname = lappend(fname, makeString(FUNC_AGTYPE_TYPECAST_PG_BIGINT));
}
+ else if ((pg_strcasecmp(ctypecast->typecast, "bool") == 0 ||
+ pg_strcasecmp(ctypecast->typecast, "boolean") == 0))
+ {
+ fname = lappend(fname, makeString(FUNC_AGTYPE_TYPECAST_BOOL));
+ }
/* if none was found, error out */
else
{
@@ -1117,7 +1199,7 @@ static Node *transform_CoalesceExpr(cypher_parsestate *cpstate, CoalesceExpr
/*
* Code borrowed from PG's transformCaseExpr and updated for AGE
*/
-static Node *transform_CaseExpr(cypher_parsestate *cpstate,CaseExpr
+static Node *transform_CaseExpr(cypher_parsestate *cpstate, CaseExpr
*cexpr)
{
ParseState *pstate = &cpstate->pstate;
@@ -1202,7 +1284,20 @@ static Node *transform_CaseExpr(cypher_parsestate *cpstate,CaseExpr
resultexprs = lcons(newcexpr->defresult, resultexprs);
- ptype = select_common_type(pstate, resultexprs, "CASE", NULL);
+ /*
+ * we pass a NULL context to select_common_type because the common types can
+ * only be AGTYPEOID or BOOLOID. If it returns invalidoid, we know there is a
+ * boolean involved.
+ */
+ ptype = select_common_type(pstate, resultexprs, NULL, NULL);
+
+ //InvalidOid shows that there is a boolean in the result expr.
+ if (ptype == InvalidOid)
+ {
+ //we manually set the type to boolean here to handle the bool casting.
+ ptype = BOOLOID;
+ }
+
Assert(OidIsValid(ptype));
newcexpr->casetype = ptype;
/* casecollid will be set by parse_collate.c */
diff --git a/src/backend/parser/cypher_gram.y b/src/backend/parser/cypher_gram.y
index 84eb957a1..fd01174b7 100644
--- a/src/backend/parser/cypher_gram.y
+++ b/src/backend/parser/cypher_gram.y
@@ -32,6 +32,7 @@
#include "nodes/cypher_nodes.h"
#include "parser/ag_scanner.h"
#include "parser/cypher_gram.h"
+#include "parser/cypher_parse_node.h"
// override the default action for locations
#define YYLLOC_DEFAULT(current, rhs, n) \
@@ -183,8 +184,11 @@
%{
//
+// internal alias check
+static bool has_internal_default_prefix(char *str);
+
// unique name generation
-#define UNIQUE_NAME_NULL_PREFIX "_unique_null_prefix"
+#define UNIQUE_NAME_NULL_PREFIX AGE_DEFAULT_PREFIX"unique_null_prefix"
static char *create_unique_name(char *prefix_name);
static unsigned long get_a_unique_number(void);
@@ -464,7 +468,6 @@ yield_item:
}
;
-
semicolon_opt:
/* empty */
| ';'
@@ -627,8 +630,11 @@ cypher_range_idx:
cypher_range_idx_opt:
cypher_range_idx
- | /* EMPTY */ { $$ = NULL; }
- ;
+ | /* EMPTY */
+ {
+ $$ = NULL;
+ }
+ ;
Iconst: INTEGER
@@ -1111,6 +1117,7 @@ path:
p = (cypher_path *)$3;
p->var_name = $1;
+ p->location = @1;
$$ = (Node *)p;
}
@@ -1176,8 +1183,9 @@ path_node:
n = make_ag_node(cypher_node);
n->name = $2;
n->label = $3;
+ n->parsed_label = $3;
n->props = $4;
- n->location = @1;
+ n->location = @2;
$$ = (Node *)n;
}
@@ -1221,6 +1229,7 @@ path_relationship_body:
n = make_ag_node(cypher_relationship);
n->name = $2;
n->label = $3;
+ n->parsed_label = $3;
n->varlen = $4;
n->props = $5;
@@ -1234,6 +1243,7 @@ path_relationship_body:
n = make_ag_node(cypher_relationship);
n->name = NULL;
n->label = NULL;
+ n->parsed_label = NULL;
n->varlen = NULL;
n->props = NULL;
@@ -1435,7 +1445,7 @@ expr:
}
/*
* This is a catch all grammar rule that allows us to avoid some
- * shift/reduce errors between expression indirection rules by colapsing
+ * shift/reduce errors between expression indirection rules by collapsing
* those rules into one generic rule. We can then inspect the expressions to
* decide what specific rule needs to be applied and then construct the
* required result.
@@ -1827,6 +1837,15 @@ property_key_name:
var_name:
symbolic_name
+ {
+ if (has_internal_default_prefix($1))
+ {
+ ereport(ERROR,
+ (errcode(ERRCODE_SYNTAX_ERROR),
+ errmsg("%s is only for internal use", AGE_DEFAULT_PREFIX),
+ ag_scanner_errposition(@1, scanner)));
+ }
+ }
;
var_name_opt:
@@ -2139,25 +2158,27 @@ static Node *make_function_expr(List *func_name, List *exprs, int location)
* functions. We may want to find a better way to do this, as there
* could be many.
*/
- if (pg_strcasecmp(name, "rand") == 0)
- funcname = SystemFuncName("random");
- else if (pg_strcasecmp(name, "pi") == 0)
- funcname = SystemFuncName("pi");
- else if (pg_strcasecmp(name, "count") == 0)
+ if (pg_strcasecmp(name, "count") == 0)
+ {
funcname = SystemFuncName("count");
+ }
else
+ {
/*
* We don't qualify AGE functions here. This is done in the
* transform layer and allows us to know which functions are ours.
*/
funcname = func_name;
+ }
/* build the function call */
fnode = makeFuncCall(funcname, exprs, location);
}
/* all other functions are passed as is */
else
+ {
fnode = makeFuncCall(func_name, exprs, location);
+ }
/* return the node */
return (Node *)fnode;
@@ -2185,7 +2206,7 @@ static char *create_unique_name(char *prefix_name)
prefix = prefix_name;
}
- /* get the length of the combinded string */
+ /* get the length of the combined string */
nlen = snprintf(NULL, 0, "%s_%lu", prefix, unique_number);
/* allocate the space */
@@ -2203,6 +2224,12 @@ static char *create_unique_name(char *prefix_name)
return name;
}
+/* function to check if given string has internal alias as prefix */
+static bool has_internal_default_prefix(char *str)
+{
+ return strncmp(AGE_DEFAULT_PREFIX, str, strlen(AGE_DEFAULT_PREFIX)) == 0;
+}
+
/* function to return a unique unsigned long number */
static unsigned long get_a_unique_number(void)
{
@@ -2222,6 +2249,7 @@ static Node *make_set_op(SetOperation op, bool all_or_distinct, List *larg,
n->all_or_distinct = all_or_distinct;
n->larg = (List *) larg;
n->rarg = (List *) rarg;
+
return (Node *) n;
}
@@ -2415,7 +2443,7 @@ static cypher_relationship *build_VLE_relation(List *left_arg,
(cnl->name == NULL && cnr->label != NULL) ||
(cnl->name == NULL && cnr->props != NULL))
{
- cnl->name = create_unique_name("_vle_function_start_var");
+ cnl->name = create_unique_name(AGE_DEFAULT_PREFIX"vle_function_start_var");
}
/* add in the start vertex as a ColumnRef if necessary */
@@ -2447,7 +2475,7 @@ static cypher_relationship *build_VLE_relation(List *left_arg,
if (cnr->name == NULL &&
(cnr->label != NULL || cnr->props != NULL))
{
- cnr->name = create_unique_name("_vle_function_end_var");
+ cnr->name = create_unique_name(AGE_DEFAULT_PREFIX"vle_function_end_var");
}
/*
* We need a NULL for the target vertex in the VLE match to
diff --git a/src/backend/parser/cypher_item.c b/src/backend/parser/cypher_item.c
index c90073b7c..6eabb60e7 100644
--- a/src/backend/parser/cypher_item.c
+++ b/src/backend/parser/cypher_item.c
@@ -112,7 +112,7 @@ List *transform_cypher_item_list(cypher_parsestate *cpstate, List *item_list,
target_list = lappend(target_list, te);
/*
- * Did the tranformed item contain an aggregate function? If it didn't,
+ * Did the transformed item contain an aggregate function? If it didn't,
* add it to the potential group_clause. If it did, flag that we found
* an aggregate in an expression
*/
diff --git a/src/backend/parser/cypher_parse_node.c b/src/backend/parser/cypher_parse_node.c
index 269dd3c8c..307ad5be0 100644
--- a/src/backend/parser/cypher_parse_node.c
+++ b/src/backend/parser/cypher_parse_node.c
@@ -131,10 +131,23 @@ RangeTblEntry *find_rte(cypher_parsestate *cpstate, char *varname)
*/
char *get_next_default_alias(cypher_parsestate *cpstate)
{
+ ParseState *pstate = (ParseState *)cpstate;
+ cypher_parsestate *parent_cpstate = (cypher_parsestate *)pstate->parentParseState;
char *alias_name;
int nlen = 0;
- /* get the length of the combinded string */
+ /*
+ * Every clause transformed as a subquery has its own cpstate which is being
+ * freed after it is transformed. The root cpstate is the one that has the
+ * default alias number initialized. So we need to reach the root cpstate to
+ * get the next correct default alias number.
+ */
+ if (parent_cpstate)
+ {
+ return get_next_default_alias(parent_cpstate);
+ }
+
+ /* get the length of the combined string */
nlen = snprintf(NULL, 0, "%s%d", AGE_DEFAULT_ALIAS_PREFIX,
cpstate->default_alias_num);
diff --git a/src/backend/parser/cypher_parser.c b/src/backend/parser/cypher_parser.c
index e12c7efdc..c6a95d398 100644
--- a/src/backend/parser/cypher_parser.c
+++ b/src/backend/parser/cypher_parser.c
@@ -143,7 +143,7 @@ List *parse_cypher(const char *s)
return NIL;
/*
- * Append the extra node node regardless of its value. Currently the extra
+ * Append the extra node regardless of its value. Currently the extra
* node is only used by EXPLAIN
*/
return lappend(extra.result, extra.extra);
diff --git a/src/backend/utils/adt/age_global_graph.c b/src/backend/utils/adt/age_global_graph.c
index 3a155e2b3..b2b34455a 100644
--- a/src/backend/utils/adt/age_global_graph.c
+++ b/src/backend/utils/adt/age_global_graph.c
@@ -50,7 +50,7 @@
/* internal data structures implementation */
-/* vertex entry for the vertex_hastable */
+/* vertex entry for the vertex_hashtable */
typedef struct vertex_entry
{
graphid vertex_id; /* vertex id, it is also the hash key */
@@ -119,7 +119,7 @@ static bool insert_vertex_entry(GRAPH_global_context *ggctx, graphid vertex_id,
/*
* Helper function to determine validity of the passed GRAPH_global_context.
- * This is based off of the current active snaphot, to see if the graph could
+ * This is based off of the current active snapshot, to see if the graph could
* have been modified. Ideally, we should find a way to more accurately know
* whether the particular graph was modified.
*/
diff --git a/src/backend/utils/adt/age_vle.c b/src/backend/utils/adt/age_vle.c
index 616f7b644..bb154e3ae 100644
--- a/src/backend/utils/adt/age_vle.c
+++ b/src/backend/utils/adt/age_vle.c
@@ -166,7 +166,7 @@ static VLE_local_context *get_cached_VLE_local_context(int64 vle_grammar_node_id
/*
* Clear (unlink) the previous context's next pointer, if needed.
- * Also clear prev as we are at the end of avaiable cached contexts
+ * Also clear prev as we are at the end of available cached contexts
* and just purging them off. Remember, this forms a loop that will
* exit the while after purging.
*/
@@ -423,7 +423,7 @@ static void free_VLE_local_context(VLE_local_context *vlelctx)
/*
* We need to free the contents of our stacks if the context is not dirty.
* These stacks are created in a more volatile memory context. If the
- * process was interupted, they will be garbage collected by PG. The only
+ * process was interrupted, they will be garbage collected by PG. The only
* time we will ever clean them here is if the cache isn't being used.
*/
if (vlelctx->is_dirty == false)
@@ -502,7 +502,7 @@ static VLE_local_context *build_local_vle_context(FunctionCallInfo fcinfo,
/*
* Get the VLE grammar node id, if it exists. Remember, we overload the
- * age_vle function, for now, for backwards compatability
+ * age_vle function, for now, for backwards compatibility
*/
if (PG_NARGS() == 8)
{
diff --git a/src/backend/utils/adt/agtype.c b/src/backend/utils/adt/agtype.c
index 6e5eaca6d..90254e491 100644
--- a/src/backend/utils/adt/agtype.c
+++ b/src/backend/utils/adt/agtype.c
@@ -159,25 +159,25 @@ static bool is_array_path(agtype_value *agtv);
/* graph entity retrieval */
static Datum get_vertex(const char *graph, const char *vertex_label,
int64 graphid);
-static char *get_label_name(const char *graph_name, int64 label_id);
+static char *get_label_name(const char *graph_name, graphid element_graphid);
static float8 get_float_compatible_arg(Datum arg, Oid type, char *funcname,
bool *is_null);
static Numeric get_numeric_compatible_arg(Datum arg, Oid type, char *funcname,
- bool *is_null,
- enum agtype_value_type *ag_type);
+ bool *is_null,
+ enum agtype_value_type *ag_type);
agtype *get_one_agtype_from_variadic_args(FunctionCallInfo fcinfo,
- int variadic_offset,
- int expected_nargs);
+ int variadic_offset,
+ int expected_nargs);
static int64 get_int64_from_int_datums(Datum d, Oid type, char *funcname,
bool *is_agnull);
static agtype_iterator *get_next_object_key(agtype_iterator *it,
- agtype_container *agtc,
- agtype_value *key);
+ agtype_container *agtc,
+ agtype_value *key);
static agtype_iterator *get_next_list_element(agtype_iterator *it,
- agtype_container *agtc,
- agtype_value *elem);
+ agtype_container *agtc,
+ agtype_value *elem);
static int extract_variadic_args_min(FunctionCallInfo fcinfo,
int variadic_start, bool convert_unknown,
Datum **args, Oid **types, bool **nulls,
@@ -195,8 +195,8 @@ Oid get_AGTYPEOID(void)
if (g_AGTYPEOID == InvalidOid)
{
g_AGTYPEOID = GetSysCacheOid2(TYPENAMENSP, Anum_pg_type_oid,
- CStringGetDatum("agtype"),
- ObjectIdGetDatum(ag_catalog_namespace_id()));
+ CStringGetDatum("agtype"),
+ ObjectIdGetDatum(ag_catalog_namespace_id()));
}
return g_AGTYPEOID;
@@ -2000,7 +2000,7 @@ Datum _agtype_build_path(PG_FUNCTION_ARGS)
/* initialize the result */
memset(&result, 0, sizeof(agtype_in_state));
- /* push in the begining of the agtype array */
+ /* push in the beginning of the agtype array */
result.res = push_agtype_value(&result.parse_state, WAGT_BEGIN_ARRAY, NULL);
/* loop through the path components */
@@ -2188,9 +2188,11 @@ Datum _agtype_build_vertex(PG_FUNCTION_ARGS)
string_to_agtype_value("id"));
if (fcinfo->args[0].isnull)
+ {
ereport(ERROR,
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
errmsg("_agtype_build_vertex() graphid cannot be NULL")));
+ }
id = AG_GETARG_GRAPHID(0);
result.res = push_agtype_value(&result.parse_state, WAGT_VALUE,
@@ -2201,8 +2203,10 @@ Datum _agtype_build_vertex(PG_FUNCTION_ARGS)
string_to_agtype_value("label"));
if (fcinfo->args[1].isnull)
+ {
ereport(ERROR, (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
errmsg("_agtype_build_vertex() label cannot be NULL")));
+ }
result.res =
push_agtype_value(&result.parse_state, WAGT_VALUE,
@@ -2225,11 +2229,11 @@ Datum _agtype_build_vertex(PG_FUNCTION_ARGS)
agtype *properties = AG_GET_ARG_AGTYPE_P(2);
if (!AGT_ROOT_IS_OBJECT(properties))
- ereport(
- ERROR,
- (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
- errmsg(
- "_agtype_build_vertex() properties argument must be an object")));
+ {
+ ereport(ERROR,
+ (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+ errmsg("_agtype_build_vertex() properties argument must be an object")));
+ }
add_agtype((Datum)properties, false, &result, AGTYPEOID, false);
}
@@ -2243,11 +2247,7 @@ Datum _agtype_build_vertex(PG_FUNCTION_ARGS)
Datum make_vertex(Datum id, Datum label, Datum properties)
{
- return DirectFunctionCall3(_agtype_build_vertex,
- id,
- label,
- properties);
-
+ return DirectFunctionCall3(_agtype_build_vertex, id, label, properties);
}
PG_FUNCTION_INFO_V1(_agtype_build_edge);
@@ -2270,9 +2270,11 @@ Datum _agtype_build_edge(PG_FUNCTION_ARGS)
string_to_agtype_value("id"));
if (fcinfo->args[0].isnull)
+ {
ereport(ERROR,
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
errmsg("_agtype_build_edge() graphid cannot be NULL")));
+ }
id = AG_GETARG_GRAPHID(0);
result.res = push_agtype_value(&result.parse_state, WAGT_VALUE,
@@ -2283,8 +2285,10 @@ Datum _agtype_build_edge(PG_FUNCTION_ARGS)
string_to_agtype_value("label"));
if (fcinfo->args[3].isnull)
+ {
ereport(ERROR, (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
errmsg("_agtype_build_vertex() label cannot be NULL")));
+ }
result.res =
push_agtype_value(&result.parse_state, WAGT_VALUE,
@@ -2295,9 +2299,11 @@ Datum _agtype_build_edge(PG_FUNCTION_ARGS)
string_to_agtype_value("end_id"));
if (fcinfo->args[2].isnull)
+ {
ereport(ERROR,
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
errmsg("_agtype_build_edge() endid cannot be NULL")));
+ }
end_id = AG_GETARG_GRAPHID(2);
result.res = push_agtype_value(&result.parse_state, WAGT_VALUE,
@@ -2308,9 +2314,11 @@ Datum _agtype_build_edge(PG_FUNCTION_ARGS)
string_to_agtype_value("start_id"));
if (fcinfo->args[1].isnull)
+ {
ereport(ERROR,
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
errmsg("_agtype_build_edge() startid cannot be NULL")));
+ }
start_id = AG_GETARG_GRAPHID(1);
result.res = push_agtype_value(&result.parse_state, WAGT_VALUE,
@@ -2333,11 +2341,11 @@ Datum _agtype_build_edge(PG_FUNCTION_ARGS)
agtype *properties = AG_GET_ARG_AGTYPE_P(4);
if (!AGT_ROOT_IS_OBJECT(properties))
- ereport(
- ERROR,
- (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
- errmsg(
- "_agtype_build_edge() properties argument must be an object")));
+ {
+ ereport(ERROR,
+ (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+ errmsg("_agtype_build_edge() properties argument must be an object")));
+ }
add_agtype((Datum)properties, false, &result, AGTYPEOID, false);
}
@@ -2585,7 +2593,9 @@ Datum agtype_to_bool(PG_FUNCTION_ARGS)
if (!agtype_extract_scalar(&agtype_in->root, &agtv) ||
agtv.type != AGTV_BOOL)
+ {
cannot_cast_agtype_value(agtv.type, "boolean");
+ }
PG_FREE_IF_COPY(agtype_in, 0);
@@ -2614,7 +2624,8 @@ Datum agtype_to_int8(PG_FUNCTION_ARGS)
(agtv.type != AGTV_FLOAT &&
agtv.type != AGTV_INTEGER &&
agtv.type != AGTV_NUMERIC &&
- agtv.type != AGTV_STRING))
+ agtv.type != AGTV_STRING &&
+ agtv.type != AGTV_BOOL))
cannot_cast_agtype_value(agtv.type, "int");
PG_FREE_IF_COPY(agtype_in, 0);
@@ -2630,6 +2641,9 @@ Datum agtype_to_int8(PG_FUNCTION_ARGS)
else if (agtv.type == AGTV_STRING)
result = DatumGetInt64(DirectFunctionCall1(int8in,
CStringGetDatum(agtv.val.string.val)));
+ else if(agtv.type == AGTV_BOOL)
+ result = DatumGetInt64(DirectFunctionCall1(bool_int4,
+ BoolGetDatum(agtv.val.boolean)));
else
elog(ERROR, "invalid agtype type: %d", (int)agtv.type);
@@ -2653,31 +2667,51 @@ Datum agtype_to_int4(PG_FUNCTION_ARGS)
/* Return null if arg_agt is null. This covers SQL and Agtype NULLS */
if (arg_agt == NULL)
+ {
PG_RETURN_NULL();
+ }
if (!agtype_extract_scalar(&arg_agt->root, &agtv) ||
(agtv.type != AGTV_FLOAT &&
agtv.type != AGTV_INTEGER &&
agtv.type != AGTV_NUMERIC &&
- agtv.type != AGTV_STRING))
+ agtv.type != AGTV_STRING &&
+ agtv.type != AGTV_BOOL))
+ {
cannot_cast_agtype_value(agtv.type, "int");
+ }
PG_FREE_IF_COPY(agtype_in, 0);
if (agtv.type == AGTV_INTEGER)
+ {
result = DatumGetInt32(DirectFunctionCall1(int84,
Int64GetDatum(agtv.val.int_value)));
+ }
else if (agtv.type == AGTV_FLOAT)
+ {
result = DatumGetInt32(DirectFunctionCall1(dtoi4,
Float8GetDatum(agtv.val.float_value)));
+ }
else if (agtv.type == AGTV_NUMERIC)
+ {
result = DatumGetInt32(DirectFunctionCall1(numeric_int4,
NumericGetDatum(agtv.val.numeric)));
+ }
else if (agtv.type == AGTV_STRING)
+ {
result = DatumGetInt32(DirectFunctionCall1(int4in,
CStringGetDatum(agtv.val.string.val)));
+ }
+ else if (agtv.type == AGTV_BOOL)
+ {
+ result = DatumGetInt64(DirectFunctionCall1(bool_int4,
+ BoolGetDatum(agtv.val.boolean)));
+ }
else
+ {
elog(ERROR, "invalid agtype type: %d", (int)agtv.type);
+ }
PG_RETURN_INT32(result);
}
@@ -2742,13 +2776,18 @@ Datum agtype_to_float8(PG_FUNCTION_ARGS)
if (!agtype_extract_scalar(&agtype_in->root, &agtv) ||
(agtv.type != AGTV_FLOAT &&
agtv.type != AGTV_INTEGER &&
- agtv.type != AGTV_NUMERIC))
+ agtv.type != AGTV_NUMERIC &&
+ agtv.type != AGTV_STRING))
+ {
cannot_cast_agtype_value(agtv.type, "float");
+ }
PG_FREE_IF_COPY(agtype_in, 0);
if (agtv.type == AGTV_FLOAT)
+ {
result = agtv.val.float_value;
+ }
else if (agtv.type == AGTV_INTEGER)
{
/*
@@ -2769,10 +2808,19 @@ Datum agtype_to_float8(PG_FUNCTION_ARGS)
errmsg("cannot cast to float8, integer value out of range")));
}
else if (agtv.type == AGTV_NUMERIC)
+ {
result = DatumGetFloat8(DirectFunctionCall1(numeric_float8,
NumericGetDatum(agtv.val.numeric)));
+ }
+ else if (agtv.type == AGTV_STRING)
+ {
+ result = DatumGetFloat8(DirectFunctionCall1(float8in,
+ CStringGetDatum(agtv.val.string.val)));
+ }
else
+ {
elog(ERROR, "invalid agtype type: %d", (int)agtv.type);
+ }
PG_RETURN_FLOAT8(result);
}
@@ -3895,7 +3943,7 @@ Datum agtype_hash_cmp(PG_FUNCTION_ARGS)
PG_RETURN_INT16(hash);
}
-// Comparision function for btree Indexes
+// Comparison function for btree Indexes
PG_FUNCTION_INFO_V1(agtype_btree_cmp);
Datum agtype_btree_cmp(PG_FUNCTION_ARGS)
@@ -4008,20 +4056,26 @@ Datum agtype_typecast_int(PG_FUNCTION_ARGS)
/* Return null if arg_agt is null. This covers SQL and Agtype NULLS */
if (arg_agt == NULL)
+ {
PG_RETURN_NULL();
+ }
/* check that we have a scalar value */
if (!AGT_ROOT_IS_SCALAR(arg_agt))
+ {
ereport(ERROR,
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
errmsg("typecast argument must be a scalar value")));
+ }
/* get the arg parameter */
arg_value = get_ith_agtype_value_from_container(&arg_agt->root, 0);
/* check for agtype null */
if (arg_value->type == AGTV_NULL)
+ {
PG_RETURN_NULL();
+ }
/* the input type drives the casting */
switch(arg_value->type)
@@ -4037,6 +4091,10 @@ Datum agtype_typecast_int(PG_FUNCTION_ARGS)
d = DirectFunctionCall1(numeric_int8,
NumericGetDatum(arg_value->val.numeric));
break;
+ case AGTV_BOOL:
+ d = DirectFunctionCall1(bool_int4,
+ BoolGetDatum(arg_value->val.boolean));
+ break;
case AGTV_STRING:
/* we need a null terminated string */
string = (char *) palloc0(sizeof(char)*arg_value->val.string.len + 1);
@@ -4064,6 +4122,68 @@ Datum agtype_typecast_int(PG_FUNCTION_ARGS)
PG_RETURN_POINTER(agtype_value_to_agtype(&result_value));
}
+PG_FUNCTION_INFO_V1(agtype_typecast_bool);
+/*
+ * Execute function to typecast an agtype to an agtype bool
+ */
+Datum agtype_typecast_bool(PG_FUNCTION_ARGS)
+{
+ agtype *arg_agt;
+ agtype_value *arg_value;
+ agtype_value result_value;
+ Datum d;
+
+ /* get the agtype equivalence of any convertable input type */
+ arg_agt = get_one_agtype_from_variadic_args(fcinfo, 0, 1);
+
+ /* Return null if arg_agt is null. This covers SQL and Agtype NULLS */
+ if (arg_agt == NULL)
+ {
+ PG_RETURN_NULL();
+ }
+
+ /* check that we have a scalar value */
+ if (!AGT_ROOT_IS_SCALAR(arg_agt))
+ {
+ ereport(ERROR,
+ (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+ errmsg("typecast argument must be a scalar value")));
+ }
+
+ /* get the arg parameter */
+ arg_value = get_ith_agtype_value_from_container(&arg_agt->root, 0);
+
+ /* check for agtype null */
+ if (arg_value->type == AGTV_NULL)
+ {
+ PG_RETURN_NULL();
+ }
+
+ /* the input type drives the casting */
+ switch(arg_value->type)
+ {
+ case AGTV_BOOL:
+ PG_RETURN_POINTER(agtype_value_to_agtype(arg_value));
+ break;
+ case AGTV_INTEGER:
+ d = DirectFunctionCall1(int4_bool,
+ Int64GetDatum(arg_value->val.int_value));
+ break;
+ /* what was given doesn't cast to a bool */
+ default:
+ ereport(ERROR,
+ (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+ errmsg("typecast expression must be an integer or a boolean")));
+ break;
+ }
+
+ /* set the result type and return our result */
+ result_value.type = AGTV_BOOL;
+ result_value.val.boolean = DatumGetBool(d);
+
+ PG_RETURN_POINTER(agtype_value_to_agtype(&result_value));
+}
+
PG_FUNCTION_INFO_V1(agtype_typecast_float);
/*
* Execute function to typecast an agtype to an agtype float
@@ -4546,11 +4666,11 @@ Datum column_get_datum(TupleDesc tupdesc, HeapTuple tuple, int column,
}
/*
- * Function to retrieve a label name, given the graph name and graphid. The
- * function returns a pointer to a duplicated string that needs to be freed
- * when you are finished using it.
+ * Function to retrieve a label name, given the graph name and graphid of the
+ * node or edge. The function returns a pointer to a duplicated string that
+ * needs to be freed when you are finished using it.
*/
-static char *get_label_name(const char *graph_name, int64 label_id)
+static char *get_label_name(const char *graph_name, graphid element_graphid)
{
ScanKeyData scan_keys[2];
Relation ag_label;
@@ -4558,16 +4678,16 @@ static char *get_label_name(const char *graph_name, int64 label_id)
HeapTuple tuple;
TupleDesc tupdesc;
char *result = NULL;
- bool column_is_null;
-
- Oid graph_id = get_graph_oid(graph_name);
+ bool column_is_null = false;
+ Oid graph_oid = get_graph_oid(graph_name);
+ int32 label_id = get_graphid_label_id(element_graphid);
/* scankey for first match in ag_label, column 2, graphoid, BTEQ, OidEQ */
ScanKeyInit(&scan_keys[0], Anum_ag_label_graph, BTEqualStrategyNumber,
- F_OIDEQ, ObjectIdGetDatum(graph_id));
+ F_OIDEQ, ObjectIdGetDatum(graph_oid));
/* scankey for second match in ag_label, column 3, label id, BTEQ, Int4EQ */
ScanKeyInit(&scan_keys[1], Anum_ag_label_id, BTEqualStrategyNumber,
- F_INT42EQ, Int32GetDatum(get_graphid_label_id(label_id)));
+ F_INT4EQ, Int32GetDatum(label_id));
ag_label = table_open(ag_label_relation_id(), ShareLock);
scan_desc = systable_beginscan(ag_label, ag_label_graph_oid_index_id(), true,
@@ -4578,7 +4698,7 @@ static char *get_label_name(const char *graph_name, int64 label_id)
{
ereport(ERROR,
(errcode(ERRCODE_UNDEFINED_SCHEMA),
- errmsg("graphid abc %lu does not exist", label_id)));
+ errmsg("graphid %lu does not exist", element_graphid)));
}
/* get the tupdesc - we don't need to release this one */
@@ -4586,13 +4706,15 @@ static char *get_label_name(const char *graph_name, int64 label_id)
/* bail if the number of columns differs */
if (tupdesc->natts != Natts_ag_label)
+ {
ereport(ERROR,
(errcode(ERRCODE_UNDEFINED_TABLE),
errmsg("Invalid number of attributes for ag_catalog.ag_label")));
+ }
/* get the label name */
- result = NameStr(*DatumGetName(
- heap_getattr(tuple, Anum_ag_label_name, tupdesc, &column_is_null)));
+ result = NameStr(*DatumGetName(heap_getattr(tuple, Anum_ag_label_name,
+ tupdesc, &column_is_null)));
/* duplicate it */
result = strdup(result);
@@ -4671,7 +4793,7 @@ Datum age_startnode(PG_FUNCTION_ARGS)
agtype_value *agtv_value = NULL;
char *graph_name = NULL;
char *label_name = NULL;
- graphid graph_oid;
+ graphid start_id;
Datum result;
/* we need the graph name */
@@ -4713,14 +4835,14 @@ Datum age_startnode(PG_FUNCTION_ARGS)
/* it must not be null and must be an integer */
Assert(agtv_value != NULL);
Assert(agtv_value->type = AGTV_INTEGER);
- graph_oid = agtv_value->val.int_value;
+ start_id = agtv_value->val.int_value;
/* get the label */
- label_name = get_label_name(graph_name, graph_oid);
+ label_name = get_label_name(graph_name, start_id);
/* it must not be null and must be a string */
Assert(label_name != NULL);
- result = get_vertex(graph_name, label_name, graph_oid);
+ result = get_vertex(graph_name, label_name, start_id);
free(label_name);
@@ -4736,7 +4858,7 @@ Datum age_endnode(PG_FUNCTION_ARGS)
agtype_value *agtv_value = NULL;
char *graph_name = NULL;
char *label_name = NULL;
- graphid graph_oid;
+ graphid end_id;
Datum result;
/* we need the graph name */
@@ -4778,14 +4900,14 @@ Datum age_endnode(PG_FUNCTION_ARGS)
/* it must not be null and must be an integer */
Assert(agtv_value != NULL);
Assert(agtv_value->type = AGTV_INTEGER);
- graph_oid = agtv_value->val.int_value;
+ end_id = agtv_value->val.int_value;
/* get the label */
- label_name = get_label_name(graph_name, graph_oid);
+ label_name = get_label_name(graph_name, end_id);
/* it must not be null and must be a string */
Assert(label_name != NULL);
- result = get_vertex(graph_name, label_name, graph_oid);
+ result = get_vertex(graph_name, label_name, end_id);
free(label_name);
@@ -8204,6 +8326,38 @@ Datum age_e(PG_FUNCTION_ARGS)
PG_RETURN_POINTER(agtype_value_to_agtype(&agtv_result));
}
+PG_FUNCTION_INFO_V1(age_pi);
+
+Datum age_pi(PG_FUNCTION_ARGS)
+{
+ agtype_value agtv_result;
+ float8 float_result;
+
+ float_result = DatumGetFloat8(DirectFunctionCall1(dpi, 0));
+
+ /* build the result */
+ agtv_result.type = AGTV_FLOAT;
+ agtv_result.val.float_value = float_result;
+
+ PG_RETURN_POINTER(agtype_value_to_agtype(&agtv_result));
+}
+
+PG_FUNCTION_INFO_V1(age_rand);
+
+Datum age_rand(PG_FUNCTION_ARGS)
+{
+ agtype_value agtv_result;
+ float8 float_result;
+
+ float_result = DatumGetFloat8(DirectFunctionCall1(drandom, 0));
+
+ /* build the result */
+ agtv_result.type = AGTV_FLOAT;
+ agtv_result.val.float_value = float_result;
+
+ PG_RETURN_POINTER(agtype_value_to_agtype(&agtv_result));
+}
+
PG_FUNCTION_INFO_V1(age_exp);
Datum age_exp(PG_FUNCTION_ARGS)
@@ -8453,10 +8607,10 @@ agtype_value *alter_property_value(agtype_value *properties, char *var_name,
tok = agtype_iterator_next(&it, r, true);
/*
- * If the the new agtype is scalar, push the agtype_value to the
+ * If the new agtype is scalar, push the agtype_value to the
* parse state. If the agtype is an object or array convert the
* agtype to a binary agtype_value to pass to the parse_state.
- * This will save uncessary deserialization and serialization
+ * This will save unnecessary deserialization and serialization
* logic from running.
*/
if (AGTYPE_CONTAINER_IS_SCALAR(&new_v->root))
@@ -8491,10 +8645,10 @@ agtype_value *alter_property_value(agtype_value *properties, char *var_name,
&parse_state, WAGT_KEY, key);
/*
- * If the the new agtype is scalar, push the agtype_value to the
+ * If the new agtype is scalar, push the agtype_value to the
* parse state. If the agtype is an object or array convert the
* agtype to a binary agtype_value to pass to the parse_state.
- * This will save uncessary deserialization and serialization
+ * This will save unnecessary deserialization and serialization
* logic from running.
*/
if (AGTYPE_CONTAINER_IS_SCALAR(&new_v->root))
@@ -10116,3 +10270,109 @@ Datum age_unnest(PG_FUNCTION_ARGS)
PG_RETURN_NULL();
}
+
+/*
+ * Volatile wrapper replacement. The previous version was PL/SQL
+ * and could only handle AGTYPE input and returned AGTYPE output.
+ * This version will create the appropriate AGTYPE based off of
+ * the input type.
+ */
+PG_FUNCTION_INFO_V1(agtype_volatile_wrapper);
+
+Datum agtype_volatile_wrapper(PG_FUNCTION_ARGS)
+{
+ int nargs = PG_NARGS();
+ Oid type = InvalidOid;
+ bool isnull = PG_ARGISNULL(0);
+
+ /* check for null and pass it through */
+ if (isnull)
+ {
+ PG_RETURN_NULL();
+ }
+
+ /* check for more than one argument */
+ if (nargs > 1)
+ {
+ ereport(ERROR,
+ (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+ errmsg("agtype_volatile_wrapper: too many args")));
+
+ }
+
+ /* get the type of the input argument */
+ type = get_fn_expr_argtype(fcinfo->flinfo, 0);
+
+ /* if it is NOT an AGTYPE, we need convert it to one, if possible */
+ if (type != AGTYPEOID)
+ {
+ agtype_value agtv_result;
+ Datum arg = PG_GETARG_DATUM(0);
+
+ /* check for PG types that easily translate to AGTYPE */
+ if (type == BOOLOID)
+ {
+ agtv_result.type = AGTV_BOOL;
+ agtv_result.val.boolean = DatumGetBool(arg);
+ }
+ else if (type == INT2OID || type == INT4OID || type == INT8OID)
+ {
+ agtv_result.type = AGTV_INTEGER;
+
+ if (type == INT8OID)
+ {
+ agtv_result.val.int_value = DatumGetInt64(arg);
+ }
+ else if (type == INT4OID)
+ {
+ agtv_result.val.int_value = (int64) DatumGetInt32(arg);
+ }
+ else if (type == INT4OID)
+ {
+ agtv_result.val.int_value = (int64) DatumGetInt16(arg);
+ }
+ }
+ else if (type == FLOAT4OID || type == FLOAT8OID)
+ {
+ agtv_result.type = AGTV_FLOAT;
+
+ if (type == FLOAT8OID)
+ {
+ agtv_result.val.float_value = DatumGetFloat8(arg);
+ }
+ else if (type == FLOAT4OID)
+ {
+ agtv_result.val.float_value = (float8) DatumGetFloat4(arg);
+ }
+ }
+ else if (type == NUMERICOID)
+ {
+ agtv_result.type = AGTV_NUMERIC;
+ agtv_result.val.numeric = DatumGetNumeric(arg);
+ }
+ else if (type == CSTRINGOID)
+ {
+ agtv_result.type = AGTV_STRING;
+ agtv_result.val.string.val = DatumGetCString(arg);
+ agtv_result.val.string.len = strlen(agtv_result.val.string.val);
+ }
+ else if (type == TEXTOID)
+ {
+ agtv_result.type = AGTV_STRING;
+ agtv_result.val.string.val = text_to_cstring(DatumGetTextPP(arg));
+ agtv_result.val.string.len = strlen(agtv_result.val.string.val);
+ }
+ else
+ {
+ ereport(ERROR,
+ (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+ errmsg("agtype_volatile_wrapper: unsupported arg type")));
+ }
+
+ /* return the built result */
+ PG_RETURN_POINTER(agtype_value_to_agtype(&agtv_result));
+ }
+
+ /* otherwise, just pass it through */
+ PG_RETURN_POINTER(PG_GETARG_DATUM(0));
+}
diff --git a/src/backend/utils/adt/agtype_util.c b/src/backend/utils/adt/agtype_util.c
index 222ba13e7..e7ba21421 100644
--- a/src/backend/utils/adt/agtype_util.c
+++ b/src/backend/utils/adt/agtype_util.c
@@ -192,25 +192,47 @@ uint32 get_agtype_length(const agtype_container *agtc, int index)
}
/*
- * Helper function to generate the sort priorty of a type. Larger
+ * Helper function to generate the sort priority of a type. Larger
* numbers have higher priority.
*/
static int get_type_sort_priority(enum agtype_value_type type)
{
- if (type == AGTV_OBJECT)
+ if (type == AGTV_PATH)
+ {
return 0;
- if (type == AGTV_VERTEX)
+ }
+ if (type == AGTV_EDGE)
+ {
return 1;
- if (type == AGTV_ARRAY)
+ }
+ if (type == AGTV_VERTEX)
+ {
return 2;
- if (type == AGTV_STRING)
+ }
+ if (type == AGTV_OBJECT)
+ {
return 3;
- if (type == AGTV_BOOL)
+ }
+ if (type == AGTV_ARRAY)
+ {
return 4;
- if (type == AGTV_NUMERIC || type == AGTV_INTEGER || type == AGTV_FLOAT)
+ }
+ if (type == AGTV_STRING)
+ {
return 5;
- if (type == AGTV_NULL)
+ }
+ if (type == AGTV_BOOL)
+ {
return 6;
+ }
+ if (type == AGTV_NUMERIC || type == AGTV_INTEGER || type == AGTV_FLOAT)
+ {
+ return 7;
+ }
+ if (type == AGTV_NULL)
+ {
+ return 8;
+ }
return -1;
}
@@ -356,6 +378,18 @@ int compare_agtype_containers_orderability(agtype_container *a,
break;
}
+ /* Correction step because AGTV_ARRAY might be there just because of the container type */
+ /* Case 1: left side is assigned to an array, right is an object */
+ if(va.type == AGTV_ARRAY && vb.type == AGTV_OBJECT)
+ {
+ ra = agtype_iterator_next(&ita, &va, false);
+ }
+ /* Case 2: left side is an object, right side is assigned to an array */
+ else if(va.type == AGTV_OBJECT && vb.type == AGTV_ARRAY)
+ {
+ rb = agtype_iterator_next(&itb, &vb, false);
+ }
+
Assert(va.type != vb.type);
Assert(va.type != AGTV_BINARY);
Assert(vb.type != AGTV_BINARY);
diff --git a/src/backend/utils/adt/graphid.c b/src/backend/utils/adt/graphid.c
index 89bd8e528..2060fc66e 100644
--- a/src/backend/utils/adt/graphid.c
+++ b/src/backend/utils/adt/graphid.c
@@ -36,9 +36,9 @@ Oid get_GRAPHIDOID(void)
{
if (g_GRAPHIDOID == InvalidOid)
{
- g_GRAPHIDOID = GetSysCacheOid2(TYPENAMENSP, Anum_pg_type_oid,
- CStringGetDatum("graphid"),
- ObjectIdGetDatum(ag_catalog_namespace_id()));
+ g_GRAPHIDOID = GetSysCacheOid2(TYPENAMENSP, Anum_pg_type_oid,
+ CStringGetDatum("graphid"),
+ ObjectIdGetDatum(ag_catalog_namespace_id()));
}
return g_GRAPHIDOID;
diff --git a/src/backend/utils/cache/ag_cache.c b/src/backend/utils/cache/ag_cache.c
index 3c16e5722..69fe41752 100644
--- a/src/backend/utils/cache/ag_cache.c
+++ b/src/backend/utils/cache/ag_cache.c
@@ -512,6 +512,18 @@ static void initialize_label_caches(void)
// ag_label.relation
ag_cache_scan_key_init(&label_relation_scan_keys[0],
Anum_ag_label_relation, F_OIDEQ);
+
+ // ag_label.seq_name, ag_label.graph
+ ag_cache_scan_key_init(&label_seq_name_graph_scan_keys[0], Anum_ag_label_seq_name,
+ F_NAMEEQ);
+ ag_cache_scan_key_init(&label_seq_name_graph_scan_keys[1], Anum_ag_label_graph,
+ F_OIDEQ);
+
+ // ag_label.seq_name, ag_label.graph
+ ag_cache_scan_key_init(&label_seq_name_graph_scan_keys[0],
+ Anum_ag_label_seq_name, F_NAMEEQ);
+ ag_cache_scan_key_init(&label_seq_name_graph_scan_keys[1],
+ Anum_ag_label_graph, F_OIDEQ);
// ag_label.seq_name, ag_label.graph
ag_cache_scan_key_init(&label_seq_name_graph_scan_keys[0],
@@ -538,7 +550,6 @@ static void create_label_caches(void)
create_label_graph_oid_cache();
create_label_relation_cache();
create_label_seq_name_graph_cache();
-
}
static void create_label_name_graph_cache(void)
diff --git a/src/backend/utils/graph_generation.c b/src/backend/utils/graph_generation.c
index ca12e9c57..7033c9bc9 100644
--- a/src/backend/utils/graph_generation.c
+++ b/src/backend/utils/graph_generation.c
@@ -49,23 +49,23 @@
#include "utils/load/ag_load_labels.h"
-int64 get_nextval_internal(graph_cache_data* graph_cache,
+int64 get_nextval_internal(graph_cache_data* graph_cache,
label_cache_data* label_cache);
/*
* Auxiliary function to get the next internal value in the graph,
* so a new object (node or edge) graph id can be composed.
*/
-int64 get_nextval_internal(graph_cache_data* graph_cache,
- label_cache_data* label_cache)
+int64 get_nextval_internal(graph_cache_data* graph_cache,
+ label_cache_data* label_cache)
{
Oid obj_seq_id;
char* label_seq_name_str;
label_seq_name_str = NameStr(label_cache->seq_name);
- obj_seq_id = get_relname_relid(label_seq_name_str,
+ obj_seq_id = get_relname_relid(label_seq_name_str,
graph_cache->namespace);
-
+
return nextval_internal(obj_seq_id, true);
}
@@ -78,7 +78,7 @@ PG_FUNCTION_INFO_V1(create_complete_graph);
Datum create_complete_graph(PG_FUNCTION_ARGS)
{
- Oid graph_id;
+ Oid graph_oid;
Name graph_name;
int64 no_vertices;
@@ -159,12 +159,12 @@ Datum create_complete_graph(PG_FUNCTION_ARGS)
DirectFunctionCall1(create_graph, CStringGetDatum(graph_name));
}
- graph_id = get_graph_oid(graph_name_str);
+ graph_oid = get_graph_oid(graph_name_str);
if (!PG_ARGISNULL(3))
{
// Check if label with the input name already exists
- if (!label_exists(vtx_name_str, graph_id))
+ if (!label_exists(vtx_name_str, graph_oid))
{
DirectFunctionCall2(create_vlabel,
CStringGetDatum(graph_name),
@@ -172,19 +172,19 @@ Datum create_complete_graph(PG_FUNCTION_ARGS)
}
}
- if (!label_exists(edge_name_str, graph_id))
+ if (!label_exists(edge_name_str, graph_oid))
{
DirectFunctionCall2(create_elabel,
CStringGetDatum(graph_name),
CStringGetDatum(edge_label_name));
}
- vtx_label_id = get_label_id(vtx_name_str, graph_id);
- edge_label_id = get_label_id(edge_name_str, graph_id);
+ vtx_label_id = get_label_id(vtx_name_str, graph_oid);
+ edge_label_id = get_label_id(edge_name_str, graph_oid);
graph_cache = search_graph_name_cache(graph_name_str);
- vertex_cache = search_label_name_graph_cache(vtx_name_str,graph_id);
- edge_cache = search_label_name_graph_cache(edge_name_str,graph_id);
+ vertex_cache = search_label_name_graph_cache(vtx_name_str, graph_oid);
+ edge_cache = search_label_name_graph_cache(edge_name_str, graph_oid);
nsp_id = graph_cache->namespace;
vtx_seq_name = &(vertex_cache->seq_name);
@@ -196,22 +196,23 @@ Datum create_complete_graph(PG_FUNCTION_ARGS)
vtx_seq_id = get_relname_relid(vtx_seq_name_str, nsp_id);
edge_seq_id = get_relname_relid(edge_seq_name_str, nsp_id);
+ props = create_empty_agtype();
+
/* Creating vertices*/
- for (i=(int64)1;i<=no_vertices;i++)
+ for (i=(int64)1; i<=no_vertices; i++)
{
vid = nextval_internal(vtx_seq_id, true);
object_graph_id = make_graphid(vtx_label_id, vid);
- props = create_empty_agtype();
- insert_vertex_simple(graph_id,vtx_name_str,object_graph_id,props);
+ insert_vertex_simple(graph_oid, vtx_name_str, object_graph_id, props);
}
lid = vid;
/* Creating edges*/
- for (i = 1;i<=no_vertices-1;i++)
+ for (i = 1; i<=no_vertices-1; i++)
{
start_vid = lid-no_vertices+i;
- for(j=i+1;j<=no_vertices;j++)
+ for(j=i+1; j<=no_vertices; j++)
{
end_vid = lid-no_vertices+j;
eid = nextval_internal(edge_seq_id, true);
@@ -220,11 +221,9 @@ Datum create_complete_graph(PG_FUNCTION_ARGS)
start_vertex_graph_id = make_graphid(vtx_label_id, start_vid);
end_vertex_graph_id = make_graphid(vtx_label_id, end_vid);
- props = create_empty_agtype();
-
- insert_edge_simple(graph_id, edge_name_str,
- object_graph_id, start_vertex_graph_id,
- end_vertex_graph_id, props);
+ insert_edge_simple(graph_oid, edge_name_str, object_graph_id,
+ start_vertex_graph_id, end_vertex_graph_id,
+ props);
}
}
PG_RETURN_VOID();
@@ -241,7 +240,7 @@ PG_FUNCTION_INFO_V1(age_create_barbell_graph);
* n int,
* vertex_label_name Name DEFAULT = NULL,
* vertex_properties agtype DEFAULT = NULL,
- * edge_label_name Name DEAULT = NULL,
+ * edge_label_name Name DEFAULT = NULL,
* edge_properties agtype DEFAULT = NULL)
* Input:
*
diff --git a/src/backend/utils/load/ag_load_edges.c b/src/backend/utils/load/ag_load_edges.c
index a7a88607b..60f15f03c 100644
--- a/src/backend/utils/load/ag_load_edges.c
+++ b/src/backend/utils/load/ag_load_edges.c
@@ -61,14 +61,14 @@ void edge_row_cb(int delim __attribute__((unused)), void *data)
size_t i, n_fields;
int64 start_id_int;
- graphid start_vertex_graph_oid;
+ graphid start_vertex_graph_id;
int start_vertex_type_id;
int64 end_id_int;
- graphid end_vertex_graph_oid;
+ graphid end_vertex_graph_id;
int end_vertex_type_id;
- graphid object_graph_oid;
+ graphid object_graph_id;
agtype* props = NULL;
@@ -81,7 +81,7 @@ void edge_row_cb(int delim __attribute__((unused)), void *data)
cr->header_len = (size_t* )malloc(sizeof(size_t *) * cr->cur_field);
cr->header = malloc((sizeof (char*) * cr->cur_field));
- for ( i = 0; icur_field; i++)
+ for (i = 0; icur_field; i++)
{
cr->header_len[i] = cr->fields_len[i];
cr->header[i] = strndup(cr->fields[i], cr->header_len[i]);
@@ -89,23 +89,22 @@ void edge_row_cb(int delim __attribute__((unused)), void *data)
}
else
{
- object_graph_oid = make_graphid(cr->object_id, (int64)cr->row);
+ object_graph_id = make_graphid(cr->object_id, (int64)cr->row);
start_id_int = strtol(cr->fields[0], NULL, 10);
start_vertex_type_id = get_label_id(cr->fields[1], cr->graph_oid);
end_id_int = strtol(cr->fields[2], NULL, 10);
end_vertex_type_id = get_label_id(cr->fields[3], cr->graph_oid);
- start_vertex_graph_oid = make_graphid(start_vertex_type_id, start_id_int);
- end_vertex_graph_oid = make_graphid(end_vertex_type_id, end_id_int);
+ start_vertex_graph_id = make_graphid(start_vertex_type_id, start_id_int);
+ end_vertex_graph_id = make_graphid(end_vertex_type_id, end_id_int);
props = create_agtype_from_list_i(cr->header, cr->fields,
n_fields, 3);
insert_edge_simple(cr->graph_oid, cr->object_name,
- object_graph_oid, start_vertex_graph_oid,
- end_vertex_graph_oid, props);
-
+ object_graph_id, start_vertex_graph_id,
+ end_vertex_graph_id, props);
}
for (i = 0; i < n_fields; ++i)
diff --git a/src/backend/utils/load/ag_load_labels.c b/src/backend/utils/load/ag_load_labels.c
index af7d20446..27e502815 100644
--- a/src/backend/utils/load/ag_load_labels.c
+++ b/src/backend/utils/load/ag_load_labels.c
@@ -101,7 +101,7 @@ void vertex_row_cb(int delim __attribute__((unused)), void *data)
csv_vertex_reader *cr = (csv_vertex_reader*)data;
agtype *props = NULL;
size_t i, n_fields;
- graphid object_graph_oid;
+ graphid object_graph_id;
int64 label_id_int;
n_fields = cr->cur_field;
@@ -131,12 +131,12 @@ void vertex_row_cb(int delim __attribute__((unused)), void *data)
label_id_int = (int64)cr->row;
}
- object_graph_oid = make_graphid(cr->object_id, label_id_int);
+ object_graph_id = make_graphid(cr->object_id, label_id_int);
props = create_agtype_from_list(cr->header, cr->fields,
n_fields, label_id_int);
insert_vertex_simple(cr->graph_oid, cr->object_name,
- object_graph_oid, props);
+ object_graph_id, props);
}
diff --git a/src/include/nodes/cypher_nodes.h b/src/include/nodes/cypher_nodes.h
index c9f77d9eb..64318eb5d 100644
--- a/src/include/nodes/cypher_nodes.h
+++ b/src/include/nodes/cypher_nodes.h
@@ -142,6 +142,7 @@ typedef struct cypher_node
ExtensibleNode extensible;
char *name;
char *label;
+ char *parsed_label;
Node *props; // map or parameter
int location;
} cypher_node;
@@ -159,6 +160,7 @@ typedef struct cypher_relationship
ExtensibleNode extensible;
char *name;
char *label;
+ char *parsed_label;
Node *props; // map or parameter
Node *varlen; // variable length relationships (A_Indices)
cypher_rel_dir dir;
@@ -360,7 +362,7 @@ typedef struct cypher_update_information
{
ExtensibleNode extensible;
List *set_items;
- int flags;
+ uint32 flags;
AttrNumber tuple_position;
char *graph_name;
char *clause_name;
@@ -382,7 +384,7 @@ typedef struct cypher_delete_information
{
ExtensibleNode extensible;
List *delete_items;
- int flags;
+ uint32 flags;
char *graph_name;
uint32 graph_oid;
bool detach;
@@ -398,7 +400,7 @@ typedef struct cypher_delete_item
typedef struct cypher_merge_information
{
ExtensibleNode extensible;
- int flags;
+ uint32 flags;
uint32 graph_oid;
AttrNumber merge_function_attr;
cypher_create_path *path;
diff --git a/src/include/nodes/cypher_readfuncs.h b/src/include/nodes/cypher_readfuncs.h
index e3bb97a33..9ae4e2d9f 100644
--- a/src/include/nodes/cypher_readfuncs.h
+++ b/src/include/nodes/cypher_readfuncs.h
@@ -32,7 +32,7 @@
*
* All functions are dependent on the pg_strtok function. We do not
- * setup pg_strtok. That is for the the caller to do. By default that
+ * setup pg_strtok. That is for the caller to do. By default that
* is the responsibility of Postgres' nodeRead function. We assume
* that was setup correctly.
*/
diff --git a/src/include/parser/cypher_parse_node.h b/src/include/parser/cypher_parse_node.h
index 268eb1abb..5a9720d7e 100644
--- a/src/include/parser/cypher_parse_node.h
+++ b/src/include/parser/cypher_parse_node.h
@@ -25,8 +25,14 @@
#include "nodes/cypher_nodes.h"
-#define AGE_DEFAULT_ALIAS_PREFIX "_age_default_alias_"
-#define AGE_DEFAULT_VARNAME_PREFIX "_age_varname_"
+/*
+ * Every internal alias or variable name should be prefixed
+ * with AGE_DEFAULT_PREFIX. Grammer restricts variables
+ * prefixed with _age_default_ in user query to be used.
+ */
+#define AGE_DEFAULT_PREFIX "_age_default_"
+#define AGE_DEFAULT_ALIAS_PREFIX AGE_DEFAULT_PREFIX"alias_"
+#define AGE_DEFAULT_VARNAME_PREFIX AGE_DEFAULT_PREFIX"varname_"
typedef struct cypher_parsestate
{
diff --git a/src/include/utils/age_global_graph.h b/src/include/utils/age_global_graph.h
index daa1e5139..17e15ffb7 100644
--- a/src/include/utils/age_global_graph.h
+++ b/src/include/utils/age_global_graph.h
@@ -29,7 +29,7 @@
* age_global_graph.c
*/
-/* vertex entry for the vertex_hastable */
+/* vertex entry for the vertex_hashtable */
typedef struct vertex_entry vertex_entry;
/* edge entry for the edge_hashtable */