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.
diff --git a/age--1.5.0--y.y.y.sql b/age--1.5.0--y.y.y.sql
index 85c2db7..6b7560a 100644
--- a/age--1.5.0--y.y.y.sql
+++ b/age--1.5.0--y.y.y.sql
@@ -137,4 +137,15 @@
 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 bf5731d..07f7777 100644
--- a/regress/expected/list_comprehension.out
+++ b/regress/expected/list_comprehension.out
@@ -166,9 +166,9 @@
 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 @@
 ------------
  [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) 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) MATCH (v {list:[i IN u.list]}) RETURN v $$) AS (result agtype);
                                             result                                             
 -----------------------------------------------------------------------------------------------
+ {"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": 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
- {"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
 (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 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": 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": 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": 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": 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
 (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 @@
 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 @@
                                                                             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": 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": 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 @@
                                                  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 @@
                                                  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 cb941a6..572b2e6 100644
--- a/regress/sql/list_comprehension.sql
+++ b/regress/sql/list_comprehension.sql
@@ -117,9 +117,6 @@
 -- 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 ={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 bdc33af..c7895fa 100644
--- a/sql/agtype_coercions.sql
+++ b/sql/agtype_coercions.sql
@@ -173,3 +173,14 @@
 
 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 aa55140..c29c0a6 100644
--- a/sql/agtype_typecast.sql
+++ b/sql/agtype_typecast.sql
@@ -181,8 +181,7 @@
 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 e20670b..9cab568 100644
--- a/src/backend/nodes/ag_nodes.c
+++ b/src/backend/nodes/ag_nodes.c
@@ -48,6 +48,7 @@
     "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 @@
     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 e245ba6..5bc824f 100644
--- a/src/backend/nodes/cypher_outfuncs.c
+++ b/src/backend/nodes/cypher_outfuncs.c
@@ -117,7 +117,6 @@
     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);
@@ -176,9 +175,20 @@
     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 a08cd30..5e43442 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 @@
 
     /* 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 128acd0..4bba104 100644
--- a/src/backend/parser/cypher_analyze.c
+++ b/src/backend/parser/cypher_analyze.c
@@ -67,12 +67,6 @@
                                         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 @@
     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_impl(Node *node,
+                                      bool (*walker)(Node *node, void *context),
+                                      void *context)
 {
-    if (!expr)
-    {
+    ListCell *temp;
+
+    if (node == NULL)
         return false;
-    }
 
-    switch (nodeTag(expr))
+#define WALK(n) walker((Node *) (n), context)
+#define LIST_WALK(l) cypher_raw_expr_tree_walker_impl((Node *) (l), walker, context)
+
+    if (IsA(node, ExtensibleNode))
     {
-    case T_A_Const:
-    case T_ColumnRef:
-    case T_A_Indirection:
+        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 */
         {
-            break;
+            return false;
         }
-    case T_A_Expr:
-    {
-        A_Expr *a = (A_Expr *)expr;
-
-        switch (a->kind)
+        else if (is_ag_node(node, cypher_map))
         {
-        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;
-        }
-
-        break;
-    }
-    case T_CaseTestExpr:
-    {
-        break;
-    }
-    case T_CoalesceExpr:
-    {
-        CoalesceExpr *cexpr = (CoalesceExpr *) expr;
-        ListCell *args;
-
-        foreach(args, cexpr->args)
-        {
-            Node *e = (Node *)lfirst(args);
-
-            if (expr_contains_node(is_expr, e))
-            {
-                return true;
-            }
-        }
-        break;
-    }
-    case T_ExtensibleNode:
-    {
-        if (is_ag_node(expr, cypher_bool_const))
-        {
-            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);
-        }
-        if (is_ag_node(expr, 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 @@
 
                 val = lfirst(le);
 
-                if (expr_contains_node(is_expr, val))
+                if (WALK(val))
                 {
                     return true;
                 }
@@ -845,216 +731,163 @@
                 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)
-            {
-                cypher_map_projection_element *elem;
-
-                elem = lfirst(lc);
-
-                if (expr_contains_node(is_expr, elem->value))
-                {
-                    return true;
-                }
-            }
-
-            break;
-        }
-        if (is_ag_node(expr, cypher_list))
-        {
-            cypher_list *cl = (cypher_list *)expr;
-            ListCell *le = NULL;
-
-            foreach(le, cl->elems)
-            {
-                Node *texpr = lfirst(le);
-
-                if (expr_contains_node(is_expr, texpr))
-                {
-                    return true;
-                }
-            }
-            break;
-        }
-        if (is_ag_node(expr, cypher_string_match))
-        {
-            cypher_string_match *csm = (cypher_string_match *)expr;
-
-            if (expr_contains_node(is_expr, csm->lhs) ||
-                expr_contains_node(is_expr, csm->rhs))
+            if (LIST_WALK(cmp->map_elements))
             {
                 return true;
             }
-            break;
         }
-        if (is_ag_node(expr, cypher_typecast))
+        else if (is_ag_node(node, cypher_list))
         {
-            cypher_typecast *t = (cypher_typecast *) expr;
-
-            if (expr_contains_node(is_expr, t->expr))
+            cypher_list *cl = (cypher_list *)node;
+            
+            if (LIST_WALK(cl->elems))
             {
                 return true;
             }
-            break;
         }
-        if (is_ag_node(expr, cypher_comparison_aexpr))
+        else if (is_ag_node(node, cypher_string_match))
         {
-            cypher_comparison_aexpr *a = (cypher_comparison_aexpr *) expr;
+            cypher_string_match *csm = (cypher_string_match *)node;
 
-            if (expr_contains_node(is_expr, a->lexpr) ||
-                expr_contains_node(is_expr, a->rexpr))
+            if (WALK(csm->lhs))
             {
                 return true;
             }
-            break;
-        }
-        if (is_ag_node(expr, cypher_comparison_boolexpr))
-        {
-            cypher_comparison_boolexpr *b = (cypher_comparison_boolexpr *) expr;
-            ListCell *la;
 
-            foreach(la, b->args)
-            {
-                Node *arg = lfirst(la);
-
-                if (expr_contains_node(is_expr, arg))
-                {
-                    return true;
-                }
-            }
-            break;
-        }
-        if (is_ag_node(expr, cypher_unwind))
-        {
-            cypher_unwind* lc = (cypher_unwind *)expr;
-
-            if (expr_contains_node(is_expr, lc->where) ||
-                expr_contains_node(is_expr, lc->collect))
+            if (WALK(csm->rhs))
             {
                 return true;
             }
-            break;
         }
-
-        if (is_ag_node(expr, cypher_sub_pattern))
+        else if (is_ag_node(node, cypher_typecast))
         {
-            break;
-        }
+            cypher_typecast *t = (cypher_typecast *)node;
 
-        if (is_ag_node(expr, cypher_sub_query))
+            if (WALK(t->expr))
+            {
+                return true;
+            }
+        }
+        else if (is_ag_node(node, cypher_comparison_aexpr))
         {
-            break;
+            cypher_comparison_aexpr *a = (cypher_comparison_aexpr *)node;
+
+            if (WALK(a->lexpr))
+            {
+                return true;
+            }
+
+            if (WALK(a->rexpr))
+            {
+                return true;
+            }
+        }
+        else if (is_ag_node(node, cypher_comparison_boolexpr))
+        {
+            cypher_comparison_boolexpr *b = (cypher_comparison_boolexpr *)node;
+            
+            if (LIST_WALK(b->args))
+            {
+                return true;
+            }
+        }
+        else if (is_ag_node(node, cypher_unwind))
+        {
+            cypher_unwind *unw = (cypher_unwind *)node;
+
+            if (WALK(unw->target))
+            {
+                return true;
+            }
         }
 
-        ereport(ERROR,
+        else if (is_ag_node(node, cypher_list_comprehension))
+        {
+            cypher_list_comprehension *lc = (cypher_list_comprehension *)node;
+
+            if (WALK(lc->expr))
+            {
+                return true;
+            }
+
+            if (WALK(lc->where))
+            {
+                return true;
+            }
+
+            if (WALK(lc->mapping_expr))
+            {
+                return true;
+            }
+        }
+        /* Add more node types here as needed */
+        else
+        {
+            ereport(ERROR,
                 (errmsg_internal("unrecognized ExtensibleNode: %s",
-                                 ((ExtensibleNode *)expr)->extnodename)));
-
-        break;
+                                 ((ExtensibleNode *)node)->extnodename)));
+        }
     }
