ASTERIXDB-1897: fix MoveFreeVariableOperatorOutOfSubplanRule.

Change-Id: If3e9f7cba7ec20e51de9160df598ebcbe88c784e
Reviewed-on: https://asterix-gerrit.ics.uci.edu/1718
Reviewed-by: Till Westmann <tillw@apache.org>
Sonar-Qube: Jenkins <jenkins@fulliautomatix.ics.uci.edu>
Tested-by: Jenkins <jenkins@fulliautomatix.ics.uci.edu>
BAD: Jenkins <jenkins@fulliautomatix.ics.uci.edu>
Integration-Tests: Jenkins <jenkins@fulliautomatix.ics.uci.edu>
diff --git a/asterixdb/asterix-app/src/test/resources/optimizerts/results/query-ASTERIXDB-1203.plan b/asterixdb/asterix-app/src/test/resources/optimizerts/results/query-ASTERIXDB-1203.plan
index b090c13..bba166e 100644
--- a/asterixdb/asterix-app/src/test/resources/optimizerts/results/query-ASTERIXDB-1203.plan
+++ b/asterixdb/asterix-app/src/test/resources/optimizerts/results/query-ASTERIXDB-1203.plan
@@ -6,32 +6,33 @@
           -- UNNEST  |PARTITIONED|
             -- STREAM_PROJECT  |PARTITIONED|
               -- ASSIGN  |PARTITIONED|
-                -- SUBPLAN  |PARTITIONED|
-                        {
-                          -- AGGREGATE  |LOCAL|
-                            -- AGGREGATE  |LOCAL|
-                              -- STREAM_SELECT  |LOCAL|
-                                -- ASSIGN  |LOCAL|
-                                  -- UNNEST  |LOCAL|
-                                    -- ASSIGN  |LOCAL|
-                                      -- ASSIGN  |LOCAL|
-                                        -- STREAM_SELECT  |LOCAL|
-                                          -- ASSIGN  |LOCAL|
-                                            -- NESTED_TUPLE_SOURCE  |LOCAL|
-                        }
+                -- STREAM_PROJECT  |PARTITIONED|
                   -- SUBPLAN  |PARTITIONED|
                           {
                             -- AGGREGATE  |LOCAL|
-                              -- ASSIGN  |LOCAL|
+                              -- AGGREGATE  |LOCAL|
+                                -- STREAM_SELECT  |LOCAL|
+                                  -- ASSIGN  |LOCAL|
+                                    -- UNNEST  |LOCAL|
+                                      -- STREAM_SELECT  |LOCAL|
+                                        -- ASSIGN  |LOCAL|
+                                          -- NESTED_TUPLE_SOURCE  |LOCAL|
+                          }
+                    -- SUBPLAN  |PARTITIONED|
+                            {
+                              -- AGGREGATE  |LOCAL|
                                 -- ASSIGN  |LOCAL|
                                   -- ASSIGN  |LOCAL|
                                     -- ASSIGN  |LOCAL|
-                                      -- STREAM_SELECT  |LOCAL|
-                                        -- NESTED_TUPLE_SOURCE  |LOCAL|
-                          }
-                    -- ASSIGN  |PARTITIONED|
+                                      -- ASSIGN  |LOCAL|
+                                        -- STREAM_SELECT  |LOCAL|
+                                          -- NESTED_TUPLE_SOURCE  |LOCAL|
+                            }
                       -- STREAM_PROJECT  |PARTITIONED|
-                        -- ONE_TO_ONE_EXCHANGE  |PARTITIONED|
-                          -- DATASOURCE_SCAN  |PARTITIONED|
-                            -- ONE_TO_ONE_EXCHANGE  |PARTITIONED|
-                              -- EMPTY_TUPLE_SOURCE  |PARTITIONED|
+                        -- ASSIGN  |PARTITIONED|
+                          -- ASSIGN  |PARTITIONED|
+                            -- STREAM_PROJECT  |PARTITIONED|
+                              -- ONE_TO_ONE_EXCHANGE  |PARTITIONED|
+                                -- DATASOURCE_SCAN  |PARTITIONED|
+                                  -- ONE_TO_ONE_EXCHANGE  |PARTITIONED|
+                                    -- EMPTY_TUPLE_SOURCE  |PARTITIONED|
diff --git a/asterixdb/asterix-app/src/test/resources/optimizerts/results/query-ASTERIXDB-865.plan b/asterixdb/asterix-app/src/test/resources/optimizerts/results/query-ASTERIXDB-865.plan
index 914a23b..7457f6b 100644
--- a/asterixdb/asterix-app/src/test/resources/optimizerts/results/query-ASTERIXDB-865.plan
+++ b/asterixdb/asterix-app/src/test/resources/optimizerts/results/query-ASTERIXDB-865.plan
@@ -15,22 +15,22 @@
                                   -- AGGREGATE  |LOCAL|
                                     -- ASSIGN  |LOCAL|
                                       -- UNNEST  |LOCAL|
