You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@age.apache.org by jg...@apache.org on 2025/06/17 05:20:30 UTC

(age) branch PG15 updated (7a7556bc -> c2faa133)

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

jgemignani pushed a change to branch PG15
in repository https://gitbox.apache.org/repos/asf/age.git


    from 7a7556bc Add support for external extensions (#2088) (#2108)
     new 7b2acfde Fix issue 1955 - List comprehension in WHERE clause (#2094)
     new 4e9c734e Fix CI build errors caused by missing dependencies (#2163)
     new 02f5e4a1 Fix issue with the CI build and labeler (#2183)
     new c2faa133 Reimplement list comprehension (#2169)

The 4 revisions listed above as "new" are entirely new to this
repository and will be described in separate emails.  The revisions
listed as "add" were already present in the repository and have only
been added to this reference.


Summary of changes:
 .github/workflows/installcheck.yaml     |   9 +-
 .github/workflows/labeler.yml           |   3 +
 age--1.5.0--y.y.y.sql                   |  13 +-
 regress/expected/list_comprehension.out | 135 +++++++++--
 regress/sql/list_comprehension.sql      |  24 +-
 sql/agtype_coercions.sql                |  11 +
 sql/agtype_typecast.sql                 |   3 +-
 src/backend/nodes/ag_nodes.c            |   2 +
 src/backend/nodes/cypher_outfuncs.c     |  14 +-
 src/backend/optimizer/cypher_pathnode.c |  82 ++++++-
 src/backend/parser/cypher_analyze.c     | 416 +++++++++-----------------------
 src/backend/parser/cypher_clause.c      | 319 ++++++++++++------------
 src/backend/parser/cypher_expr.c        |  68 +-----
 src/backend/parser/cypher_gram.y        | 148 ++++--------
 src/backend/parser/cypher_item.c        | 242 +------------------
 src/backend/utils/adt/agtype.c          |  55 +----
 src/include/nodes/ag_nodes.h            |   2 +-
 src/include/nodes/cypher_nodes.h        |  14 +-
 src/include/nodes/cypher_outfuncs.h     |   1 +
 src/include/parser/cypher_analyze.h     |  12 +-
 src/include/parser/cypher_clause.h      |   9 -
 src/include/parser/cypher_item.h        |   2 -
 src/include/parser/cypher_parse_node.h  |   1 -
 23 files changed, 629 insertions(+), 956 deletions(-)


(age) 03/04: Fix issue with the CI build and labeler (#2183)

Posted by jg...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

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

commit 02f5e4a13ff9ade55085c4fa5321604ad63f6409
Author: John Gemignani <jr...@gmail.com>
AuthorDate: Mon Jun 2 12:23:11 2025 -0700

    Fix issue with the CI build and labeler (#2183)
    
    The build now needs to use https:, instead of git:
    The labeler now requires a new step before running.
    
    Modified the following files -
    
        .github/workflows/installcheck.yaml
        .github/workflows/labeler.yml
---
 .github/workflows/installcheck.yaml | 4 ++--
 .github/workflows/labeler.yml       | 3 +++
 2 files changed, 5 insertions(+), 2 deletions(-)

diff --git a/.github/workflows/installcheck.yaml b/.github/workflows/installcheck.yaml
index 7e7214cc..51bfd2a4 100644
--- a/.github/workflows/installcheck.yaml
+++ b/.github/workflows/installcheck.yaml
@@ -13,7 +13,7 @@ jobs:
     steps:
       - name: Get latest commit id of PostgreSQL 15
         run: |
-          echo "PG_COMMIT_HASH=$(git ls-remote git://git.postgresql.org/git/postgresql.git refs/heads/REL_15_STABLE | awk '{print $1}')" >> $GITHUB_ENV
+          echo "PG_COMMIT_HASH=$(git ls-remote https://git.postgresql.org/git/postgresql.git refs/heads/REL_15_STABLE | awk '{print $1}')" >> $GITHUB_ENV
 
       - name: Cache PostgreSQL 15
         uses: actions/cache@v3
@@ -30,7 +30,7 @@ jobs:
       - name: Install PostgreSQL 15 and some extensions
         if: steps.pg15cache.outputs.cache-hit != 'true'
         run: |
-          git clone --depth 1 --branch REL_15_STABLE git://git.postgresql.org/git/postgresql.git ~/pg15source
+          git clone --depth 1 --branch REL_15_STABLE https://git.postgresql.org/git/postgresql.git ~/pg15source
           cd ~/pg15source
           ./configure --prefix=$HOME/pg15 CFLAGS="-std=gnu99 -ggdb -O0" --enable-cassert
           make install -j$(nproc) > /dev/null
diff --git a/.github/workflows/labeler.yml b/.github/workflows/labeler.yml
index 4234e358..266df4ce 100644
--- a/.github/workflows/labeler.yml
+++ b/.github/workflows/labeler.yml
@@ -9,6 +9,9 @@ jobs:
       pull-requests: write
     runs-on: ubuntu-latest
     steps:
+    - name: Checkout
+      uses: actions/checkout@v3
+
     - name: Apply branch labels
       uses: actions/labeler@v5.0.0
 


(age) 04/04: Reimplement list comprehension (#2169)

Posted by jg...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

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

commit c2faa133308b79d579507003fc790c0f00045500
Author: Muhammad Taha Naveed <mt...@apache.org>
AuthorDate: Tue Jun 10 23:07:49 2025 +0500

    Reimplement list comprehension (#2169)
    
    * Revert "Fix issue 1955 - List comprehension in WHERE clause (#2094)"
    
    This reverts commit 0f0d9be9ba02fb90272d2053986f2b5aa4a0c25c.
    
    * Revert "Fix error using list comprehension with WITH * (#1838)"
    
    This reverts commit 5e08a2f58693adca55085da8d56eb1831d963d20.
    
    * Revert "Fix shift/reduce conflict in grammar (#1719)"
    
    This reverts commit fab3119a109280fd63237ce17c6d4dd60b7dfc03.
    
    * Revert "Implement list comprehension (#1610)"
    
    This reverts commit 3b2b394eb669c4f80fc893ad224cf5ea4e10c5a9.
    
    * Reimplement list comprehension
    
    - Reimplement list comprehension to use ARRAY sublinks.
    - Some test results were not correct. All the test results that are
      modified are correct and are verified with neo4j.
    - Also resolves the issue of using list comprehension in MERGE clause (1611)
      and issue 1850
    
    * Add expression tree walkers for cypher nodes
    
    - Added cypher_raw_expr_tree_walker and cypher_expr_tree_walker, based
      on Postgres's raw_expression_tree_walker and expression_tree_walker.
      These follow the same walker API as Postgres and add support for
      Cypher-specific nodes.
    - Also added the agtype[] to agtype func and typecast to 1.5.0-y.y.y.sql
    - Simplifies logic for cypher_subquery handling in where clause.
    - Fixes a crash when list comprehension in the WHERE clause references a
      variable from the preceding MATCH clause.
---
 age--1.5.0--y.y.y.sql                   |  13 +-
 regress/expected/list_comprehension.out | 102 ++++++--
 regress/sql/list_comprehension.sql      |  16 +-
 sql/agtype_coercions.sql                |  11 +
 sql/agtype_typecast.sql                 |   3 +-
 src/backend/nodes/ag_nodes.c            |   2 +
 src/backend/nodes/cypher_outfuncs.c     |  14 +-
 src/backend/optimizer/cypher_pathnode.c |  82 ++++++-
 src/backend/parser/cypher_analyze.c     | 416 +++++++++-----------------------
 src/backend/parser/cypher_clause.c      | 319 ++++++++++++------------
 src/backend/parser/cypher_expr.c        |  67 +----
 src/backend/parser/cypher_gram.y        | 148 ++++--------
 src/backend/parser/cypher_item.c        | 242 +------------------
 src/backend/utils/adt/agtype.c          |  55 +----
 src/include/nodes/ag_nodes.h            |   2 +-
 src/include/nodes/cypher_nodes.h        |  14 +-
 src/include/nodes/cypher_outfuncs.h     |   1 +
 src/include/parser/cypher_analyze.h     |  12 +-
 src/include/parser/cypher_clause.h      |   9 -
 src/include/parser/cypher_item.h        |   2 -
 src/include/parser/cypher_parse_node.h  |   1 -
 21 files changed, 570 insertions(+), 961 deletions(-)

diff --git a/age--1.5.0--y.y.y.sql b/age--1.5.0--y.y.y.sql
index 1d4aa602..5bf45a2a 100644
--- a/age--1.5.0--y.y.y.sql
+++ b/age--1.5.0--y.y.y.sql
@@ -156,4 +156,15 @@ PARALLEL SAFE
 AS 'MODULE_PATHNAME';
 
 CREATE CAST (agtype AS json)
-    WITH FUNCTION ag_catalog.agtype_to_json(agtype);
\ No newline at end of file
+    WITH FUNCTION ag_catalog.agtype_to_json(agtype);
+
+CREATE FUNCTION ag_catalog.agtype_array_to_agtype(agtype[])
+    RETURNS agtype
+    LANGUAGE c
+    IMMUTABLE
+RETURNS NULL ON NULL INPUT
+PARALLEL SAFE
+AS 'MODULE_PATHNAME';
+
+CREATE CAST (agtype[] AS agtype)
+    WITH FUNCTION ag_catalog.agtype_array_to_agtype(agtype[]);
\ No newline at end of file
diff --git a/regress/expected/list_comprehension.out b/regress/expected/list_comprehension.out
index bf5731d2..07f77770 100644
--- a/regress/expected/list_comprehension.out
+++ b/regress/expected/list_comprehension.out
@@ -166,9 +166,9 @@ SELECT * FROM cypher('list_comprehension', $$ MATCH p=(u) RETURN [i IN range(0,
 SELECT * FROM cypher('list_comprehension', $$ MATCH (u) RETURN [i IN u.list] $$) AS (result agtype);
          result          
 -------------------------
+ [0, 2, 4, 6, 8, 10, 12]
  [1, 3, 5, 7, 9, 11, 13]
  []
- [0, 2, 4, 6, 8, 10, 12]
 (3 rows)
 
 SELECT * FROM cypher('list_comprehension', $$ MATCH (u) RETURN [i IN u.list WHERE i % 3 = 0] $$) AS (result agtype);
@@ -176,35 +176,40 @@ SELECT * FROM cypher('list_comprehension', $$ MATCH (u) RETURN [i IN u.list WHER
 ------------
  [0, 6, 12]
  [3, 9]
-(2 rows)
+ []
+(3 rows)
 
 SELECT * FROM cypher('list_comprehension', $$ MATCH (u) RETURN [i IN u.list WHERE i % 3 = 0 | i/3] $$) AS (result agtype);
   result   
 -----------
  [0, 2, 4]
  [1, 3]
-(2 rows)
+ []
+(3 rows)
 
 SELECT * FROM cypher('list_comprehension', $$ MATCH (u) RETURN [i IN u.list WHERE i % 3 = 0 | i/3][1] $$) AS (result agtype);
  result 
 --------
  2
  3
-(2 rows)
+ 
+(3 rows)
 
 SELECT * FROM cypher('list_comprehension', $$ MATCH (u) RETURN [i IN u.list WHERE i % 3 = 0 | i/3][0..2] $$) AS (result agtype);
  result 
 --------
  [0, 2]
  [1, 3]
-(2 rows)
+ []
+(3 rows)
 
 SELECT * FROM cypher('list_comprehension', $$ MATCH (u) RETURN [i IN u.list WHERE i % 3 = 0 | i/3][0..2][1] $$) AS (result agtype);
  result 
 --------
  2
  3
-(2 rows)
+ 
+(3 rows)
 
 -- Nested cases
 SELECT * FROM cypher('list_comprehension', $$ RETURN [i IN [i IN [1,2,3]]] $$) AS (result agtype);
@@ -299,9 +304,9 @@ SELECT * FROM cypher('list_comprehension', $$ MATCH (u) WHERE u.list@>[i IN rang
 SELECT * FROM cypher('list_comprehension', $$ MATCH (u) MATCH (v) WHERE v.list=[i IN u.list] RETURN v $$) AS (result agtype);
                                             result                                             
 -----------------------------------------------------------------------------------------------
- {"id": 281474976710659, "label": "", "properties": {"list": []}}::vertex
  {"id": 281474976710657, "label": "", "properties": {"list": [0, 2, 4, 6, 8, 10, 12]}}::vertex
  {"id": 281474976710658, "label": "", "properties": {"list": [1, 3, 5, 7, 9, 11, 13]}}::vertex
+ {"id": 281474976710659, "label": "", "properties": {"list": []}}::vertex
 (3 rows)
 
 SELECT * FROM cypher('list_comprehension', $$ MATCH (u {list:[i IN range(0,12,2)]}) RETURN u $$) AS (result agtype);
@@ -330,11 +335,11 @@ SELECT * FROM cypher('list_comprehension', $$ MATCH (u {list:[i IN range(1,13,2)
 SELECT * FROM cypher('list_comprehension', $$ MATCH (u) MATCH (v {list:[i IN u.list]}) RETURN v $$) AS (result agtype);
                                             result                                             
 -----------------------------------------------------------------------------------------------
- {"id": 281474976710658, "label": "", "properties": {"list": [1, 3, 5, 7, 9, 11, 13]}}::vertex
  {"id": 281474976710657, "label": "", "properties": {"list": [0, 2, 4, 6, 8, 10, 12]}}::vertex
- {"id": 281474976710659, "label": "", "properties": {"list": []}}::vertex
+ {"id": 281474976710658, "label": "", "properties": {"list": [1, 3, 5, 7, 9, 11, 13]}}::vertex
  {"id": 281474976710657, "label": "", "properties": {"list": [0, 2, 4, 6, 8, 10, 12]}}::vertex
  {"id": 281474976710658, "label": "", "properties": {"list": [1, 3, 5, 7, 9, 11, 13]}}::vertex
+ {"id": 281474976710659, "label": "", "properties": {"list": []}}::vertex
 (5 rows)
 
 SELECT * FROM cypher('list_comprehension', $$ CREATE (u {list:[i IN range(12,24,2)]}) RETURN u $$) AS (result agtype);
@@ -371,26 +376,26 @@ SELECT * FROM cypher('list_comprehension', $$ MATCH(u) WITH u WHERE u.list = [u
 SELECT * FROM cypher('list_comprehension', $$ MATCH(u) WITH u WHERE u.list = [u IN [0, 2, 4, 6, 8, 10, 12]] OR size(u.list) = 0 RETURN u $$) AS (u agtype);
                                                u                                               
 -----------------------------------------------------------------------------------------------
- {"id": 281474976710659, "label": "", "properties": {"list": []}}::vertex
  {"id": 281474976710657, "label": "", "properties": {"list": [0, 2, 4, 6, 8, 10, 12]}}::vertex
+ {"id": 281474976710659, "label": "", "properties": {"list": []}}::vertex
 (2 rows)
 
 SELECT * FROM cypher('list_comprehension', $$ MATCH(u) WITH u WHERE u.list = [u IN [0, 2, 4, 6, 8, 10, 12]] OR size(u.list)::bool RETURN u $$) AS (u agtype);
                                                    u                                                    
 --------------------------------------------------------------------------------------------------------
+ {"id": 281474976710657, "label": "", "properties": {"list": [0, 2, 4, 6, 8, 10, 12]}}::vertex
  {"id": 281474976710658, "label": "", "properties": {"list": [1, 3, 5, 7, 9, 11, 13]}}::vertex
- {"id": 281474976710661, "label": "", "properties": {"list": [6, 8, 10, 12]}}::vertex
  {"id": 281474976710660, "label": "", "properties": {"list": [12, 14, 16, 18, 20, 22, 24]}}::vertex
+ {"id": 281474976710661, "label": "", "properties": {"list": [6, 8, 10, 12]}}::vertex
  {"id": 281474976710662, "label": "", "properties": {"list": [25.0, 49.0, 81.0, 121.0, 169.0]}}::vertex
- {"id": 281474976710657, "label": "", "properties": {"list": [0, 2, 4, 6, 8, 10, 12]}}::vertex
  {"id": 281474976710663, "label": "", "properties": {"list": [1, 2, 3]}}::vertex
 (6 rows)
 
 SELECT * FROM cypher('list_comprehension', $$ MATCH(u) WITH u WHERE u.list = [u IN [0, 2, 4, 6, 8, 10, 12]] OR NOT size(u.list)::bool RETURN u $$) AS (u agtype);
                                                u                                               
 -----------------------------------------------------------------------------------------------
- {"id": 281474976710659, "label": "", "properties": {"list": []}}::vertex
  {"id": 281474976710657, "label": "", "properties": {"list": [0, 2, 4, 6, 8, 10, 12]}}::vertex
+ {"id": 281474976710659, "label": "", "properties": {"list": []}}::vertex
 (2 rows)
 
 SELECT * FROM cypher('list_comprehension', $$ CREATE(u:csm_match {list: ['abc', 'def', 'ghi']}) $$) AS (u agtype);
@@ -473,9 +478,6 @@ SELECT * FROM cypher('list_comprehension', $$ MATCH(u {list: [0, 2, 4, 6, 8, 10,
 ERROR:  Invalid use of aggregation in this context
 LINE 1: ..., $$ MATCH(u {list: [0, 2, 4, 6, 8, 10, 12]}) SET u.c = coll...
                                                              ^
--- Known issue
-SELECT * FROM cypher('list_comprehension', $$ MERGE (u {list:[i IN [1,2,3]]}) RETURN u $$) AS (result agtype);
-ERROR:  Aggref found in non-Agg plan node
 -- List comprehension variable scoping
 SELECT * FROM cypher('list_comprehension', $$ WITH 1 AS m, [m IN [1, 2, 3]] AS n RETURN [m IN [1, 2, 3]] $$) AS (result agtype);
   result   
@@ -622,12 +624,12 @@ SELECT * FROM cypher('list_comprehension', $$ MATCH (u) WHERE u.list=[i IN u.lis
                                                                             result                                                                             
 ---------------------------------------------------------------------------------------------------------------------------------------------------------------
  {"id": 281474976710658, "label": "", "properties": {"list": [1, 3, 5, 7, 9, 11, 13]}}::vertex
+ {"id": 281474976710659, "label": "", "properties": {"list": []}}::vertex
  {"id": 281474976710660, "label": "", "properties": {"list": [12, 14, 16, 18, 20, 22, 24]}}::vertex
+ {"id": 281474976710661, "label": "", "properties": {"list": [6, 8, 10, 12]}}::vertex
  {"id": 281474976710662, "label": "", "properties": {"list": [25.0, 49.0, 81.0, 121.0, 169.0]}}::vertex
- {"id": 281474976710657, "label": "", "properties": {"a": [], "b": [0, 1, 2, 3, 4, 5], "c": [0, 2, 4, 6, 8, 10, 12], "list": [0, 2, 4, 6, 8, 10, 12]}}::vertex
  {"id": 281474976710663, "label": "", "properties": {"list": [1, 2, 3]}}::vertex
- {"id": 281474976710659, "label": "", "properties": {"list": []}}::vertex
- {"id": 281474976710661, "label": "", "properties": {"list": [6, 8, 10, 12]}}::vertex
+ {"id": 281474976710657, "label": "", "properties": {"a": [], "b": [0, 1, 2, 3, 4, 5], "c": [0, 2, 4, 6, 8, 10, 12], "list": [0, 2, 4, 6, 8, 10, 12]}}::vertex
  {"id": 844424930131969, "label": "csm_match", "properties": {"list": ["abc", "def", "ghi"]}}::vertex
 (8 rows)
 
@@ -635,11 +637,12 @@ SELECT * FROM cypher('list_comprehension', $$ MATCH (u) WHERE u.list=[i IN u.lis
                                                  result                                                 
 --------------------------------------------------------------------------------------------------------
  {"id": 281474976710658, "label": "", "properties": {"list": [1, 3, 5, 7, 9, 11, 13]}}::vertex
+ {"id": 281474976710659, "label": "", "properties": {"list": []}}::vertex
  {"id": 281474976710660, "label": "", "properties": {"list": [12, 14, 16, 18, 20, 22, 24]}}::vertex
+ {"id": 281474976710661, "label": "", "properties": {"list": [6, 8, 10, 12]}}::vertex
  {"id": 281474976710662, "label": "", "properties": {"list": [25.0, 49.0, 81.0, 121.0, 169.0]}}::vertex
  {"id": 281474976710663, "label": "", "properties": {"list": [1, 2, 3]}}::vertex
- {"id": 281474976710661, "label": "", "properties": {"list": [6, 8, 10, 12]}}::vertex
-(5 rows)
+(6 rows)
 
 SELECT * FROM cypher('list_comprehension', $$ MATCH (u) WHERE size([e in u.list where e starts with "a"])>0 RETURN u $$) AS (result agtype);
                                                 result                                                
@@ -657,11 +660,64 @@ SELECT * FROM cypher('list_comprehension', $$ MATCH (u ={list:[i IN u.list WHERE
                                                  result                                                 
 --------------------------------------------------------------------------------------------------------
  {"id": 281474976710658, "label": "", "properties": {"list": [1, 3, 5, 7, 9, 11, 13]}}::vertex
+ {"id": 281474976710659, "label": "", "properties": {"list": []}}::vertex
  {"id": 281474976710660, "label": "", "properties": {"list": [12, 14, 16, 18, 20, 22, 24]}}::vertex
+ {"id": 281474976710661, "label": "", "properties": {"list": [6, 8, 10, 12]}}::vertex
  {"id": 281474976710662, "label": "", "properties": {"list": [25.0, 49.0, 81.0, 121.0, 169.0]}}::vertex
  {"id": 281474976710663, "label": "", "properties": {"list": [1, 2, 3]}}::vertex
- {"id": 281474976710661, "label": "", "properties": {"list": [6, 8, 10, 12]}}::vertex
-(5 rows)
+(6 rows)
+
+-- List comprehension in MERGE
+SELECT * FROM cypher('list_comprehension', $$ MERGE (u {list:[i IN [1,2,3]]}) RETURN u $$) AS (result agtype);
+                                     result                                      
+---------------------------------------------------------------------------------
+ {"id": 281474976710663, "label": "", "properties": {"list": [1, 2, 3]}}::vertex
+(1 row)
+
+SELECT * FROM cypher('list_comprehension', $$ MERGE (u ={list:[i IN [1,2,3] WHERE i>1]}) RETURN u $$) AS (result agtype);
+                                    result                                    
+------------------------------------------------------------------------------
+ {"id": 281474976710666, "label": "", "properties": {"list": [2, 3]}}::vertex
+(1 row)
+
+SELECT * FROM cypher('list_comprehension', $$ MERGE (u ={list:[i IN [1,2,3] WHERE i>1 | i^2]}) RETURN u $$) AS (result agtype);
+                                      result                                      
+----------------------------------------------------------------------------------
+ {"id": 281474976710667, "label": "", "properties": {"list": [4.0, 9.0]}}::vertex
+(1 row)
+
+-- Issue 1850
+SELECT * FROM cypher('list_comprehension', $$ CREATE (u {list: [0, 2, 4, 6, 8, 10, 12]}) $$) AS (result agtype);
+ result 
+--------
+(0 rows)
+
+SELECT * FROM cypher('list_comprehension', $$ WITH [1, 2, 3] AS u UNWIND collect(u) AS v RETURN v $$) AS (result agtype);
+ERROR:  Invalid use of aggregation in this context
+LINE 1: ...ist_comprehension', $$ WITH [1, 2, 3] AS u UNWIND collect(u)...
+                                                             ^
+SELECT * FROM cypher('list_comprehension', $$ MATCH (u {list: [0, 2, 4, 6, 8, 10, 12]}) WITH u, collect(u.list) AS v SET u += {b: [u IN range(0, 5)]} SET u.c = [u IN v[0]] RETURN u $$) AS (result agtype);
+                                                                            result                                                                             
+---------------------------------------------------------------------------------------------------------------------------------------------------------------
+ {"id": 281474976710657, "label": "", "properties": {"a": [], "b": [0, 1, 2, 3, 4, 5], "c": [0, 2, 4, 6, 8, 10, 12], "list": [0, 2, 4, 6, 8, 10, 12]}}::vertex
+ {"id": 281474976710668, "label": "", "properties": {"b": [0, 1, 2, 3, 4, 5], "c": [0, 2, 4, 6, 8, 10, 12], "list": [0, 2, 4, 6, 8, 10, 12]}}::vertex
+(2 rows)
+
+SELECT * FROM cypher('list_comprehension', $$ MATCH (u {list: [0, 2, 4, 6, 8, 10, 12]}) SET u.c = collect(u.list) RETURN u $$) AS (u agtype);
+ERROR:  Invalid use of aggregation in this context
+LINE 1: ... $$ MATCH (u {list: [0, 2, 4, 6, 8, 10, 12]}) SET u.c = coll...
+                                                             ^
+SELECT * FROM cypher('list_comprehension', $$ MATCH (u {list: [0, 2, 4, 6, 8, 10, 12]}) WHERE u.list = [u IN [1, u]] RETURN u $$) AS (u agtype);
+ u 
+---
+(0 rows)
+
+SELECT * FROM cypher('list_comprehension', $$ MATCH (u {list: [0, 2, 4, 6, 8, 10, 12]}) WHERE u.list IN [u IN [1, u.list]] RETURN u $$) AS (u agtype);
+                                                                               u                                                                               
+---------------------------------------------------------------------------------------------------------------------------------------------------------------
+ {"id": 281474976710657, "label": "", "properties": {"a": [], "b": [0, 1, 2, 3, 4, 5], "c": [0, 2, 4, 6, 8, 10, 12], "list": [0, 2, 4, 6, 8, 10, 12]}}::vertex
+ {"id": 281474976710668, "label": "", "properties": {"b": [0, 1, 2, 3, 4, 5], "c": [0, 2, 4, 6, 8, 10, 12], "list": [0, 2, 4, 6, 8, 10, 12]}}::vertex
+(2 rows)
 
 -- Clean up
 SELECT * FROM drop_graph('list_comprehension', true);
diff --git a/regress/sql/list_comprehension.sql b/regress/sql/list_comprehension.sql
index cb941a61..572b2e6b 100644
--- a/regress/sql/list_comprehension.sql
+++ b/regress/sql/list_comprehension.sql
@@ -117,9 +117,6 @@ SELECT * FROM cypher('list_comprehension', $$ MATCH(u {list: [0, 2, 4, 6, 8, 10,
 -- invalid use of aggregation in SET
 SELECT * FROM cypher('list_comprehension', $$ MATCH(u {list: [0, 2, 4, 6, 8, 10, 12]}) SET u.c = collect(u.list) RETURN u $$) AS (u agtype);
 
--- Known issue
-SELECT * FROM cypher('list_comprehension', $$ MERGE (u {list:[i IN [1,2,3]]}) RETURN u $$) AS (result agtype);
-
 -- List comprehension variable scoping
 SELECT * FROM cypher('list_comprehension', $$ WITH 1 AS m, [m IN [1, 2, 3]] AS n RETURN [m IN [1, 2, 3]] $$) AS (result agtype);
 SELECT * FROM cypher('list_comprehension', $$ WITH 1 AS m RETURN [m IN [1, 2, 3]], m $$) AS (result agtype, result2 agtype);
@@ -164,5 +161,18 @@ SELECT * FROM cypher('list_comprehension', $$ MATCH (u) WHERE size([e in u.list
 SELECT * FROM cypher('list_comprehension', $$ MATCH (u ={list:[i IN u.list | i+1]}) RETURN u $$) AS (result agtype);
 SELECT * FROM cypher('list_comprehension', $$ MATCH (u ={list:[i IN u.list WHERE i>0]}) RETURN u$$) AS (result agtype);
 
+-- List comprehension in MERGE
+SELECT * FROM cypher('list_comprehension', $$ MERGE (u {list:[i IN [1,2,3]]}) RETURN u $$) AS (result agtype);
+SELECT * FROM cypher('list_comprehension', $$ MERGE (u ={list:[i IN [1,2,3] WHERE i>1]}) RETURN u $$) AS (result agtype);
+SELECT * FROM cypher('list_comprehension', $$ MERGE (u ={list:[i IN [1,2,3] WHERE i>1 | i^2]}) RETURN u $$) AS (result agtype);
+
+-- Issue 1850
+SELECT * FROM cypher('list_comprehension', $$ CREATE (u {list: [0, 2, 4, 6, 8, 10, 12]}) $$) AS (result agtype);
+SELECT * FROM cypher('list_comprehension', $$ WITH [1, 2, 3] AS u UNWIND collect(u) AS v RETURN v $$) AS (result agtype);
+SELECT * FROM cypher('list_comprehension', $$ MATCH (u {list: [0, 2, 4, 6, 8, 10, 12]}) WITH u, collect(u.list) AS v SET u += {b: [u IN range(0, 5)]} SET u.c = [u IN v[0]] RETURN u $$) AS (result agtype);
+SELECT * FROM cypher('list_comprehension', $$ MATCH (u {list: [0, 2, 4, 6, 8, 10, 12]}) SET u.c = collect(u.list) RETURN u $$) AS (u agtype);
+SELECT * FROM cypher('list_comprehension', $$ MATCH (u {list: [0, 2, 4, 6, 8, 10, 12]}) WHERE u.list = [u IN [1, u]] RETURN u $$) AS (u agtype);
+SELECT * FROM cypher('list_comprehension', $$ MATCH (u {list: [0, 2, 4, 6, 8, 10, 12]}) WHERE u.list IN [u IN [1, u.list]] RETURN u $$) AS (u agtype);
+
 -- Clean up
 SELECT * FROM drop_graph('list_comprehension', true);
\ No newline at end of file
diff --git a/sql/agtype_coercions.sql b/sql/agtype_coercions.sql
index bdc33af8..c7895fab 100644
--- a/sql/agtype_coercions.sql
+++ b/sql/agtype_coercions.sql
@@ -173,3 +173,14 @@ AS 'MODULE_PATHNAME';
 
 CREATE CAST (agtype AS json)
     WITH FUNCTION ag_catalog.agtype_to_json(agtype);
+
+CREATE FUNCTION ag_catalog.agtype_array_to_agtype(agtype[])
+    RETURNS agtype
+    LANGUAGE c
+    IMMUTABLE
+RETURNS NULL ON NULL INPUT
+PARALLEL SAFE
+AS 'MODULE_PATHNAME';
+
+CREATE CAST (agtype[] AS agtype)
+    WITH FUNCTION ag_catalog.agtype_array_to_agtype(agtype[]);
diff --git a/sql/agtype_typecast.sql b/sql/agtype_typecast.sql
index aa551407..c29c0a65 100644
--- a/sql/agtype_typecast.sql
+++ b/sql/agtype_typecast.sql
@@ -181,8 +181,7 @@ CREATE FUNCTION ag_catalog.age_range(variadic "any")
 PARALLEL SAFE
 AS 'MODULE_PATHNAME';
 
-CREATE FUNCTION ag_catalog.age_unnest(agtype,
-                                      list_comprehension boolean = false)
+CREATE FUNCTION ag_catalog.age_unnest(agtype)
     RETURNS SETOF agtype
 LANGUAGE c
 IMMUTABLE
diff --git a/src/backend/nodes/ag_nodes.c b/src/backend/nodes/ag_nodes.c
index e20670b2..9cab568d 100644
--- a/src/backend/nodes/ag_nodes.c
+++ b/src/backend/nodes/ag_nodes.c
@@ -48,6 +48,7 @@ const char *node_names[] = {
     "cypher_map_projection",
     "cypher_map_projection_element",
     "cypher_list",
+    "cypher_list_comprehension",
     "cypher_comparison_aexpr",
     "cypher_comparison_boolexpr",
     "cypher_string_match",
@@ -115,6 +116,7 @@ const ExtensibleNodeMethods node_methods[] = {
     DEFINE_NODE_METHODS(cypher_map),
     DEFINE_NODE_METHODS(cypher_map_projection),
     DEFINE_NODE_METHODS(cypher_list),
+    DEFINE_NODE_METHODS(cypher_list_comprehension),
     DEFINE_NODE_METHODS(cypher_comparison_aexpr),
     DEFINE_NODE_METHODS(cypher_comparison_boolexpr),
     DEFINE_NODE_METHODS(cypher_string_match),
diff --git a/src/backend/nodes/cypher_outfuncs.c b/src/backend/nodes/cypher_outfuncs.c
index ea3d3f98..7aebbc31 100644
--- a/src/backend/nodes/cypher_outfuncs.c
+++ b/src/backend/nodes/cypher_outfuncs.c
@@ -116,7 +116,6 @@ void out_cypher_with(StringInfo str, const ExtensibleNode *node)
     DEFINE_AG_NODE(cypher_with);
 
     WRITE_BOOL_FIELD(distinct);
-    WRITE_BOOL_FIELD(subquery_intermediate);
     WRITE_NODE_FIELD(items);
     WRITE_NODE_FIELD(order_by);
     WRITE_NODE_FIELD(skip);
@@ -175,9 +174,20 @@ void out_cypher_unwind(StringInfo str, const ExtensibleNode *node)
     DEFINE_AG_NODE(cypher_unwind);
 
     WRITE_NODE_FIELD(target);
-    WRITE_NODE_FIELD(collect);
 }
 
+/* serialization function for the cypher_list_comprehension ExtensibleNode. */
+void out_cypher_list_comprehension(StringInfo str, const ExtensibleNode *node)
+{
+    DEFINE_AG_NODE(cypher_list_comprehension);
+
+    WRITE_STRING_FIELD(varname);
+    WRITE_NODE_FIELD(expr);
+    WRITE_NODE_FIELD(where);
+    WRITE_NODE_FIELD(mapping_expr);
+}
+
+
 /* serialization function for the cypher_delete ExtensibleNode. */
 void out_cypher_merge(StringInfo str, const ExtensibleNode *node)
 {
diff --git a/src/backend/optimizer/cypher_pathnode.c b/src/backend/optimizer/cypher_pathnode.c
index a08cd30e..5e434425 100644
--- a/src/backend/optimizer/cypher_pathnode.c
+++ b/src/backend/optimizer/cypher_pathnode.c
@@ -23,6 +23,14 @@
 
 #include "optimizer/cypher_createplan.h"
 #include "optimizer/cypher_pathnode.h"
+#include "parser/cypher_analyze.h"
+#include "executor/cypher_utils.h"
+#include "optimizer/subselect.h"
+#include "nodes/makefuncs.h"
+
+static Const *convert_sublink_to_subplan(PlannerInfo *root,
+                                         List *custom_private);
+static bool expr_has_sublink(Node *node, void *context);
 
 const CustomPathMethods cypher_create_path_methods = {
     CREATE_PATH_NAME, plan_cypher_create_path, NULL};
@@ -183,10 +191,80 @@ CustomPath *create_cypher_merge_path(PlannerInfo *root, RelOptInfo *rel,
 
     /* Make the original paths the children of the new path */
     cp->custom_paths = rel->pathlist;
-    /* Store the metadata Delete will need in the execution phase. */
-    cp->custom_private = custom_private;
+
+    /*
+     * Store the metadata Merge will need in the execution phase.
+     * We may have a sublink here in case the user used a list
+     * comprehension in merge.
+     */
+    if (rel->subroot->parse->hasSubLinks)
+    {
+        cp->custom_private = list_make1(convert_sublink_to_subplan(root, custom_private));
+    }
+    else
+    {
+        cp->custom_private = custom_private;
+    }
+
     /* Tells Postgres how to turn this path to the correct CustomScan */
     cp->methods = &cypher_merge_path_methods;
 
     return cp;
 }
+
+/*
+ * Deserializes the merge information and checks if any property
+ * expression (prop_expr) contains a SubLink.
+ * If found, converts the SubLink to a SubPlan, updates the
+ * structure accordingly, and serializes it back.
+ */
+static Const *convert_sublink_to_subplan(PlannerInfo *root, List *custom_private)
+{
+    cypher_merge_information *merge_information;
+    char *serialized_data = NULL;
+    Const *c = NULL;
+    ListCell *lc = NULL;
+    StringInfo str = makeStringInfo();
+
+    c = linitial(custom_private);
+    serialized_data = (char *)c->constvalue;
+    merge_information = stringToNode(serialized_data);
+
+    Assert(is_ag_node(merge_information, cypher_merge_information));
+
+    /* Only part where we can expect a sublink is in prop_expr. */
+    foreach (lc, merge_information->path->target_nodes)
+    {
+        cypher_target_node *node = (cypher_target_node *)lfirst(lc);
+        Node *prop_expr = (Node *) node->prop_expr;
+
+        if (expr_has_sublink(prop_expr, NULL))
+        {
+            node->prop_expr = (Expr *) SS_process_sublinks(root, prop_expr, false);
+        }
+    }
+
+    /* Serialize the information again and return it. */
+    outNode(str, (Node *)merge_information);
+
+    return makeConst(INTERNALOID, -1, InvalidOid, str->len,
+                     PointerGetDatum(str->data), false, false);
+}
+
+/*
+ * Helper function to check if the node has a sublink.
+ */
+static bool expr_has_sublink(Node *node, void *context)
+{
+    if (node == NULL)
+    {
+        return false;
+    }
+
+    if (IsA(node, SubLink))
+    {
+        return true;
+    }
+
+    return cypher_expr_tree_walker(node, expr_has_sublink, context);
+}
diff --git a/src/backend/parser/cypher_analyze.c b/src/backend/parser/cypher_analyze.c
index 6390b342..14f8547f 100644
--- a/src/backend/parser/cypher_analyze.c
+++ b/src/backend/parser/cypher_analyze.c
@@ -67,12 +67,6 @@ static Query *analyze_cypher_and_coerce(List *stmt, RangeTblFunction *rtfunc,
                                         const char *query_str, int query_loc,
                                         char *graph_name, uint32 graph_oid,
                                         Param *params);
-cypher_clause *build_subquery_node(cypher_clause *next);
-
-/* expr tree walker */
-bool expr_contains_node(cypher_expression_condition is_expr, Node *expr);
-bool expr_has_subquery(Node * expr);
-
 
 void post_parse_analyze_init(void)
 {
@@ -681,148 +675,40 @@ static int get_query_location(const int location, const char *source_str)
     return strchr(p + 1, '$') - source_str + 1;
 }
 
+
 /*
- * This is a specialized expression tree walker for finding exprs of a specified
- * type. Create a function that checks for the type you want, and this function
- * will iterate through the tree.
+ * This is an extension of postgres's raw_expression_tree_walker() function.
+ * It can walk cypher-specific nodes found in the expression tree during
+ * parse analysis.
+ * 
+ * More nodes can be added to this function as needed.
  */
-
-bool expr_contains_node(cypher_expression_condition is_expr, Node *expr)
+bool cypher_raw_expr_tree_walker(Node *node,
+                                 bool (*walker)(),
+                                 void *context)
 {
-    if (!expr)
-    {
-        return false;
-    }
-
-    switch (nodeTag(expr))
-    {
-    case T_A_Const:
-    case T_ColumnRef:
-    case T_A_Indirection:
-        {
-            break;
-        }
-    case T_A_Expr:
-    {
-        A_Expr *a = (A_Expr *)expr;
-
-        switch (a->kind)
-        {
-        case AEXPR_OP:
-        case AEXPR_IN:
-            {
-                if (expr_contains_node(is_expr, a->lexpr) ||
-                    expr_contains_node(is_expr, a->rexpr))
-                {
-                    return true;
-                }
-                break;
-            }
-        default:
-            ereport(ERROR, (errmsg_internal("unrecognized A_Expr kind: %d",
-                                            a->kind)));
-        }
-        break;
-    }
-    case T_BoolExpr:
-    {
-        BoolExpr *b = (BoolExpr *)expr;
-        ListCell *la;
-
-        foreach(la, b->args)
-        {
-            Node *arg = lfirst(la);
-
-            if (expr_contains_node(is_expr, arg))
-            {
-                return true;
-            }
-        }
-        break;
-    }
-    case T_NullTest:
-    {
-        NullTest *n = (NullTest *)expr;
-
-        if (expr_contains_node(is_expr, (Node *)n->arg))
-        {
-            return true;
-        }
-        break;
-    }
-    case T_CaseExpr:
-    {
-        CaseExpr *cexpr = (CaseExpr *)expr;
-        ListCell   *l;
-
-        if (cexpr->arg && expr_contains_node(is_expr, (Node *) cexpr->arg))
-        {
-            return true;
-        }
-
-        foreach(l, cexpr->args)
-        {
-            CaseWhen *w = lfirst_node(CaseWhen, l);
-            Node *warg;
-
-            warg = (Node *) w->expr;
-
-            if (expr_contains_node(is_expr, warg))
-            {
-                return true;
-            }
-            warg = (Node *)w->result;
-
-            if (expr_contains_node(is_expr, warg))
-            {
-                return true;
-            }
-        }
-
-        if (expr_contains_node(is_expr , (Node *)cexpr->defresult))
-        {
-            return true;
-        }
+    ListCell *temp;
 
-        break;
-    }
-    case T_CaseTestExpr:
-    {
-        break;
-    }
-    case T_CoalesceExpr:
-    {
-        CoalesceExpr *cexpr = (CoalesceExpr *) expr;
-        ListCell *args;
+    if (node == NULL)
+        return false;
 
-        foreach(args, cexpr->args)
-        {
-            Node *e = (Node *)lfirst(args);
+#define WALK(n) walker((Node *) (n), context)
+#define LIST_WALK(l) cypher_raw_expr_tree_walker((Node *) (l), walker, context)
 
-            if (expr_contains_node(is_expr, e))
-            {
-                return true;
-            }
-        }
-        break;
-    }
-    case T_ExtensibleNode:
+    if (IsA(node, ExtensibleNode))
     {
-        if (is_ag_node(expr, cypher_bool_const))
+        if (is_ag_node(node, cypher_bool_const) ||
+            is_ag_node(node, cypher_integer_const) ||
+            is_ag_node(node, cypher_param) ||
+            is_ag_node(node, cypher_sub_pattern) ||
+            is_ag_node(node, cypher_sub_query))
+            /* Add more non-recursible node types here as needed */
         {
-            return is_expr(expr);
-        }
-        if (is_ag_node(expr, cypher_integer_const))
-        {
-            return is_expr(expr);
-        }
-        if (is_ag_node(expr, cypher_param))
-        {
-            return is_expr(expr);
+            return false;
         }
-        if (is_ag_node(expr, cypher_map))
+        else if (is_ag_node(node, cypher_map))
         {
-            cypher_map *cm = (cypher_map *)expr;
+            cypher_map *cm = (cypher_map *)node;
             ListCell *le;
 
             Assert(list_length(cm->keyvals) % 2 == 0);
@@ -837,7 +723,7 @@ bool expr_contains_node(cypher_expression_condition is_expr, Node *expr)
 
                 val = lfirst(le);
 
-                if (expr_contains_node(is_expr, val))
+                if (WALK(val))
                 {
                     return true;
                 }
@@ -845,216 +731,163 @@ bool expr_contains_node(cypher_expression_condition is_expr, Node *expr)
                 le = lnext(cm->keyvals, le);
 
             }
-            break;
         }
-        if (is_ag_node(expr, cypher_map_projection))
+        else if (is_ag_node(node, cypher_map_projection))
         {
-            cypher_map_projection *cmp = (cypher_map_projection *)expr;
-            ListCell *lc;
+            cypher_map_projection *cmp = (cypher_map_projection *)node;
 
-            foreach(lc, cmp->map_elements)
+            if (LIST_WALK(cmp->map_elements))
             {
-                cypher_map_projection_element *elem;
-
-                elem = lfirst(lc);
-
-                if (expr_contains_node(is_expr, elem->value))
-                {
-                    return true;
-                }
+                return true;
             }
-
-            break;
         }
-        if (is_ag_node(expr, cypher_list))
+        else if (is_ag_node(node, cypher_list))
         {
-            cypher_list *cl = (cypher_list *)expr;
-            ListCell *le = NULL;
-
-            foreach(le, cl->elems)
+            cypher_list *cl = (cypher_list *)node;
+            
+            if (LIST_WALK(cl->elems))
             {
-                Node *texpr = lfirst(le);
-
-                if (expr_contains_node(is_expr, texpr))
-                {
-                    return true;
-                }
+                return true;
             }
-            break;
         }
-        if (is_ag_node(expr, cypher_string_match))
+        else if (is_ag_node(node, cypher_string_match))
         {
-            cypher_string_match *csm = (cypher_string_match *)expr;
+            cypher_string_match *csm = (cypher_string_match *)node;
 
-            if (expr_contains_node(is_expr, csm->lhs) ||
-                expr_contains_node(is_expr, csm->rhs))
+            if (WALK(csm->lhs))
             {
                 return true;
             }
-            break;
-        }
-        if (is_ag_node(expr, cypher_typecast))
-        {
-            cypher_typecast *t = (cypher_typecast *) expr;
 
-            if (expr_contains_node(is_expr, t->expr))
+            if (WALK(csm->rhs))
             {
                 return true;
             }
-            break;
         }
-        if (is_ag_node(expr, cypher_comparison_aexpr))
+        else if (is_ag_node(node, cypher_typecast))
         {
-            cypher_comparison_aexpr *a = (cypher_comparison_aexpr *) expr;
+            cypher_typecast *t = (cypher_typecast *)node;
 
-            if (expr_contains_node(is_expr, a->lexpr) ||
-                expr_contains_node(is_expr, a->rexpr))
+            if (WALK(t->expr))
             {
                 return true;
             }
-            break;
         }
-        if (is_ag_node(expr, cypher_comparison_boolexpr))
+        else if (is_ag_node(node, cypher_comparison_aexpr))
         {
-            cypher_comparison_boolexpr *b = (cypher_comparison_boolexpr *) expr;
-            ListCell *la;
+            cypher_comparison_aexpr *a = (cypher_comparison_aexpr *)node;
 
-            foreach(la, b->args)
+            if (WALK(a->lexpr))
             {
-                Node *arg = lfirst(la);
+                return true;
+            }
 
-                if (expr_contains_node(is_expr, arg))
-                {
-                    return true;
-                }
+            if (WALK(a->rexpr))
+            {
+                return true;
             }
-            break;
         }
-        if (is_ag_node(expr, cypher_unwind))
+        else if (is_ag_node(node, cypher_comparison_boolexpr))
         {
-            cypher_unwind* lc = (cypher_unwind *)expr;
-
-            if (expr_contains_node(is_expr, lc->where) ||
-                expr_contains_node(is_expr, lc->collect))
+            cypher_comparison_boolexpr *b = (cypher_comparison_boolexpr *)node;
+            
+            if (LIST_WALK(b->args))
             {
                 return true;
             }
-            break;
         }
-
-        if (is_ag_node(expr, cypher_sub_pattern))
+        else if (is_ag_node(node, cypher_unwind))
         {
-            break;
-        }
+            cypher_unwind *unw = (cypher_unwind *)node;
 
-        if (is_ag_node(expr, cypher_sub_query))
-        {
-            break;
+            if (WALK(unw->target))
+            {
+                return true;
+            }
         }
 
-        ereport(ERROR,
-                (errmsg_internal("unrecognized ExtensibleNode: %s",
-                                 ((ExtensibleNode *)expr)->extnodename)));
-
-        break;
-    }
-    case T_FuncCall:
-    {
-        FuncCall *fn = (FuncCall *)expr;
-        ListCell *arg;
-
-        foreach(arg, fn->args)
+        else if (is_ag_node(node, cypher_list_comprehension))
         {
-            Node *farg = NULL;
+            cypher_list_comprehension *lc = (cypher_list_comprehension *)node;
 
-            farg = (Node *)lfirst(arg);
+            if (WALK(lc->expr))
+            {
+                return true;
+            }
+
+            if (WALK(lc->where))
+            {
+                return true;
+            }
 
-            if (expr_contains_node(is_expr, farg))
+            if (WALK(lc->mapping_expr))
             {
                 return true;
             }
         }
-        break;
+        /* Add more node types here as needed */
+        else
+        {
+            ereport(ERROR,
+                (errmsg_internal("unrecognized ExtensibleNode: %s",
+                                 ((ExtensibleNode *)node)->extnodename)));
+        }
     }
-    case T_SubLink:
+    /*
+     * postgres's raw expresssion tree walker does not handle List
+     */
+    else if (IsA(node, List))
     {
-       SubLink *s = (SubLink *)expr;
-
-        if (expr_contains_node(is_expr, s->subselect))
+        foreach(temp, (List *) node)
         {
-            return true;
+            if (WALK((Node *) lfirst(temp)))
+                return true;
         }
-      break;
-    }
-    default:
-        ereport(ERROR, (errmsg_internal("unrecognized node type: %d",
-                                        nodeTag(expr))));
     }
 
-    return (is_expr(expr));
+#undef LIST_WALK
+    else
+    {
+        return raw_expression_tree_walker(node, walker, context);
+    }
+    
+    return false;
 }
 
 /*
- * Function that checks if an expr is a cypher_sub_query. Used in tandem with
- * expr_contains_node. Can write more similar to this to find similar nodes.
+ * This is an extension of postgres's expression_tree_walker() function.
+ * It is meant to walk cypher-specific nodes found in the expression tree
+ * post parse analysis.
+ *
+ * More nodes can be added to this function as needed.
  */
-
-bool expr_has_subquery(Node * expr)
+bool cypher_expr_tree_walker(Node *node,
+                             bool (*walker)(),
+                             void *context)
 {
-    if (expr == NULL)
+    if (node == NULL)
     {
         return false;
     }
 
-    if (IsA(expr, ExtensibleNode))
-    {
-        if (is_ag_node(expr, cypher_sub_query))
-        {
-            return true;
-        }
-    }
-    return false;
-}
+#define LIST_WALK(l) cypher_expr_tree_walker((Node *) (l), walker, context)
 
-/*
- * This function constructs an intermediate WITH node for processing subqueries
- */
-cypher_clause *build_subquery_node(cypher_clause *next)
-{
-    cypher_match *match = (cypher_match *)next->self;
-    cypher_clause *where_container_clause;
-    cypher_with *with_clause = make_ag_node(cypher_with);
-    ColumnRef *cr;
-    ResTarget *rt;
-
-    /* construct the column ref star */
-    cr = makeNode(ColumnRef);
-    cr->fields = list_make1(makeNode(A_Star));
-    cr->location = exprLocation((Node *)next);
-
-    /*construct the restarget */
-    rt = makeNode(ResTarget);
-    rt->name = NULL;
-    rt->indirection = NIL;
-    rt->val = (Node *)cr;
-    rt->location = exprLocation((Node *)next);
-
-
-    /* construct the with_clause */
-    with_clause->items = list_make1(rt);
-    with_clause->where = match->where;
-    with_clause->subquery_intermediate = true;
+    if (IsA(node, ExtensibleNode))
+    {
+        /* Add our nodes that can appear post parsing stage */
 
-    /*
-     * create the where container, and set the match (next) as the
-     * prev of the where container
-     */
-    where_container_clause = palloc(sizeof(*where_container_clause));
-    where_container_clause->self = (Node *)with_clause;
-    where_container_clause->next = NULL;
-    where_container_clause->prev = next;
+        ereport(ERROR,
+                (errmsg_internal("unrecognized ExtensibleNode: %s",
+                                 ((ExtensibleNode *)node)->extnodename)));
+    }
+#undef WALK
+#undef LIST_WALK
+    else
+    {
+        return expression_tree_walker(node, walker, context);
+    }
 
-    return where_container_clause;
+    return false;
 }
 
 static Query *analyze_cypher(List *stmt, ParseState *parent_pstate,
@@ -1083,23 +916,6 @@ static Query *analyze_cypher(List *stmt, ParseState *parent_pstate,
         next->self = lfirst(lc);
         next->prev = clause;
 
-        /* check for subqueries in match */
-        if (is_ag_node(next->self, cypher_match))
-        {
-            cypher_match *match = (cypher_match *)next->self;
-
-            if (match->where != NULL && expr_contains_node(expr_has_subquery, match->where))
-            {
-               /* advance the clause iterator to the intermediate clause position */
-               clause = build_subquery_node(next);
-
-               /* set the next of the match to the where_container_clause */
-               match->where = NULL;
-               next->next = clause;
-               continue;
-            }
-        }
-
         if (clause != NULL)
         {
             clause->next = next;
diff --git a/src/backend/parser/cypher_clause.c b/src/backend/parser/cypher_clause.c
index 2495042a..22c5b823 100644
--- a/src/backend/parser/cypher_clause.c
+++ b/src/backend/parser/cypher_clause.c
@@ -248,6 +248,11 @@ static void get_res_cols(ParseState *pstate, ParseNamespaceItem *l_pnsi,
 /* unwind */
 static Query *transform_cypher_unwind(cypher_parsestate *cpstate,
                                       cypher_clause *clause);
+
+/* list comprehension */
+static Query *transform_cypher_list_comprehension(cypher_parsestate *cpstate,
+                                                  cypher_clause *clause);
+
 /* merge */
 static Query *transform_cypher_merge(cypher_parsestate *cpstate,
                                      cypher_clause *clause);
@@ -290,7 +295,7 @@ static Query *transform_cypher_call_subquery(cypher_parsestate *cpstate,
 #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)
-ParseNamespaceItem
+static ParseNamespaceItem
 *transform_cypher_clause_as_subquery(cypher_parsestate *cpstate,
                                      transform_method transform,
                                      cypher_clause *clause,
@@ -326,9 +331,9 @@ static FuncExpr *make_clause_func_expr(char *function_name,
 /* for VLE support */
 static ParseNamespaceItem *transform_RangeFunction(cypher_parsestate *cpstate,
                                                    RangeFunction *r);
-static Node *transform_VLE_Function(cypher_parsestate *cpstate, Node *n,
-                                    RangeTblEntry **top_rte, int *top_rti,
-                                    List **namespace);
+static Node *transform_from_clause_item(cypher_parsestate *cpstate, Node *n,
+                                        RangeTblEntry **top_rte, int *top_rti,
+                                        List **namespace);
 static ParseNamespaceItem *append_VLE_Func_to_FromClause(cypher_parsestate *cpstate,
                                                          Node *n);
 static void setNamespaceLateralState(List *namespace, bool lateral_only,
@@ -336,6 +341,7 @@ static void setNamespaceLateralState(List *namespace, bool lateral_only,
 static bool isa_special_VLE_case(cypher_path *path);
 
 static ParseNamespaceItem *find_pnsi(cypher_parsestate *cpstate, char *varname);
+static bool has_list_comp_or_subquery(Node *expr, void *context);
 
 /*
  * transform a cypher_clause
@@ -398,19 +404,16 @@ Query *transform_cypher_clause(cypher_parsestate *cpstate,
     }
     else if (is_ag_node(self, cypher_unwind))
     {
-        cypher_unwind *n = (cypher_unwind *) self;
-        if (n->collect != NULL)
-        {
-            cpstate->p_list_comp = true;
-        }
-        result = transform_cypher_clause_with_where(cpstate,
-                                                    transform_cypher_unwind,
-                                                    clause, n->where);
+        result = transform_cypher_unwind(cpstate, clause);
     }
     else if (is_ag_node(self, cypher_call))
     {
         result = transform_cypher_call_stmt(cpstate, clause);
     }
+    else if (is_ag_node(self, cypher_list_comprehension))
+    {
+        result = transform_cypher_list_comprehension(cpstate, clause);
+    }
     else
     {
         ereport(ERROR, (errmsg_internal("unexpected Node for cypher_clause")));
@@ -446,23 +449,6 @@ static cypher_clause *make_cypher_clause(List *stmt)
         next->self = lfirst(lc);
         next->prev = clause;
 
-        /* check for subqueries in match */
-        if (is_ag_node(next->self, cypher_match))
-        {
-            cypher_match *match = (cypher_match *)next->self;
-
-            if (match->where != NULL && expr_contains_node(expr_has_subquery, match->where))
-            {
-               /* advance the clause iterator to the intermediate clause position */
-               clause = build_subquery_node(next);
-
-               /* set the next of the match to the where_container_clause */
-               match->where = NULL;
-               next->next = clause;
-               continue;
-            }
-        }
-
         if (clause != NULL)
         {
             clause->next = next;
@@ -660,6 +646,7 @@ static Query *transform_cypher_union(cypher_parsestate *cpstate,
     qry->rtable = pstate->p_rtable;
     qry->jointree = makeFromExpr(pstate->p_joinlist, NULL);
     qry->hasAggs = pstate->p_hasAggs;
+    qry->hasSubLinks = pstate->p_hasSubLinks;
 
     assign_query_collations(pstate, qry);
 
@@ -1259,6 +1246,7 @@ static Query *transform_cypher_call_subquery(cypher_parsestate *cpstate,
     query->rtable = cpstate->pstate.p_rtable;
     query->jointree = makeFromExpr(cpstate->pstate.p_joinlist, (Node *)where_qual);
     query->hasAggs = pstate->p_hasAggs;
+    query->hasSubLinks = pstate->p_hasSubLinks;
 
     assign_query_collations(pstate, query);
 
@@ -1330,6 +1318,8 @@ static Query *transform_cypher_delete(cypher_parsestate *cpstate,
 
     query->rtable = pstate->p_rtable;
     query->jointree = makeFromExpr(pstate->p_joinlist, NULL);
+    query->hasAggs = pstate->p_hasAggs;
+    query->hasSubLinks = pstate->p_hasSubLinks;
 
     return query;
 }
@@ -1353,9 +1343,6 @@ static Query *transform_cypher_unwind(cypher_parsestate *cpstate,
     Node *funcexpr;
     TargetEntry *te;
     ParseNamespaceItem *pnsi;
-    bool is_list_comp = self->collect != NULL;
-    bool has_agg =
-        is_list_comp || has_a_cypher_list_comprehension_node(self->target->val);
 
     query = makeNode(Query);
     query->commandType = CMD_SELECT;
@@ -1384,13 +1371,14 @@ static Query *transform_cypher_unwind(cypher_parsestate *cpstate,
         ereport(ERROR,
                 (errcode(ERRCODE_DUPLICATE_ALIAS),
                         errmsg("duplicate variable \"%s\"", self->target->name),
-                        parser_errposition(pstate, target_syntax_loc)));
+                        parser_errposition((ParseState *) cpstate,
+                                           target_syntax_loc)));
     }
 
     expr = transform_cypher_expr(cpstate, self->target->val,
                                  EXPR_KIND_SELECT_TARGET);
 
-    if (!has_agg && nodeTag(expr) == T_Aggref)
+    if (nodeTag(expr) == T_Aggref)
     {
         ereport(ERROR, errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
                 errmsg("Invalid use of aggregation in this context"),
@@ -1403,12 +1391,11 @@ static Query *transform_cypher_unwind(cypher_parsestate *cpstate,
     old_expr_kind = pstate->p_expr_kind;
     pstate->p_expr_kind = EXPR_KIND_SELECT_TARGET;
     funcexpr = ParseFuncOrColumn(pstate, unwind->funcname,
-                                 list_make2(expr, makeBoolConst(is_list_comp, false)),
+                                 list_make1(expr),
                                  pstate->p_last_srf, unwind, false,
                                  target_syntax_loc);
 
     pstate->p_expr_kind = old_expr_kind;
-    pstate->p_hasAggs = has_agg;
 
     te = makeTargetEntry((Expr *) funcexpr,
                          (AttrNumber) pstate->p_next_resno++,
@@ -1419,12 +1406,94 @@ static Query *transform_cypher_unwind(cypher_parsestate *cpstate,
     query->jointree = makeFromExpr(pstate->p_joinlist, NULL);
     query->hasTargetSRFs = pstate->p_hasTargetSRFs;
     query->hasAggs = pstate->p_hasAggs;
+    query->hasSubLinks = pstate->p_hasSubLinks;
 
     assign_query_collations(pstate, query);
 
     return query;
 }
 
+/*
+ * [i IN u WHERE i<2 | i^2]
+ *
+ *   |    |    |    |    |
+ *  \|/  \|/  \|/  \|/  \|/
+ *
+ * SELECT i^2 FROM age_unnest(u) AS i WHERE i>2;
+ */
+static Query *transform_cypher_list_comprehension(cypher_parsestate *cpstate,
+                                                  cypher_clause *clause)
+{
+    Query *query;
+    RangeFunction *rf;
+    cypher_list_comprehension *list_comp = (cypher_list_comprehension *) clause->self;
+    FuncCall *func_call;
+    Node *return_expr, *qual, *n;
+    RangeTblEntry *rte = NULL;
+    int rtindex;
+    List *namespace = NULL;
+    TargetEntry *te;
+    cypher_parsestate *child_cpstate = make_cypher_parsestate(cpstate);
+    ParseState *child_pstate = (ParseState *) child_cpstate;
+
+    query = makeNode(Query);
+    query->commandType = CMD_SELECT;
+
+    func_call = makeFuncCall(list_make1(makeString("unnest")),
+                             list_make1(list_comp->expr),
+                             COERCE_SQL_SYNTAX, -1);
+
+    rf = makeNode(RangeFunction);
+    rf->lateral = false;
+    rf->ordinality = false;
+    rf->is_rowsfrom = false;
+    rf->functions = list_make1(list_make2((Node *) func_call, NIL));
+    rf->alias = makeAlias(list_comp->varname, NIL);
+    rf->coldeflist = NIL;
+
+    n = transform_from_clause_item(child_cpstate, (Node *) rf,
+                                   &rte, &rtindex, &namespace);
+    checkNameSpaceConflicts(child_pstate, child_pstate->p_namespace, namespace);
+    child_pstate->p_joinlist = lappend(child_pstate->p_joinlist, n);
+    child_pstate->p_namespace = list_concat(child_pstate->p_namespace, namespace);
+
+    /* make all namespace items unconditionally visible */
+    setNamespaceLateralState(child_pstate->p_namespace, false, true);
+
+    return_expr = transform_cypher_expr(child_cpstate, list_comp->mapping_expr,
+                                        EXPR_KIND_SELECT_TARGET);
+    
+    te = makeTargetEntry((Expr *) return_expr,
+                         (AttrNumber) child_pstate->p_next_resno++,
+                         list_comp->varname, false);
+
+    qual = transform_cypher_expr(child_cpstate, list_comp->where,
+                                 EXPR_KIND_WHERE);
+    if (qual)
+    {
+        qual = coerce_to_boolean(child_pstate, qual, "WHERE");
+    }
+
+    query->targetList = lappend(query->targetList, te);
+    query->jointree = makeFromExpr(child_pstate->p_joinlist, (Node *) qual);
+    query->rtable = child_pstate->p_rtable;
+    query->hasAggs = child_pstate->p_hasAggs;
+    query->hasSubLinks = child_pstate->p_hasSubLinks;
+    query->hasTargetSRFs = child_pstate->p_hasTargetSRFs;
+
+    assign_query_collations(child_pstate, query);
+
+    if (child_pstate->p_hasAggs ||
+        query->groupClause || query->groupingSets || query->havingQual)
+    {
+        parse_check_aggregates(child_pstate, query);
+    }
+
+    free_cypher_parsestate(child_cpstate);
+
+    return query;
+}
+
 /*
  * Iterate through the list of items to delete and extract the variable name.
  * Then find the resno that the variable name belongs to.
@@ -1560,6 +1629,8 @@ static Query *transform_cypher_set(cypher_parsestate *cpstate,
 
     query->rtable = pstate->p_rtable;
     query->jointree = makeFromExpr(pstate->p_joinlist, NULL);
+    query->hasAggs = pstate->p_hasAggs;
+    query->hasSubLinks = pstate->p_hasSubLinks;
 
     return query;
 }
@@ -1833,12 +1904,7 @@ cypher_update_information *transform_cypher_set_item_list(
                                             EXPR_KIND_SELECT_TARGET, NULL,
                                             false);
 
-        if (has_a_cypher_list_comprehension_node(set_item->expr))
-        {
-            query->hasAggs = true;
-        }
-
-        if (!query->hasAggs && nodeTag(target_item->expr) == T_Aggref)
+        if (nodeTag(target_item->expr) == T_Aggref)
         {
             ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
                     errmsg("Invalid use of aggregation in this context"),
@@ -2169,6 +2235,7 @@ static Query *transform_cypher_return(cypher_parsestate *cpstate,
     query->rtable = pstate->p_rtable;
     query->jointree = makeFromExpr(pstate->p_joinlist, NULL);
     query->hasAggs = pstate->p_hasAggs;
+    query->hasSubLinks = pstate->p_hasSubLinks;
 
     assign_query_collations(pstate, query);
 
@@ -2357,7 +2424,6 @@ static Query *transform_cypher_clause_with_where(cypher_parsestate *cpstate,
 {
     ParseState *pstate = (ParseState *)cpstate;
     Query *query;
-    Node *self = clause->self;
     Node *where_qual = NULL;
 
     if (where)
@@ -2370,10 +2436,8 @@ static Query *transform_cypher_clause_with_where(cypher_parsestate *cpstate,
 
         pnsi = transform_cypher_clause_as_subquery(cpstate, transform, clause,
                                                    NULL, true);
-        
         Assert(pnsi != NULL);
         rtindex = list_length(pstate->p_rtable);
-
         /* rte is the only RangeTblEntry in pstate */
         if (rtindex != 1)
         {
@@ -2388,78 +2452,14 @@ static Query *transform_cypher_clause_with_where(cypher_parsestate *cpstate,
          * next clause
          */
         query->targetList = expandNSItemAttrs(pstate, pnsi, 0, true, -1);
-
         markTargetListOrigins(pstate, query->targetList);
-
         query->rtable = pstate->p_rtable;
 
         where_qual = transform_cypher_expr(cpstate, where, EXPR_KIND_WHERE);
-
         where_qual = coerce_to_boolean(pstate, where_qual, "WHERE");
 
-        /* check if we have a list comprehension in the where clause */
-        if (cpstate->p_list_comp &&
-            has_a_cypher_list_comprehension_node(where))
-        {
-            List *groupClause = NIL;
-            ListCell *li;
-            bool has_a_star;
-
-            has_a_star = false;
-            query->jointree = makeFromExpr(pstate->p_joinlist, NULL);
-            query->havingQual = where_qual;
-
-            foreach (li, ((cypher_return *)self)->items)
-            {
-                ResTarget *item = lfirst(li);
-                ColumnRef *cref;
-                
-                /*
-                 * We need to handle the case where the item is a A_star. In this
-                 * case we will need to build group by using targetList.
-                 */
-                if (IsA(item->val, ColumnRef))
-                {
-                    cref = (ColumnRef *)item->val;
-                    if (IsA(linitial(cref->fields), A_Star))
-                    {
-                        has_a_star = true;
-                        continue;
-                    }
-                }
-
-                groupClause = lappend(groupClause, item->val);
-            }
-
-            /*
-             * If there is A_star flag, build the group by clause
-             * using the targetList.
-             */
-            if (has_a_star)
-            {
-                ListCell *lc;
-                foreach (lc, query->targetList)
-                {
-                    TargetEntry *te = lfirst(lc);
-                    ColumnRef *cref = makeNode(ColumnRef);
-
-                    cref->fields = list_make1(makeString(te->resname));
-                    cref->location = exprLocation((Node *)te->expr);
-
-                    groupClause = lappend(groupClause, cref);
-                }
-            }
-            query->groupClause = transform_group_clause(cpstate, groupClause,
-                                                        &query->groupingSets,
-                                                        &query->targetList,
-                                                        query->sortClause,
-                                                        EXPR_KIND_GROUP_BY);
-            
-        }
-        else
-        {
-            query->jointree = makeFromExpr(pstate->p_joinlist, where_qual);
-        }
+        query->jointree = makeFromExpr(pstate->p_joinlist, where_qual);
+        assign_query_collations(pstate, query);
     }
     else
     {
@@ -2470,8 +2470,6 @@ static Query *transform_cypher_clause_with_where(cypher_parsestate *cpstate,
     query->hasTargetSRFs = pstate->p_hasTargetSRFs;
     query->hasAggs = pstate->p_hasAggs;
 
-    assign_query_collations(pstate, query);
-
     return query;
 }
 
@@ -2479,6 +2477,7 @@ static Query *transform_cypher_match(cypher_parsestate *cpstate,
                                      cypher_clause *clause)
 {
     cypher_match *match_self = (cypher_match*) clause->self;
+    Node *where = match_self->where;
 
     if(!match_check_valid_label(match_self, cpstate))
     {
@@ -2496,9 +2495,39 @@ static Query *transform_cypher_match(cypher_parsestate *cpstate,
                                                      (Node *)r, -1);
     }
 
+    if (has_list_comp_or_subquery((Node *)match_self->where, NULL))
+    {
+        match_self->where = NULL;
+        return transform_cypher_clause_with_where(cpstate,
+                transform_cypher_match_pattern, clause, where);
+    }
+
     return transform_cypher_match_pattern(cpstate, clause);
 }
 
+/*
+ * Function that checks if an expr has a cypher_sub_query or
+ * cypher_list_comprehension.
+ */
+static bool has_list_comp_or_subquery(Node *expr, void *context)
+{
+    if (expr == NULL)
+    {
+        return false;
+    }
+
+    if (IsA(expr, ExtensibleNode))
+    {
+        if (is_ag_node(expr, cypher_sub_query) ||
+            is_ag_node(expr, cypher_list_comprehension))
+        {
+            return true;
+        }
+    }
+
+    return cypher_raw_expr_tree_walker(expr, has_list_comp_or_subquery, context);
+}
+
 /*
  * Transform the clause into a subquery. This subquery will be used
  * in a join so setup the namespace item and the created the rtr
@@ -2679,18 +2708,6 @@ static Query *transform_cypher_match_pattern(cypher_parsestate *cpstate,
     {
         cypher_clause *next = clause->next;
 
-        /*
-         * check if optional match has a subquery node-- it could still
-         * be following a match
-         */
-        if(is_ag_node(next->self, cypher_with))
-        {
-            cypher_with *next_with = (cypher_with *)next->self;
-            if (next_with->subquery_intermediate == true)
-            {
-                next = next->next;
-            }
-        }
         if (is_ag_node(next->self, cypher_match))
         {
             cypher_match *next_self = (cypher_match *)next->self;
@@ -2950,9 +2967,9 @@ static Query *transform_cypher_sub_query(cypher_parsestate *cpstate,
  * will transform the VLE function, depending on type. Currently, only
  * RangeFunctions are supported. But, others may be in the future.
  */
-static Node *transform_VLE_Function(cypher_parsestate *cpstate, Node *n,
-                                    RangeTblEntry **top_rte, int *top_rti,
-                                    List **namespace)
+static Node *transform_from_clause_item(cypher_parsestate *cpstate, Node *n,
+                                        RangeTblEntry **top_rte, int *top_rti,
+                                        List **namespace)
 {
     ParseState *pstate = &cpstate->pstate;
 
@@ -3020,7 +3037,7 @@ static ParseNamespaceItem *append_VLE_Func_to_FromClause(cypher_parsestate *cpst
      * Following PG's FROM clause logic, just in case we need to expand it in
      * the future, we process the items in another function.
      */
-    n = transform_VLE_Function(cpstate, n, &rte, &rtindex, &namespace);
+    n = transform_from_clause_item(cpstate, n, &rte, &rtindex, &namespace);
 
     /* this should not happen */
     Assert(n != NULL);
@@ -3212,36 +3229,7 @@ static void transform_match_pattern(cypher_parsestate *cpstate, Query *query,
     }
 
     query->rtable = cpstate->pstate.p_rtable;
-
-    if (cpstate->p_list_comp)
-    {
-        List *groupList = NIL;
-
-        query->jointree = makeFromExpr(cpstate->pstate.p_joinlist, NULL);
-        query->havingQual = (Node *)expr;
-
-        foreach (lc, query->targetList)
-        {
-            TargetEntry *te = lfirst(lc);
-            ColumnRef *cref = makeNode(ColumnRef);
-
-            cref->fields = list_make1(makeString(te->resname));
-            cref->location = exprLocation((Node *)te->expr);
-
-            groupList = lappend(groupList, cref);
-        }
-
-        query->groupClause = transform_group_clause(cpstate, groupList,
-                                                    &query->groupingSets,
-                                                    &query->targetList,
-                                                    query->sortClause,
-                                                    EXPR_KIND_GROUP_BY);
-    }
-    else
-    {
-        query->jointree = makeFromExpr(cpstate->pstate.p_joinlist,
-                                       (Node *)expr);
-    }
+    query->jointree = makeFromExpr(cpstate->pstate.p_joinlist, (Node *)expr);
 }
 
 /*
@@ -5610,6 +5598,7 @@ static Query *transform_cypher_create(cypher_parsestate *cpstate,
     query->rtable = pstate->p_rtable;
     query->jointree = makeFromExpr(pstate->p_joinlist, NULL);
     query->hasAggs = pstate->p_hasAggs;
+    query->hasSubLinks = pstate->p_hasSubLinks;
 
     return query;
 }
@@ -6245,7 +6234,7 @@ static Expr *cypher_create_properties(cypher_parsestate *cpstate,
  * This function is similar to transformFromClause() that is called with a
  * single RangeSubselect.
  */
-ParseNamespaceItem *
+static ParseNamespaceItem *
 transform_cypher_clause_as_subquery(cypher_parsestate *cpstate,
                                     transform_method transform,
                                     cypher_clause *clause,
@@ -6267,8 +6256,7 @@ transform_cypher_clause_as_subquery(cypher_parsestate *cpstate,
            pstate->p_expr_kind == EXPR_KIND_OTHER ||
            pstate->p_expr_kind == EXPR_KIND_WHERE ||
            pstate->p_expr_kind == EXPR_KIND_SELECT_TARGET ||
-           pstate->p_expr_kind == EXPR_KIND_FROM_SUBSELECT ||
-           pstate->p_expr_kind == EXPR_KIND_INSERT_TARGET);
+           pstate->p_expr_kind == EXPR_KIND_FROM_SUBSELECT);
 
     /*
      * As these are all sub queries, if this is just of type NONE, note it as a
@@ -6472,6 +6460,12 @@ Query *cypher_parse_sub_analyze(Node *parseTree,
     cypher_clause *clause;
     Query *query;
 
+    if (IsA(parseTree, Query))
+    {
+        /* Already transformed, just return it */
+        return (Query *)parseTree;
+    }
+
     pstate->p_parent_cte = parentCTE;
     pstate->p_locked_from_parent = locked_from_parent;
     pstate->p_resolve_unknowns = resolve_unknowns;
@@ -6621,7 +6615,6 @@ static Query *transform_cypher_merge(cypher_parsestate *cpstate,
     query->rtable = pstate->p_rtable;
     query->jointree = makeFromExpr(pstate->p_joinlist, NULL);
     query->hasAggs = pstate->p_hasAggs;
-
     query->hasSubLinks = pstate->p_hasSubLinks;
 
     assign_query_collations(pstate, query);
diff --git a/src/backend/parser/cypher_expr.c b/src/backend/parser/cypher_expr.c
index 8afc0c41..5b3ab319 100644
--- a/src/backend/parser/cypher_expr.c
+++ b/src/backend/parser/cypher_expr.c
@@ -98,8 +98,6 @@ static Node *transform_WholeRowRef(ParseState *pstate, ParseNamespaceItem *pnsi,
 static ArrayExpr *make_agtype_array_expr(List *args);
 static Node *transform_column_ref_for_indirection(cypher_parsestate *cpstate,
                                                   ColumnRef *cr);
-static Node *transform_cypher_list_comprehension(cypher_parsestate *cpstate,
-                                                 cypher_unwind *expr);
 static Node *transform_external_ext_FuncCall(cypher_parsestate *cpstate,
                                              FuncCall *fn, List *targs,
                                              Form_pg_proc procform,
@@ -242,12 +240,6 @@ static Node *transform_cypher_expr_recurse(cypher_parsestate *cpstate,
             return transform_cypher_comparison_boolexpr(cpstate,
                                              (cypher_comparison_boolexpr *)expr);
         }
-        if (is_ag_node(expr, cypher_unwind))
-        {
-            return transform_cypher_list_comprehension(cpstate,
-                                                       (cypher_unwind *) expr);
-        }
-
         ereport(ERROR,
                 (errmsg_internal("unrecognized ExtensibleNode: %s",
                                  ((ExtensibleNode *)expr)->extnodename)));
@@ -258,7 +250,9 @@ static Node *transform_cypher_expr_recurse(cypher_parsestate *cpstate,
         return transform_FuncCall(cpstate, (FuncCall *)expr);
     case T_SubLink:
         return transform_SubLink(cpstate, (SubLink *)expr);
-        break;
+    case T_Const:
+        /* Already transformed */
+        return expr;
     default:
         ereport(ERROR, (errmsg_internal("unrecognized node type: %d",
                                         nodeTag(expr))));
@@ -390,26 +384,8 @@ static Node *transform_ColumnRef(cypher_parsestate *cpstate, ColumnRef *cref)
                 Assert(IsA(field1, String));
                 colname = strVal(field1);
 
-                if (cpstate->p_list_comp &&
-                    (pstate->p_expr_kind == EXPR_KIND_WHERE ||
-                     pstate->p_expr_kind == EXPR_KIND_SELECT_TARGET) &&
-                     list_length(pstate->p_namespace) > 0)
-                {
-                    /*
-                     * Just scan through the last pnsi(that is for list comp)
-                     * to find the column.
-                     */
-                    node = scanNSItemForColumn(pstate,
-                                               llast(pstate->p_namespace),
-                                               0, colname, cref->location);
-                }
-                else
-                {
-                    /* Try to identify as an unqualified column */
-                    node = colNameToVar(pstate, colname, false,
-                                        cref->location);
-                }
-
+                /* Try to identify as an unqualified column */
+                node = colNameToVar(pstate, colname, false, cref->location);
                 if (node != NULL)
                 {
                         break;
@@ -1321,7 +1297,7 @@ static Node *transform_column_ref_for_indirection(cypher_parsestate *cpstate,
     }
 
     /* find the properties column of the NSI and return a var for it */
-    node = scanNSItemForColumn(pstate, pnsi, levels_up, "properties",
+    node = scanNSItemForColumn(pstate, pnsi, levels_up, "properties", 
                                cr->location);
 
     /*
@@ -2372,6 +2348,7 @@ static Node *transform_SubLink(cypher_parsestate *cpstate, SubLink *sublink)
         case EXPR_KIND_SELECT_TARGET:
         case EXPR_KIND_FROM_SUBSELECT:
         case EXPR_KIND_WHERE:
+        case EXPR_KIND_INSERT_TARGET:
             /* okay */
             break;
         default:
@@ -2411,7 +2388,7 @@ static Node *transform_SubLink(cypher_parsestate *cpstate, SubLink *sublink)
         sublink->operName = NIL;
     }
     else if (sublink->subLinkType == EXPR_SUBLINK ||
-            sublink->subLinkType == ARRAY_SUBLINK)
+             sublink->subLinkType == ARRAY_SUBLINK)
     {
         /*
          * Make sure the subselect delivers a single column (ignoring resjunk
@@ -2443,31 +2420,3 @@ static Node *transform_SubLink(cypher_parsestate *cpstate, SubLink *sublink)
 
     return result;
 }
-
-static Node *transform_cypher_list_comprehension(cypher_parsestate *cpstate,
-                                                 cypher_unwind *unwind)
-{
-    cypher_clause cc;
-    Node* expr;
-    ParseNamespaceItem *pnsi;
-    ParseState *pstate = (ParseState *)cpstate;
-
-    cpstate->p_list_comp = true;
-    pstate->p_lateral_active = true;
-
-    cc.prev = NULL;
-    cc.next = NULL;
-    cc.self = (Node *)unwind;
-
-    pnsi = transform_cypher_clause_as_subquery(cpstate,
-                                               transform_cypher_clause,
-                                               &cc, NULL, true);
-
-    expr = transform_cypher_expr(cpstate, unwind->collect,
-                                 EXPR_KIND_SELECT_TARGET);
-
-    pnsi->p_cols_visible = false;
-    pstate->p_lateral_active = false;
-
-    return expr;
-}
diff --git a/src/backend/parser/cypher_gram.y b/src/backend/parser/cypher_gram.y
index 31e062a5..31c07a5b 100644
--- a/src/backend/parser/cypher_gram.y
+++ b/src/backend/parser/cypher_gram.y
@@ -21,6 +21,7 @@
 #include "postgres.h"
 
 #include "nodes/makefuncs.h"
+#include "nodes/nodeFuncs.h"
 #include "parser/parser.h"
 
 #include "parser/cypher_gram.h"
@@ -259,15 +260,9 @@ static Node *build_comparison_expression(Node *left_grammar_node,
                                          char *opr_name, int location);
 
 /* list_comprehension */
-static Node *verify_rule_as_list_comprehension(Node *expr, Node *expr2,
-                                               Node *where, Node *mapping_expr,
-                                               int var_loc, int expr_loc,
-                                               int where_loc, int mapping_loc);
-
-static Node *build_list_comprehension_node(ColumnRef *var_name, Node *expr,
+static Node *build_list_comprehension_node(Node *var, Node *expr,
                                            Node *where, Node *mapping_expr,
-                                           int var_loc, int expr_loc,
-                                           int where_loc,int mapping_loc);
+                                           int location);
 
 %}
 %%
@@ -1078,8 +1073,6 @@ unwind:
 
             n = make_ag_node(cypher_unwind);
             n->target = res;
-            n->where = NULL;
-            n->collect = NULL;
             $$ = (Node *) n;
         }
 
@@ -2083,6 +2076,7 @@ expr_literal:
     | map
     | map_projection
     | list
+    | list_comprehension
     ;
 
 map:
@@ -2202,7 +2196,6 @@ list:
 
             $$ = (Node *)n;
         }
-    | list_comprehension
     ;
 
 /*
@@ -2213,40 +2206,28 @@ list:
 list_comprehension:
     '[' expr IN expr ']'
         {
-            Node *n = $2;
-            Node *result = NULL;
-            
             /*
-             * If the first expr is a ColumnRef(variable), then the rule
-             * should evaluate as a list comprehension. Otherwise, it should
-             * evaluate as an IN operator.
+             * If the first expr is not a ColumnRef(variable), then the rule
+             * should evaluate as an IN operator.
              */
-            if (nodeTag(n) == T_ColumnRef)
+            if (!IsA($2, ColumnRef))
             {
-                ColumnRef *cref = (ColumnRef *)n;
-                result = build_list_comprehension_node(cref, $4, NULL, NULL,
-                                                       @2, @4, 0, 0);
+                $$ = (Node *)makeSimpleA_Expr(AEXPR_IN, "=", $2, $4, @3);
             }
-            else
-            {
-                result = (Node *)makeSimpleA_Expr(AEXPR_IN, "=", n, $4, @3);
-            }
-            $$ = result;
+
+            $$ = build_list_comprehension_node($2, $4, NULL, NULL, @1);
         }
     | '[' expr IN expr WHERE expr ']'
         {
-            $$ = verify_rule_as_list_comprehension($2, $4, $6, NULL,
-                                                   @2, @4, @6, 0);
+            $$ = build_list_comprehension_node($2, $4, $6, NULL, @1);
         }
     | '[' expr IN expr '|' expr ']'
         {
-            $$ = verify_rule_as_list_comprehension($2, $4, NULL, $6,
-                                                   @2, @4, 0, @6);
+            $$ = build_list_comprehension_node($2, $4, NULL, $6, @1);
         }
     | '[' expr IN expr WHERE expr '|' expr ']'
         {
-            $$ = verify_rule_as_list_comprehension($2, $4, $6, $8,
-                                                   @2, @4, @6, @8);
+            $$ = build_list_comprehension_node($2, $4, $6, $8, @1);
         }
     ;
 
@@ -2897,6 +2878,10 @@ static FuncCall *node_to_agtype(Node * fnode, char *type, int location)
     {
         funcname = lappend(funcname, makeString("bool_to_agtype"));
     }
+    else if (pg_strcasecmp(type, "agtype[]") == 0)
+    {
+        funcname = lappend(funcname, makeString("agtype_array_to_agtype"));
+    }
     else
     {
         ereport(ERROR,
@@ -3277,90 +3262,51 @@ static cypher_relationship *build_VLE_relation(List *left_arg,
     return cr;
 }
 
-/* Helper function to verify that the rule is a list comprehension */
-static Node *verify_rule_as_list_comprehension(Node *expr, Node *expr2,
-                                               Node *where, Node *mapping_expr,
-                                               int var_loc, int expr_loc,
-                                               int where_loc, int mapping_loc)
+/* helper function to build a list_comprehension grammar node */
+static Node *build_list_comprehension_node(Node *var, Node *expr,
+                                           Node *where, Node *mapping_expr,
+                                           int location)
 {
-    Node *result = NULL;
+    SubLink *sub;
+    String *val;
+    ColumnRef *cref = NULL;
+    cypher_list_comprehension *list_comp = NULL;
 
-    /*
-     * If the first expression is a ColumnRef, then we can build a
-     * list_comprehension node.
-     * Else its an invalid use of IN operator.
-     */
-    if (nodeTag(expr) == T_ColumnRef)
-    {
-        ColumnRef *cref = (ColumnRef *)expr;
-        result = build_list_comprehension_node(cref, expr2, where,
-                                               mapping_expr, var_loc,
-                                               expr_loc, where_loc,
-                                               mapping_loc);
-    }
-    else
+    if (!IsA(var, ColumnRef))
     {
         ereport(ERROR,
                 (errcode(ERRCODE_SYNTAX_ERROR),
                  errmsg("Syntax error at or near IN")));
     }
-    return result;
-}
 
-/* helper function to build a list_comprehension grammar node */
-static Node *build_list_comprehension_node(ColumnRef *cref, Node *expr,
-                                           Node *where, Node *mapping_expr,
-                                           int var_loc, int expr_loc,
-                                           int where_loc, int mapping_loc)
-{
-    ResTarget *res = NULL;
-    cypher_unwind *unwind = NULL;
-    char *var_name = NULL;
-    String *val;
-
-    /* Extract name from cref */
+    cref = (ColumnRef *)var;
     val = linitial(cref->fields);
-
     if (!IsA(val, String))
     {
         ereport(ERROR,
-                (errmsg_internal("unexpected Node for cypher_clause")));
+                (errcode(ERRCODE_SYNTAX_ERROR),
+                 errmsg("Invalid list comprehension variable name")));
     }
 
-    var_name = val->sval;
+    /* build the list comprehension node */
+    list_comp = make_ag_node(cypher_list_comprehension);
+    list_comp->varname = val->sval;
+    list_comp->expr = expr;
+    list_comp->where = where;
+    list_comp->mapping_expr = (mapping_expr != NULL) ? mapping_expr :
+                                                       (Node *) cref;
 
     /*
-     * Build the ResTarget node for the UNWIND variable var_name attached to
-     * expr.
+     * Build an ARRAY sublink and attach list_comp as sub-select,
+     * it will be transformed in to query tree by us and reattached for 
+     * pg to process.
      */
-    res = makeNode(ResTarget);
-    res->name = var_name;
-    res->val = (Node *)expr;
-    res->location = expr_loc;
-
-    /* build the UNWIND node */
-    unwind = make_ag_node(cypher_unwind);
-    unwind->target = res;
-    unwind->where = where;
-
-    /* if there is a mapping function, add its arg to collect */
-    if (mapping_expr != NULL)
-    {
-        unwind->collect = make_function_expr(list_make1(makeString("collect")),
-                                             list_make1(mapping_expr),
-                                             mapping_loc);
-    }
-    /*
-     * Otherwise, we need to add in the ColumnRef of the variable var_name as
-     * the arg to collect instead. This implies that the RETURN variable is
-     * var_name.
-     */
-    else
-    {
-        unwind->collect = make_function_expr(list_make1(makeString("collect")),
-                                             list_make1(cref), mapping_loc);
-    }
-
-    /* return the UNWIND node */
-    return (Node *)unwind;
+    sub = makeNode(SubLink);
+    sub->subLinkType = ARRAY_SUBLINK;
+    sub->subLinkId = 0;
+    sub->testexpr = NULL;
+    sub->subselect = (Node *)list_comp;
+    sub->location = location;
+
+    return (Node *) node_to_agtype((Node *)sub, "agtype[]", location);
 }
diff --git a/src/backend/parser/cypher_item.c b/src/backend/parser/cypher_item.c
index 81413fa3..e9fb046e 100644
--- a/src/backend/parser/cypher_item.c
+++ b/src/backend/parser/cypher_item.c
@@ -27,16 +27,13 @@
 #include "nodes/makefuncs.h"
 #include "parser/parse_relation.h"
 #include "parser/parse_target.h"
-#include "miscadmin.h"
 
 #include "parser/cypher_expr.h"
 #include "parser/cypher_item.h"
-#include "parser/cypher_clause.h"
 
 static List *ExpandAllTables(ParseState *pstate, int location);
 static List *expand_rel_attrs(ParseState *pstate, RangeTblEntry *rte,
                               int rtindex, int sublevels_up, int location);
-bool has_a_cypher_list_comprehension_node(Node *expr);
 
 /* see transformTargetEntry() */
 TargetEntry *transform_cypher_item(cypher_parsestate *cpstate, Node *node,
@@ -44,17 +41,10 @@ TargetEntry *transform_cypher_item(cypher_parsestate *cpstate, Node *node,
                                    char *colname, bool resjunk)
 {
     ParseState *pstate = (ParseState *)cpstate;
-    bool old_p_lateral_active = pstate->p_lateral_active;
-
-    /* we want to see lateral variables */
-    pstate->p_lateral_active = true;
 
     if (!expr)
         expr = transform_cypher_expr(cpstate, node, expr_kind);
 
-    /* set lateral back to what it was */
-    pstate->p_lateral_active = old_p_lateral_active;
-
     if (!colname && !resjunk)
         colname = FigureColname(node);
 
@@ -62,143 +52,6 @@ TargetEntry *transform_cypher_item(cypher_parsestate *cpstate, Node *node,
                            colname, resjunk);
 }
 
-/*
- * Helper function to determine if the passed node has a list_comprehension
- * node embedded in it.
- */
-bool has_a_cypher_list_comprehension_node(Node *expr)
-{
-    /* return false on NULL input */
-    if (expr == NULL)
-    {
-        return false;
-    }
-
-    /* since this function recurses, it could be driven to stack overflow */
-    check_stack_depth();
-
-    switch (nodeTag(expr))
-    {
-    case T_A_Expr:
-    {
-        /*
-         * We need to recurse into the left and right nodes
-         * to check if there is an unwind node in there
-         */
-        A_Expr *a_expr = (A_Expr *)expr;
-
-        return (has_a_cypher_list_comprehension_node(a_expr->lexpr) ||
-                has_a_cypher_list_comprehension_node(a_expr->rexpr));
-    }
-    case T_BoolExpr:
-    {
-        BoolExpr *bexpr = (BoolExpr *)expr;
-        ListCell *lc;
-
-        /* is any of the boolean expression argument a list comprehension? */
-        foreach(lc, bexpr->args)
-        {
-            Node *arg = lfirst(lc);
-
-            if (has_a_cypher_list_comprehension_node(arg))
-            {
-                return true;
-            }
-        }
-        break;
-    }
-    case T_A_Indirection:
-    {
-        /* set expr to the object of the indirection */
-        expr = ((A_Indirection *)expr)->arg;
-
-        /* check the object of the indirection */
-        return has_a_cypher_list_comprehension_node(expr);
-    }
-    case T_ExtensibleNode:
-    {
-        if (is_ag_node(expr, cypher_unwind))
-        {
-            cypher_unwind *cu = (cypher_unwind *)expr;
-
-            /* it is a list comprehension if it has a collect node */
-            return cu->collect != NULL;
-        }
-        else if (is_ag_node(expr, cypher_map))
-        {
-            cypher_map *map;
-            int i;
-
-            map = (cypher_map *)expr;
-
-            if (map->keyvals == NULL || map->keyvals->length == 0)
-            {
-                return false;
-            }
-
-            /* check each key and value for a list comprehension */
-            for (i = 0; i < map->keyvals->length; i += 2)
-            {
-                Node *val;
-
-                /* get the value */
-                val = (Node *)map->keyvals->elements[i + 1].ptr_value;
-
-                /* check the value */
-                if (has_a_cypher_list_comprehension_node(val))
-                {
-                    return true;
-                }
-            }
-        }
-        else if (is_ag_node(expr, cypher_string_match))
-        {
-            cypher_string_match *csm_match = (cypher_string_match *)expr;
-
-            /* is lhs or rhs of the string match a list comprehension? */
-            return (has_a_cypher_list_comprehension_node(csm_match->lhs) ||
-                    has_a_cypher_list_comprehension_node(csm_match->rhs));
-        }
-        else if (is_ag_node(expr, cypher_typecast))
-        {
-            cypher_typecast *ctypecast = (cypher_typecast *)expr;
-
-            /* is expr being typecasted a list comprehension? */
-            return has_a_cypher_list_comprehension_node(ctypecast->expr);
-        }
-        else if (is_ag_node(expr, cypher_comparison_aexpr))
-        {
-            cypher_comparison_aexpr *aexpr = (cypher_comparison_aexpr *)expr;
-
-            /* is left or right argument a list comprehension? */
-            return (has_a_cypher_list_comprehension_node(aexpr->lexpr) ||
-                    has_a_cypher_list_comprehension_node(aexpr->rexpr));
-        }
-        else if (is_ag_node(expr, cypher_comparison_boolexpr))
-        {
-            cypher_comparison_boolexpr *bexpr = (cypher_comparison_boolexpr *)expr;
-            ListCell *lc;
-
-            /* is any of the boolean expression argument a list comprehension? */
-            foreach(lc, bexpr->args)
-            {
-                Node *arg = lfirst(lc);
-
-                if (has_a_cypher_list_comprehension_node(arg))
-                {
-                    return true;
-                }
-            }
-        }
-        break;
-    }
-    default:
-        break;
-    }
-    /* otherwise, return false */
-    return false;
-}
-
 /* see transformTargetList() */
 List *transform_cypher_item_list(cypher_parsestate *cpstate, List *item_list,
                                  List **groupClause, ParseExprKind expr_kind)
@@ -215,7 +68,6 @@ List *transform_cypher_item_list(cypher_parsestate *cpstate, List *item_list,
     {
         ResTarget *item = lfirst(li);
         TargetEntry *te;
-        bool has_list_comp = false;
 
         if (expand_star)
         {
@@ -243,48 +95,14 @@ List *transform_cypher_item_list(cypher_parsestate *cpstate, List *item_list,
                 }
             }
         }
-
-        /* Check if we have a list comprehension */
-        has_list_comp = has_a_cypher_list_comprehension_node(item->val);
-
         /* Clear the exprHasAgg flag to check transform for an aggregate */
         cpstate->exprHasAgg = false;
 
-        if (has_list_comp && item_list->length > 1)
-        {
-            /*
-             * Create a subquery for the list comprehension and transform it
-             * as a subquery. Then expand the target list of the subquery.
-             * This is to avoid multiple unnest functions in the same query
-             * level and collect not able to distinguish correctly.
-             */
-            ParseNamespaceItem *pnsi;
-            cypher_return *cr;
-            cypher_clause cc;
-
-            cr = make_ag_node(cypher_return);
-            cr->items = list_make1(item);
-
-            cc.prev = NULL;
-            cc.next = NULL;
-            cc.self = (Node *)cr;
-
-            pnsi = transform_cypher_clause_as_subquery(cpstate,
-                                                       transform_cypher_clause,
-                                                       &cc, NULL, true);
-
-            target_list = list_concat(target_list,
-                                      expandNSItemAttrs(&cpstate->pstate, pnsi,
-                                                        0, true, -1));
-        }
-        else
-        {
-            /* transform the item */
-            te = transform_cypher_item(cpstate, item->val, NULL, expr_kind,
-                                       item->name, false);
+        /* transform the item */
+        te = transform_cypher_item(cpstate, item->val, NULL, expr_kind,
+                                   item->name, false);
 
-            target_list = lappend(target_list, te);
-        }
+        target_list = lappend(target_list, te);
 
         /*
          * Did the transformed item contain an aggregate function? If it didn't,
@@ -299,58 +117,6 @@ List *transform_cypher_item_list(cypher_parsestate *cpstate, List *item_list,
         {
             hasAgg = true;
         }
-
-        /*
-         * This is for a special case with list comprehension, which is embedded
-         * in a cypher_unwind node. We need to group the results but not expose
-         * the grouping expression.
-         */
-        if (has_list_comp)
-        {
-            ParseState *pstate = &cpstate->pstate;
-            ParseNamespaceItem *nsitem = NULL;
-            RangeTblEntry *rte = NULL;
-
-            /*
-             * There should be at least 2 entries in p_namespace. One for the
-             * variable in the reading clause and one for the variable in the
-             * list_comprehension expression. Otherwise, there is nothing to
-             * group with.
-             */
-            if (list_length(pstate->p_namespace) > 1)
-            {
-                /*
-                 * Get the first namespace item which should be the first
-                 * variable from the reading clause.
-                 */
-                nsitem = lfirst(list_head(pstate->p_namespace));
-                /* extract the rte */
-                rte = nsitem->p_rte;
-
-                /*
-                 * If we have a non-null column name make a ColumnRef to it.
-                 * Otherwise, there wasn't a variable specified in the reading
-                 * clause. If that is the case don't. Because there isn't
-                 * anything to group with.
-                 */
-                if (rte->eref->colnames != NULL && nsitem->p_cols_visible)
-                {
-                    ColumnRef *cref = NULL;
-                    char *colname = NULL;
-
-                    /* get the name of the column (varname) */
-                    colname = strVal(lfirst(list_head(rte->eref->colnames)));
-
-                    /* create the ColumnRef */
-                    cref = makeNode(ColumnRef);
-                    cref->fields = list_make1(makeString(colname));
-                    cref->location = -1;
-
-                    /* add the expression for grouping */
-                    group_clause = lappend(group_clause, cref);
-                }
-            }
-        }
     }
 
     /*
diff --git a/src/backend/utils/adt/agtype.c b/src/backend/utils/adt/agtype.c
index c0e14110..78559a36 100644
--- a/src/backend/utils/adt/agtype.c
+++ b/src/backend/utils/adt/agtype.c
@@ -1836,6 +1836,19 @@ static void array_to_agtype_internal(Datum array, agtype_in_state *result)
     pfree_if_not_null(nulls);
 }
 
+PG_FUNCTION_INFO_V1(agtype_array_to_agtype);
+Datum agtype_array_to_agtype(PG_FUNCTION_ARGS)
+{
+    agtype_in_state result;
+
+    result.parse_state = NULL;
+    result.res = NULL;
+
+    array_to_agtype_internal(PG_GETARG_DATUM(0), &result);
+
+    PG_RETURN_POINTER(agtype_value_to_agtype(result.res));
+}
+
 /*
  * Turn a composite / record into agtype.
  */
@@ -11910,7 +11923,6 @@ PG_FUNCTION_INFO_V1(age_unnest);
 Datum age_unnest(PG_FUNCTION_ARGS)
 {
     agtype *agtype_arg = NULL;
-    bool list_comprehension = false;
     ReturnSetInfo *rsi;
     Tuplestorestate *tuple_store;
     TupleDesc tupdesc;
@@ -11921,35 +11933,13 @@ Datum age_unnest(PG_FUNCTION_ARGS)
     agtype_value v;
     agtype_iterator_token r;
 
-    /* verify that we have the correct number of args */
-    if (PG_NARGS() != 2)
-    {
-        ereport(ERROR,
-                (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
-                        errmsg("invalid number of arguments to unnest")));
-    }
-
-    /* verify that our flags are not null */
-    if (PG_ARGISNULL(1))
-    {
-        ereport(ERROR,
-                (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
-                        errmsg("invalid unnest boolean flags passed")));
-    }
-
     /* check for a NULL expr */
     if (PG_ARGISNULL(0))
     {
         PG_RETURN_NULL();
     }
 
-    /* get our flags */
-    list_comprehension = PG_GETARG_BOOL(1);
-
-    /* get the input expression */
     agtype_arg = AG_GET_ARG_AGTYPE_P(0);
-
-    /* verify that it resolves to an array */
     if (!AGT_ROOT_IS_ARRAY(agtype_arg))
     {
         ereport(ERROR,
@@ -12006,25 +11996,6 @@ Datum age_unnest(PG_FUNCTION_ARGS)
         }
     }
 
-    /*
-     * If this is for list_comprehension, we need to add a NULL as the last row.
-     * This NULL will allow empty lists (either filtered out by where, creating
-     * an empty list, or just a generic empty list) to be preserved.
-     */
-    if (list_comprehension)
-    {
-        Datum values[1] = {0};
-        bool nulls[1] = {true};
-
-        old_cxt = MemoryContextSwitchTo(tmp_cxt);
-
-        tuplestore_puttuple(tuple_store,
-                            heap_form_tuple(ret_tdesc, values, nulls));
-
-        MemoryContextSwitchTo(old_cxt);
-        MemoryContextReset(tmp_cxt);
-    }
-
     MemoryContextDelete(tmp_cxt);
 
     rsi->setResult = tuple_store;
diff --git a/src/include/nodes/ag_nodes.h b/src/include/nodes/ag_nodes.h
index fe9c9663..f0cc2204 100644
--- a/src/include/nodes/ag_nodes.h
+++ b/src/include/nodes/ag_nodes.h
@@ -50,6 +50,7 @@ typedef enum ag_node_tag
     cypher_map_projection_t,
     cypher_map_projection_element_t,
     cypher_list_t,
+    cypher_list_comprehension_t,
     /* comparison expression */
     cypher_comparison_aexpr_t,
     cypher_comparison_boolexpr_t,
@@ -106,6 +107,5 @@ static inline bool _is_ag_node(Node *node, const char *extnodename)
 }
 
 #define is_ag_node(node, type) _is_ag_node((Node *)(node), CppAsString(type))
-#define get_ag_node_tag(node) ((ag_node_tag)(((ExtensibleNode *)(node))->extnodename))
 
 #endif
diff --git a/src/include/nodes/cypher_nodes.h b/src/include/nodes/cypher_nodes.h
index e7e293f3..0b5fec1c 100644
--- a/src/include/nodes/cypher_nodes.h
+++ b/src/include/nodes/cypher_nodes.h
@@ -67,7 +67,6 @@ typedef struct cypher_with
 {
     ExtensibleNode extensible;
     bool distinct;
-    bool subquery_intermediate; /* flag that denotes a subquery node */
     List *items; /* a list of ResTarget's */
     List *order_by;
     Node *skip;
@@ -118,10 +117,6 @@ typedef struct cypher_unwind
 {
     ExtensibleNode extensible;
     ResTarget *target;
-
-    /* for list comprehension */
-    Node *where;
-    Node *collect;
 } cypher_unwind;
 
 typedef struct cypher_merge
@@ -219,6 +214,15 @@ typedef struct cypher_map_projection
     int location;
 } cypher_map_projection;
 
+ typedef struct cypher_list_comprehension
+ {
+    ExtensibleNode extensible;
+    char *varname;
+    Node *expr;
+    Node *where;
+    Node *mapping_expr;
+ } cypher_list_comprehension;
+
 typedef enum cypher_map_projection_element_type
 {
     PROPERTY_SELECTOR = 0,  /* map_var { .key } */
diff --git a/src/include/nodes/cypher_outfuncs.h b/src/include/nodes/cypher_outfuncs.h
index 4a04cf94..418d35f4 100644
--- a/src/include/nodes/cypher_outfuncs.h
+++ b/src/include/nodes/cypher_outfuncs.h
@@ -48,6 +48,7 @@ void out_cypher_param(StringInfo str, const ExtensibleNode *node);
 void out_cypher_map(StringInfo str, const ExtensibleNode *node);
 void out_cypher_map_projection(StringInfo str, const ExtensibleNode *node);
 void out_cypher_list(StringInfo str, const ExtensibleNode *node);
+void out_cypher_list_comprehension(StringInfo str, const ExtensibleNode *node);
 
 /* comparison expression */
 void out_cypher_comparison_aexpr(StringInfo str, const ExtensibleNode *node);
diff --git a/src/include/parser/cypher_analyze.h b/src/include/parser/cypher_analyze.h
index 97616e54..c74de0b0 100644
--- a/src/include/parser/cypher_analyze.h
+++ b/src/include/parser/cypher_analyze.h
@@ -21,16 +21,14 @@
 #define AG_CYPHER_ANALYZE_H
 
 #include "parser/cypher_clause.h"
-
-typedef bool (*cypher_expression_condition)( Node *expr);
+#include "nodes/nodeFuncs.h"
 
 void post_parse_analyze_init(void);
 void post_parse_analyze_fini(void);
 
-cypher_clause *build_subquery_node(cypher_clause *next);
-
-/*expr tree walker */
-bool expr_contains_node(cypher_expression_condition is_expr, Node *expr);
-bool expr_has_subquery(Node * expr);
+bool cypher_expr_tree_walker(Node *node, bool (*walker) (),
+                             void *context);
+bool cypher_raw_expr_tree_walker(Node *node, bool (*walker) (),
+                                 void *context);
 
 #endif
diff --git a/src/include/parser/cypher_clause.h b/src/include/parser/cypher_clause.h
index 51ba5cb4..461a664c 100644
--- a/src/include/parser/cypher_clause.h
+++ b/src/include/parser/cypher_clause.h
@@ -39,13 +39,4 @@ Query *cypher_parse_sub_analyze(Node *parseTree,
                                 CommonTableExpr *parentCTE,
                                 bool locked_from_parent,
                                 bool resolve_unknowns);
-
-typedef Query *(*transform_method)(cypher_parsestate *cpstate,
-                                   cypher_clause *clause);
-
-ParseNamespaceItem *transform_cypher_clause_as_subquery(cypher_parsestate *cpstate,
-                                                        transform_method transform,
-                                                        cypher_clause *clause,
-                                                        Alias *alias,
-                                                        bool add_rte_to_query);
 #endif
diff --git a/src/include/parser/cypher_item.h b/src/include/parser/cypher_item.h
index ba4e7e9a..92b6c95f 100644
--- a/src/include/parser/cypher_item.h
+++ b/src/include/parser/cypher_item.h
@@ -26,6 +26,4 @@ TargetEntry *transform_cypher_item(cypher_parsestate *cpstate, Node *node,
 List *transform_cypher_item_list(cypher_parsestate *cpstate, List *item_list,
                                  List **groupClause, ParseExprKind expr_kind);
 
-bool has_a_cypher_list_comprehension_node(Node *expr);
-
 #endif
diff --git a/src/include/parser/cypher_parse_node.h b/src/include/parser/cypher_parse_node.h
index f7169380..263ea197 100644
--- a/src/include/parser/cypher_parse_node.h
+++ b/src/include/parser/cypher_parse_node.h
@@ -50,7 +50,6 @@ typedef struct cypher_parsestate
      */
     bool exprHasAgg;
     bool p_opt_match;
-    bool p_list_comp;
 } cypher_parsestate;
 
 typedef struct errpos_ecb_state


(age) 01/04: Fix issue 1955 - List comprehension in WHERE clause (#2094)

Posted by jg...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

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

commit 7b2acfdecc52bbc9ade60cd9bb6af9eb69e59da8
Author: Muhammad Taha Naveed <mt...@apache.org>
AuthorDate: Mon Sep 9 22:31:26 2024 +0500

    Fix issue 1955 - List comprehension in WHERE clause (#2094)
    
    - Fixed issue when MATCH clause variable is referenced inside list
      comprehension in WHERE clause or in property constraint.
    - Added regression tests.
---
 regress/expected/list_comprehension.out | 47 +++++++++++++++++++++++++++++++++
 regress/sql/list_comprehension.sql      |  8 ++++++
 src/backend/parser/cypher_expr.c        |  3 ++-
 3 files changed, 57 insertions(+), 1 deletion(-)

diff --git a/regress/expected/list_comprehension.out b/regress/expected/list_comprehension.out
index 0260fa5e..bf5731d2 100644
--- a/regress/expected/list_comprehension.out
+++ b/regress/expected/list_comprehension.out
@@ -617,6 +617,53 @@ SELECT * FROM cypher('list_comprehension', $$ MATCH (u) WITH *, [i in [1,2,3]] a
  [1, 2, 3]
 (1 row)
 
+-- Issue 1955 - variable reference in list comprehension
+SELECT * FROM cypher('list_comprehension', $$ MATCH (u) WHERE u.list=[i IN u.list] RETURN u $$) AS (result agtype);
+                                                                            result                                                                             
+---------------------------------------------------------------------------------------------------------------------------------------------------------------
+ {"id": 281474976710658, "label": "", "properties": {"list": [1, 3, 5, 7, 9, 11, 13]}}::vertex
+ {"id": 281474976710660, "label": "", "properties": {"list": [12, 14, 16, 18, 20, 22, 24]}}::vertex
+ {"id": 281474976710662, "label": "", "properties": {"list": [25.0, 49.0, 81.0, 121.0, 169.0]}}::vertex
+ {"id": 281474976710657, "label": "", "properties": {"a": [], "b": [0, 1, 2, 3, 4, 5], "c": [0, 2, 4, 6, 8, 10, 12], "list": [0, 2, 4, 6, 8, 10, 12]}}::vertex
+ {"id": 281474976710663, "label": "", "properties": {"list": [1, 2, 3]}}::vertex
+ {"id": 281474976710659, "label": "", "properties": {"list": []}}::vertex
+ {"id": 281474976710661, "label": "", "properties": {"list": [6, 8, 10, 12]}}::vertex
+ {"id": 844424930131969, "label": "csm_match", "properties": {"list": ["abc", "def", "ghi"]}}::vertex
+(8 rows)
+
+SELECT * FROM cypher('list_comprehension', $$ MATCH (u) WHERE u.list=[i IN u.list WHERE i>0] RETURN u $$) AS (result agtype);
+                                                 result                                                 
+--------------------------------------------------------------------------------------------------------
+ {"id": 281474976710658, "label": "", "properties": {"list": [1, 3, 5, 7, 9, 11, 13]}}::vertex
+ {"id": 281474976710660, "label": "", "properties": {"list": [12, 14, 16, 18, 20, 22, 24]}}::vertex
+ {"id": 281474976710662, "label": "", "properties": {"list": [25.0, 49.0, 81.0, 121.0, 169.0]}}::vertex
+ {"id": 281474976710663, "label": "", "properties": {"list": [1, 2, 3]}}::vertex
+ {"id": 281474976710661, "label": "", "properties": {"list": [6, 8, 10, 12]}}::vertex
+(5 rows)
+
+SELECT * FROM cypher('list_comprehension', $$ MATCH (u) WHERE size([e in u.list where e starts with "a"])>0 RETURN u $$) AS (result agtype);
+                                                result                                                
+------------------------------------------------------------------------------------------------------
+ {"id": 844424930131969, "label": "csm_match", "properties": {"list": ["abc", "def", "ghi"]}}::vertex
+(1 row)
+
+SELECT * FROM cypher('list_comprehension', $$ MATCH (u ={list:[i IN u.list | i+1]}) RETURN u $$) AS (result agtype);
+                                  result                                  
+--------------------------------------------------------------------------
+ {"id": 281474976710659, "label": "", "properties": {"list": []}}::vertex
+(1 row)
+
+SELECT * FROM cypher('list_comprehension', $$ MATCH (u ={list:[i IN u.list WHERE i>0]}) RETURN u$$) AS (result agtype);
+                                                 result                                                 
+--------------------------------------------------------------------------------------------------------
+ {"id": 281474976710658, "label": "", "properties": {"list": [1, 3, 5, 7, 9, 11, 13]}}::vertex
+ {"id": 281474976710660, "label": "", "properties": {"list": [12, 14, 16, 18, 20, 22, 24]}}::vertex
+ {"id": 281474976710662, "label": "", "properties": {"list": [25.0, 49.0, 81.0, 121.0, 169.0]}}::vertex
+ {"id": 281474976710663, "label": "", "properties": {"list": [1, 2, 3]}}::vertex
+ {"id": 281474976710661, "label": "", "properties": {"list": [6, 8, 10, 12]}}::vertex
+(5 rows)
+
+-- Clean up
 SELECT * FROM drop_graph('list_comprehension', true);
 NOTICE:  drop cascades to 4 other objects
 DETAIL:  drop cascades to table list_comprehension._ag_label_vertex
diff --git a/regress/sql/list_comprehension.sql b/regress/sql/list_comprehension.sql
index cef92a22..cb941a61 100644
--- a/regress/sql/list_comprehension.sql
+++ b/regress/sql/list_comprehension.sql
@@ -157,4 +157,12 @@ SELECT * FROM cypher('list_comprehension', $$ MATCH (u) WITH * WHERE u.list=[i I
 SELECT * FROM cypher('list_comprehension', $$ MATCH (u) WITH * WITH *, [i in [1,2,3]] as list RETURN list LIMIT 1 $$) AS (result agtype);
 SELECT * FROM cypher('list_comprehension', $$ MATCH (u) WITH *, [i in [1,2,3]] as list WITH * RETURN list LIMIT 1 $$) AS (result agtype);
 
+-- Issue 1955 - variable reference in list comprehension
+SELECT * FROM cypher('list_comprehension', $$ MATCH (u) WHERE u.list=[i IN u.list] RETURN u $$) AS (result agtype);
+SELECT * FROM cypher('list_comprehension', $$ MATCH (u) WHERE u.list=[i IN u.list WHERE i>0] RETURN u $$) AS (result agtype);
+SELECT * FROM cypher('list_comprehension', $$ MATCH (u) WHERE size([e in u.list where e starts with "a"])>0 RETURN u $$) AS (result agtype);
+SELECT * FROM cypher('list_comprehension', $$ MATCH (u ={list:[i IN u.list | i+1]}) RETURN u $$) AS (result agtype);
+SELECT * FROM cypher('list_comprehension', $$ MATCH (u ={list:[i IN u.list WHERE i>0]}) RETURN u$$) AS (result agtype);
+
+-- Clean up
 SELECT * FROM drop_graph('list_comprehension', true);
\ No newline at end of file
diff --git a/src/backend/parser/cypher_expr.c b/src/backend/parser/cypher_expr.c
index 8eaf3bd2..8afc0c41 100644
--- a/src/backend/parser/cypher_expr.c
+++ b/src/backend/parser/cypher_expr.c
@@ -1321,7 +1321,8 @@ static Node *transform_column_ref_for_indirection(cypher_parsestate *cpstate,
     }
 
     /* find the properties column of the NSI and return a var for it */
-    node = scanNSItemForColumn(pstate, pnsi, 0, "properties", cr->location);
+    node = scanNSItemForColumn(pstate, pnsi, levels_up, "properties",
+                               cr->location);
 
     /*
      * Error out if we couldn't find it.


(age) 02/04: Fix CI build errors caused by missing dependencies (#2163)

Posted by jg...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

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

commit 4e9c734e7d0eaa777412d46c17d394fc6883838a
Author: Muhammad Taha Naveed <mt...@apache.org>
AuthorDate: Thu May 8 20:32:58 2025 +0500

    Fix CI build errors caused by missing dependencies (#2163)
    
    Adds extra step in CI workflow to install missing necessary
    dependencies.
---
 .github/workflows/installcheck.yaml | 5 +++++
 1 file changed, 5 insertions(+)

diff --git a/.github/workflows/installcheck.yaml b/.github/workflows/installcheck.yaml
index fcd1a4fd..7e7214cc 100644
--- a/.github/workflows/installcheck.yaml
+++ b/.github/workflows/installcheck.yaml
@@ -22,6 +22,11 @@ jobs:
           path: ~/pg15
           key: ${{ runner.os }}-v1-pg15-${{ env.PG_COMMIT_HASH }}
 
+      - name: Install necessary dependencies
+        run: |
+          sudo apt-get update
+          sudo apt-get install -y build-essential libreadline-dev zlib1g-dev flex bison
+
       - name: Install PostgreSQL 15 and some extensions
         if: steps.pg15cache.outputs.cache-hit != 'true'
         run: |