-    case T_FuncCall:
+    /*
+     * postgres's raw expresssion tree walker does not handle List
+     */
+    else if (IsA(node, List))
     {
-        FuncCall *fn = (FuncCall *)expr;
-        ListCell *arg;
-
-        foreach(arg, fn->args)
+        foreach(temp, (List *) node)
         {
-            Node *farg = NULL;
-
-            farg = (Node *)lfirst(arg);
-
-            if (expr_contains_node(is_expr, farg))
-            {
+            if (WALK((Node *) lfirst(temp)))
                 return true;
-            }
-        }
-        break;
-    }
-    case T_SubLink:
-    {
-       SubLink *s = (SubLink *)expr;
-
-        if (expr_contains_node(is_expr, s->subselect))
-        {
-            return true;
-        }
-      break;
-    }
-    default:
-        ereport(ERROR, (errmsg_internal("unrecognized node type: %d",
-                                        nodeTag(expr))));
-    }
-
-    return (is_expr(expr));
-}
-
-/*
- * 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.
- */
-
-bool expr_has_subquery(Node * expr)
-{
-    if (expr == NULL)
-    {
-        return false;
-    }
-
-    if (IsA(expr, ExtensibleNode))
-    {
-        if (is_ag_node(expr, cypher_sub_query))
-        {
-            return true;
         }
     }
+
+#undef LIST_WALK
+    else
+    {
+        return raw_expression_tree_walker(node, walker, context);
+    }
+    
     return false;
 }
 
 /*
- * This function constructs an intermediate WITH node for processing subqueries
+ * 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.
  */