-                                        -- ASSIGN  |LOCAL|
-                                          -- STREAM_SELECT  |LOCAL|
-                                            -- ASSIGN  |LOCAL|
-                                              -- NESTED_TUPLE_SOURCE  |LOCAL|
+                                        -- STREAM_SELECT  |LOCAL|
+                                          -- ASSIGN  |LOCAL|
+                                            -- NESTED_TUPLE_SOURCE  |LOCAL|
                               }
-                        -- SUBPLAN  |PARTITIONED|
-                                {
-                                  -- AGGREGATE  |LOCAL|
-                                    -- ASSIGN  |LOCAL|
+                        -- STREAM_PROJECT  |PARTITIONED|
+                          -- SUBPLAN  |PARTITIONED|
+                                  {
+                                    -- AGGREGATE  |LOCAL|
                                       -- ASSIGN  |LOCAL|
                                         -- ASSIGN  |LOCAL|
-                                          -- STREAM_SELECT  |LOCAL|
-                                            -- NESTED_TUPLE_SOURCE  |LOCAL|
-                                }
-                          -- ASSIGN  |PARTITIONED|
-                            -- ONE_TO_ONE_EXCHANGE  |PARTITIONED|
-                              -- DATASOURCE_SCAN  |PARTITIONED|
-                                -- ONE_TO_ONE_EXCHANGE  |PARTITIONED|
-                                  -- EMPTY_TUPLE_SOURCE  |PARTITIONED|
+                                          -- ASSIGN  |LOCAL|
+                                            -- STREAM_SELECT  |LOCAL|
+                                              -- NESTED_TUPLE_SOURCE  |LOCAL|
+                                  }
+                            -- ASSIGN  |PARTITIONED|
+                              -- ONE_TO_ONE_EXCHANGE  |PARTITIONED|
+                                -- DATASOURCE_SCAN  |PARTITIONED|
+                                  -- ONE_TO_ONE_EXCHANGE  |PARTITIONED|
+                                    -- EMPTY_TUPLE_SOURCE  |PARTITIONED|
diff --git a/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/group-by/policy-02/policy-02.1.ddl.sqlpp b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/group-by/policy-02/policy-02.1.ddl.sqlpp
new file mode 100644
index 0000000..0242f1c
--- /dev/null
+++ b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/group-by/policy-02/policy-02.1.ddl.sqlpp
@@ -0,0 +1,29 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+DROP DATAVERSE gby IF EXISTS;
+CREATE DATAVERSE gby;
+
+USE gby;
+
+CREATE TYPE PolicyType AS {
+    id: UUID
+}
+
+CREATE DATASET policies(PolicyType) PRIMARY KEY id AUTOGENERATED;
diff --git a/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/group-by/policy-02/policy-02.2.update.sqlpp b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/group-by/policy-02/policy-02.2.update.sqlpp
new file mode 100644
index 0000000..3b865bb
--- /dev/null
+++ b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/group-by/policy-02/policy-02.2.update.sqlpp
@@ -0,0 +1,137 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+USE gby;
+
+INSERT INTO policies
+(
+  [ {
+    "policyno": "C123",
+    "state": "CA",
+    "zipcode": "96008",
+    "make": "Honda",
+    "accidents": [
+          {
+            "year": "2015",
+            "cost": 5000
+          },
+          {
+            "year": "2016",
+            "cost": 8000
+          },
+          {
+            "year": "2016",
+            "cost": 6000
+          }
+    ]
+  },
+  {
+    "policyno": "C124",
+    "state": "CA",
+    "zipcode": "96853",
+    "make": "Ford",
+    "accidents": [
+          {
+            "year": "2015",
+            "cost": 5000
+          },
+          {
+            "year": "2015",
+            "cost": 8000
+          },
+          {
+            "year": "2016",
+            "cost": 6000
+          }
+    ]
+  },
+  {
+    "policyno": "A123",
+    "state": "AZ",
+    "zipcode": "86008",
+    "make": "Honda",
+    "accidents": [
+        {
+          "year": "2015",
+          "cost": 5000
+        },
+        {
+          "year": "2016",
+          "cost": 8000
+        },
+        {
+          "year": "2016",
+          "cost": 6000
+        }
+    ]
+  },
+  {
+    "policyno": "A124",
+    "state": "AZ",
+    "zipcode": "86853",
+    "make": "Ford",
+    "accidents": [
+        {
+          "year": "2015",
+          "cost": 5000
+        },
+        {
+          "year": "2016",
+          "cost": 8000
+        },
+        {
+          "year": "2016",
+          "cost": 6000
+        }
+    ]
+  },
+  {
+    "policyno": "U123",
+    "state": "UT",
+    "zipcode": "66008",
+    "make": "Honda",
+    "accidents": [
+        {
+          "year": "2015",
+          "cost": 5000
+        },
+        {
+          "year": "2016",
+          "cost": 8000
+        },
+        {
+          "year": "2016",
+          "cost": 6000
+        }
+    ]
+  },
+  {
+    "policyno": "U124",
+    "state": "UT",
+    "zipcode": "66853",
+    "make": "Ford",
+    "accidents": [ ]
+  },
+  {
+    "policyno": "U125",
+    "state": "UT",
+    "zipcode": "66853",
+    "make": "Ford"
+  } ]
+);
\ No newline at end of file
diff --git a/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/group-by/policy-02/policy-02.3.query.sqlpp b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/group-by/policy-02/policy-02.3.query.sqlpp
new file mode 100644
index 0000000..5a524a2
--- /dev/null
+++ b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/group-by/policy-02/policy-02.3.query.sqlpp
@@ -0,0 +1,36 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+USE gby;
+
+FROM policies p
+GROUP BY state GROUP AS g
+SELECT state,
+       (
+         FROM g
+         SELECT VALUE SUM(
+                  (
+                    FROM g.p.accidents a
+                    WHERE a.year = "2016"
+                    SELECT VALUE COUNT(*)
+                   )[0]
+                )
+       )[0]  / (COUNT(*) * 1.0 ) AS risk
+ORDER BY risk DESC
+LIMIT 5;
diff --git a/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/group-by/policy-03/policy-03.1.ddl.sqlpp b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/group-by/policy-03/policy-03.1.ddl.sqlpp
new file mode 100644
index 0000000..0242f1c
--- /dev/null
+++ b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/group-by/policy-03/policy-03.1.ddl.sqlpp
@@ -0,0 +1,29 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+DROP DATAVERSE gby IF EXISTS;
+CREATE DATAVERSE gby;
+
+USE gby;
+
+CREATE TYPE PolicyType AS {
+    id: UUID
+}
+
+CREATE DATASET policies(PolicyType) PRIMARY KEY id AUTOGENERATED;
diff --git a/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/group-by/policy-03/policy-03.2.update.sqlpp b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/group-by/policy-03/policy-03.2.update.sqlpp
new file mode 100644
index 0000000..3b865bb
--- /dev/null
+++ b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/group-by/policy-03/policy-03.2.update.sqlpp
@@ -0,0 +1,137 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+USE gby;
+
+INSERT INTO policies
+(
+  [ {
+    "policyno": "C123",
+    "state": "CA",
+    "zipcode": "96008",
+    "make": "Honda",
+    "accidents": [
+          {
+            "year": "2015",
+            "cost": 5000
+          },
+          {
+            "year": "2016",
+            "cost": 8000
+          },
+          {
+            "year": "2016",
+            "cost": 6000
+          }
+    ]
+  },
+  {
+    "policyno": "C124",
+    "state": "CA",
+    "zipcode": "96853",
+    "make": "Ford",
+    "accidents": [
+          {
+            "year": "2015",
+            "cost": 5000
+          },
+          {
+            "year": "2015",
+            "cost": 8000
+          },
+          {
+            "year": "2016",
+            "cost": 6000
+          }
+    ]
+  },
+  {
+    "policyno": "A123",
+    "state": "AZ",
+    "zipcode": "86008",
+    "make": "Honda",
+    "accidents": [
+        {
+          "year": "2015",
+          "cost": 5000
+        },
+        {
+          "year": "2016",
+          "cost": 8000
+        },
+        {
+          "year": "2016",
+          "cost": 6000
+        }
+    ]
+  },
+  {
+    "policyno": "A124",
+    "state": "AZ",
+    "zipcode": "86853",
+    "make": "Ford",
+    "accidents": [
+        {
+          "year": "2015",
+          "cost": 5000
+        },
+        {
+          "year": "2016",
+          "cost": 8000
+        },
+        {
+          "year": "2016",
+          "cost": 6000
+        }
+    ]
+  },
+  {
+    "policyno": "U123",
+    "state": "UT",
+    "zipcode": "66008",
+    "make": "Honda",
+    "accidents": [
+        {
+          "year": "2015",
+          "cost": 5000
+        },
+        {
+          "year": "2016",
+          "cost": 8000
+        },
+        {
+          "year": "2016",
+          "cost": 6000
+        }
+    ]
+  },
+  {
+    "policyno": "U124",
+    "state": "UT",
+    "zipcode": "66853",
+    "make": "Ford",
+    "accidents": [ ]
+  },
+  {
+    "policyno": "U125",
+    "state": "UT",
+    "zipcode": "66853",
+    "make": "Ford"
+  } ]
+);
\ No newline at end of file
diff --git a/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/group-by/policy-03/policy-03.3.query.sqlpp b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/group-by/policy-03/policy-03.3.query.sqlpp
new file mode 100644
index 0000000..4d12f37
--- /dev/null
+++ b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/group-by/policy-03/policy-03.3.query.sqlpp
@@ -0,0 +1,37 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+USE gby;
+
+FROM policies p
+GROUP BY state GROUP AS g
+SELECT state,
+       COUNT(*) num_policies,
+       (
+         FROM g
+         SELECT VALUE SUM(
+                  (
+                    FROM g.p.accidents a
+                    WHERE a.year = "2016"
+                    SELECT VALUE COUNT(*)
+                   )[0]
+                )
+       )[0]  / (COUNT(*) * 1.0 ) AS risk
+ORDER BY risk DESC
+LIMIT 5;
diff --git a/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/group-by/policy-04/policy-04.1.ddl.sqlpp b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/group-by/policy-04/policy-04.1.ddl.sqlpp
new file mode 100644
index 0000000..0242f1c
--- /dev/null
+++ b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/group-by/policy-04/policy-04.1.ddl.sqlpp
@@ -0,0 +1,29 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+DROP DATAVERSE gby IF EXISTS;
+CREATE DATAVERSE gby;
+
+USE gby;
+
+CREATE TYPE PolicyType AS {
+    id: UUID
+}
+
+CREATE DATASET policies(PolicyType) PRIMARY KEY id AUTOGENERATED;
diff --git a/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/group-by/policy-04/policy-04.2.update.sqlpp b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/group-by/policy-04/policy-04.2.update.sqlpp
new file mode 100644
index 0000000..3b865bb
--- /dev/null
+++ b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/group-by/policy-04/policy-04.2.update.sqlpp
@@ -0,0 +1,137 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+USE gby;
+
+INSERT INTO policies
+(
+  [ {
+    "policyno": "C123",
+    "state": "CA",
+    "zipcode": "96008",
+    "make": "Honda",
+    "accidents": [
+          {
+            "year": "2015",
+            "cost": 5000
+          },
+          {
+            "year": "2016",
+            "cost": 8000
+          },
+          {
+            "year": "2016",
+            "cost": 6000
+          }
+    ]
+  },
+  {
+    "policyno": "C124",
+    "state": "CA",
+    "zipcode": "96853",
+    "make": "Ford",
+    "accidents": [
+          {
+            "year": "2015",
+            "cost": 5000
+          },
+          {
+            "year": "2015",
+            "cost": 8000
+          },
+          {
+            "year": "2016",
+            "cost": 6000
+          }
+    ]
+  },
+  {
+    "policyno": "A123",
+    "state": "AZ",
+    "zipcode": "86008",
+    "make": "Honda",
+    "accidents": [
+        {
+          "year": "2015",
+          "cost": 5000
+        },
+        {
+          "year": "2016",
+          "cost": 8000
+        },
+        {
+          "year": "2016",
+          "cost": 6000
+        }
+    ]
+  },
+  {
+    "policyno": "A124",
+    "state": "AZ",
+    "zipcode": "86853",
+    "make": "Ford",
+    "accidents": [
+        {
+          "year": "2015",
+          "cost": 5000
+        },
+        {
+          "year": "2016",
+          "cost": 8000
+        },
+        {
+          "year": "2016",
+          "cost": 6000
+        }
+    ]
+  },
+  {
+    "policyno": "U123",
+    "state": "UT",
+    "zipcode": "66008",
+    "make": "Honda",
+    "accidents": [
+        {
+          "year": "2015",
+          "cost": 5000
+        },
+        {
+          "year": "2016",
+          "cost": 8000
+        },
+        {
+          "year": "2016",
+          "cost": 6000
+        }
+    ]
+  },
+  {
+    "policyno": "U124",
+    "state": "UT",
+    "zipcode": "66853",
+    "make": "Ford",
+    "accidents": [ ]
+  },
+  {
+    "policyno": "U125",
+    "state": "UT",
+    "zipcode": "66853",
+    "make": "Ford"
+  } ]
+);
\ No newline at end of file
diff --git a/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/group-by/policy-04/policy-04.3.query.sqlpp b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/group-by/policy-04/policy-04.3.query.sqlpp
new file mode 100644
index 0000000..e2e3f23
--- /dev/null
+++ b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/group-by/policy-04/policy-04.3.query.sqlpp
@@ -0,0 +1,47 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+USE gby;
+
+FROM policies p
+GROUP BY state GROUP AS g
+SELECT state,
+       COUNT(*) num_policies,
+       (
+         FROM g
+         SELECT VALUE SUM(
+                  (
+                    FROM g.p.accidents a
+                    WHERE a.year = "2016"
+                    SELECT VALUE COUNT(*)
+                  )[0]
+                )
+       )[0]  / (COUNT(*) * 1.0 ) AS risk_2016,
+       (
+         FROM g
+         SELECT VALUE SUM(
+                  (
+                    FROM g.p.accidents a
+                    WHERE a.year = "2015"
+                    SELECT VALUE COUNT(*)
+                  )[0]
+                )
+       )[0] / (COUNT(*) * 1.0 ) AS risk_2015
+ORDER BY risk_2016 DESC
+LIMIT 5;
diff --git a/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/group-by/policy/policy.1.ddl.sqlpp b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/group-by/policy/policy.1.ddl.sqlpp
new file mode 100644
index 0000000..0242f1c
--- /dev/null
+++ b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/group-by/policy/policy.1.ddl.sqlpp
@@ -0,0 +1,29 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+DROP DATAVERSE gby IF EXISTS;
+CREATE DATAVERSE gby;
+
+USE gby;
+
+CREATE TYPE PolicyType AS {
+    id: UUID
+}
+
+CREATE DATASET policies(PolicyType) PRIMARY KEY id AUTOGENERATED;
diff --git a/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/group-by/policy/policy.2.update.sqlpp b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/group-by/policy/policy.2.update.sqlpp
new file mode 100644
index 0000000..3b865bb
--- /dev/null
+++ b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/group-by/policy/policy.2.update.sqlpp
@@ -0,0 +1,137 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+USE gby;
+
+INSERT INTO policies
+(
+  [ {
+    "policyno": "C123",
+    "state": "CA",
+    "zipcode": "96008",
+    "make": "Honda",
+    "accidents": [
+          {
+            "year": "2015",
+            "cost": 5000
+          },
+          {
+            "year": "2016",
+            "cost": 8000
+          },
+          {
+            "year": "2016",
+            "cost": 6000
+          }
+    ]
+  },
+  {
+    "policyno": "C124",
+    "state": "CA",
+    "zipcode": "96853",
+    "make": "Ford",
+    "accidents": [
+          {
+            "year": "2015",
+            "cost": 5000
+          },
+          {
+            "year": "2015",
+            "cost": 8000
+          },
+          {
+            "year": "2016",
+            "cost": 6000
+          }
+    ]
+  },
+  {
+    "policyno": "A123",
+    "state": "AZ",
+    "zipcode": "86008",
+    "make": "Honda",
+    "accidents": [
+        {
+          "year": "2015",
+          "cost": 5000
+        },
+        {
+          "year": "2016",
+          "cost": 8000
+        },
+        {
+          "year": "2016",
+          "cost": 6000
+        }
+    ]
+  },
+  {
+    "policyno": "A124",
+    "state": "AZ",
+    "zipcode": "86853",
+    "make": "Ford",
+    "accidents": [
+        {
+          "year": "2015",
+          "cost": 5000
+        },
+        {
+          "year": "2016",
+          "cost": 8000
+        },
+        {
+          "year": "2016",
+          "cost": 6000
+        }
+    ]
+  },
+  {
+    "policyno": "U123",
+    "state": "UT",
+    "zipcode": "66008",
+    "make": "Honda",
+    "accidents": [
+        {
+          "year": "2015",
+          "cost": 5000
+        },
+        {
+          "year": "2016",
+          "cost": 8000
+        },
+        {
+          "year": "2016",
+          "cost": 6000
+        }
+    ]
+  },
+  {
+    "policyno": "U124",
+    "state": "UT",
+    "zipcode": "66853",
+    "make": "Ford",
+    "accidents": [ ]
+  },
+  {
+    "policyno": "U125",
+    "state": "UT",
+    "zipcode": "66853",
+    "make": "Ford"
+  } ]
+);
\ No newline at end of file
diff --git a/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/group-by/policy/policy.3.query.sqlpp b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/group-by/policy/policy.3.query.sqlpp
new file mode 100644
index 0000000..2259be8
--- /dev/null
+++ b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/group-by/policy/policy.3.query.sqlpp
@@ -0,0 +1,23 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+USE gby;
+
+FROM policies p
+SELECT VALUE SUM( (FROM p.accidents a SELECT VALUE COUNT(*))[0] );
diff --git a/asterixdb/asterix-app/src/test/resources/runtimets/results/group-by/policy-02/policy-02.1.adm b/asterixdb/asterix-app/src/test/resources/runtimets/results/group-by/policy-02/policy-02.1.adm
new file mode 100644
index 0000000..4ab977a
--- /dev/null
+++ b/asterixdb/asterix-app/src/test/resources/runtimets/results/group-by/policy-02/policy-02.1.adm
@@ -0,0 +1,3 @@
+{ "state": "AZ", "risk": 2.0 }
+{ "state": "CA", "risk": 1.5 }
+{ "state": "UT", "risk": 0.6666666666666666 }
diff --git a/asterixdb/asterix-app/src/test/resources/runtimets/results/group-by/policy-03/policy-03.1.adm b/asterixdb/asterix-app/src/test/resources/runtimets/results/group-by/policy-03/policy-03.1.adm
new file mode 100644
index 0000000..9b2db25
--- /dev/null
+++ b/asterixdb/asterix-app/src/test/resources/runtimets/results/group-by/policy-03/policy-03.1.adm
@@ -0,0 +1,3 @@
+{ "num_policies": 2, "state": "AZ", "risk": 2.0 }
+{ "num_policies": 2, "state": "CA", "risk": 1.5 }
+{ "num_policies": 3, "state": "UT", "risk": 0.6666666666666666 }
diff --git a/asterixdb/asterix-app/src/test/resources/runtimets/results/group-by/policy-04/policy-04.1.adm b/asterixdb/asterix-app/src/test/resources/runtimets/results/group-by/policy-04/policy-04.1.adm
new file mode 100644
index 0000000..e52dcc0
--- /dev/null
+++ b/asterixdb/asterix-app/src/test/resources/runtimets/results/group-by/policy-04/policy-04.1.adm
@@ -0,0 +1,3 @@
+{ "num_policies": 2, "state": "AZ", "risk_2016": 2.0, "risk_2015": 1.0 }
+{ "num_policies": 2, "state": "CA", "risk_2016": 1.5, "risk_2015": 1.5 }
+{ "num_policies": 3, "state": "UT", "risk_2016": 0.6666666666666666, "risk_2015": 0.3333333333333333 }
diff --git a/asterixdb/asterix-app/src/test/resources/runtimets/results/group-by/policy/policy.1.adm b/asterixdb/asterix-app/src/test/resources/runtimets/results/group-by/policy/policy.1.adm
new file mode 100644
index 0000000..60d3b2f
--- /dev/null
+++ b/asterixdb/asterix-app/src/test/resources/runtimets/results/group-by/policy/policy.1.adm
@@ -0,0 +1 @@
+15
diff --git a/asterixdb/asterix-app/src/test/resources/runtimets/testsuite_sqlpp.xml b/asterixdb/asterix-app/src/test/resources/runtimets/testsuite_sqlpp.xml
index 8638d83..cf1ea5b 100644
--- a/asterixdb/asterix-app/src/test/resources/runtimets/testsuite_sqlpp.xml
+++ b/asterixdb/asterix-app/src/test/resources/runtimets/testsuite_sqlpp.xml
@@ -2736,6 +2736,26 @@
         <output-dir compare="Text">core-02</output-dir>
       </compilation-unit>
     </test-case>
