This is an automated email from the ASF dual-hosted git repository.

jgemignani pushed a commit to branch alpha/PG13
in repository https://gitbox.apache.org/repos/asf/age.git

commit 126d3b0c0b9277ab624dcdc5f81187b893fa5844
Merge: eb1bf6fb 15ea6787
Author: John Gemignani <[email protected]>
AuthorDate: Mon Mar 27 16:59:25 2023 -0700

    Merge branch 'PG12' into PG13
    
    Conflicts:
            .github/workflows/go-driver.yml
            .github/workflows/installcheck.yml
            .github/workflows/python-driver.yaml
            .gitignore
            README.md
            age--1.1.1.sql
            drivers/python/requirements.txt
            drivers/python/setup.py
            regress/expected/catalog.out
            regress/expected/graph_generation.out
            regress/sql/cypher_match.sql
            regress/sql/graph_generation.sql
            src/backend/parser/cypher_clause.c
            src/backend/utils/adt/agtype.c
            src/backend/utils/graph_generation.c

 .github/workflows/go-driver.yml          |   6 +-
 .github/workflows/installcheck.yml       |   2 +-
 .github/workflows/python-driver.yaml     |   6 +-
 .gitignore                               |   2 +
 README.md                                | 270 ++++++++++++--
 age--1.1.1.sql                           |  21 ++
 drivers/README                           |   5 +
 drivers/nodejs/README.md                 |   4 +
 drivers/nodejs/test/Agtype.test.ts       |   8 +-
 drivers/python/age/gen/AgtypeLexer.py    | 153 ++++----
 drivers/python/age/gen/AgtypeListener.py |   3 +-
 drivers/python/age/gen/AgtypeParser.py   | 100 +++--
 drivers/python/age/gen/AgtypeVisitor.py  |   3 +-
 drivers/python/antlr/README.md           |  14 +-
 drivers/python/setup.py                  |   2 +-
 regress/expected/analyze.out             |   5 +
 regress/expected/catalog.out             |   2 +-
 regress/expected/cypher_match.out        | 616 ++++++++++++++++++++++++++++++-
 regress/expected/cypher_merge.out        |  40 +-
 regress/expected/cypher_with.out         | 317 +++++++++++++++-
 regress/expected/graph_generation.out    | 119 ++++++
 regress/sql/analyze.sql                  |   4 +
 regress/sql/cypher_match.sql             | 240 +++++++++++-
 regress/sql/cypher_with.sql              | 204 +++++++++-
 regress/sql/graph_generation.sql         |  35 ++
 src/backend/catalog/ag_label.c           |  15 +
 src/backend/parser/cypher_analyze.c      |   9 +
 src/backend/parser/cypher_clause.c       | 335 ++++++++++++-----
 src/backend/parser/cypher_expr.c         |  16 +-
 src/backend/utils/adt/agtype.c           |  81 ++++
 src/backend/utils/graph_generation.c     | 179 ++++++++-
 src/backend/utils/load/age_load.c        |  15 +-
 src/include/catalog/ag_label.h           |   2 +
 33 files changed, 2509 insertions(+), 324 deletions(-)

diff --cc .github/workflows/go-driver.yml
index bf6b621e,dfd1c055..beefb992
--- a/.github/workflows/go-driver.yml
+++ b/.github/workflows/go-driver.yml
@@@ -2,10 -2,10 +2,10 @@@ name: Go Driver Test
  
  on:
    push:
 -    branches: [ "PG12" ]
 -    
 +    branches: [ "PG13" ]
-     
++
    pull_request:
 -    branches: [ "PG12" ]
 +    branches: [ "PG13" ]
  
  jobs:
    build:
@@@ -16,10 -16,10 +16,10 @@@
      defaults:
        run:
          working-directory: drivers/golang/age/
--  
++
      steps:
      - uses: actions/checkout@v3
--    
++
      - name: Run apache/age docker image
        run: docker-compose up -d
  
diff --cc .github/workflows/installcheck.yml
index 05c59872,8372203e..6bc6a727
--- a/.github/workflows/installcheck.yml
+++ b/.github/workflows/installcheck.yml
@@@ -40,9 -40,9 +40,9 @@@ jobs
        - name: Regression tests
          id: regression_tests
          run: |
 -          make PG_CONFIG=$HOME/pg12/bin/pg_config installcheck
 +          make PG_CONFIG=$HOME/pg13/bin/pg_config installcheck
          continue-on-error: true