-cypher_clause *build_subquery_node(cypher_clause *next)
+bool cypher_expr_tree_walker_impl(Node *node,
+                                  bool (*walker)(Node *node, void *context),
+                                  void *context)
 {
-    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;
+    if (node == NULL)
+    {
+        return false;
+    }
 
-    /* construct the column ref star */
-    cr = makeNode(ColumnRef);
-    cr->fields = list_make1(makeNode(A_Star));
-    cr->location = exprLocation((Node *)next);
+#define LIST_WALK(l) cypher_expr_tree_walker_impl((Node *) (l), walker, context)
 
-    /*construct the restarget */
-    rt = makeNode(ResTarget);
-    rt->name = NULL;
-    rt->indirection = NIL;
-    rt->val = (Node *)cr;
-    rt->location = exprLocation((Node *)next);
+    if (IsA(node, ExtensibleNode))
+    {
+        /* Add our nodes that can appear post parsing stage */
 
+        ereport(ERROR,
+                (errmsg_internal("unrecognized ExtensibleNode: %s",
+                                 ((ExtensibleNode *)node)->extnodename)));
+    }
+#undef WALK
+#undef LIST_WALK
+    else
+    {
+        return expression_tree_walker(node, walker, context);
+    }
 
-    /* construct the with_clause */
-    with_clause->items = list_make1(rt);
-    with_clause->where = match->where;
-    with_clause->subquery_intermediate = true;
-
-    /*
-     * 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;
-
-    return where_container_clause;
+    return false;
 }
 
 static Query *analyze_cypher(List *stmt, ParseState *parent_pstate,
@@ -1083,23 +916,6 @@
         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 e301daa..172e630 100644
--- a/src/backend/parser/cypher_clause.c
+++ b/src/backend/parser/cypher_clause.c
@@ -248,6 +248,11 @@
 /* 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 @@
 #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,
@@ -328,9 +333,9 @@
 /* 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,
@@ -338,6 +343,7 @@
 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
@@ -400,19 +406,16 @@
     }
     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")));
@@ -448,23 +451,6 @@
         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;
@@ -663,6 +649,7 @@
     qry->rteperminfos = pstate->p_rteperminfos;
     qry->jointree = makeFromExpr(pstate->p_joinlist, NULL);
     qry->hasAggs = pstate->p_hasAggs;
+    qry->hasSubLinks = pstate->p_hasSubLinks;
 
     assign_query_collations(pstate, qry);
 
@@ -1268,6 +1255,7 @@
     query->rteperminfos = cpstate->pstate.p_rteperminfos;
     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);
 
@@ -1340,6 +1328,8 @@
     query->rtable = pstate->p_rtable;
     query->rteperminfos = pstate->p_rteperminfos;
     query->jointree = makeFromExpr(pstate->p_joinlist, NULL);
+    query->hasAggs = pstate->p_hasAggs;
+    query->hasSubLinks = pstate->p_hasSubLinks;
 
     return query;
 }
@@ -1363,9 +1353,6 @@
     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;
@@ -1394,13 +1381,14 @@
         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"),
@@ -1413,12 +1401,11 @@
     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++,
@@ -1430,6 +1417,7 @@
     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);
 
@@ -1437,6 +1425,88 @@
 }
 
 /*
+ * [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->rteperminfos = child_pstate->p_rteperminfos;
+    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.
  */
@@ -1572,6 +1642,8 @@
     query->rtable = pstate->p_rtable;
     query->rteperminfos = pstate->p_rteperminfos;
     query->jointree = makeFromExpr(pstate->p_joinlist, NULL);
+    query->hasAggs = pstate->p_hasAggs;
+    query->hasSubLinks = pstate->p_hasSubLinks;
 
     return query;
 }