+    <test-case FilePath="group-by">
+      <compilation-unit name="policy">
+        <output-dir compare="Text">policy</output-dir>
+      </compilation-unit>
+    </test-case>
+    <test-case FilePath="group-by">
+      <compilation-unit name="policy-02">
+        <output-dir compare="Text">policy-02</output-dir>
+      </compilation-unit>
+    </test-case>
+    <test-case FilePath="group-by">
+      <compilation-unit name="policy-03">
+        <output-dir compare="Text">policy-03</output-dir>
+      </compilation-unit>
+    </test-case>
+    <test-case FilePath="group-by">
+      <compilation-unit name="policy-04">
+        <output-dir compare="Text">policy-04</output-dir>
+      </compilation-unit>
+    </test-case>
   </test-group>
   <test-group name="index-join">
     <test-case FilePath="index-join">
diff --git a/hyracks-fullstack/algebricks/algebricks-rewriter/src/main/java/org/apache/hyracks/algebricks/rewriter/rules/AbstractDecorrelationRule.java b/hyracks-fullstack/algebricks/algebricks-rewriter/src/main/java/org/apache/hyracks/algebricks/rewriter/rules/AbstractDecorrelationRule.java
index 734090e..7802b29 100644
--- a/hyracks-fullstack/algebricks/algebricks-rewriter/src/main/java/org/apache/hyracks/algebricks/rewriter/rules/AbstractDecorrelationRule.java
+++ b/hyracks-fullstack/algebricks/algebricks-rewriter/src/main/java/org/apache/hyracks/algebricks/rewriter/rules/AbstractDecorrelationRule.java
@@ -49,7 +49,7 @@
         return false;
     }
 