--          
++
        - name: Dump regression test errors
          if: steps.regression_tests.outcome != 'success'
          run: |
diff --cc .github/workflows/python-driver.yaml
index da431648,1f15cf9a..29d4b98b
--- a/.github/workflows/python-driver.yaml
+++ b/.github/workflows/python-driver.yaml
@@@ -2,10 -2,10 +2,10 @@@ name: Python Driver Test
  
  on:
    push:
 -    branches: [ "PG12" ]
 -      
 +    branches: [ "PG13" ]
-       
++
    pull_request:
 -    branches: [ "PG12" ]
 +    branches: [ "PG13" ]
  
  jobs:
    build:
@@@ -14,10 -14,10 +14,10 @@@
      defaults:
        run:
          working-directory: drivers/python
--  
++
      steps:
      - uses: actions/checkout@v3
--    
++
      - name: Run apache/age docker image
        run: docker-compose up -d
  
diff --cc .gitignore
index c5062f53,ac4fd622..cfc48d6e
--- a/.gitignore
+++ b/.gitignore
@@@ -4,4 -4,5 +4,6 @@@ build.s
  .idea
  .deps
  .DS_Store
 +.vscode
+ *.tokens
+ *.interp
diff --cc README.md
index 63689293,5521d399..24e9ee2c
--- a/README.md
+++ b/README.md
@@@ -55,55 -55,255 +55,255 @@@
  
  <br>
  