@@ -1845,12 +1917,7 @@
                                             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"),
@@ -2182,6 +2249,7 @@
     query->rteperminfos = pstate->p_rteperminfos;
     query->jointree = makeFromExpr(pstate->p_joinlist, NULL);
     query->hasAggs = pstate->p_hasAggs;
+    query->hasSubLinks = pstate->p_hasSubLinks;
 
     assign_query_collations(pstate, query);
 
@@ -2370,7 +2438,6 @@
 {
     ParseState *pstate = (ParseState *)cpstate;
     Query *query;
-    Node *self = clause->self;
     Node *where_qual = NULL;
 
     if (where)
@@ -2383,10 +2450,8 @@
 
         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)
         {
@@ -2401,79 +2466,15 @@
          * next clause
          */
         query->targetList = expandNSItemAttrs(pstate, pnsi, 0, true, -1);
-
         markTargetListOrigins(pstate, query->targetList);
-
         query->rtable = pstate->p_rtable;
         query->rteperminfos = pstate->p_rteperminfos;
 
         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
     {
@@ -2484,8 +2485,6 @@
     query->hasTargetSRFs = pstate->p_hasTargetSRFs;
     query->hasAggs = pstate->p_hasAggs;
 
-    assign_query_collations(pstate, query);
-
     return query;
 }
 
@@ -2493,6 +2492,7 @@
                                      cypher_clause *clause)
 {
     cypher_match *match_self = (cypher_match*) clause->self;
+    Node *where = match_self->where;
 
     if(!match_check_valid_label(match_self, cpstate))
     {
@@ -2510,10 +2510,40 @@
                                                      (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
  * for the join to use.
@@ -2709,18 +2739,6 @@
     {
         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;
@@ -2983,9 +3001,9 @@
  * 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;
 
@@ -3053,7 +3071,7 @@
      * 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);
@@ -3246,36 +3264,7 @@
 
     query->rtable = cpstate->pstate.p_rtable;
     query->rteperminfos = cpstate->pstate.p_rteperminfos;
-
-    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);
 }
 
 /*
@@ -5645,6 +5634,7 @@
     query->rteperminfos = pstate->p_rteperminfos;
     query->jointree = makeFromExpr(pstate->p_joinlist, NULL);
     query->hasAggs = pstate->p_hasAggs;
+    query->hasSubLinks = pstate->p_hasSubLinks;
 
     return query;
 }
@@ -6280,7 +6270,7 @@
  * 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,
@@ -6302,8 +6292,7 @@
            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
@@ -6507,6 +6496,12 @@
     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;
@@ -6657,7 +6652,6 @@
     query->rteperminfos = pstate->p_rteperminfos;
     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 16d6e35..19bc71d 100644
--- a/src/backend/parser/cypher_expr.c
+++ b/src/backend/parser/cypher_expr.c
@@ -98,8 +98,6 @@
 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 @@
             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 @@
         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))));
@@ -391,26 +385,8 @@
                 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;
@@ -1322,7 +1298,7 @@
     }
 
     /* 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);
 
     /*
@@ -2373,6 +2349,7 @@
         case EXPR_KIND_SELECT_TARGET:
         case EXPR_KIND_FROM_SUBSELECT:
         case EXPR_KIND_WHERE:
+        case EXPR_KIND_INSERT_TARGET:
             /* okay */
             break;
         default:
@@ -2412,7 +2389,7 @@
         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
@@ -2444,31 +2421,3 @@
 
     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 6cb15e5..a4d8f0f 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"
@@ -263,15 +264,9 @@
                                          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);
 
 %}
 %%