-    protected boolean descOrSelfIsScanOrJoin(AbstractLogicalOperator op2) {
+    protected boolean descOrSelfIsScanOrJoin(ILogicalOperator op2) {
         LogicalOperatorTag t = op2.getOperatorTag();
         if (isScanOrJoin(t)) {
             return true;
@@ -57,7 +57,7 @@
         if (op2.getInputs().size() != 1) {
             return false;
         }
-        AbstractLogicalOperator alo = (AbstractLogicalOperator) op2.getInputs().get(0).getValue();
+        ILogicalOperator alo = op2.getInputs().get(0).getValue();
         if (descOrSelfIsScanOrJoin(alo)) {
             return true;
         }
diff --git a/hyracks-fullstack/algebricks/algebricks-rewriter/src/main/java/org/apache/hyracks/algebricks/rewriter/rules/subplan/MoveFreeVariableOperatorOutOfSubplanRule.java b/hyracks-fullstack/algebricks/algebricks-rewriter/src/main/java/org/apache/hyracks/algebricks/rewriter/rules/subplan/MoveFreeVariableOperatorOutOfSubplanRule.java
index d176d05..fa893d5 100644
--- a/hyracks-fullstack/algebricks/algebricks-rewriter/src/main/java/org/apache/hyracks/algebricks/rewriter/rules/subplan/MoveFreeVariableOperatorOutOfSubplanRule.java
+++ b/hyracks-fullstack/algebricks/algebricks-rewriter/src/main/java/org/apache/hyracks/algebricks/rewriter/rules/subplan/MoveFreeVariableOperatorOutOfSubplanRule.java
@@ -19,11 +19,9 @@
 package org.apache.hyracks.algebricks.rewriter.rules.subplan;
 
 import java.util.HashSet;
-import java.util.ListIterator;
 import java.util.Set;
 
 import org.apache.commons.lang3.mutable.Mutable;
-
 import org.apache.hyracks.algebricks.common.exceptions.AlgebricksException;
 import org.apache.hyracks.algebricks.core.algebra.base.ILogicalOperator;
 import org.apache.hyracks.algebricks.core.algebra.base.ILogicalPlan;
@@ -31,9 +29,9 @@
 import org.apache.hyracks.algebricks.core.algebra.base.LogicalOperatorTag;
 import org.apache.hyracks.algebricks.core.algebra.base.LogicalVariable;
 import org.apache.hyracks.algebricks.core.algebra.operators.logical.AbstractLogicalOperator;
+import org.apache.hyracks.algebricks.core.algebra.operators.logical.ProjectOperator;
 import org.apache.hyracks.algebricks.core.algebra.operators.logical.SubplanOperator;
 import org.apache.hyracks.algebricks.core.algebra.operators.logical.visitors.VariableUtilities;
-import org.apache.hyracks.algebricks.core.algebra.util.OperatorPropertiesUtil;
 import org.apache.hyracks.algebricks.rewriter.rules.AbstractDecorrelationRule;
 
 /**
@@ -71,114 +69,87 @@
     @Override
     public boolean rewritePost(Mutable<ILogicalOperator> opRef, IOptimizationContext context)
             throws AlgebricksException {
-        AbstractLogicalOperator op0 = (AbstractLogicalOperator) opRef.getValue();
-        if (op0.getOperatorTag() != LogicalOperatorTag.SUBPLAN) {
+        AbstractLogicalOperator op = (AbstractLogicalOperator) opRef.getValue();
+        if (op.getOperatorTag() != LogicalOperatorTag.SUBPLAN) {
             return false;
         }
-        SubplanOperator subplan = (SubplanOperator) op0;
+        SubplanOperator subplanOp = (SubplanOperator) op;
+        ILogicalOperator inputOp = subplanOp.getInputs().get(0).getValue();
+        Set<LogicalVariable> liveVarsBeforeSubplan = new HashSet<>();
+        VariableUtilities.getLiveVariables(inputOp, liveVarsBeforeSubplan);
 
-        Mutable<ILogicalOperator> leftRef = subplan.getInputs().get(0);
-        if (((AbstractLogicalOperator) leftRef.getValue()).getOperatorTag() == LogicalOperatorTag.EMPTYTUPLESOURCE) {
-            return false;
-        }
-
-        ListIterator<ILogicalPlan> plansIter = subplan.getNestedPlans().listIterator();
-        ILogicalPlan p = null;
-        while (plansIter.hasNext()) {
-            p = plansIter.next();
-        }
-        if (p == null) {
-            return false;
-        }
-        if (p.getRoots().size() != 1) {
-            return false;
-        }
-        Mutable<ILogicalOperator> opRef1 = p.getRoots().get(0);
-
-        //The root operator will not be movable. Start with the second op
-        AbstractLogicalOperator op1 = (AbstractLogicalOperator) opRef1.getValue();
-        if (op1.getInputs().size() != 1) {
-            return false;
-        }
-        Mutable<ILogicalOperator> op2Ref = op1.getInputs().get(0);
-
-        //Get all variables that come from outside of the loop
-        Set<LogicalVariable> free = new HashSet<LogicalVariable>();
-        OperatorPropertiesUtil.getFreeVariablesInSelfOrDesc(op1, free);
-
-        while (op2Ref != null) {
-            //Get the operator that we want to look at
-            AbstractLogicalOperator op2 = (AbstractLogicalOperator) op2Ref.getValue();
-
-            //Make sure we are looking at subplan with a scan/join
-            if (op2.getInputs().size() != 1 || !descOrSelfIsScanOrJoin(op2)) {
-                return false;
-            }
-            boolean notApplicable = false;
-
-            //Get its used variables
-            Set<LogicalVariable> used = new HashSet<LogicalVariable>();
-
-            //not movable if the operator is not an assign or subplan
-            //Might be helpful in the future for other operations in the future
-            if (movableOperator(op2.getOperatorTag())) {
-                if (op2.getOperatorTag() == LogicalOperatorTag.ASSIGN) {
-                    VariableUtilities.getUsedVariables(op2, used);
-                } else if (op2.getOperatorTag() == LogicalOperatorTag.SUBPLAN) {
-                    // Nested plan must have an aggregate root.
-                    ListIterator<ILogicalPlan> subplansIter = ((SubplanOperator) op2).getNestedPlans().listIterator();
-                    ILogicalPlan plan = null;
-                    while (subplansIter.hasNext()) {
-                        plan = subplansIter.next();
-                    }
-                    if (plan == null) {
-                        return false;
-                    }
-                    if (plan.getRoots().size() != 1) {
-                        return false;
-                    }
-                    ILogicalOperator op3 = plan.getRoots().get(0).getValue();
-                    if (op3.getOperatorTag() != LogicalOperatorTag.AGGREGATE) {
-                        return false;
-                    }
-                    // Used variables do not include ones created in the subplan.
-                    VariableUtilities.getUsedVariables(op2, used);
-                    Set<LogicalVariable> subplanProducedAndDown = new HashSet<LogicalVariable>();
-                    VariableUtilities.getProducedVariablesInDescendantsAndSelf(op3, subplanProducedAndDown);
-                    used.removeAll(subplanProducedAndDown);
-                } else {
-                    notApplicable = true;
+        boolean changed = false;
+        for (ILogicalPlan plan : subplanOp.getNestedPlans()) {
+            for (Mutable<ILogicalOperator> rootRef : plan.getRoots()) {
+                //Make sure we are looking at subplan with a scan/join
+                if (!descOrSelfIsScanOrJoin(rootRef.getValue())) {
+                    continue;
                 }
-            } else {
-                notApplicable = true;
-            }
+                Mutable<ILogicalOperator> currentOpRef = rootRef;
+                ILogicalOperator currentOp = rootRef.getValue();
+                while (currentOp.getInputs().size() == 1) {
+                    Mutable<ILogicalOperator> childOpRef = currentOp.getInputs().get(0);
+                    ILogicalOperator childOp = childOpRef.getValue();
 
-            //Make sure that all of its used variables come from outside
-            for (LogicalVariable var : used) {
-                if (!free.contains(var)) {
-                    notApplicable = true;
+                    // Try to move operators that only uses free variables out of the subplan.
+                    if (movableOperator(currentOp.getOperatorTag())
+                            && independentOperator(currentOp, liveVarsBeforeSubplan)
+                            && producedVariablesCanbePropagated(currentOp)) {
+                        extractOperator(subplanOp, inputOp, currentOpRef);
+                        inputOp = currentOp;
+                        changed = true;
+                    } else {
+                        // in the case the operator is not moved, move currentOpRef to childOpRef.
+                        currentOpRef = childOpRef;
+                    }
+                    currentOp = childOp;
                 }
             }
-
-            if (notApplicable) {
-                op2Ref = op2.getInputs().get(0);
-            } else {
-                //Make the input of op2 be the input of op1
-                op2Ref.setValue(op2.getInputs().get(0).getValue());
-
-                //Make the outside of the subplan the input of op2
-                Mutable<ILogicalOperator> outsideRef = op2.getInputs().get(0);
-                outsideRef.setValue(op0.getInputs().get(0).getValue());
-
-                //Make op2 the input of the subplan
-                Mutable<ILogicalOperator> op2OutsideRef = op0.getInputs().get(0);
-                op2OutsideRef.setValue(op2);
-
-                return true;
-            }
-
         }
-        return false;
+        return changed;
+    }
+
+    // Checks whether the current operator is independent of the nested input pipeline in the subplan.
+    private boolean independentOperator(ILogicalOperator op, Set<LogicalVariable> liveVarsBeforeSubplan)
+            throws AlgebricksException {
+        Set<LogicalVariable> usedVars = new HashSet<>();
+        VariableUtilities.getUsedVariables(op, usedVars);
+        return liveVarsBeforeSubplan.containsAll(usedVars);
+    }
+
+    // Checks whether there is a variable killing operator in the nested pipeline
+    private boolean producedVariablesCanbePropagated(ILogicalOperator operator) throws AlgebricksException {
+        ILogicalOperator currentOperator = operator;
+        // Makes sure the produced variables by operator are not killed in the nested pipeline below it.
+        while (!currentOperator.getInputs().isEmpty()) {
+            LogicalOperatorTag operatorTag = currentOperator.getOperatorTag();
+            if (operatorTag == LogicalOperatorTag.AGGREGATE || operatorTag == LogicalOperatorTag.RUNNINGAGGREGATE
+                    || operatorTag == LogicalOperatorTag.GROUP) {
+                        return false;
+            }
+            if (operatorTag == LogicalOperatorTag.PROJECT) {
+                Set<LogicalVariable> producedVars = new HashSet<>();
+                VariableUtilities.getProducedVariables(currentOperator, producedVars);
+                ProjectOperator projectOperator = (ProjectOperator) currentOperator;
+                if (!projectOperator.getVariables().containsAll(producedVars)) {
+                    return false;
+                }
+            }
+            currentOperator = currentOperator.getInputs().get(0).getValue();
+        }
+        return true;
+    }
+
+    // Extracts the current operator out of the subplan.
+    private void extractOperator(ILogicalOperator subplan, ILogicalOperator inputOp,
+            Mutable<ILogicalOperator> currentOpRef) {
+        // Removes currentOp from the nested pipeline inside subplan.
+        ILogicalOperator currentOp = currentOpRef.getValue();
+        currentOpRef.setValue(currentOp.getInputs().get(0).getValue());
+
+        // Inserts currentOp between subplanOp and inputOp.
+        subplan.getInputs().get(0).setValue(currentOp);
+        currentOp.getInputs().get(0).setValue(inputOp);
     }
 
     protected boolean movableOperator(LogicalOperatorTag operatorTag) {