- 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 d [...]
+ <h2><img height="30" src="/img/AGE.png">&nbsp;&nbsp;What is Apache AGE?</h2>
+ 
+ [Apache AGE](https://age.apache.org/#) is an extension for PostgreSQL that 
enables users to leverage a graph database on top of the existing relational 
databases. AGE is an acronym for A Graph Extension and is inspired by Bitnine's 
AgensGraph, a multi-model database fork of PostgreSQL. The basic principle of 
the project is to create a single storage that handles both the relational and 
graph data model so that the users can use the standard ANSI SQL along with 
openCypher, one of the mos [...]
+ </br>
+ </br>
+ </br>
+ 
+ <p align="center">
+ <img src="/img/age-01.png" width="80%" height="80%">
+ </p>
+ 
+ </br>
+ 
+ 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 exten [...]
+ 
+ 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.
+ 
+ 
+ <h2><img height="30" src="/img/tick.svg">&nbsp;&nbsp;Overview</h2>
+ 
+ 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.
+ - **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.
+ 
+ <h2><img height="30" src="/img/features.svg">&nbsp;&nbsp;Features</h2>
+ </br>
+ </br>
+ 
+ <p align="center">
+ <img src="/img/age-03.png" width="80%" height="80%">
+ </p>
+ </br>
+ 
+ - **Cypher Query**: supports graph query language
+ - **Hybrid Querying**: enables SQL and/or Cypher
+ - **Querying**: enables multiple graphs
+ - **Hierarchical**: graph label organization
+ - **Property Indexes**: on both vertices(nodes) and edges
+ - **Full PostgreSQL**: supports PG features
+ 
  
- Apache AGE is:
  
- Powerful -- AGE adds graph database support to the already popular PostgreSQL 
database: PostgreSQL is used by organizations including Apple, Spotify, and 
NASA.
- Flexible -- AGE allows you to perform openCypher queries, which make complex 
queries much easier to write.
- Intelligent -- AGE allows you to perform graph queries that are the basis for 
many next level web services such as fraud & intrusion detection, master data 
management, product recommendations, identity and relationship management, 
experience personalization, knowledge management and more.
+ <h2><img height="30" 
src="/img/documentation.svg">&nbsp;&nbsp;Documentation</h2>
  
- ## Overview
+ Refer to our latest [Apache AGE 
documentation](https://age.apache.org/age-manual/master/index.html) to learn 
about installation, features, built-in functions, and  Cypher queries.
  
- **This README file is for the Apache AGE PostgreSQL 12 release**
  
- - **Apache AGE is currently being developed for the PostgreSQL 11 and 12 
releases** and will support all the future releases of PostgreSQL.
- - Apache AGE supports the openCypher graph query language.
- - Apache AGE enables querying multiple graphs at the same time.
- - Apache AGE will be enhanced with an aim to support all of the key features 
of AgensGraph (PostgreSQL fork extended with graph DB functionality).
  
- ## Latest happenings
+ <h2><img height="30" 
src="/img/installation.svg">&nbsp;&nbsp;Pre-Installation</h2>
  
- - Latest Apache AGE for PG 12 release, [Apache AGE 1.1.1 
(https://github.com/apache/age/releases/tag/PG12/v1.1.1-rc0).
- - The latest Apache AGE documentation is now available at 
[here](https://age.apache.org/docs/master/index.html).
- - The roadmap - [Apache AGE website](http://age.apache.org/).
- - Send all your comments and inquiries to the user mailing list, 
[email protected].
+ Install the following essential libraries according to each OS. Building AGE 
from the source depends on the following Linux libraries (Ubuntu package names 
shown below):
  
- ## Installation
+ - **CentOS**
+ ```bash
+ yum install gcc glibc glib-common readline readline-devel zlib zlib-devel 
flex bison
+ ```
+ - **Fedora**
+ ```bash
+ dnf install gcc glibc bison flex readline readline-devel zlib zlib-devel
+ ```
+ - **Ubuntu**
+ ```bash
+ sudo apt-get install build-essential libreadline-dev zlib1g-dev flex bison
+ ```
  
- - [Use a docker image - official ver.](https://hub.docker.com/r/apache/age)
- - [Installing from source](https://age.apache.org/#)
+ <h2><img height="30" src="/img/installation.svg">&nbsp;&nbsp;Installation</h2>
  
- ## Graph visualization tool for AGE
+ Apache AGE is intended to be simple to install and run. It can be installed 
with Docker and other traditional ways. 
  
- Apache AGE Viewer is a subproject of the Apache AGE project:  
<https://github.com/apache/age-viewer>
+ <h4><a><img width="20" src="/img/pg.svg"></a>
+ &nbsp;Install PosgtreSQL
+ </h4>
+ 
 -You will need to install an AGE compatible version of Postgres<a>, for now 
AGE supports Postgres 11 and 12. Supporting the latest versions is on AGE 
roadmap. 
++You will need to install an AGE compatible version of Postgres<a>, AGE 
supports Postgres 11, 12 and 13. Supporting the latest versions is on AGE 
roadmap. 
+      
+ <h4>
+ &nbsp;Install From Package Manager
+ </h4>
+ 
+ You can use a <a href="https://www.postgresql.org/download/";>package 
management </a> that your OS provides to download AGE.
+ 
+ <br>
  
- - This is a visualization tool.
- After AGE Extension Installation
- You can use this tool to use the visualization features.
- - Follow the instructions on the link to run it.
- Under Connect to Database , select database type as "Apache AGE"
+ ```bash
+ sudo apt install postgresql 
+ 
+ ```
+ <h4>
+ &nbsp;Install From Source Code
+ </h4>
+ 
+ You can <a href="https://www.postgresql.org/ftp/source/";> download the 
Postgres </a> 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 <a href="https://www.postgresql.org/docs/11/installation.html";>official 
Postgres Website.</a>
+ 
+ 
+ 
+ <h4><img width="20" src="/img/tux.svg"><img width="20" src="/img/apple.svg"> 
&nbsp;Install AGE on Linux and MacOS
+ </h4>
+ 
+ Clone the <a href="https://github.com/apache/age";>github repository</a> or 
download the<a href="https://github.com/apache/age/releases";>download an 
official release.
+ </a>
 -Run the pg_config utility and check the version of PostgreSQL. Currently, 
only PostgreSQL versions 11 & 12 are supported. If you have any other version 
of Postgres, you will need to install PostgreSQL version 11 & 12.
++Run the pg_config utility and check the version of PostgreSQL. Currently, 
only PostgreSQL versions 11, 12 and 13 are supported. If you have any other 
version of Postgres, you will need to install PostgreSQL version 11, 12, or 13.
+ <br>
+     
+ ```bash
+ pg_config
+ ```
+ Run the following command in the source code directory of Apache AGE to build 
and install the extension.  
+      
+ ```bash
+ make install
+ ```
+      
+ If the path to your Postgres installation is not in the PATH variable, add 
the path in the arguments:
+ ```bash
+ make PG_CONFIG=/path/to/postgres/bin/pg_config install
+ ```
  
- ## Documentation
  
- Here is the link to the latest [Apache AGE 
documentation](https://age.apache.org/docs/master/index.html).
- You can learn about how to install Apache AGE, its features and built-in 
functions and how to use various Cypher queries.
+ <h4></a><img width="30" src="/img/docker.svg"></a>
+ &nbsp;Run using Docker
+ </h4>
+ 
+ <h5> Get the docker image </h5>
+ 
+ ```bash
+ docker pull apache/age
+ 
+ ```
+ <h5> Create AGE docker container </h5>
+ 
+ ```bash
+ docker run \
+     --name age  \
+     -p 5455:5432 \
+     -e POSTGRES_USER=postgresUser \
+     -e POSTGRES_PASSWORD=postgresPW \
+     -e POSTGRES_DB=postgresDB \
+     -d \
+     apache/age
+ ```
+ 
+ 
+ 
+ <h2><img height="20" src="/img/contents.svg">&nbsp;&nbsp;Post 
Installation</h2>
+ 
+ For every connection of AGE you start, you will need to load the AGE 
extension.
+ 
+ ```bash
+ CREATE EXTENSION age;
+ ```
+ ```bash
+ LOAD 'age';
+ ```
+ ```bash
+ SET search_path = ag_catalog, "$user", public;
+ ```
+ 
+ 
+ 
+ <h2><img height="20" src="/img/contents.svg">&nbsp;&nbsp;Quick Start</h2>
+ 
+ To create a graph, use the create_graph function located in the ag_catalog 
namespace.
+ 
+ ```bash
+ SELECT create_graph('graph_name');
+ ```
+ 
+ To create a single vertex, use the CREATE clause. 
+ 
+ ```bash
+ SELECT * 
+ FROM cypher('graph_name', $$
+     CREATE (n)
+ $$) as (v agtype);
+ ```
+ 
+ 
+ To create a single vertex with the label, use the CREATE clause. 
+ 
+ ```bash
+ SELECT * 
+ FROM cypher('graph_name', $$
+     CREATE (:label)
+ $$) as (v agtype);
+ ```
+ 
+ To query the graph, you can use the MATCH clause.  
+ 
+ ```bash
+ SELECT * FROM cypher('graph_name', $$
+ MATCH (v)
+ RETURN v
+ $$) as (v agtype);
+ ```
+ 
+ You can use the following to create an edge, for example, between two nodes. 
+ 
+ ```bash
+ SELECT * 
+ FROM cypher('graph_name', $$
+     MATCH (a:label), (b:label)
+     WHERE a.property = 'Node A' AND b.property = 'Node B'
+     CREATE (a)-[e:RELTYPE]->(b)
+     RETURN e
+ $$) as (e agtype);
+ ```
+ 
+ 
+ To create an edge and set properties.
+ 
+ ```bash
+ SELECT * 
+ FROM cypher('graph_name', $$
+     MATCH (a:label), (b:label)
+     WHERE a.property = 'Node A' AND b.property = 'Node B'
+     CREATE (a)-[e:RELTYPE {property:a.property + '<->' + b.property}]->(b)
+     RETURN e
+ $$) as (e agtype);
+ ```
+ 
+ Example 
+ 
+ ```bash
+ SELECT * 
+ FROM cypher('graph_name', $$
+     MATCH (a:Person), (b:Person)
+     WHERE a.name = 'Node A' AND b.name = 'Node B'
+     CREATE (a)-[e:RELTYPE {name:a.name + '<->' + b.name}]->(b)
+     RETURN e
+ $$) as (e agtype);
+ ```
+ 
+ 
+ 
+ <h2><img height="20" src="/img/gettingstarted.svg">&nbsp;&nbsp;Language 
Specific Drivers</h2>
+ 
+ Starting with Apache AGE is very simple. You can easily select your platform 
and incorporate the relevant SDK into your code.
+ </br>
+ </br>
+ 
+ <p align="center">
+ <img src="/img/age-02.png" width="80%" height="80%">
+ </p>
  
- ## Language Specific Drivers
  
- ### Built-in
+ <h4>Built-in</h4>
  
  - [Go driver](./drivers/golang)
  - [Java driver](./drivers/jdbc)
diff --cc age--1.1.1.sql
index 08d4021c,304d27db..13179898
--- a/age--1.1.1.sql
+++ b/age--1.1.1.sql
@@@ -4186,6 -4194,19 +4194,19 @@@ CALLED ON NULL INPU
  PARALLEL SAFE
  AS 'MODULE_PATHNAME';
  
 -CREATE FUNCTION ag_catalog.age_create_barbell_graph(graph_name name, 
 -                                                graph_size int, 
++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';
+ 
  CREATE FUNCTION ag_catalog.age_prepare_cypher(cstring, cstring)
  RETURNS boolean
  LANGUAGE c
diff --cc src/backend/parser/cypher_clause.c
index ff9117d1,f0e6ba8c..2d39c96d
--- a/src/backend/parser/cypher_clause.c
+++ b/src/backend/parser/cypher_clause.c
@@@ -141,9 -142,9 +142,11 @@@ static Expr *transform_cypher_edge(cyph
  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, ParseNamespaceItem 
*pnsi,
 -static Node *make_vertex_expr(cypher_parsestate *cpstate, RangeTblEntry *rte,
++static Node *make_vertex_expr(cypher_parsestate *cpstate,
++                              ParseNamespaceItem *pnsi,
                                char *label);
- static Node *make_edge_expr(cypher_parsestate *cpstate, ParseNamespaceItem 
*pnsi,
 -static Node *make_edge_expr(cypher_parsestate *cpstate, RangeTblEntry *rte,
++static Node *make_edge_expr(cypher_parsestate *cpstate,
++                            ParseNamespaceItem *pnsi,
                              char *label);
  static Node *make_qual(cypher_parsestate *cpstate,
                             transform_entity *entity, char *name);
@@@ -281,12 -287,11 +291,12 @@@ static Query *transform_cypher_call_sub
  #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)
- static ParseNamespaceItem 
 -static RangeTblEntry *transform_cypher_clause_as_subquery(cypher_parsestate 
*cpstate,
 -                                                          transform_method 
transform,
 -                                                          cypher_clause 
*clause,
 -                                                          Alias *alias,
 -                                                          bool 
add_rte_to_query);
++static ParseNamespaceItem
 +*transform_cypher_clause_as_subquery(cypher_parsestate *cpstate,
 +                                     transform_method transform,
 +                                     cypher_clause *clause,
 +                                     Alias *alias,
 +                                     bool add_rte_to_query);
  static Query *analyze_cypher_clause(transform_method transform,
                                      cypher_clause *clause,
                                      cypher_parsestate *parent_cpstate);
@@@ -321,8 -327,8 +331,8 @@@ static ParseNamespaceItem *transform_Ra
  static Node *transform_VLE_Function(cypher_parsestate *cpstate, Node *n,
                                      RangeTblEntry **top_rte, int *top_rti,
                                      List **namespace);
- static ParseNamespaceItem 
 -static RangeTblEntry *append_VLE_Func_to_FromClause(cypher_parsestate 
*cpstate,
 -                                                    Node *n);
++static ParseNamespaceItem
 +*append_VLE_Func_to_FromClause(cypher_parsestate *cpstate, Node *n);
  static void setNamespaceLateralState(List *namespace, bool lateral_only,
                                       bool lateral_ok);
  static bool isa_special_VLE_case(cypher_path *path);
@@@ -1037,8 -1031,8 +1047,8 @@@ transform_cypher_union_tree(cypher_pars
   * be picked up by the grammar. transform_cypher_call_subquery handles the
   * call transformation itself.
   */
 -static Query * transform_cypher_call_stmt(cypher_parsestate *cpstate,
 -                                          cypher_clause *clause)
 +static Query *transform_cypher_call_stmt(cypher_parsestate *cpstate,
-                                           cypher_clause *clause)
++                                         cypher_clause *clause)
  {
      ParseState *pstate = (ParseState *)cpstate;
      cypher_call *self = (cypher_call *)clause->self;
@@@ -2270,10 -2265,9 +2280,11 @@@ static bool match_check_valid_label(cyp
  
      return true;
  }
 +
  static Query *transform_cypher_clause_with_where(cypher_parsestate *cpstate,
                                                   transform_method transform,
-                                                  cypher_clause *clause)
 -                                                 cypher_clause *clause, Node 
*where)
++                                                 cypher_clause *clause,
++                                                 Node *where)
  {
      ParseState *pstate = (ParseState *)cpstate;
      Query *query;
@@@ -2301,37 -2282,32 +2299,33 @@@
          query = makeNode(Query);
          query->commandType = CMD_SELECT;
  
-         pnsi = transform_cypher_clause_as_subquery(cpstate, transform, 
clause, NULL, true);
 -        rte = transform_cypher_clause_as_subquery(cpstate, transform, clause, 
NULL, true);
 -
++        pnsi = transform_cypher_clause_as_subquery(cpstate, transform, clause,
++                                                   NULL, true);
 +        Assert(pnsi != NULL);
          rtindex = list_length(pstate->p_rtable);
          Assert(rtindex == 1); // rte is the only RangeTblEntry in pstate
  
+         /*
 -         * add all the target entries in rte to the current target list to 
pass
++         * add all the target entries in pnsi to the current target list to 
pass
+          * all the variables that are introduced in the previous clause to the
+          * next clause
+          */
 -        query->targetList = expandRelAttrs(pstate, rte, rtindex, 0, -1);
 +        query->targetList = expandNSItemAttrs(pstate, pnsi, 0, -1);
  
          markTargetListOrigins(pstate, query->targetList);
  
          query->rtable = pstate->p_rtable;
  
-         if (is_ag_node(clause->self, cypher_call))
+         if (!is_ag_node(self, cypher_match))
          {
-             cypher_call *call = (cypher_call *)clause->self;
- 
-             if (call->where != NULL)
-             {
-                 Expr *where_qual = NULL;
+             where_qual = transform_cypher_expr(cpstate, where,
+                                                         EXPR_KIND_WHERE);
  
-                 where_qual = (Expr *)transform_cypher_expr(cpstate, 
call->where,
-                                                            EXPR_KIND_WHERE);
- 
-                 where_qual = (Expr *)coerce_to_boolean(pstate, (Node 
*)where_qual,
-                                                "WHERE");
-                 query->jointree = makeFromExpr(pstate->p_joinlist, (Node 
*)where_qual);
-             }
-         }
-         else
-         {
-             query->jointree = makeFromExpr(pstate->p_joinlist, NULL);
+             where_qual = coerce_to_boolean(pstate, where_qual,
+                                             "WHERE");
          }
 -        
++
+         query->jointree = makeFromExpr(pstate->p_joinlist, where_qual);
          assign_query_collations(pstate, query);
      }
      else
@@@ -3529,29 -3512,38 +3542,37 @@@ static Node *create_property_constraint
  {
      ParseState *pstate = (ParseState *)cpstate;
      char *entity_name;
-     ColumnRef *cr;
-     Node *prop_expr, *const_expr;
+     Node *const_expr;
 -    RangeTblEntry *rte;
      Node *last_srf = pstate->p_last_srf;
 +    ParseNamespaceItem *pnsi;
  
-     cr = makeNode(ColumnRef);
- 
-     entity_name = get_entity_name(entity);
+     /*
+      * If the prop_expr node wasn't passed in, create it. Otherwise, skip
+      * the creation step.
+      */
+     if (prop_expr == NULL)
+     {
+         ColumnRef *cr = NULL;
  
-     cr->fields = list_make2(makeString(entity_name), 
makeString("properties"));
+         cr = makeNode(ColumnRef);
+         entity_name = get_entity_name(entity);
+         cr->fields = list_make2(makeString(entity_name),
+                                 makeString("properties"));
  
-     // use Postgres to get the properties' transform node
-     if ((pnsi = find_pnsi(cpstate, entity_name)))
-     {
-         prop_expr = scanNSItemForColumn(pstate, pnsi, 0,
-                                         AG_VERTEX_COLNAME_PROPERTIES, -1);
-     }
-     else
-     {
-         prop_expr = transformExpr(pstate, (Node *)cr, EXPR_KIND_WHERE);
+         /* use Postgres to get the properties' transform node */
 -        rte = find_rte(cpstate, entity_name);
 -        if (rte != NULL)
++        pnsi = find_pnsi(cpstate, entity_name);
++        if (pnsi != NULL)
+         {
 -            prop_expr = scanRTEForColumn(pstate, rte,
 -                                         AG_VERTEX_COLNAME_PROPERTIES, -1, 0,
 -                                         NULL);
++            prop_expr = scanNSItemForColumn(pstate, pnsi, 0,
++                                            AG_VERTEX_COLNAME_PROPERTIES, -1);
+         }
+         else
+         {
+             prop_expr = transformExpr(pstate, (Node *)cr, EXPR_KIND_WHERE);
+         }
      }
  
-     // use cypher to get the constraints' transform node
+     /* use cypher to get the constraints' transform node */
      const_expr = transform_cypher_expr(cpstate, property_constraints,
                                         EXPR_KIND_WHERE);
  
@@@ -3902,13 -3948,66 +3978,67 @@@ static List *transform_match_entities(c
  
                  if (rel->props)
                  {
-                     Node *n;
+                     Node *r = NULL;
+                     Node *prop_var = NULL;
+                     Node *prop_expr = NULL;
+ 
+                     /*
+                      * We need to build a transformed properties(prop_var)
+                      * expression IF the properties variable already exists 
from
+                      * a previous clause. Please note that the "found" 
prop_var
+                      * was previously transformed.
+                      */
+ 
+                     /* get the prop_var if it was previously resolved */
+                     if (rel->name != NULL)
+                     {
+                         prop_var = colNameToVar(pstate, rel->name, false,
+                                                 rel->location);
+                     }
+ 
+                     /*
+                      * If prop_var exists and is an alias, just pass it 
through by
+                      * assigning the prop_expr the prop_var.
+                      */
+                     if (prop_var != NULL &&
+                         pg_strncasecmp(rel->name, AGE_DEFAULT_ALIAS_PREFIX,
+                                        strlen(AGE_DEFAULT_ALIAS_PREFIX)) == 0)
+                     {
+                         prop_expr = prop_var;
+                     }
+                     /*
+                      * Else, if it exists and is not an alias, create the 
prop_expr
+                      * as a transformed properties(prop_var) function node.
+                      */
+                     else if (prop_var != NULL)
+                     {
+                         /*
+                          * Remember that prop_var is already transformed. We 
need
+                          * to built the transform manually.
+                          */
+                         FuncCall *fc = NULL;
+                         List *targs = NIL;
+                         List *fname = NIL;
+ 
+                         targs = lappend(targs, prop_var);
+                         fname = list_make2(makeString("ag_catalog"),
+                                            makeString("age_properties"));
+                         fc = makeFuncCall(fname, targs, -1);
+ 
+                         /*
+                          * Hand off to ParseFuncOrColumn to create the 
function
+                          * expression for properties(prop_var)
+                          */
+                         prop_expr = ParseFuncOrColumn(pstate, fname, targs,
+                                                       pstate->p_last_srf, fc,
+                                                       false, -1);
+                     }
  
                      ((cypher_map*)rel->props)->keep_null = true;
-                     n = create_property_constraints(cpstate, entity,
-                                                           rel->props);
+                     r = create_property_constraints(cpstate, entity, 
rel->props, prop_expr);
++
                      cpstate->property_constraint_quals =
-                         lappend(cpstate->property_constraint_quals, n);
+                         lappend(cpstate->property_constraint_quals, r);
                  }
  
                  entities = lappend(entities, entity);
@@@ -4464,10 -4585,9 +4621,9 @@@ static Expr *transform_cypher_node(cyph
  
      resno = pstate->p_next_resno++;
  
-     expr = (Expr *)make_vertex_expr(cpstate, pnsi, node->label);
      if (valid_label)
      {
 -        expr = (Expr *)make_vertex_expr(cpstate, rte, node->label);
 +        expr = (Expr *)make_vertex_expr(cpstate, pnsi, node->label);
      }
      else
      {
diff --cc src/backend/utils/graph_generation.c
index c11be6ab,ca12e9c5..9569c83f
--- a/src/backend/utils/graph_generation.c
+++ b/src/backend/utils/graph_generation.c
@@@ -48,7 -48,28 +48,26 @@@
  #include "utils/load/ag_load_edges.h"
  #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);
+ }
  
 -
  PG_FUNCTION_INFO_V1(create_complete_graph);
  
  /*
@@@ -129,8 -163,6 +161,9 @@@ Datum create_complete_graph(PG_FUNCTION
  
      if (!PG_ARGISNULL(3))
      {
 +        vtx_label_name = PG_GETARG_NAME(3);
 +        vtx_name_str = NameStr(*vtx_label_name);
++
          // Check if label with the input name already exists
          if (!label_exists(vtx_name_str, graph_id))
          {
@@@ -199,3 -230,148 +232,147 @@@
      PG_RETURN_VOID();
  }
  
 -
+ PG_FUNCTION_INFO_V1(age_create_barbell_graph);
+ 
+ /*
+  * The barbell graph is two complete graphs connected by a bridge path
+  * Syntax:
+  * ag_catalog.age_create_barbell_graph(graph_name Name,
+  *                                     m int,
+  *                                     n int,
+  *                                     vertex_label_name Name DEFAULT = NULL,
+  *                                     vertex_properties agtype DEFAULT = 
NULL,
+  *                                     edge_label_name Name DEAULT = NULL,
+  *                                     edge_properties agtype DEFAULT = NULL)
+  * Input:
+  *
+  * graph_name - Name of the graph to be created.
+  * m - number of vertices in one complete graph.
+  * n - number of vertices in the bridge path.
+  * vertex_label_name - Name of the label to assign each vertex to.
+  * vertex_properties - Property values to assign each vertex. Default is NULL
+  * edge_label_name - Name of the label to assign each edge to.
+  * edge_properties - Property values to assign each edge. Default is NULL
+  *
+  * https://en.wikipedia.org/wiki/Barbell_graph
+  */
+ 
+ Datum age_create_barbell_graph(PG_FUNCTION_ARGS)
+ {
+     FunctionCallInfo arguments;
+     Oid graph_oid;
+     Name graph_name;
+     char* graph_name_str;
+ 
+     int64 start_node_index, end_node_index, nextval;
+ 
+     Name node_label_name = NULL;
+     int32 node_label_id;
+     char* node_label_str;
+ 
+     Name edge_label_name;
+     int32 edge_label_id;
+     char* edge_label_str;
+ 
+     graphid object_graph_id;
+     graphid start_node_graph_id;
+     graphid end_node_graph_id;
+ 
+     graph_cache_data* graph_cache;
+     label_cache_data* edge_cache;
+ 
+     agtype* properties = NULL;
+ 
+     arguments = fcinfo;
+ 
+     // Checking for possible NULL arguments
+     // Name graph_name
+     if (PG_ARGISNULL(0))
+     {
+         ereport(ERROR, (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+                         errmsg("Graph name cannot be NULL")));
+     }
+ 
+     graph_name = PG_GETARG_NAME(0);
+     graph_name_str = NameStr(*graph_name);
+ 
+     // int graph size (number of nodes in each complete graph)
+     if (PG_ARGISNULL(1) && PG_GETARG_INT32(1) < 3)
+     {
+         ereport(ERROR, (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+                 errmsg("Graph size cannot be NULL or lower than 3")));
+     }
+ 
+     /*
+      * int64 bridge_size: currently only stays at zero.
+      * to do: implement bridge with variable number of nodes.
+      */
+     if (PG_ARGISNULL(2) || PG_GETARG_INT32(2) < 0 )
+     {
+         ereport(ERROR, (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+                 errmsg("Bridge size cannot be NULL or lower than 0")));
+     }
+ 
+     // node label: if null, gets default label, which is "_ag_label_vertex"
+     if (PG_ARGISNULL(3))
+     {
+         namestrcpy(node_label_name, AG_DEFAULT_LABEL_VERTEX);
+     }
+     else
+     {
+         node_label_name = PG_GETARG_NAME(3);
+     }
+     node_label_str = NameStr(*node_label_name);
+ 
+     /* Name edge_label */
+     if (PG_ARGISNULL(5))
+     {
+         ereport(ERROR, (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+                 errmsg("edge label can not be NULL")));
+     }
+ 
+     edge_label_name = PG_GETARG_NAME(5);
+     edge_label_str = NameStr(*edge_label_name);
+ 
+ 
+     // create two separate complete graphs
+     DirectFunctionCall4(create_complete_graph, arguments->args[0].value,
+                                                arguments->args[1].value,
+                                                arguments->args[5].value,
+                                                arguments->args[3].value);
+     DirectFunctionCall4(create_complete_graph, arguments->args[0].value,
+                                                arguments->args[1].value,
+                                                arguments->args[5].value,
+                                                arguments->args[3].value);
+ 
+     graph_oid = get_graph_oid(graph_name_str);
+     node_label_id = get_label_id(node_label_str, graph_oid);
+     edge_label_id = get_label_id(edge_label_str, graph_oid);
+ 
+     /*
+      * Fetching caches to get next values for graph id's, and access nodes
+      * to be connected with edges.
+      */
+     graph_cache = search_graph_name_cache(graph_name_str);
+     edge_cache = search_label_name_graph_cache(edge_label_str,graph_oid);
+ 
+     // connect a node from each graph
+     start_node_index = 1; // first created node, from the first complete graph
+     end_node_index = arguments->args[1].value*2; // last created node, second 
graph
+ 
+     // next index to be assigned to a node or edge
+     nextval = get_nextval_internal(graph_cache, edge_cache);
+ 
+     // build the graph id's of the edge to be created
+     object_graph_id = make_graphid(edge_label_id, nextval);
+     start_node_graph_id = make_graphid(node_label_id, start_node_index);
+     end_node_graph_id = make_graphid(node_label_id, end_node_index);
+     properties = create_empty_agtype();
+ 
+     // connect two nodes
+     insert_edge_simple(graph_oid, edge_label_str,
+                        object_graph_id, start_node_graph_id,
+                        end_node_graph_id, properties);
+ 
+     PG_RETURN_VOID();
+ }


Reply via email to