@@ -1062,8 +1057,6 @@
 
             n = make_ag_node(cypher_unwind);
             n->target = res;
-            n->where = NULL;
-            n->collect = NULL;
             $$ = (Node *) n;
         }
 
@@ -2067,6 +2060,7 @@
     | map
     | map_projection
     | list
+    | list_comprehension
     ;
 
 map:
@@ -2186,7 +2180,6 @@
 
             $$ = (Node *)n;
         }
-    | list_comprehension
     ;
 
 /*
@@ -2197,40 +2190,28 @@
 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);
         }
     ;
 
@@ -2881,6 +2862,10 @@
     {
         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,
@@ -3275,90 +3260,51 @@
     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;
+    sub = makeNode(SubLink);
+    sub->subLinkType = ARRAY_SUBLINK;
+    sub->subLinkId = 0;
+    sub->testexpr = NULL;
+    sub->subselect = (Node *)list_comp;
+    sub->location = location;
 
-    /* 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;
+    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 c32b46c..c2feb27 100644
--- a/src/backend/parser/cypher_item.c
+++ b/src/backend/parser/cypher_item.c
@@ -27,17 +27,14 @@
 #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_pnsi_attrs(ParseState *pstate, ParseNamespaceItem *pnsi,
 			       int sublevels_up, bool require_col_privs,
                                int location);
-bool has_a_cypher_list_comprehension_node(Node *expr);
 
 /* see transformTargetEntry() */
 TargetEntry *transform_cypher_item(cypher_parsestate *cpstate, Node *node,
@@ -45,17 +42,10 @@
                                    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);
 
@@ -63,143 +53,6 @@
                            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)
@@ -216,7 +69,6 @@
     {
         ResTarget *item = lfirst(li);
         TargetEntry *te;
-        bool has_list_comp = false;
 
         if (expand_star)
         {
@@ -244,48 +96,14 @@
                 }
             }
         }
-
-        /* 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;
+        /* transform the item */
+        te = transform_cypher_item(cpstate, item->val, NULL, expr_kind,
+                                   item->name, false);
 
-            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);
-
-            target_list = lappend(target_list, te);
-        }
+        target_list = lappend(target_list, te);
 
         /*
          * Did the transformed item contain an aggregate function? If it didn't,
@@ -300,58 +118,6 @@
         {
             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 090b178..86f41f2 100644
--- a/src/backend/utils/adt/agtype.c
+++ b/src/backend/utils/adt/agtype.c
@@ -1838,6 +1838,19 @@
     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.
  */
@@ -11916,7 +11929,6 @@
 Datum age_unnest(PG_FUNCTION_ARGS)
 {
     agtype *agtype_arg = NULL;
-    bool list_comprehension = false;
     ReturnSetInfo *rsi;
     Tuplestorestate *tuple_store;
     TupleDesc tupdesc;
@@ -11927,35 +11939,13 @@
     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,
@@ -12012,25 +12002,6 @@
         }
     }
 
-    /*
-     * 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 fe9c966..f0cc220 100644
--- a/src/include/nodes/ag_nodes.h
+++ b/src/include/nodes/ag_nodes.h
@@ -50,6 +50,7 @@
     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 @@
 }
 
 #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 fc9a974..f252701 100644
--- a/src/include/nodes/cypher_nodes.h
+++ b/src/include/nodes/cypher_nodes.h
@@ -68,7 +68,6 @@
 {
     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;
@@ -119,10 +118,6 @@
 {
     ExtensibleNode extensible;
     ResTarget *target;
-
-    /* for list comprehension */
-    Node *where;
-    Node *collect;
 } cypher_unwind;
 
 typedef struct cypher_merge
@@ -220,6 +215,15 @@
     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 4a04cf9..418d35f 100644
--- a/src/include/nodes/cypher_outfuncs.h
+++ b/src/include/nodes/cypher_outfuncs.h
@@ -48,6 +48,7 @@
 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 97616e5..b45a468 100644
--- a/src/include/parser/cypher_analyze.h
+++ b/src/include/parser/cypher_analyze.h
@@ -21,16 +21,22 @@
 #define AG_CYPHER_ANALYZE_H
 
 #include "parser/cypher_clause.h"
+#include "nodes/nodeFuncs.h"
 
-typedef bool (*cypher_expression_condition)( Node *expr);
+#define cypher_expr_tree_walker(n, w, c) \
+	cypher_expr_tree_walker_impl(n, (tree_walker_callback) (w), c)
+#define cypher_raw_expr_tree_walker(n, w, c) \
+	cypher_raw_expr_tree_walker_impl(n, (tree_walker_callback) (w), c)
 
 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);
+/* expr tree walker */
+bool cypher_expr_tree_walker_impl(Node *node,
+                                  bool (*walker)(Node *node, void *context),
+                                  void *context);
+bool cypher_raw_expr_tree_walker_impl(Node *node,
+                                      bool (*walker)(Node *node, void *context),
+                                      void *context);
 
 #endif
diff --git a/src/include/parser/cypher_clause.h b/src/include/parser/cypher_clause.h
index 51ba5cb..461a664 100644
--- a/src/include/parser/cypher_clause.h
+++ b/src/include/parser/cypher_clause.h
@@ -39,13 +39,4 @@
                                 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 ba4e7e9..92b6c95 100644
--- a/src/include/parser/cypher_item.h
+++ b/src/include/parser/cypher_item.h
@@ -26,6 +26,4 @@
 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 f716938..263ea19 100644
--- a/src/include/parser/cypher_parse_node.h
+++ b/src/include/parser/cypher_parse_node.h
@@ -50,7 +50,6 @@
      */
     bool exprHasAgg;
     bool p_opt_match;
-    bool p_list_comp;
 } cypher_parsestate;
 
 typedef struct errpos_ecb_state