[NO ISSUE][COMP] Implement FILTER clause in aggregates

- user model changes: yes
- storage format changes: no
- interface changes: no

Details:
- Implement FILTER subclause in SQL aggregate function calls
- Add testcases and documentation

Change-Id: Ie346a09db51907757df78575d2145085b1d89782
Reviewed-on: https://asterix-gerrit.ics.uci.edu/c/asterixdb/+/6424
Integration-Tests: Jenkins <jenkins@fulliautomatix.ics.uci.edu>
Tested-by: Jenkins <jenkins@fulliautomatix.ics.uci.edu>
Reviewed-by: Ali Alsuliman <ali.al.solaiman@gmail.com>
diff --git a/asterixdb/asterix-algebra/src/main/java/org/apache/asterix/translator/LangExpressionToPlanTranslator.java b/asterixdb/asterix-algebra/src/main/java/org/apache/asterix/translator/LangExpressionToPlanTranslator.java
index 2706ac3..3bd8f42 100644
--- a/asterixdb/asterix-algebra/src/main/java/org/apache/asterix/translator/LangExpressionToPlanTranslator.java
+++ b/asterixdb/asterix-algebra/src/main/java/org/apache/asterix/translator/LangExpressionToPlanTranslator.java
@@ -848,6 +848,10 @@
             throw new CompilationException(ErrorCode.UNKNOWN_FUNCTION, sourceLoc, signature.getName());
         }
 
+        if (fcall.hasAggregateFilterExpr()) {
+            throw new CompilationException(ErrorCode.COMPILATION_ILLEGAL_USE_OF_FILTER_CLAUSE, sourceLoc);
+        }
+
         // Put hints into function call expr.
         if (fcall.hasHints()) {
             for (IExpressionAnnotation hint : fcall.getHints()) {
diff --git a/asterixdb/asterix-algebra/src/main/java/org/apache/asterix/translator/SqlppExpressionToPlanTranslator.java b/asterixdb/asterix-algebra/src/main/java/org/apache/asterix/translator/SqlppExpressionToPlanTranslator.java
index 59642cb..b689e8f 100644
--- a/asterixdb/asterix-algebra/src/main/java/org/apache/asterix/translator/SqlppExpressionToPlanTranslator.java
+++ b/asterixdb/asterix-algebra/src/main/java/org/apache/asterix/translator/SqlppExpressionToPlanTranslator.java
@@ -1090,6 +1090,10 @@
         boolean allowFromFirstLast = isWin && BuiltinFunctions.builtinFunctionHasProperty(fi,
                 BuiltinFunctions.WindowFunctionProperty.ALLOW_FROM_FIRST_LAST);
 
+        if (winExpr.hasAggregateFilterExpr()) {
+            throw new CompilationException(ErrorCode.COMPILATION_ILLEGAL_USE_OF_FILTER_CLAUSE, sourceLoc);
+        }
+
         Mutable<ILogicalOperator> currentOpRef = tupSource;
 
         List<Mutable<ILogicalExpression>> partExprListOut = Collections.emptyList();
diff --git a/asterixdb/asterix-app/src/test/java/org/apache/asterix/test/runtime/SqlppRQGGroupingSetsIT.java b/asterixdb/asterix-app/src/test/java/org/apache/asterix/test/runtime/SqlppRQGGroupingSetsIT.java
index 838d980..b1125a5 100644
--- a/asterixdb/asterix-app/src/test/java/org/apache/asterix/test/runtime/SqlppRQGGroupingSetsIT.java
+++ b/asterixdb/asterix-app/src/test/java/org/apache/asterix/test/runtime/SqlppRQGGroupingSetsIT.java
@@ -292,7 +292,12 @@
             selectClause.append(String.format("GROUPING(%s) AS grp", String.join(",", randomize(allColumns, random))))
                     .append(',');
         }
-        selectClause.append(String.format("SUM(%s) AS agg_sum", UNIQUE_1));
+        String agg = String.format("SUM(%s)", UNIQUE_1);
+        if (random.nextInt(3) == 0) {
+            int filterLimit = 1 + random.nextInt(9999);
+            agg = String.format("%s FILTER(WHERE %s < %d)", agg, UNIQUE_2, filterLimit);
+        }
+        selectClause.append(String.format("%s AS agg_sum", agg));
 
         String groupingElementText = groupingElements.isEmpty() ? "()" : String.join(",", groupingElements);
         String groupbyClause = String.format("GROUP BY %s", groupingElementText);
diff --git a/asterixdb/asterix-app/src/test/resources/optimizerts/queries/aggregate-subclause/agg_filter_01/agg_filter_01.3.sqlpp b/asterixdb/asterix-app/src/test/resources/optimizerts/queries/aggregate-subclause/agg_filter_01/agg_filter_01.3.sqlpp
new file mode 100644
index 0000000..f9f7539
--- /dev/null
+++ b/asterixdb/asterix-app/src/test/resources/optimizerts/queries/aggregate-subclause/agg_filter_01/agg_filter_01.3.sqlpp
@@ -0,0 +1,53 @@
+/*
+ * 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 test if exists;
+create  dataverse test;
+
+use test;
+
+create type tenkType as closed {
+  unique1         : integer,
+  unique2         : integer,
+  two             : integer,
+  four            : integer,
+  ten             : integer,
+  twenty          : integer,
+  hundred         : integer,
+  thousand        : integer,
+  twothous        : integer,
+  fivethous       : integer,
+  tenthous        : integer,
+  odd100          : integer,
+  even100         : integer,
+  stringu1        : string,
+  stringu2        : string,
+  string4         : string
+};
+
+create dataset tenk(tenkType) primary key unique2;
+
+/*
+ * Test aggregate filter without GROUP BY clause
+ */
+
+select
+  count(*) filter(where four > 0) as cnt,
+  sum(four) filter(where four > 0) as sm
+from tenk;
\ No newline at end of file
diff --git a/asterixdb/asterix-app/src/test/resources/optimizerts/queries/aggregate-subclause/agg_filter_01/agg_filter_01.4.sqlpp b/asterixdb/asterix-app/src/test/resources/optimizerts/queries/aggregate-subclause/agg_filter_01/agg_filter_01.4.sqlpp
new file mode 100644
index 0000000..bfd70f6
--- /dev/null
+++ b/asterixdb/asterix-app/src/test/resources/optimizerts/queries/aggregate-subclause/agg_filter_01/agg_filter_01.4.sqlpp
@@ -0,0 +1,55 @@
+/*
+ * 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 test if exists;
+create  dataverse test;
+
+use test;
+
+create type tenkType as closed {
+  unique1         : integer,
+  unique2         : integer,
+  two             : integer,
+  four            : integer,
+  ten             : integer,
+  twenty          : integer,
+  hundred         : integer,
+  thousand        : integer,
+  twothous        : integer,
+  fivethous       : integer,
+  tenthous        : integer,
+  odd100          : integer,
+  even100         : integer,
+  stringu1        : string,
+  stringu2        : string,
+  string4         : string
+};
+
+create dataset tenk(tenkType) primary key unique2;
+
+/*
+ * Test aggregate FILTER with simple GROUP BY clause
+ */
+
+select two,
+  count(*) filter(where four > 0) as cnt,
+  sum(four) filter(where four > 0) as sm
+from tenk
+group by two
+order by two;
\ No newline at end of file
diff --git a/asterixdb/asterix-app/src/test/resources/optimizerts/queries/aggregate-subclause/agg_filter_01/agg_filter_01.5.sqlpp b/asterixdb/asterix-app/src/test/resources/optimizerts/queries/aggregate-subclause/agg_filter_01/agg_filter_01.5.sqlpp
new file mode 100644
index 0000000..065b6dc
--- /dev/null
+++ b/asterixdb/asterix-app/src/test/resources/optimizerts/queries/aggregate-subclause/agg_filter_01/agg_filter_01.5.sqlpp
@@ -0,0 +1,55 @@
+/*
+ * 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 test if exists;
+create  dataverse test;
+
+use test;
+
+create type tenkType as closed {
+  unique1         : integer,
+  unique2         : integer,
+  two             : integer,
+  four            : integer,
+  ten             : integer,
+  twenty          : integer,
+  hundred         : integer,
+  thousand        : integer,
+  twothous        : integer,
+  fivethous       : integer,
+  tenthous        : integer,
+  odd100          : integer,
+  even100         : integer,
+  stringu1        : string,
+  stringu2        : string,
+  string4         : string
+};
+
+create dataset tenk(tenkType) primary key unique2;
+
+/*
+ * Test aggregate FILTER with grouping sets in GROUP BY clause
+ */
+
+select two,
+  count(*) filter(where four > 0) as cnt,
+  sum(four) filter(where four > 0) as sm
+from tenk
+group by rollup(two)
+order by two;
\ No newline at end of file
diff --git a/asterixdb/asterix-app/src/test/resources/optimizerts/queries/aggregate-subclause/agg_filter_01/agg_filter_01.6.sqlpp b/asterixdb/asterix-app/src/test/resources/optimizerts/queries/aggregate-subclause/agg_filter_01/agg_filter_01.6.sqlpp
new file mode 100644
index 0000000..8f3bd56
--- /dev/null
+++ b/asterixdb/asterix-app/src/test/resources/optimizerts/queries/aggregate-subclause/agg_filter_01/agg_filter_01.6.sqlpp
@@ -0,0 +1,58 @@
+/*
+ * 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 test if exists;
+create  dataverse test;
+
+use test;
+
+create type tenkType as closed {
+  unique1         : integer,
+  unique2         : integer,
+  two             : integer,
+  four            : integer,
+  ten             : integer,
+  twenty          : integer,
+  hundred         : integer,
+  thousand        : integer,
+  twothous        : integer,
+  fivethous       : integer,
+  tenthous        : integer,
+  odd100          : integer,
+  even100         : integer,
+  stringu1        : string,
+  stringu2        : string,
+  string4         : string
+};
+
+create dataset tenk(tenkType) primary key unique2;
+
+/*
+ * Test aggregate FILTER in window function call,
+ * without GROUP BY clause
+ */
+
+select twothous, fivethous, tenthous,
+  count(tenthous) over(order by tenthous) as cnt,
+  sum(tenthous) over(order by tenthous) as sm,
+  count(tenthous) filter(where twothous = 0) over(order by tenthous) as cnt_filter,
+  sum(tenthous) filter(where twothous = 0) over(order by tenthous) as sm_filter
+from tenk
+where thousand = 0
+order by tenthous;
\ No newline at end of file
diff --git a/asterixdb/asterix-app/src/test/resources/optimizerts/queries/aggregate-subclause/agg_filter_01/agg_filter_01.7.sqlpp b/asterixdb/asterix-app/src/test/resources/optimizerts/queries/aggregate-subclause/agg_filter_01/agg_filter_01.7.sqlpp
new file mode 100644
index 0000000..61efd1c
--- /dev/null
+++ b/asterixdb/asterix-app/src/test/resources/optimizerts/queries/aggregate-subclause/agg_filter_01/agg_filter_01.7.sqlpp
@@ -0,0 +1,58 @@
+/*
+ * 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 test if exists;
+create  dataverse test;
+
+use test;
+
+create type tenkType as closed {
+  unique1         : integer,
+  unique2         : integer,
+  two             : integer,
+  four            : integer,
+  ten             : integer,
+  twenty          : integer,
+  hundred         : integer,
+  thousand        : integer,
+  twothous        : integer,
+  fivethous       : integer,
+  tenthous        : integer,
+  odd100          : integer,
+  even100         : integer,
+  stringu1        : string,
+  stringu2        : string,
+  string4         : string
+};
+
+create dataset tenk(tenkType) primary key unique2;
+
+/*
+ * Test aggregate FILTER in window function call,
+ * with GROUP BY clause
+ */
+
+select twenty,
+  sum(ten) as sm,
+  sum(sum(ten)) over (order by twenty) as sm_sm,
+  sum(sum(ten)) filter(where twenty < 10) over (order by twenty) as sm_sm_where_twenty_lt_10,
+  sum(sum(ten)) filter(where sum(ten) < 3000) over (order by twenty) as sm_sm_where_sm_lt_3k
+from tenk
+group by twenty
+order by twenty;
\ No newline at end of file
diff --git a/asterixdb/asterix-app/src/test/resources/optimizerts/queries/aggregate-subclause/agg_filter_01/agg_filter_01.8.sqlpp b/asterixdb/asterix-app/src/test/resources/optimizerts/queries/aggregate-subclause/agg_filter_01/agg_filter_01.8.sqlpp
new file mode 100644
index 0000000..2d74690
--- /dev/null
+++ b/asterixdb/asterix-app/src/test/resources/optimizerts/queries/aggregate-subclause/agg_filter_01/agg_filter_01.8.sqlpp
@@ -0,0 +1,54 @@
+/*
+ * 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 test if exists;
+create  dataverse test;
+
+use test;
+
+create type tenkType as closed {
+  unique1         : integer,
+  unique2         : integer,
+  two             : integer,
+  four            : integer,
+  ten             : integer,
+  twenty          : integer,
+  hundred         : integer,
+  thousand        : integer,
+  twothous        : integer,
+  fivethous       : integer,
+  tenthous        : integer,
+  odd100          : integer,
+  even100         : integer,
+  stringu1        : string,
+  stringu2        : string,
+  string4         : string
+};
+
+create dataset tenk(tenkType) primary key unique2;
+
+/*
+ * Test FILTER with complex expression
+ */
+
+select two,
+  sum(four) filter(where every x in [four, four + four] satisfies x > 0) as sm
+from tenk
+group by two
+order by two;
\ No newline at end of file
diff --git a/asterixdb/asterix-app/src/test/resources/optimizerts/results/aggregate-subclause/agg_filter_01/agg_filter_01.3.plan b/asterixdb/asterix-app/src/test/resources/optimizerts/results/aggregate-subclause/agg_filter_01/agg_filter_01.3.plan
new file mode 100644
index 0000000..34aaec9
--- /dev/null
+++ b/asterixdb/asterix-app/src/test/resources/optimizerts/results/aggregate-subclause/agg_filter_01/agg_filter_01.3.plan
@@ -0,0 +1,34 @@
+-- DISTRIBUTE_RESULT  |UNPARTITIONED|
+  -- ONE_TO_ONE_EXCHANGE  |UNPARTITIONED|
+    -- STREAM_PROJECT  |UNPARTITIONED|
+      -- ASSIGN  |UNPARTITIONED|
+        -- STREAM_PROJECT  |UNPARTITIONED|
+          -- SUBPLAN  |UNPARTITIONED|
+                  {
+                    -- AGGREGATE  |LOCAL|
+                      -- AGGREGATE  |LOCAL|
+                        -- STREAM_SELECT  |UNPARTITIONED|
+                          -- ASSIGN  |UNPARTITIONED|
+                            -- ASSIGN  |UNPARTITIONED|
+                              -- UNNEST  |UNPARTITIONED|
+                                -- NESTED_TUPLE_SOURCE  |UNPARTITIONED|
+                  }
+            -- SUBPLAN  |UNPARTITIONED|
+                    {
+                      -- AGGREGATE  |LOCAL|
+                        -- AGGREGATE  |LOCAL|
+                          -- STREAM_SELECT  |UNPARTITIONED|
+                            -- ASSIGN  |UNPARTITIONED|
+                              -- ASSIGN  |UNPARTITIONED|
+                                -- UNNEST  |UNPARTITIONED|
+                                  -- NESTED_TUPLE_SOURCE  |UNPARTITIONED|
+                    }
+              -- AGGREGATE  |UNPARTITIONED|
+                -- RANDOM_MERGE_EXCHANGE  |PARTITIONED|
+                  -- STREAM_PROJECT  |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/aggregate-subclause/agg_filter_01/agg_filter_01.4.plan b/asterixdb/asterix-app/src/test/resources/optimizerts/results/aggregate-subclause/agg_filter_01/agg_filter_01.4.plan
new file mode 100644
index 0000000..4282df2
--- /dev/null
+++ b/asterixdb/asterix-app/src/test/resources/optimizerts/results/aggregate-subclause/agg_filter_01/agg_filter_01.4.plan
@@ -0,0 +1,38 @@
+-- DISTRIBUTE_RESULT  |PARTITIONED|
+  -- ONE_TO_ONE_EXCHANGE  |PARTITIONED|
+    -- STREAM_PROJECT  |PARTITIONED|
+      -- ASSIGN  |PARTITIONED|
+        -- SORT_MERGE_EXCHANGE [$$two(ASC) ]  |PARTITIONED|
+          -- PRE_CLUSTERED_GROUP_BY[$$66]  |PARTITIONED|
+                  {
+                    -- AGGREGATE  |LOCAL|
+                      -- NESTED_TUPLE_SOURCE  |LOCAL|
+                  }
+                  {
+                    -- AGGREGATE  |LOCAL|
+                      -- NESTED_TUPLE_SOURCE  |LOCAL|
+                  }
+            -- ONE_TO_ONE_EXCHANGE  |PARTITIONED|
+              -- STABLE_SORT [$$66(ASC)]  |PARTITIONED|
+                -- HASH_PARTITION_EXCHANGE [$$66]  |PARTITIONED|
+                  -- PRE_CLUSTERED_GROUP_BY[$$55]  |PARTITIONED|
+                          {
+                            -- AGGREGATE  |LOCAL|
+                              -- STREAM_SELECT  |LOCAL|
+                                -- NESTED_TUPLE_SOURCE  |LOCAL|
+                          }
+                          {
+                            -- AGGREGATE  |LOCAL|
+                              -- STREAM_SELECT  |LOCAL|
+                                -- NESTED_TUPLE_SOURCE  |LOCAL|
+                          }
+                    -- ONE_TO_ONE_EXCHANGE  |PARTITIONED|
+                      -- STABLE_SORT [$$55(ASC)]  |PARTITIONED|
+                        -- ONE_TO_ONE_EXCHANGE  |PARTITIONED|
+                          -- STREAM_PROJECT  |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/aggregate-subclause/agg_filter_01/agg_filter_01.5.plan b/asterixdb/asterix-app/src/test/resources/optimizerts/results/aggregate-subclause/agg_filter_01/agg_filter_01.5.plan
new file mode 100644
index 0000000..92f10c3
--- /dev/null
+++ b/asterixdb/asterix-app/src/test/resources/optimizerts/results/aggregate-subclause/agg_filter_01/agg_filter_01.5.plan
@@ -0,0 +1,94 @@
+-- DISTRIBUTE_RESULT  |PARTITIONED|
+  -- ONE_TO_ONE_EXCHANGE  |PARTITIONED|
+    -- STREAM_PROJECT  |PARTITIONED|
+      -- SORT_MERGE_EXCHANGE [$$128(ASC) ]  |PARTITIONED|
+        -- STABLE_SORT [$$128(ASC)]  |PARTITIONED|
+          -- ONE_TO_ONE_EXCHANGE  |PARTITIONED|
+            -- UNION_ALL  |PARTITIONED|
+              -- ONE_TO_ONE_EXCHANGE  |PARTITIONED|
+                -- STREAM_PROJECT  |PARTITIONED|
+                  -- ASSIGN  |PARTITIONED|
+                    -- STREAM_PROJECT  |PARTITIONED|
+                      -- ASSIGN  |PARTITIONED|
+                        -- ONE_TO_ONE_EXCHANGE  |PARTITIONED|
+                          -- PRE_CLUSTERED_GROUP_BY[$$133]  |PARTITIONED|
+                                  {
+                                    -- AGGREGATE  |LOCAL|
+                                      -- NESTED_TUPLE_SOURCE  |LOCAL|
+                                  }
+                                  {
+                                    -- AGGREGATE  |LOCAL|
+                                      -- NESTED_TUPLE_SOURCE  |LOCAL|
+                                  }
+                            -- ONE_TO_ONE_EXCHANGE  |PARTITIONED|
+                              -- STABLE_SORT [$$133(ASC)]  |PARTITIONED|
+                                -- HASH_PARTITION_EXCHANGE [$$133]  |PARTITIONED|
+                                  -- PRE_CLUSTERED_GROUP_BY[$$112]  |PARTITIONED|
+                                          {
+                                            -- AGGREGATE  |LOCAL|
+                                              -- STREAM_SELECT  |LOCAL|
+                                                -- NESTED_TUPLE_SOURCE  |LOCAL|
+                                          }
+                                          {
+                                            -- AGGREGATE  |LOCAL|
+                                              -- STREAM_SELECT  |LOCAL|
+                                                -- NESTED_TUPLE_SOURCE  |LOCAL|
+                                          }
+                                    -- ONE_TO_ONE_EXCHANGE  |PARTITIONED|
+                                      -- STABLE_SORT [$$112(ASC)]  |PARTITIONED|
+                                        -- ONE_TO_ONE_EXCHANGE  |PARTITIONED|
+                                          -- STREAM_PROJECT  |PARTITIONED|
+                                            -- ASSIGN  |PARTITIONED|
+                                              -- STREAM_PROJECT  |PARTITIONED|
+                                                -- ASSIGN  |PARTITIONED|
+                                                  -- ONE_TO_ONE_EXCHANGE  |PARTITIONED|
+                                                    -- REPLICATE  |PARTITIONED|
+                                                      -- ONE_TO_ONE_EXCHANGE  |PARTITIONED|
+                                                        -- STREAM_PROJECT  |PARTITIONED|
+                                                          -- ONE_TO_ONE_EXCHANGE  |PARTITIONED|
+                                                            -- DATASOURCE_SCAN  |PARTITIONED|
+                                                              -- ONE_TO_ONE_EXCHANGE  |PARTITIONED|
+                                                                -- EMPTY_TUPLE_SOURCE  |PARTITIONED|
+              -- ONE_TO_ONE_EXCHANGE  |PARTITIONED|
+                -- STREAM_PROJECT  |PARTITIONED|
+                  -- ASSIGN  |PARTITIONED|
+                    -- STREAM_PROJECT  |PARTITIONED|
+                      -- ASSIGN  |PARTITIONED|
+                        -- STREAM_PROJECT  |PARTITIONED|
+                          -- ONE_TO_ONE_EXCHANGE  |PARTITIONED|
+                            -- PRE_CLUSTERED_GROUP_BY[$$136]  |PARTITIONED|
+                                    {
+                                      -- AGGREGATE  |LOCAL|
+                                        -- NESTED_TUPLE_SOURCE  |LOCAL|
+                                    }
+                                    {
+                                      -- AGGREGATE  |LOCAL|
+                                        -- NESTED_TUPLE_SOURCE  |LOCAL|
+                                    }
+                              -- ONE_TO_ONE_EXCHANGE  |PARTITIONED|
+                                -- STABLE_SORT [$$136(ASC)]  |PARTITIONED|
+                                  -- HASH_PARTITION_EXCHANGE [$$136]  |PARTITIONED|
+                                    -- PRE_CLUSTERED_GROUP_BY[$$113]  |PARTITIONED|
+                                            {
+                                              -- AGGREGATE  |LOCAL|
+                                                -- STREAM_SELECT  |LOCAL|
+                                                  -- NESTED_TUPLE_SOURCE  |LOCAL|
+                                            }
+                                            {
+                                              -- AGGREGATE  |LOCAL|
+                                                -- STREAM_SELECT  |LOCAL|
+                                                  -- NESTED_TUPLE_SOURCE  |LOCAL|
+                                            }
+                                      -- ONE_TO_ONE_EXCHANGE  |PARTITIONED|
+                                        -- STABLE_SORT [$$113(ASC)]  |PARTITIONED|
+                                          -- ONE_TO_ONE_EXCHANGE  |PARTITIONED|
+                                            -- STREAM_PROJECT  |PARTITIONED|
+                                              -- ASSIGN  |PARTITIONED|
+                                                -- ONE_TO_ONE_EXCHANGE  |PARTITIONED|
+                                                  -- REPLICATE  |PARTITIONED|
+                                                    -- ONE_TO_ONE_EXCHANGE  |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/aggregate-subclause/agg_filter_01/agg_filter_01.6.plan b/asterixdb/asterix-app/src/test/resources/optimizerts/results/aggregate-subclause/agg_filter_01/agg_filter_01.6.plan
new file mode 100644
index 0000000..17f9f91
--- /dev/null
+++ b/asterixdb/asterix-app/src/test/resources/optimizerts/results/aggregate-subclause/agg_filter_01/agg_filter_01.6.plan
@@ -0,0 +1,26 @@
+-- DISTRIBUTE_RESULT  |UNPARTITIONED|
+  -- ONE_TO_ONE_EXCHANGE  |UNPARTITIONED|
+    -- STREAM_PROJECT  |UNPARTITIONED|
+      -- ASSIGN  |UNPARTITIONED|
+        -- WINDOW  |UNPARTITIONED|
+                {
+                  -- AGGREGATE  |UNPARTITIONED|
+                    -- STREAM_SELECT  |UNPARTITIONED|
+                      -- NESTED_TUPLE_SOURCE  |UNPARTITIONED|
+                }
+          -- WINDOW  |UNPARTITIONED|
+                  {
+                    -- AGGREGATE  |LOCAL|
+                      -- NESTED_TUPLE_SOURCE  |LOCAL|
+                  }
+            -- SORT_MERGE_EXCHANGE [$$tenk.tenthous(ASC) ]  |PARTITIONED|
+              -- STABLE_SORT [$$tenk.tenthous(ASC)]  |PARTITIONED|
+                -- ONE_TO_ONE_EXCHANGE  |PARTITIONED|
+                  -- STREAM_PROJECT  |PARTITIONED|
+                    -- STREAM_SELECT  |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/aggregate-subclause/agg_filter_01/agg_filter_01.7.plan b/asterixdb/asterix-app/src/test/resources/optimizerts/results/aggregate-subclause/agg_filter_01/agg_filter_01.7.plan
new file mode 100644
index 0000000..8cb579b
--- /dev/null
+++ b/asterixdb/asterix-app/src/test/resources/optimizerts/results/aggregate-subclause/agg_filter_01/agg_filter_01.7.plan
@@ -0,0 +1,44 @@
+-- DISTRIBUTE_RESULT  |UNPARTITIONED|
+  -- ONE_TO_ONE_EXCHANGE  |UNPARTITIONED|
+    -- STREAM_PROJECT  |UNPARTITIONED|
+      -- ASSIGN  |UNPARTITIONED|
+        -- STREAM_PROJECT  |UNPARTITIONED|
+          -- WINDOW  |UNPARTITIONED|
+                  {
+                    -- AGGREGATE  |UNPARTITIONED|
+                      -- STREAM_SELECT  |UNPARTITIONED|
+                        -- NESTED_TUPLE_SOURCE  |UNPARTITIONED|
+                  }
+            -- STREAM_PROJECT  |UNPARTITIONED|
+              -- WINDOW  |UNPARTITIONED|
+                      {
+                        -- AGGREGATE  |UNPARTITIONED|
+                          -- STREAM_SELECT  |UNPARTITIONED|
+                            -- NESTED_TUPLE_SOURCE  |UNPARTITIONED|
+                      }
+                -- STREAM_PROJECT  |UNPARTITIONED|
+                  -- WINDOW  |UNPARTITIONED|
+                          {
+                            -- AGGREGATE  |LOCAL|
+                              -- NESTED_TUPLE_SOURCE  |LOCAL|
+                          }
+                    -- SORT_MERGE_EXCHANGE [$$twenty(ASC) ]  |PARTITIONED|
+                      -- SORT_GROUP_BY[$$187]  |PARTITIONED|
+                              {
+                                -- AGGREGATE  |LOCAL|
+                                  -- NESTED_TUPLE_SOURCE  |LOCAL|
+                              }
+                        -- HASH_PARTITION_EXCHANGE [$$187]  |PARTITIONED|
+                          -- SORT_GROUP_BY[$$169]  |PARTITIONED|
+                                  {
+                                    -- AGGREGATE  |LOCAL|
+                                      -- NESTED_TUPLE_SOURCE  |LOCAL|
+                                  }
+                            -- ONE_TO_ONE_EXCHANGE  |PARTITIONED|
+                              -- STREAM_PROJECT  |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/aggregate-subclause/agg_filter_01/agg_filter_01.8.plan b/asterixdb/asterix-app/src/test/resources/optimizerts/results/aggregate-subclause/agg_filter_01/agg_filter_01.8.plan
new file mode 100644
index 0000000..6dd7373
--- /dev/null
+++ b/asterixdb/asterix-app/src/test/resources/optimizerts/results/aggregate-subclause/agg_filter_01/agg_filter_01.8.plan
@@ -0,0 +1,28 @@
+-- DISTRIBUTE_RESULT  |PARTITIONED|
+  -- ONE_TO_ONE_EXCHANGE  |PARTITIONED|
+    -- STREAM_PROJECT  |PARTITIONED|
+      -- ASSIGN  |PARTITIONED|
+        -- SORT_MERGE_EXCHANGE [$$two(ASC) ]  |PARTITIONED|
+          -- PRE_CLUSTERED_GROUP_BY[$$62]  |PARTITIONED|
+                  {
+                    -- AGGREGATE  |LOCAL|
+                      -- STREAM_SELECT  |LOCAL|
+                        -- SUBPLAN  |LOCAL|
+                                {
+                                  -- AGGREGATE  |LOCAL|
+                                    -- STREAM_SELECT  |LOCAL|
+                                      -- UNNEST  |LOCAL|
+                                        -- NESTED_TUPLE_SOURCE  |LOCAL|
+                                }
+                          -- NESTED_TUPLE_SOURCE  |LOCAL|
+                  }
+            -- ONE_TO_ONE_EXCHANGE  |PARTITIONED|
+              -- STABLE_SORT [$$62(ASC)]  |PARTITIONED|
+                -- HASH_PARTITION_EXCHANGE [$$62]  |PARTITIONED|
+                  -- STREAM_PROJECT  |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/runtimets/queries_sqlpp/aggregate-subclause/agg_filter_01/agg_filter_01.1.ddl.sqlpp b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/aggregate-subclause/agg_filter_01/agg_filter_01.1.ddl.sqlpp
new file mode 100644
index 0000000..e4895f7
--- /dev/null
+++ b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/aggregate-subclause/agg_filter_01/agg_filter_01.1.ddl.sqlpp
@@ -0,0 +1,48 @@
+/*
+ * 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.
+ */
+
+/*
+ * Test various combinations of grouping sets
+ */
+
+drop  dataverse test if exists;
+create  dataverse test;
+
+use test;
+
+create type tenkType as closed {
+  unique1         : integer,
+  unique2         : integer,
+  two             : integer,
+  four            : integer,
+  ten             : integer,
+  twenty          : integer,
+  hundred         : integer,
+  thousand        : integer,
+  twothous        : integer,
+  fivethous       : integer,
+  tenthous        : integer,
+  odd100          : integer,
+  even100         : integer,
+  stringu1        : string,
+  stringu2        : string,
+  string4         : string
+};
+
+create dataset tenk(tenkType) primary key unique2;
diff --git a/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/aggregate-subclause/agg_filter_01/agg_filter_01.2.update.sqlpp b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/aggregate-subclause/agg_filter_01/agg_filter_01.2.update.sqlpp
new file mode 100644
index 0000000..2d7e768
--- /dev/null
+++ b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/aggregate-subclause/agg_filter_01/agg_filter_01.2.update.sqlpp
@@ -0,0 +1,22 @@
+/*
+ * 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 test;
+
+load  dataset tenk using localfs ((`path`=`asterix_nc1://data/tenk.tbl`),(`format`=`delimited-text`),(`delimiter`=`|`));
diff --git a/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/aggregate-subclause/agg_filter_01/agg_filter_01.3.query.sqlpp b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/aggregate-subclause/agg_filter_01/agg_filter_01.3.query.sqlpp
new file mode 100644
index 0000000..354b3b4
--- /dev/null
+++ b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/aggregate-subclause/agg_filter_01/agg_filter_01.3.query.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.
+ */
+
+/*
+ * Test aggregate filter without GROUP BY clause
+ */
+
+use test;
+
+select
+  count(*) filter(where four > 0) as cnt,
+  sum(four) filter(where four > 0) as sm
+from tenk;
\ No newline at end of file
diff --git a/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/aggregate-subclause/agg_filter_01/agg_filter_01.4.query.sqlpp b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/aggregate-subclause/agg_filter_01/agg_filter_01.4.query.sqlpp
new file mode 100644
index 0000000..3ea455c
--- /dev/null
+++ b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/aggregate-subclause/agg_filter_01/agg_filter_01.4.query.sqlpp
@@ -0,0 +1,31 @@
+/*
+ * 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.
+ */
+
+/*
+ * Test aggregate FILTER with simple GROUP BY clause
+ */
+
+use test;
+
+select two,
+  count(*) filter(where four > 0) as cnt,
+  sum(four) filter(where four > 0) as sm
+from tenk
+group by two
+order by two;
\ No newline at end of file
diff --git a/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/aggregate-subclause/agg_filter_01/agg_filter_01.5.query.sqlpp b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/aggregate-subclause/agg_filter_01/agg_filter_01.5.query.sqlpp
new file mode 100644
index 0000000..d497b7d
--- /dev/null
+++ b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/aggregate-subclause/agg_filter_01/agg_filter_01.5.query.sqlpp
@@ -0,0 +1,31 @@
+/*
+ * 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.
+ */
+
+/*
+ * Test aggregate FILTER with grouping sets in GROUP BY clause
+ */
+
+use test;
+
+select two,
+  count(*) filter(where four > 0) as cnt,
+  sum(four) filter(where four > 0) as sm
+from tenk
+group by rollup(two)
+order by two;
\ No newline at end of file
diff --git a/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/aggregate-subclause/agg_filter_01/agg_filter_01.6.query.sqlpp b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/aggregate-subclause/agg_filter_01/agg_filter_01.6.query.sqlpp
new file mode 100644
index 0000000..65e18a0
--- /dev/null
+++ b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/aggregate-subclause/agg_filter_01/agg_filter_01.6.query.sqlpp
@@ -0,0 +1,34 @@
+/*
+ * 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.
+ */
+
+/*
+ * Test aggregate FILTER in window function call,
+ * without GROUP BY clause
+ */
+
+use test;
+
+select twothous, fivethous, tenthous,
+  count(tenthous) over(order by tenthous) as cnt,
+  sum(tenthous) over(order by tenthous) as sm,
+  count(tenthous) filter(where twothous = 0) over(order by tenthous) as cnt_filter,
+  sum(tenthous) filter(where twothous = 0) over(order by tenthous) as sm_filter
+from tenk
+where thousand = 0
+order by tenthous;
diff --git a/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/aggregate-subclause/agg_filter_01/agg_filter_01.7.query.sqlpp b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/aggregate-subclause/agg_filter_01/agg_filter_01.7.query.sqlpp
new file mode 100644
index 0000000..fd746b4
--- /dev/null
+++ b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/aggregate-subclause/agg_filter_01/agg_filter_01.7.query.sqlpp
@@ -0,0 +1,34 @@
+/*
+ * 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.
+ */
+
+/*
+ * Test aggregate FILTER in window function call,
+ * with GROUP BY clause
+ */
+
+use test;
+
+select twenty,
+  sum(ten) as sm,
+  sum(sum(ten)) over (order by twenty) as sm_sm,
+  sum(sum(ten)) filter(where twenty < 10) over (order by twenty) as sm_sm_where_twenty_lt_10,
+  sum(sum(ten)) filter(where sum(ten) < 3000) over (order by twenty) as sm_sm_where_sm_lt_3k
+from tenk
+group by twenty
+order by twenty;
\ No newline at end of file
diff --git a/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/aggregate-subclause/agg_filter_01/agg_filter_01.8.query.sqlpp b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/aggregate-subclause/agg_filter_01/agg_filter_01.8.query.sqlpp
new file mode 100644
index 0000000..0ded863
--- /dev/null
+++ b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/aggregate-subclause/agg_filter_01/agg_filter_01.8.query.sqlpp
@@ -0,0 +1,30 @@
+/*
+ * 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.
+ */
+
+/*
+ * Test FILTER with complex expression
+ */
+
+use test;
+
+select two,
+  sum(four) filter(where every x in [four, four + four] satisfies x > 0) as sm
+from tenk
+group by two
+order by two;
\ No newline at end of file
diff --git a/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/aggregate-subclause/agg_filter_02_neg/agg_filter_02.neg.5.query.sqlpp b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/aggregate-subclause/agg_filter_02_neg/agg_filter_02.neg.5.query.sqlpp
new file mode 100644
index 0000000..1a2bed4
--- /dev/null
+++ b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/aggregate-subclause/agg_filter_02_neg/agg_filter_02.neg.5.query.sqlpp
@@ -0,0 +1,27 @@
+/*
+ * 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.
+ */
+
+/*
+ * Negative: FILTER used in SQL++ aggregate function call (strict_*)
+ */
+
+select m,
+  strict_sum((select value gi.r from g as gi)) filter(where strict_count(g) > 2)
+from range(1, 10) r
+group by r % 2 as m group as g;
\ No newline at end of file
diff --git a/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/aggregate-subclause/agg_filter_02_neg/agg_filter_02_neg.1.query.sqlpp b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/aggregate-subclause/agg_filter_02_neg/agg_filter_02_neg.1.query.sqlpp
new file mode 100644
index 0000000..7f65998
--- /dev/null
+++ b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/aggregate-subclause/agg_filter_02_neg/agg_filter_02_neg.1.query.sqlpp
@@ -0,0 +1,25 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+/*
+ * Negative: FILTER used in non-aggregate function call
+ */
+
+select to_double(r) filter(where r > 5)
+from range(1, 10) r;
diff --git a/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/aggregate-subclause/agg_filter_02_neg/agg_filter_02_neg.2.query.sqlpp b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/aggregate-subclause/agg_filter_02_neg/agg_filter_02_neg.2.query.sqlpp
new file mode 100644
index 0000000..ce12917
--- /dev/null
+++ b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/aggregate-subclause/agg_filter_02_neg/agg_filter_02_neg.2.query.sqlpp
@@ -0,0 +1,25 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+/*
+ * Negative: FILTER used in non-aggregate windows function call
+ */
+
+select rank() filter(where r > 5) over (order by r)
+from range(1, 10) r;
diff --git a/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/aggregate-subclause/agg_filter_02_neg/agg_filter_02_neg.3.query.sqlpp b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/aggregate-subclause/agg_filter_02_neg/agg_filter_02_neg.3.query.sqlpp
new file mode 100644
index 0000000..8d91562
--- /dev/null
+++ b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/aggregate-subclause/agg_filter_02_neg/agg_filter_02_neg.3.query.sqlpp
@@ -0,0 +1,26 @@
+/*
+ * 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.
+ */
+
+/*
+ * Negative: FILTER in SQL++ aggregate function call. Invalid identifier.
+ */
+
+select m, array_sum(r) filter(where r > 5)
+from range(1, 10) r
+group by r % 2 as m;
\ No newline at end of file
diff --git a/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/aggregate-subclause/agg_filter_02_neg/agg_filter_02_neg.4.query.sqlpp b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/aggregate-subclause/agg_filter_02_neg/agg_filter_02_neg.4.query.sqlpp
new file mode 100644
index 0000000..7c58c6d
--- /dev/null
+++ b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/aggregate-subclause/agg_filter_02_neg/agg_filter_02_neg.4.query.sqlpp
@@ -0,0 +1,27 @@
+/*
+ * 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.
+ */
+
+/*
+ * Negative: FILTER used in SQL++ aggregate function call (array_*)
+ */
+
+select m,
+  array_sum((select value gi.r from g as gi)) filter(where array_count(g) > 2)
+from range(1, 10) r
+group by r % 2 as m group as g;
\ No newline at end of file
diff --git a/asterixdb/asterix-app/src/test/resources/runtimets/results/aggregate-subclause/agg_filter_01/agg_filter_01.3.adm b/asterixdb/asterix-app/src/test/resources/runtimets/results/aggregate-subclause/agg_filter_01/agg_filter_01.3.adm
new file mode 100644
index 0000000..58ca85e
--- /dev/null
+++ b/asterixdb/asterix-app/src/test/resources/runtimets/results/aggregate-subclause/agg_filter_01/agg_filter_01.3.adm
@@ -0,0 +1 @@
+{ "cnt": 7500, "sm": 15000 }
\ No newline at end of file
diff --git a/asterixdb/asterix-app/src/test/resources/runtimets/results/aggregate-subclause/agg_filter_01/agg_filter_01.4.adm b/asterixdb/asterix-app/src/test/resources/runtimets/results/aggregate-subclause/agg_filter_01/agg_filter_01.4.adm
new file mode 100644
index 0000000..06f5e2b
--- /dev/null
+++ b/asterixdb/asterix-app/src/test/resources/runtimets/results/aggregate-subclause/agg_filter_01/agg_filter_01.4.adm
@@ -0,0 +1,2 @@
+{ "two": 0, "cnt": 2500, "sm": 5000 }
+{ "two": 1, "cnt": 5000, "sm": 10000 }
\ No newline at end of file
diff --git a/asterixdb/asterix-app/src/test/resources/runtimets/results/aggregate-subclause/agg_filter_01/agg_filter_01.5.adm b/asterixdb/asterix-app/src/test/resources/runtimets/results/aggregate-subclause/agg_filter_01/agg_filter_01.5.adm
new file mode 100644
index 0000000..d65a86d
--- /dev/null
+++ b/asterixdb/asterix-app/src/test/resources/runtimets/results/aggregate-subclause/agg_filter_01/agg_filter_01.5.adm
@@ -0,0 +1,3 @@
+{ "two": null, "cnt": 7500, "sm": 15000 }
+{ "two": 0, "cnt": 2500, "sm": 5000 }
+{ "two": 1, "cnt": 5000, "sm": 10000 }
\ No newline at end of file
diff --git a/asterixdb/asterix-app/src/test/resources/runtimets/results/aggregate-subclause/agg_filter_01/agg_filter_01.6.adm b/asterixdb/asterix-app/src/test/resources/runtimets/results/aggregate-subclause/agg_filter_01/agg_filter_01.6.adm
new file mode 100644
index 0000000..44b4513
--- /dev/null
+++ b/asterixdb/asterix-app/src/test/resources/runtimets/results/aggregate-subclause/agg_filter_01/agg_filter_01.6.adm
@@ -0,0 +1,10 @@
+{ "twothous": 0, "fivethous": 0, "tenthous": 0, "cnt": 1, "sm": 0, "cnt_filter": 1, "sm_filter": 0 }
+{ "twothous": 1000, "fivethous": 1000, "tenthous": 1000, "cnt": 2, "sm": 1000, "cnt_filter": 1, "sm_filter": 0 }
+{ "twothous": 0, "fivethous": 2000, "tenthous": 2000, "cnt": 3, "sm": 3000, "cnt_filter": 2, "sm_filter": 2000 }
+{ "twothous": 1000, "fivethous": 3000, "tenthous": 3000, "cnt": 4, "sm": 6000, "cnt_filter": 2, "sm_filter": 2000 }
+{ "twothous": 0, "fivethous": 4000, "tenthous": 4000, "cnt": 5, "sm": 10000, "cnt_filter": 3, "sm_filter": 6000 }
+{ "twothous": 1000, "fivethous": 0, "tenthous": 5000, "cnt": 6, "sm": 15000, "cnt_filter": 3, "sm_filter": 6000 }
+{ "twothous": 0, "fivethous": 1000, "tenthous": 6000, "cnt": 7, "sm": 21000, "cnt_filter": 4, "sm_filter": 12000 }
+{ "twothous": 1000, "fivethous": 2000, "tenthous": 7000, "cnt": 8, "sm": 28000, "cnt_filter": 4, "sm_filter": 12000 }
+{ "twothous": 0, "fivethous": 3000, "tenthous": 8000, "cnt": 9, "sm": 36000, "cnt_filter": 5, "sm_filter": 20000 }
+{ "twothous": 1000, "fivethous": 4000, "tenthous": 9000, "cnt": 10, "sm": 45000, "cnt_filter": 5, "sm_filter": 20000 }
\ No newline at end of file
diff --git a/asterixdb/asterix-app/src/test/resources/runtimets/results/aggregate-subclause/agg_filter_01/agg_filter_01.7.adm b/asterixdb/asterix-app/src/test/resources/runtimets/results/aggregate-subclause/agg_filter_01/agg_filter_01.7.adm
new file mode 100644
index 0000000..7614e6e4
--- /dev/null
+++ b/asterixdb/asterix-app/src/test/resources/runtimets/results/aggregate-subclause/agg_filter_01/agg_filter_01.7.adm
@@ -0,0 +1,20 @@
+{ "twenty": 0, "sm": 0, "sm_sm": 0, "sm_sm_where_twenty_lt_10": 0, "sm_sm_where_sm_lt_3k": 0 }
+{ "twenty": 1, "sm": 500, "sm_sm": 500, "sm_sm_where_twenty_lt_10": 500, "sm_sm_where_sm_lt_3k": 500 }
+{ "twenty": 2, "sm": 1000, "sm_sm": 1500, "sm_sm_where_twenty_lt_10": 1500, "sm_sm_where_sm_lt_3k": 1500 }
+{ "twenty": 3, "sm": 1500, "sm_sm": 3000, "sm_sm_where_twenty_lt_10": 3000, "sm_sm_where_sm_lt_3k": 3000 }
+{ "twenty": 4, "sm": 2000, "sm_sm": 5000, "sm_sm_where_twenty_lt_10": 5000, "sm_sm_where_sm_lt_3k": 5000 }
+{ "twenty": 5, "sm": 2500, "sm_sm": 7500, "sm_sm_where_twenty_lt_10": 7500, "sm_sm_where_sm_lt_3k": 7500 }
+{ "twenty": 6, "sm": 3000, "sm_sm": 10500, "sm_sm_where_twenty_lt_10": 10500, "sm_sm_where_sm_lt_3k": 7500 }
+{ "twenty": 7, "sm": 3500, "sm_sm": 14000, "sm_sm_where_twenty_lt_10": 14000, "sm_sm_where_sm_lt_3k": 7500 }
+{ "twenty": 8, "sm": 4000, "sm_sm": 18000, "sm_sm_where_twenty_lt_10": 18000, "sm_sm_where_sm_lt_3k": 7500 }
+{ "twenty": 9, "sm": 4500, "sm_sm": 22500, "sm_sm_where_twenty_lt_10": 22500, "sm_sm_where_sm_lt_3k": 7500 }
+{ "twenty": 10, "sm": 0, "sm_sm": 22500, "sm_sm_where_twenty_lt_10": 22500, "sm_sm_where_sm_lt_3k": 7500 }
+{ "twenty": 11, "sm": 500, "sm_sm": 23000, "sm_sm_where_twenty_lt_10": 22500, "sm_sm_where_sm_lt_3k": 8000 }
+{ "twenty": 12, "sm": 1000, "sm_sm": 24000, "sm_sm_where_twenty_lt_10": 22500, "sm_sm_where_sm_lt_3k": 9000 }
+{ "twenty": 13, "sm": 1500, "sm_sm": 25500, "sm_sm_where_twenty_lt_10": 22500, "sm_sm_where_sm_lt_3k": 10500 }
+{ "twenty": 14, "sm": 2000, "sm_sm": 27500, "sm_sm_where_twenty_lt_10": 22500, "sm_sm_where_sm_lt_3k": 12500 }
+{ "twenty": 15, "sm": 2500, "sm_sm": 30000, "sm_sm_where_twenty_lt_10": 22500, "sm_sm_where_sm_lt_3k": 15000 }
+{ "twenty": 16, "sm": 3000, "sm_sm": 33000, "sm_sm_where_twenty_lt_10": 22500, "sm_sm_where_sm_lt_3k": 15000 }
+{ "twenty": 17, "sm": 3500, "sm_sm": 36500, "sm_sm_where_twenty_lt_10": 22500, "sm_sm_where_sm_lt_3k": 15000 }
+{ "twenty": 18, "sm": 4000, "sm_sm": 40500, "sm_sm_where_twenty_lt_10": 22500, "sm_sm_where_sm_lt_3k": 15000 }
+{ "twenty": 19, "sm": 4500, "sm_sm": 45000, "sm_sm_where_twenty_lt_10": 22500, "sm_sm_where_sm_lt_3k": 15000 }
\ No newline at end of file
diff --git a/asterixdb/asterix-app/src/test/resources/runtimets/results/aggregate-subclause/agg_filter_01/agg_filter_01.8.adm b/asterixdb/asterix-app/src/test/resources/runtimets/results/aggregate-subclause/agg_filter_01/agg_filter_01.8.adm
new file mode 100644
index 0000000..a0630c4
--- /dev/null
+++ b/asterixdb/asterix-app/src/test/resources/runtimets/results/aggregate-subclause/agg_filter_01/agg_filter_01.8.adm
@@ -0,0 +1,2 @@
+{ "two": 0, "sm": 5000 }
+{ "two": 1, "sm": 10000 }
\ No newline at end of file
diff --git a/asterixdb/asterix-app/src/test/resources/runtimets/results_parser_sqlpp/aggregate-subclause/agg_filter_01/agg_filter_1.01.ast b/asterixdb/asterix-app/src/test/resources/runtimets/results_parser_sqlpp/aggregate-subclause/agg_filter_01/agg_filter_1.01.ast
new file mode 100644
index 0000000..66ac84b
--- /dev/null
+++ b/asterixdb/asterix-app/src/test/resources/runtimets/results_parser_sqlpp/aggregate-subclause/agg_filter_01/agg_filter_1.01.ast
@@ -0,0 +1,22 @@
+DataverseUse test
+TypeDecl tenkType [
+  closed RecordType {
+    unique1 : integer,
+    unique2 : integer,
+    two : integer,
+    four : integer,
+    ten : integer,
+    twenty : integer,
+    hundred : integer,
+    thousand : integer,
+    twothous : integer,
+    fivethous : integer,
+    tenthous : integer,
+    odd100 : integer,
+    even100 : integer,
+    stringu1 : string,
+    stringu2 : string,
+    string4 : string
+  }
+]
+DatasetDecl tenk(tenkType) partitioned by [[unique2]]
diff --git a/asterixdb/asterix-app/src/test/resources/runtimets/results_parser_sqlpp/aggregate-subclause/agg_filter_01/agg_filter_1.02.ast b/asterixdb/asterix-app/src/test/resources/runtimets/results_parser_sqlpp/aggregate-subclause/agg_filter_01/agg_filter_1.02.ast
new file mode 100644
index 0000000..916a59e
--- /dev/null
+++ b/asterixdb/asterix-app/src/test/resources/runtimets/results_parser_sqlpp/aggregate-subclause/agg_filter_01/agg_filter_1.02.ast
@@ -0,0 +1 @@
+DataverseUse test
diff --git a/asterixdb/asterix-app/src/test/resources/runtimets/results_parser_sqlpp/aggregate-subclause/agg_filter_01/agg_filter_1.03.ast b/asterixdb/asterix-app/src/test/resources/runtimets/results_parser_sqlpp/aggregate-subclause/agg_filter_01/agg_filter_1.03.ast
new file mode 100644
index 0000000..af7fbff
--- /dev/null
+++ b/asterixdb/asterix-app/src/test/resources/runtimets/results_parser_sqlpp/aggregate-subclause/agg_filter_01/agg_filter_1.03.ast
@@ -0,0 +1,67 @@
+DataverseUse test
+Query:
+SELECT [
+FunctionCall asterix.sql-count@1[
+  (
+    SELECT ELEMENT [
+    LiteralExpr [LONG] [1]
+    ]
+    FROM [      Variable [ Name=#1 ]
+      AS Variable [ Name=#2 ]
+    ]
+    Where
+      OperatorExpr [
+        FieldAccessor [
+          FieldAccessor [
+            Variable [ Name=#2 ]
+            Field=tenk
+          ]
+          Field=four
+        ]
+        >
+        LiteralExpr [LONG] [0]
+      ]
+  )
+]
+cnt
+FunctionCall asterix.sql-sum@1[
+  (
+    SELECT ELEMENT [
+    FieldAccessor [
+      FieldAccessor [
+        Variable [ Name=#3 ]
+        Field=tenk
+      ]
+      Field=four
+    ]
+    ]
+    FROM [      Variable [ Name=#1 ]
+      AS Variable [ Name=#3 ]
+    ]
+    Where
+      OperatorExpr [
+        FieldAccessor [
+          FieldAccessor [
+            Variable [ Name=#3 ]
+            Field=tenk
+          ]
+          Field=four
+        ]
+        >
+        LiteralExpr [LONG] [0]
+      ]
+  )
+]
+sm
+]
+FROM [  FunctionCall asterix.dataset@1[
+    LiteralExpr [STRING] [test.tenk]
+  ]
+  AS Variable [ Name=$tenk ]
+]
+Group All
+  GROUP AS Variable [ Name=#1 ]
+  (
+    tenk:=Variable [ Name=$tenk ]
+  )
+
diff --git a/asterixdb/asterix-app/src/test/resources/runtimets/results_parser_sqlpp/aggregate-subclause/agg_filter_01/agg_filter_1.04.ast b/asterixdb/asterix-app/src/test/resources/runtimets/results_parser_sqlpp/aggregate-subclause/agg_filter_01/agg_filter_1.04.ast
new file mode 100644
index 0000000..11d3ca1
--- /dev/null
+++ b/asterixdb/asterix-app/src/test/resources/runtimets/results_parser_sqlpp/aggregate-subclause/agg_filter_01/agg_filter_1.04.ast
@@ -0,0 +1,79 @@
+DataverseUse test
+Query:
+SELECT [
+Variable [ Name=$two ]
+two
+FunctionCall asterix.sql-count@1[
+  (
+    SELECT ELEMENT [
+    LiteralExpr [LONG] [1]
+    ]
+    FROM [      Variable [ Name=#1 ]
+      AS Variable [ Name=#2 ]
+    ]
+    Where
+      OperatorExpr [
+        FieldAccessor [
+          FieldAccessor [
+            Variable [ Name=#2 ]
+            Field=tenk
+          ]
+          Field=four
+        ]
+        >
+        LiteralExpr [LONG] [0]
+      ]
+  )
+]
+cnt
+FunctionCall asterix.sql-sum@1[
+  (
+    SELECT ELEMENT [
+    FieldAccessor [
+      FieldAccessor [
+        Variable [ Name=#3 ]
+        Field=tenk
+      ]
+      Field=four
+    ]
+    ]
+    FROM [      Variable [ Name=#1 ]
+      AS Variable [ Name=#3 ]
+    ]
+    Where
+      OperatorExpr [
+        FieldAccessor [
+          FieldAccessor [
+            Variable [ Name=#3 ]
+            Field=tenk
+          ]
+          Field=four
+        ]
+        >
+        LiteralExpr [LONG] [0]
+      ]
+  )
+]
+sm
+]
+FROM [  FunctionCall asterix.dataset@1[
+    LiteralExpr [STRING] [test.tenk]
+  ]
+  AS Variable [ Name=$tenk ]
+]
+Groupby
+  Variable [ Name=$two ]
+  :=
+  FieldAccessor [
+    Variable [ Name=$tenk ]
+    Field=two
+  ]
+  GROUP AS Variable [ Name=#1 ]
+  (
+    tenk:=Variable [ Name=$tenk ]
+  )
+
+Orderby
+  Variable [ Name=$two ]
+  ASC
+
diff --git a/asterixdb/asterix-app/src/test/resources/runtimets/results_parser_sqlpp/aggregate-subclause/agg_filter_01/agg_filter_1.05.ast b/asterixdb/asterix-app/src/test/resources/runtimets/results_parser_sqlpp/aggregate-subclause/agg_filter_01/agg_filter_1.05.ast
new file mode 100644
index 0000000..28919af
--- /dev/null
+++ b/asterixdb/asterix-app/src/test/resources/runtimets/results_parser_sqlpp/aggregate-subclause/agg_filter_01/agg_filter_1.05.ast
@@ -0,0 +1,83 @@
+DataverseUse test
+Query:
+SELECT [
+Variable [ Name=$two ]
+two
+FunctionCall asterix.sql-count@1[
+  (
+    SELECT ELEMENT [
+    LiteralExpr [LONG] [1]
+    ]
+    FROM [      Variable [ Name=#1 ]
+      AS Variable [ Name=#2 ]
+    ]
+    Where
+      OperatorExpr [
+        FieldAccessor [
+          FieldAccessor [
+            Variable [ Name=#2 ]
+            Field=tenk
+          ]
+          Field=four
+        ]
+        >
+        LiteralExpr [LONG] [0]
+      ]
+  )
+]
+cnt
+FunctionCall asterix.sql-sum@1[
+  (
+    SELECT ELEMENT [
+    FieldAccessor [
+      FieldAccessor [
+        Variable [ Name=#3 ]
+        Field=tenk
+      ]
+      Field=four
+    ]
+    ]
+    FROM [      Variable [ Name=#1 ]
+      AS Variable [ Name=#3 ]
+    ]
+    Where
+      OperatorExpr [
+        FieldAccessor [
+          FieldAccessor [
+            Variable [ Name=#3 ]
+            Field=tenk
+          ]
+          Field=four
+        ]
+        >
+        LiteralExpr [LONG] [0]
+      ]
+  )
+]
+sm
+]
+FROM [  FunctionCall asterix.dataset@1[
+    LiteralExpr [STRING] [test.tenk]
+  ]
+  AS Variable [ Name=$tenk ]
+]
+Groupby
+  GROUPING SET (
+    Variable [ Name=$two ]
+    :=
+    FieldAccessor [
+      Variable [ Name=$tenk ]
+      Field=two
+    ]
+  )
+  GROUPING SET (
+  ),
+  GROUP AS Variable [ Name=#1 ]
+  (
+    tenk:=Variable [ Name=$tenk ]
+  )
+
+Orderby
+  Variable [ Name=$two ]
+  ASC
+
diff --git a/asterixdb/asterix-app/src/test/resources/runtimets/results_parser_sqlpp/aggregate-subclause/agg_filter_01/agg_filter_1.06.ast b/asterixdb/asterix-app/src/test/resources/runtimets/results_parser_sqlpp/aggregate-subclause/agg_filter_01/agg_filter_1.06.ast
new file mode 100644
index 0000000..60815f4
--- /dev/null
+++ b/asterixdb/asterix-app/src/test/resources/runtimets/results_parser_sqlpp/aggregate-subclause/agg_filter_01/agg_filter_1.06.ast
@@ -0,0 +1,102 @@
+DataverseUse test
+Query:
+SELECT [
+FieldAccessor [
+  Variable [ Name=$tenk ]
+  Field=twothous
+]
+twothous
+FieldAccessor [
+  Variable [ Name=$tenk ]
+  Field=fivethous
+]
+fivethous
+FieldAccessor [
+  Variable [ Name=$tenk ]
+  Field=tenthous
+]
+tenthous
+WINDOW test.count@1[
+  Variable [ Name=$tenthous ]
+]
+OVER (
+  ORDER BY
+    FieldAccessor [
+      Variable [ Name=$tenk ]
+      Field=tenthous
+    ]
+    ASC
+)
+cnt
+WINDOW test.sum@1[
+  Variable [ Name=$tenthous ]
+]
+OVER (
+  ORDER BY
+    FieldAccessor [
+      Variable [ Name=$tenk ]
+      Field=tenthous
+    ]
+    ASC
+)
+sm
+WINDOW test.count@1[
+  Variable [ Name=$tenthous ]
+]
+  FILTER (WHERE
+    OperatorExpr [
+      Variable [ Name=$twothous ]
+      =
+      LiteralExpr [LONG] [0]
+    ]
+  )
+OVER (
+  ORDER BY
+    FieldAccessor [
+      Variable [ Name=$tenk ]
+      Field=tenthous
+    ]
+    ASC
+)
+cnt_filter
+WINDOW test.sum@1[
+  Variable [ Name=$tenthous ]
+]
+  FILTER (WHERE
+    OperatorExpr [
+      Variable [ Name=$twothous ]
+      =
+      LiteralExpr [LONG] [0]
+    ]
+  )
+OVER (
+  ORDER BY
+    FieldAccessor [
+      Variable [ Name=$tenk ]
+      Field=tenthous
+    ]
+    ASC
+)
+sm_filter
+]
+FROM [  FunctionCall asterix.dataset@1[
+    LiteralExpr [STRING] [test.tenk]
+  ]
+  AS Variable [ Name=$tenk ]
+]
+Where
+  OperatorExpr [
+    FieldAccessor [
+      Variable [ Name=$tenk ]
+      Field=thousand
+    ]
+    =
+    LiteralExpr [LONG] [0]
+  ]
+Orderby
+  FieldAccessor [
+    Variable [ Name=$tenk ]
+    Field=tenthous
+  ]
+  ASC
+
diff --git a/asterixdb/asterix-app/src/test/resources/runtimets/results_parser_sqlpp/aggregate-subclause/agg_filter_01/agg_filter_1.07.ast b/asterixdb/asterix-app/src/test/resources/runtimets/results_parser_sqlpp/aggregate-subclause/agg_filter_01/agg_filter_1.07.ast
new file mode 100644
index 0000000..48f5c9d
--- /dev/null
+++ b/asterixdb/asterix-app/src/test/resources/runtimets/results_parser_sqlpp/aggregate-subclause/agg_filter_01/agg_filter_1.07.ast
@@ -0,0 +1,145 @@
+DataverseUse test
+Query:
+SELECT [
+Variable [ Name=$twenty ]
+twenty
+FunctionCall asterix.sql-sum@1[
+  (
+    SELECT ELEMENT [
+    FieldAccessor [
+      FieldAccessor [
+        Variable [ Name=#2 ]
+        Field=tenk
+      ]
+      Field=ten
+    ]
+    ]
+    FROM [      Variable [ Name=#1 ]
+      AS Variable [ Name=#2 ]
+    ]
+  )
+]
+sm
+WINDOW test.sum@1[
+  FunctionCall asterix.sql-sum@1[
+    (
+      SELECT ELEMENT [
+      FieldAccessor [
+        FieldAccessor [
+          Variable [ Name=#3 ]
+          Field=tenk
+        ]
+        Field=ten
+      ]
+      ]
+      FROM [        Variable [ Name=#1 ]
+        AS Variable [ Name=#3 ]
+      ]
+    )
+  ]
+]
+OVER (
+  ORDER BY
+    Variable [ Name=$twenty ]
+    ASC
+)
+sm_sm
+WINDOW test.sum@1[
+  FunctionCall asterix.sql-sum@1[
+    (
+      SELECT ELEMENT [
+      FieldAccessor [
+        FieldAccessor [
+          Variable [ Name=#4 ]
+          Field=tenk
+        ]
+        Field=ten
+      ]
+      ]
+      FROM [        Variable [ Name=#1 ]
+        AS Variable [ Name=#4 ]
+      ]
+    )
+  ]
+]
+  FILTER (WHERE
+    OperatorExpr [
+      Variable [ Name=$twenty ]
+      <
+      LiteralExpr [LONG] [10]
+    ]
+  )
+OVER (
+  ORDER BY
+    Variable [ Name=$twenty ]
+    ASC
+)
+sm_sm_where_twenty_lt_10
+WINDOW test.sum@1[
+  FunctionCall asterix.sql-sum@1[
+    (
+      SELECT ELEMENT [
+      FieldAccessor [
+        FieldAccessor [
+          Variable [ Name=#5 ]
+          Field=tenk
+        ]
+        Field=ten
+      ]
+      ]
+      FROM [        Variable [ Name=#1 ]
+        AS Variable [ Name=#5 ]
+      ]
+    )
+  ]
+]
+  FILTER (WHERE
+    OperatorExpr [
+      FunctionCall asterix.sql-sum@1[
+        (
+          SELECT ELEMENT [
+          FieldAccessor [
+            FieldAccessor [
+              Variable [ Name=#6 ]
+              Field=tenk
+            ]
+            Field=ten
+          ]
+          ]
+          FROM [            Variable [ Name=#1 ]
+            AS Variable [ Name=#6 ]
+          ]
+        )
+      ]
+      <
+      LiteralExpr [LONG] [3000]
+    ]
+  )
+OVER (
+  ORDER BY
+    Variable [ Name=$twenty ]
+    ASC
+)
+sm_sm_where_sm_lt_3k
+]
+FROM [  FunctionCall asterix.dataset@1[
+    LiteralExpr [STRING] [test.tenk]
+  ]
+  AS Variable [ Name=$tenk ]
+]
+Groupby
+  Variable [ Name=$twenty ]
+  :=
+  FieldAccessor [
+    Variable [ Name=$tenk ]
+    Field=twenty
+  ]
+  GROUP AS Variable [ Name=#1 ]
+  (
+    tenk:=Variable [ Name=$tenk ]
+  )
+
+Orderby
+  Variable [ Name=$twenty ]
+  ASC
+
diff --git a/asterixdb/asterix-app/src/test/resources/runtimets/results_parser_sqlpp/aggregate-subclause/agg_filter_01/agg_filter_1.08.ast b/asterixdb/asterix-app/src/test/resources/runtimets/results_parser_sqlpp/aggregate-subclause/agg_filter_01/agg_filter_1.08.ast
new file mode 100644
index 0000000..311e37f
--- /dev/null
+++ b/asterixdb/asterix-app/src/test/resources/runtimets/results_parser_sqlpp/aggregate-subclause/agg_filter_01/agg_filter_1.08.ast
@@ -0,0 +1,83 @@
+DataverseUse test
+Query:
+SELECT [
+Variable [ Name=$two ]
+two
+FunctionCall asterix.sql-sum@1[
+  (
+    SELECT ELEMENT [
+    FieldAccessor [
+      FieldAccessor [
+        Variable [ Name=#2 ]
+        Field=tenk
+      ]
+      Field=four
+    ]
+    ]
+    FROM [      Variable [ Name=#1 ]
+      AS Variable [ Name=#2 ]
+    ]
+    Where
+      QuantifiedExpression EVERY [
+        [Variable [ Name=$x ]
+        In
+          OrderedListConstructor [
+            FieldAccessor [
+              FieldAccessor [
+                Variable [ Name=#2 ]
+                Field=tenk
+              ]
+              Field=four
+            ]
+            OperatorExpr [
+              FieldAccessor [
+                FieldAccessor [
+                  Variable [ Name=#2 ]
+                  Field=tenk
+                ]
+                Field=four
+              ]
+              +
+              FieldAccessor [
+                FieldAccessor [
+                  Variable [ Name=#2 ]
+                  Field=tenk
+                ]
+                Field=four
+              ]
+            ]
+          ]
+        ]
+        Satifies [
+          OperatorExpr [
+            Variable [ Name=$x ]
+            >
+            LiteralExpr [LONG] [0]
+          ]
+        ]
+      ]
+  )
+]
+sm
+]
+FROM [  FunctionCall asterix.dataset@1[
+    LiteralExpr [STRING] [test.tenk]
+  ]
+  AS Variable [ Name=$tenk ]
+]
+Groupby
+  Variable [ Name=$two ]
+  :=
+  FieldAccessor [
+    Variable [ Name=$tenk ]
+    Field=two
+  ]
+  GROUP AS Variable [ Name=#1 ]
+  (
+    tenk:=Variable [ Name=$tenk ]
+  )
+
+Orderby
+  Variable [ Name=$two ]
+  ASC
+
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 ed56b6e..abee116 100644
--- a/asterixdb/asterix-app/src/test/resources/runtimets/testsuite_sqlpp.xml
+++ b/asterixdb/asterix-app/src/test/resources/runtimets/testsuite_sqlpp.xml
@@ -2862,6 +2862,23 @@
       </compilation-unit>
     </test-case>
   </test-group>
+  <test-group name="aggregate-subclause">
+    <test-case FilePath="aggregate-subclause">
+      <compilation-unit name="agg_filter_01">
+        <output-dir compare="Text">agg_filter_01</output-dir>
+      </compilation-unit>
+    </test-case>
+    <test-case FilePath="aggregate-subclause">
+      <compilation-unit name="agg_filter_02_neg">
+        <output-dir compare="Text">agg_filter_01</output-dir>
+        <expected-error>ASX1116: Illegal use of aggregate FILTER clause</expected-error>
+        <expected-error>ASX1116: Illegal use of aggregate FILTER clause</expected-error>
+        <expected-error>ASX1073: Cannot resolve alias reference for undefined identifier r</expected-error>
+        <expected-error>ASX1116: Illegal use of aggregate FILTER clause</expected-error>
+        <expected-error>ASX1116: Illegal use of aggregate FILTER clause</expected-error>
+      </compilation-unit>
+    </test-case>
+  </test-group>
   <test-group name="array_fun">
     <test-case FilePath="array_fun">
       <compilation-unit name="array_prepend">
@@ -5395,7 +5412,7 @@
     <test-case FilePath="global-aggregate">
       <compilation-unit name="q12_error">
         <output-dir compare="Text">q01</output-dir>
-        <expected-error>The parameter * can only be used in COUNT().</expected-error>
+        <expected-error>The parameter * can only be used in count().</expected-error>
       </compilation-unit>
     </test-case>
     <test-case FilePath="global-aggregate">
@@ -11687,13 +11704,13 @@
     <test-case FilePath="user-defined-functions">
       <compilation-unit name="bad-function-ddl-9">
         <output-dir compare="Text">bad-function-ddl-9</output-dir>
-        <expected-error>function experiments.function_that_does_not_exist@0 is not defined</expected-error>
+        <expected-error>function experiments.function_that_does_not_exist@0 which is undefined</expected-error>
       </compilation-unit>
     </test-case>
     <test-case FilePath="user-defined-functions">
       <compilation-unit name="bad-function-ddl-10">
         <output-dir compare="Text">bad-function-ddl-10</output-dir>
-        <expected-error>function experiments.f0@2 is not defined</expected-error>
+        <expected-error>function experiments.f0@2 which is undefined</expected-error>
       </compilation-unit>
     </test-case>
     <test-case FilePath="user-defined-functions">
diff --git a/asterixdb/asterix-app/src/test/resources/runtimets/testsuite_sqlpp_parser.xml b/asterixdb/asterix-app/src/test/resources/runtimets/testsuite_sqlpp_parser.xml
index e9b1de0..9c4bb41 100644
--- a/asterixdb/asterix-app/src/test/resources/runtimets/testsuite_sqlpp_parser.xml
+++ b/asterixdb/asterix-app/src/test/resources/runtimets/testsuite_sqlpp_parser.xml
@@ -868,6 +868,13 @@
       </compilation-unit>
     </test-case>
   </test-group>
+  <test-group name="aggregate-subclause">
+    <test-case FilePath="aggregate-subclause">
+      <compilation-unit name="agg_filter_01">
+        <output-dir compare="AST">agg_filter_01</output-dir>
+      </compilation-unit>
+    </test-case>
+  </test-group>
   <test-group name="boolean">
     <test-case FilePath="boolean">
       <compilation-unit name="and_01">
diff --git a/asterixdb/asterix-common/src/main/java/org/apache/asterix/common/exceptions/ErrorCode.java b/asterixdb/asterix-common/src/main/java/org/apache/asterix/common/exceptions/ErrorCode.java
index 97d653d..2bb62b1 100644
--- a/asterixdb/asterix-common/src/main/java/org/apache/asterix/common/exceptions/ErrorCode.java
+++ b/asterixdb/asterix-common/src/main/java/org/apache/asterix/common/exceptions/ErrorCode.java
@@ -205,6 +205,7 @@
     public static final int COMPILATION_GROUPING_SETS_OVERFLOW = 1113;
     public static final int COMPILATION_GROUPING_OPERATION_INVALID_ARG = 1114;
     public static final int COMPILATION_UNEXPECTED_ALIAS = 1115;
+    public static final int COMPILATION_ILLEGAL_USE_OF_FILTER_CLAUSE = 1116;
 
     // Feed errors
     public static final int DATAFLOW_ILLEGAL_STATE = 3001;
diff --git a/asterixdb/asterix-common/src/main/resources/asx_errormsg/en.properties b/asterixdb/asterix-common/src/main/resources/asx_errormsg/en.properties
index 2de5f48..fa0f469 100644
--- a/asterixdb/asterix-common/src/main/resources/asx_errormsg/en.properties
+++ b/asterixdb/asterix-common/src/main/resources/asx_errormsg/en.properties
@@ -200,6 +200,7 @@
 1113 = Too many grouping sets in group by clause: %1$s. Maximum allowed: %2$s.
 1114 = Invalid argument to grouping() function
 1115 = Unexpected alias: %1$s
+1116 = Illegal use of aggregate FILTER clause
 
 # Feed Errors
 3001 = Illegal state.
diff --git a/asterixdb/asterix-doc/src/main/markdown/sqlpp/3_query.md b/asterixdb/asterix-doc/src/main/markdown/sqlpp/3_query.md
index e19e2ec..d2ec756 100644
--- a/asterixdb/asterix-doc/src/main/markdown/sqlpp/3_query.md
+++ b/asterixdb/asterix-doc/src/main/markdown/sqlpp/3_query.md
@@ -1266,7 +1266,7 @@
 Rather, the `COUNT` query above is using a special "sugared" function symbol that the query compiler
 will rewrite as follows:
 
-    SELECT uid AS uid, ARRAY_COUNT( (SELECT VALUE 1 FROM `$1` as g) ) AS msgCnt
+    SELECT uid AS uid, ARRAY_COUNT( (SELECT VALUE 1 FROM `$1` AS g) ) AS msgCnt
     FROM GleambookMessages msg
     GROUP BY msg.authorId AS uid
     GROUP AS `$1`(msg AS msg);
@@ -1298,6 +1298,23 @@
 Note that the `ARRAY_AGG` function symbol is rewritten simply to return the result of the generated subquery,
 without applying any built-in function.
 
+SQL aggregate function calls optionally support a FILTER subclause.
+
+##### Example
+
+    SELECT uid, COUNT(*) FILTER (WHERE msg.message LIKE "%awesome%") AS msgCnt
+    FROM GleambookMessages msg
+    GROUP BY msg.authorId AS uid;
+
+The query compiler rewrites this query to use the built-in aggregate as follows:
+
+    SELECT uid AS uid, ARRAY_COUNT( (SELECT VALUE 1 FROM `$1` AS g WHERE g.msg.message LIKE "%awesome%") ) AS msgCnt
+    FROM GleambookMessages msg
+    GROUP BY msg.authorId AS uid
+    GROUP AS `$1`(msg AS msg);
+
+Note that the FILTER subclause is not supported for built-in aggregate function calls.
+
 ### <a id="SQL-92_compliant_gby">SQL-92 Compliant GROUP BY Aggregations</a>
 The query language provides full support for SQL-92 `GROUP BY` aggregation queries.
 The following query is such an example:
diff --git a/asterixdb/asterix-lang-aql/src/main/java/org/apache/asterix/lang/aql/rewrites/visitor/AqlBuiltinFunctionRewriteVisitor.java b/asterixdb/asterix-lang-aql/src/main/java/org/apache/asterix/lang/aql/rewrites/visitor/AqlBuiltinFunctionRewriteVisitor.java
index 4f422d4..5b52ef5 100644
--- a/asterixdb/asterix-lang-aql/src/main/java/org/apache/asterix/lang/aql/rewrites/visitor/AqlBuiltinFunctionRewriteVisitor.java
+++ b/asterixdb/asterix-lang-aql/src/main/java/org/apache/asterix/lang/aql/rewrites/visitor/AqlBuiltinFunctionRewriteVisitor.java
@@ -41,7 +41,9 @@
             newExprList.add(expr.accept(this, arg));
         }
         callExpr.setExprList(newExprList);
+        if (callExpr.hasAggregateFilterExpr()) {
+            callExpr.setAggregateFilterExpr(callExpr.getAggregateFilterExpr().accept(this, arg));
+        }
         return callExpr;
     }
-
 }
diff --git a/asterixdb/asterix-lang-aql/src/main/java/org/apache/asterix/lang/aql/visitor/base/AbstractAqlSimpleExpressionVisitor.java b/asterixdb/asterix-lang-aql/src/main/java/org/apache/asterix/lang/aql/visitor/base/AbstractAqlSimpleExpressionVisitor.java
index 16bf19e..b8c3470 100644
--- a/asterixdb/asterix-lang-aql/src/main/java/org/apache/asterix/lang/aql/visitor/base/AbstractAqlSimpleExpressionVisitor.java
+++ b/asterixdb/asterix-lang-aql/src/main/java/org/apache/asterix/lang/aql/visitor/base/AbstractAqlSimpleExpressionVisitor.java
@@ -177,6 +177,9 @@
     @Override
     public Expression visit(CallExpr callExpr, ILangExpression arg) throws CompilationException {
         callExpr.setExprList(visit(callExpr.getExprList(), arg));
+        if (callExpr.hasAggregateFilterExpr()) {
+            callExpr.setAggregateFilterExpr(visit(callExpr.getAggregateFilterExpr(), arg));
+        }
         return callExpr;
     }
 
diff --git a/asterixdb/asterix-lang-common/src/main/java/org/apache/asterix/lang/common/expression/CallExpr.java b/asterixdb/asterix-lang-common/src/main/java/org/apache/asterix/lang/common/expression/CallExpr.java
index 6582b9d..d0c29d5 100644
--- a/asterixdb/asterix-lang-common/src/main/java/org/apache/asterix/lang/common/expression/CallExpr.java
+++ b/asterixdb/asterix-lang-common/src/main/java/org/apache/asterix/lang/common/expression/CallExpr.java
@@ -30,10 +30,16 @@
 public class CallExpr extends AbstractExpression {
     private FunctionSignature functionSignature;
     private List<Expression> exprList;
+    private Expression aggFilterExpr;
 
     public CallExpr(FunctionSignature functionSignature, List<Expression> exprList) {
+        this(functionSignature, exprList, null);
+    }
+
+    public CallExpr(FunctionSignature functionSignature, List<Expression> exprList, Expression aggFilterExpr) {
         this.functionSignature = functionSignature;
         this.exprList = exprList;
+        this.aggFilterExpr = aggFilterExpr;
     }
 
     public FunctionSignature getFunctionSignature() {
@@ -44,6 +50,14 @@
         return exprList;
     }
 
+    public boolean hasAggregateFilterExpr() {
+        return aggFilterExpr != null;
+    }
+
+    public Expression getAggregateFilterExpr() {
+        return aggFilterExpr;
+    }
+
     @Override
     public Kind getKind() {
         return Kind.CALL_EXPRESSION;
@@ -57,6 +71,10 @@
         this.exprList = exprList;
     }
 
+    public void setAggregateFilterExpr(Expression aggFilterExpr) {
+        this.aggFilterExpr = aggFilterExpr;
+    }
+
     @Override
     public <R, T> R accept(ILangVisitor<R, T> visitor, T arg) throws CompilationException {
         return visitor.visit(this, arg);
@@ -69,7 +87,7 @@
 
     @Override
     public int hashCode() {
-        return Objects.hash(exprList, functionSignature);
+        return Objects.hash(exprList, aggFilterExpr, functionSignature);
     }
 
     @Override
@@ -81,6 +99,7 @@
             return false;
         }
         CallExpr target = (CallExpr) object;
-        return Objects.equals(exprList, target.exprList) && Objects.equals(functionSignature, target.functionSignature);
+        return Objects.equals(exprList, target.exprList) && Objects.equals(aggFilterExpr, target.aggFilterExpr)
+                && Objects.equals(functionSignature, target.functionSignature);
     }
 }
diff --git a/asterixdb/asterix-lang-common/src/main/java/org/apache/asterix/lang/common/visitor/AbstractInlineUdfsVisitor.java b/asterixdb/asterix-lang-common/src/main/java/org/apache/asterix/lang/common/visitor/AbstractInlineUdfsVisitor.java
index 282be2f..984ceb4 100644
--- a/asterixdb/asterix-lang-common/src/main/java/org/apache/asterix/lang/common/visitor/AbstractInlineUdfsVisitor.java
+++ b/asterixdb/asterix-lang-common/src/main/java/org/apache/asterix/lang/common/visitor/AbstractInlineUdfsVisitor.java
@@ -132,10 +132,16 @@
     }
 
     @Override
-    public Boolean visit(CallExpr pf, List<FunctionDecl> arg) throws CompilationException {
-        Pair<Boolean, List<Expression>> p = inlineUdfsInExprList(pf.getExprList(), arg);
-        pf.setExprList(p.second);
-        return p.first;
+    public Boolean visit(CallExpr callExpr, List<FunctionDecl> arg) throws CompilationException {
+        Pair<Boolean, List<Expression>> p = inlineUdfsInExprList(callExpr.getExprList(), arg);
+        callExpr.setExprList(p.second);
+        boolean changed = p.first;
+        if (callExpr.hasAggregateFilterExpr()) {
+            Pair<Boolean, Expression> be = inlineUdfsInExpr(callExpr.getAggregateFilterExpr(), arg);
+            callExpr.setAggregateFilterExpr(be.second);
+            changed |= be.first;
+        }
+        return changed;
     }
 
     @Override
@@ -289,6 +295,10 @@
         if (implem == null) {
             return new Pair<>(r, expr);
         } else {
+            if (f.hasAggregateFilterExpr()) {
+                throw new CompilationException(ErrorCode.COMPILATION_ILLEGAL_USE_OF_FILTER_CLAUSE,
+                        f.getSourceLocation());
+            }
             // Rewrite the function body itself (without setting unbounded variables to dataset access).
             // TODO(buyingyi): throw an exception for recursive function definition or limit the stack depth.
             implem.setFuncBody(rewriteFunctionBody(implem));
diff --git a/asterixdb/asterix-lang-common/src/main/java/org/apache/asterix/lang/common/visitor/CloneAndSubstituteVariablesVisitor.java b/asterixdb/asterix-lang-common/src/main/java/org/apache/asterix/lang/common/visitor/CloneAndSubstituteVariablesVisitor.java
index 8d37e41..567dd4a 100644
--- a/asterixdb/asterix-lang-common/src/main/java/org/apache/asterix/lang/common/visitor/CloneAndSubstituteVariablesVisitor.java
+++ b/asterixdb/asterix-lang-common/src/main/java/org/apache/asterix/lang/common/visitor/CloneAndSubstituteVariablesVisitor.java
@@ -143,12 +143,19 @@
     }
 
     @Override
-    public Pair<ILangExpression, VariableSubstitutionEnvironment> visit(CallExpr pf,
+    public Pair<ILangExpression, VariableSubstitutionEnvironment> visit(CallExpr callExpr,
             VariableSubstitutionEnvironment env) throws CompilationException {
-        List<Expression> exprList = VariableCloneAndSubstitutionUtil.visitAndCloneExprList(pf.getExprList(), env, this);
-        CallExpr f = new CallExpr(pf.getFunctionSignature(), exprList);
-        f.setSourceLocation(pf.getSourceLocation());
-        f.addHints(pf.getHints());
+        List<Expression> exprList =
+                VariableCloneAndSubstitutionUtil.visitAndCloneExprList(callExpr.getExprList(), env, this);
+        Expression newFilterExpr = null;
+        if (callExpr.hasAggregateFilterExpr()) {
+            Pair<ILangExpression, VariableSubstitutionEnvironment> paf =
+                    callExpr.getAggregateFilterExpr().accept(this, env);
+            newFilterExpr = (Expression) paf.first;
+        }
+        CallExpr f = new CallExpr(callExpr.getFunctionSignature(), exprList, newFilterExpr);
+        f.setSourceLocation(callExpr.getSourceLocation());
+        f.addHints(callExpr.getHints());
         return new Pair<>(f, env);
     }
 
diff --git a/asterixdb/asterix-lang-common/src/main/java/org/apache/asterix/lang/common/visitor/FormatPrintVisitor.java b/asterixdb/asterix-lang-common/src/main/java/org/apache/asterix/lang/common/visitor/FormatPrintVisitor.java
index c9ec456..1de5dcf 100644
--- a/asterixdb/asterix-lang-common/src/main/java/org/apache/asterix/lang/common/visitor/FormatPrintVisitor.java
+++ b/asterixdb/asterix-lang-common/src/main/java/org/apache/asterix/lang/common/visitor/FormatPrintVisitor.java
@@ -226,6 +226,11 @@
                 callExpr.getFunctionSignature().getName()) + "(");
         printDelimitedExpressions(callExpr.getExprList(), COMMA, step);
         out.print(")");
+        if (callExpr.hasAggregateFilterExpr()) {
+            out.println(" FILTER ( WHERE ");
+            callExpr.getAggregateFilterExpr().accept(this, step + 1);
+            out.println(skip(step) + ")");
+        }
         return null;
     }
 
diff --git a/asterixdb/asterix-lang-common/src/main/java/org/apache/asterix/lang/common/visitor/GatherFunctionCallsVisitor.java b/asterixdb/asterix-lang-common/src/main/java/org/apache/asterix/lang/common/visitor/GatherFunctionCallsVisitor.java
index 73f489c..3d7b467 100644
--- a/asterixdb/asterix-lang-common/src/main/java/org/apache/asterix/lang/common/visitor/GatherFunctionCallsVisitor.java
+++ b/asterixdb/asterix-lang-common/src/main/java/org/apache/asterix/lang/common/visitor/GatherFunctionCallsVisitor.java
@@ -19,7 +19,7 @@
 
 package org.apache.asterix.lang.common.visitor;
 
-import java.util.HashSet;
+import java.util.LinkedHashSet;
 import java.util.List;
 import java.util.Map;
 import java.util.Set;
@@ -57,14 +57,17 @@
 
 public class GatherFunctionCallsVisitor extends AbstractQueryExpressionVisitor<Void, Void> {
 
-    protected final Set<CallExpr> calls = new HashSet<>();
+    protected final Set<CallExpr> calls = new LinkedHashSet<>();
 
     @Override
-    public Void visit(CallExpr pf, Void arg) throws CompilationException {
-        calls.add(pf);
-        for (Expression e : pf.getExprList()) {
+    public Void visit(CallExpr callExpr, Void arg) throws CompilationException {
+        calls.add(callExpr);
+        for (Expression e : callExpr.getExprList()) {
             e.accept(this, arg);
         }
+        if (callExpr.hasAggregateFilterExpr()) {
+            callExpr.getAggregateFilterExpr().accept(this, arg);
+        }
         return null;
     }
 
diff --git a/asterixdb/asterix-lang-common/src/main/java/org/apache/asterix/lang/common/visitor/QueryPrintVisitor.java b/asterixdb/asterix-lang-common/src/main/java/org/apache/asterix/lang/common/visitor/QueryPrintVisitor.java
index fd7ef7a..f83a89e 100644
--- a/asterixdb/asterix-lang-common/src/main/java/org/apache/asterix/lang/common/visitor/QueryPrintVisitor.java
+++ b/asterixdb/asterix-lang-common/src/main/java/org/apache/asterix/lang/common/visitor/QueryPrintVisitor.java
@@ -140,20 +140,26 @@
     }
 
     @Override
-    public Void visit(CallExpr pf, Integer step) throws CompilationException {
-        return printFunctionCall(pf.getFunctionSignature(), pf.getFunctionSignature().getArity(), pf.getExprList(),
-                step);
+    public Void visit(CallExpr callExpr, Integer step) throws CompilationException {
+        return printFunctionCall(callExpr.getFunctionSignature(), callExpr.getFunctionSignature().getArity(),
+                callExpr.getExprList(), callExpr.getAggregateFilterExpr(), step);
     }
 
-    protected Void printFunctionCall(FunctionSignature fs, int arity, List<Expression> argList, Integer step)
-            throws CompilationException {
+    protected Void printFunctionCall(FunctionSignature fs, int arity, List<Expression> argList,
+            Expression aggFilterExpr, Integer step) throws CompilationException {
         out.print(skip(step) + "FunctionCall ");
         printFunctionSignature(out, fs, arity);
         out.println("[");
         for (Expression expr : argList) {
             expr.accept(this, step + 1);
         }
-        out.println(skip(step) + "]");
+        out.print(skip(step) + "]");
+        if (aggFilterExpr != null) {
+            out.println(" filter [");
+            aggFilterExpr.accept(this, step + 1);
+            out.print(skip(step) + "]");
+        }
+        out.println();
         return null;
     }
 
diff --git a/asterixdb/asterix-lang-sqlpp/src/main/java/org/apache/asterix/lang/sqlpp/expression/WindowExpression.java b/asterixdb/asterix-lang-sqlpp/src/main/java/org/apache/asterix/lang/sqlpp/expression/WindowExpression.java
index e0a6c6b..9b603cd 100644
--- a/asterixdb/asterix-lang-sqlpp/src/main/java/org/apache/asterix/lang/sqlpp/expression/WindowExpression.java
+++ b/asterixdb/asterix-lang-sqlpp/src/main/java/org/apache/asterix/lang/sqlpp/expression/WindowExpression.java
@@ -39,6 +39,7 @@
 
     private FunctionSignature functionSignature;
     private List<Expression> exprList;
+    private Expression aggFilterExpr;
 
     private List<Expression> partitionList;
     private List<Expression> orderbyList;
@@ -57,7 +58,7 @@
     private Boolean ignoreNulls;
     private Boolean fromLast;
 
-    public WindowExpression(FunctionSignature functionSignature, List<Expression> exprList,
+    public WindowExpression(FunctionSignature functionSignature, List<Expression> exprList, Expression aggFilterExpr,
             List<Expression> partitionList, List<Expression> orderbyList,
             List<OrderbyClause.OrderModifier> orderbyModifierList, FrameMode frameMode,
             FrameBoundaryKind frameStartKind, Expression frameStartExpr, FrameBoundaryKind frameEndKind,
@@ -68,6 +69,7 @@
         }
         this.functionSignature = functionSignature;
         this.exprList = exprList;
+        this.aggFilterExpr = aggFilterExpr;
         this.partitionList = partitionList;
         this.orderbyList = orderbyList;
         this.orderbyModifierList = orderbyModifierList;
@@ -110,6 +112,18 @@
         this.exprList = exprList;
     }
 
+    public boolean hasAggregateFilterExpr() {
+        return aggFilterExpr != null;
+    }
+
+    public Expression getAggregateFilterExpr() {
+        return aggFilterExpr;
+    }
+
+    public void setAggregateFilterExpr(Expression filterExpr) {
+        this.aggFilterExpr = filterExpr;
+    }
+
     public boolean hasPartitionList() {
         return partitionList != null && !partitionList.isEmpty();
     }
@@ -244,7 +258,7 @@
 
     @Override
     public int hashCode() {
-        return Objects.hash(functionSignature, exprList, ExpressionUtils.emptyIfNull(partitionList),
+        return Objects.hash(functionSignature, exprList, aggFilterExpr, ExpressionUtils.emptyIfNull(partitionList),
                 ExpressionUtils.emptyIfNull(orderbyList), ExpressionUtils.emptyIfNull(orderbyModifierList), frameMode,
                 frameStartKind, frameStartExpr, frameEndKind, frameEndExpr, frameExclusionKind, windowVar,
                 ExpressionUtils.emptyIfNull(windowFieldList), ignoreNulls, fromLast);
@@ -261,6 +275,7 @@
         }
         WindowExpression target = (WindowExpression) object;
         return Objects.equals(functionSignature, target.functionSignature) && Objects.equals(exprList, target.exprList)
+                && Objects.equals(aggFilterExpr, target.aggFilterExpr)
                 && Objects.equals(ExpressionUtils.emptyIfNull(partitionList),
                         ExpressionUtils.emptyIfNull(target.partitionList))
                 && Objects.equals(ExpressionUtils.emptyIfNull(orderbyList),
@@ -290,6 +305,9 @@
         if (ignoreNulls != null && ignoreNulls) {
             sb.append(" IGNORE NULLS");
         }
+        if (aggFilterExpr != null) {
+            sb.append(" FILTER (WHERE ").append(aggFilterExpr).append(')');
+        }
         sb.append(" OVER ");
         if (hasWindowVar()) {
             sb.append(windowVar);
@@ -346,7 +364,7 @@
         ROWS("rows"),
         GROUPS("groups");
 
-        private String text;
+        private final String text;
 
         FrameMode(String text) {
             this.text = text;
@@ -365,7 +383,7 @@
         BOUNDED_PRECEDING("preceding"),
         BOUNDED_FOLLOWING("following");
 
-        private String text;
+        private final String text;
 
         FrameBoundaryKind(String text) {
             this.text = text;
@@ -383,7 +401,7 @@
         TIES("ties"),
         NO_OTHERS("no others");
 
-        private String text;
+        private final String text;
 
         FrameExclusionKind(String text) {
             this.text = text;
diff --git a/asterixdb/asterix-lang-sqlpp/src/main/java/org/apache/asterix/lang/sqlpp/rewrites/SqlppQueryRewriter.java b/asterixdb/asterix-lang-sqlpp/src/main/java/org/apache/asterix/lang/sqlpp/rewrites/SqlppQueryRewriter.java
index 1ccea2a..1674940 100644
--- a/asterixdb/asterix-lang-sqlpp/src/main/java/org/apache/asterix/lang/sqlpp/rewrites/SqlppQueryRewriter.java
+++ b/asterixdb/asterix-lang-sqlpp/src/main/java/org/apache/asterix/lang/sqlpp/rewrites/SqlppQueryRewriter.java
@@ -489,6 +489,9 @@
                     p.first.accept(this, arg);
                 }
             }
+            if (winExpr.hasAggregateFilterExpr()) {
+                winExpr.getAggregateFilterExpr().accept(this, arg);
+            }
             for (Expression expr : winExpr.getExprList()) {
                 expr.accept(this, arg);
             }
diff --git a/asterixdb/asterix-lang-sqlpp/src/main/java/org/apache/asterix/lang/sqlpp/rewrites/visitor/AbstractSqlppExpressionExtractionVisitor.java b/asterixdb/asterix-lang-sqlpp/src/main/java/org/apache/asterix/lang/sqlpp/rewrites/visitor/AbstractSqlppExpressionExtractionVisitor.java
index 6b57089..d251fb1 100644
--- a/asterixdb/asterix-lang-sqlpp/src/main/java/org/apache/asterix/lang/sqlpp/rewrites/visitor/AbstractSqlppExpressionExtractionVisitor.java
+++ b/asterixdb/asterix-lang-sqlpp/src/main/java/org/apache/asterix/lang/sqlpp/rewrites/visitor/AbstractSqlppExpressionExtractionVisitor.java
@@ -23,7 +23,6 @@
 import java.util.ArrayList;
 import java.util.Deque;
 import java.util.List;
-import java.util.function.Predicate;
 
 import org.apache.asterix.common.exceptions.CompilationException;
 import org.apache.asterix.lang.common.base.AbstractClause;
@@ -42,8 +41,6 @@
 
 /**
  * Base class for visitors that extract expressions into LET clauses.
- * Subclasses should call {@link #extractExpressionsFromList(List, int, Predicate)} or
- * {@link #extractExpression(Expression)} to perform the extraction.
  */
 abstract class AbstractSqlppExpressionExtractionVisitor extends AbstractSqlppSimpleExpressionVisitor {
 
@@ -142,35 +139,6 @@
         fromBindingList.clear();
     }
 
-    protected List<Expression> extractExpressionsFromList(List<Expression> exprList, int limit,
-            Predicate<Expression> exprTest) {
-        StackElement outElement = stack.peek();
-        if (outElement == null) {
-            return null;
-        }
-        int n = exprList.size();
-        List<Expression> newExprList = new ArrayList<>(n);
-        for (int i = 0; i < n; i++) {
-            Expression expr = exprList.get(i);
-            Expression newExpr = i < limit && exprTest.test(expr) ? extractExpressionImpl(expr, outElement) : expr;
-            newExprList.add(newExpr);
-        }
-        return newExprList;
-    }
-
-    protected Expression extractExpression(Expression expr) {
-        StackElement outLetList = stack.peek();
-        return outLetList != null ? extractExpressionImpl(expr, outLetList) : null;
-    }
-
-    private VariableExpr extractExpressionImpl(Expression expr, StackElement outElement) {
-        VarIdentifier v = context.newVariable();
-        VariableExpr vExpr = new VariableExpr(v);
-        vExpr.setSourceLocation(expr.getSourceLocation());
-        outElement.extractionList.add(new Pair<>(expr, v));
-        return vExpr;
-    }
-
     abstract void handleUnsupportedClause(FromClause clause) throws CompilationException;
 
     protected final class StackElement {
diff --git a/asterixdb/asterix-lang-sqlpp/src/main/java/org/apache/asterix/lang/sqlpp/rewrites/visitor/Sql92AggregateFunctionVisitor.java b/asterixdb/asterix-lang-sqlpp/src/main/java/org/apache/asterix/lang/sqlpp/rewrites/visitor/Sql92AggregateFunctionVisitor.java
index cb6b396..f93e843 100644
--- a/asterixdb/asterix-lang-sqlpp/src/main/java/org/apache/asterix/lang/sqlpp/rewrites/visitor/Sql92AggregateFunctionVisitor.java
+++ b/asterixdb/asterix-lang-sqlpp/src/main/java/org/apache/asterix/lang/sqlpp/rewrites/visitor/Sql92AggregateFunctionVisitor.java
@@ -30,8 +30,10 @@
 import org.apache.asterix.common.exceptions.CompilationException;
 import org.apache.asterix.common.exceptions.ErrorCode;
 import org.apache.asterix.common.functions.FunctionSignature;
+import org.apache.asterix.lang.common.base.AbstractClause;
 import org.apache.asterix.lang.common.base.Expression;
 import org.apache.asterix.lang.common.base.ILangExpression;
+import org.apache.asterix.lang.common.clause.WhereClause;
 import org.apache.asterix.lang.common.expression.CallExpr;
 import org.apache.asterix.lang.common.expression.FieldAccessor;
 import org.apache.asterix.lang.common.expression.VariableExpr;
@@ -58,7 +60,14 @@
  * <code>SUM(e.salary + i.bonus)</code>
  * is turned into
  * <code>array_sum( (FROM g AS gi SELECT ELEMENT gi.e.salary + gi.i.bonus) )</code>
- * where <code>g</code> is a 'group as' variable
+ * where <code>g</code> is a 'group as' variable.
+ * <br/>
+ * If the SQL-92 aggregate function call contains a filter expression then that filter expression
+ * becomes a WHERE clause. <br/>
+ * For example
+ * <code>SUM(e.salary + i.bonus) FILTER (WHERE e.dept = 100)</code>
+ * is turned into
+ * <code>array_sum( (FROM g AS gi WHERE gi.e.dept = 100 SELECT ELEMENT gi.e.salary + gi.i.bonus) )</code>
  */
 class Sql92AggregateFunctionVisitor extends AbstractSqlppSimpleExpressionVisitor {
 
@@ -87,78 +96,74 @@
 
     @Override
     public Expression visit(CallExpr callExpr, ILangExpression arg) throws CompilationException {
-        List<Expression> newExprList = new ArrayList<>();
         FunctionSignature signature = callExpr.getFunctionSignature();
-        boolean aggregate = FunctionMapUtil.isSql92AggregateFunction(signature);
-        boolean rewritten = false;
-        for (Expression expr : callExpr.getExprList()) {
-            Expression newExpr = aggregate ? wrapAggregationArgument(expr, groupVar, groupVarFieldMap,
-                    preGroupContextVars, preGroupUnmappedVars, outerVars, context) : expr;
-            rewritten |= newExpr != expr;
-            newExprList.add(newExpr.accept(this, arg));
+        if (FunctionMapUtil.isSql92AggregateFunction(signature)) {
+            rewriteSql92AggregateFunction(callExpr, arg);
+            return callExpr;
+        } else {
+            return super.visit(callExpr, arg);
         }
-        if (rewritten) {
-            // Rewrites the SQL-92 function name to core functions,
-            // e.g., SUM --> array_sum
-            callExpr.setFunctionSignature(FunctionMapUtil.sql92ToCoreAggregateFunction(signature));
-        }
-        callExpr.setExprList(newExprList);
-        return callExpr;
     }
 
-    static Expression wrapAggregationArgument(Expression expr, Expression groupVar,
+    private void rewriteSql92AggregateFunction(CallExpr callExpr, ILangExpression arg) throws CompilationException {
+        FunctionSignature signature = callExpr.getFunctionSignature();
+        List<Expression> argList = callExpr.getExprList();
+        if (argList.size() != 1) {
+            // binary SQL-92 aggregate functions are not yet supported
+            throw new CompilationException(ErrorCode.COMPILATION_INVALID_PARAMETER_NUMBER, callExpr.getSourceLocation(),
+                    signature.getName(), argList.size());
+        }
+        Expression filterExpr = callExpr.getAggregateFilterExpr();
+        Expression expr = argList.get(0);
+        Expression newExpr = wrapAggregationArgument(expr, filterExpr, groupVar, groupVarFieldMap, preGroupContextVars,
+                preGroupUnmappedVars, outerVars, context);
+        List<Expression> newExprList = new ArrayList<>(1);
+        newExprList.add(newExpr.accept(this, arg));
+        // Rewrites the SQL-92 function name to core functions,
+        // e.g., SUM --> array_sum
+        callExpr.setFunctionSignature(FunctionMapUtil.sql92ToCoreAggregateFunction(signature));
+        callExpr.setExprList(newExprList);
+        callExpr.setAggregateFilterExpr(null);
+    }
+
+    static Expression wrapAggregationArgument(Expression expr, Expression filterExpr, Expression groupVar,
             Map<VariableExpr, Identifier> groupVarFieldMap, Collection<VariableExpr> preGroupContextVars,
             Collection<VariableExpr> preGroupUnmappedVars, Collection<VariableExpr> outerVars,
             LangRewritingContext context) throws CompilationException {
         SourceLocation sourceLoc = expr.getSourceLocation();
-        Set<VariableExpr> freeVars = SqlppRewriteUtil.getFreeVariable(expr);
 
-        VariableExpr fromBindingVar = new VariableExpr(context.newVariable());
-        fromBindingVar.setSourceLocation(sourceLoc);
-        FromTerm fromTerm = new FromTerm(groupVar, fromBindingVar, null, null);
+        // From clause
+        VariableExpr groupItemVar = new VariableExpr(context.newVariable());
+        groupItemVar.setSourceLocation(sourceLoc);
+
+        FromTerm fromTerm = new FromTerm(groupVar, groupItemVar, null, null);
         fromTerm.setSourceLocation(sourceLoc);
         FromClause fromClause = new FromClause(Collections.singletonList(fromTerm));
         fromClause.setSourceLocation(sourceLoc);
 
-        // Maps field variable expressions to field accesses.
-        Map<Expression, Expression> varExprMap = new HashMap<>();
-        for (VariableExpr usedVar : freeVars) {
-            // Reference to a field in the group variable.
-            if (groupVarFieldMap.containsKey(usedVar)) {
-                // Rewrites to a reference to a field in the group variable.
-                FieldAccessor fa =
-                        new FieldAccessor(fromBindingVar, new VarIdentifier(groupVarFieldMap.get(usedVar).getValue()));
-                fa.setSourceLocation(usedVar.getSourceLocation());
-                varExprMap.put(usedVar, fa);
-            } else if (outerVars.contains(usedVar)) {
-                // Do nothing
-            } else if (preGroupUnmappedVars != null && preGroupUnmappedVars.contains(usedVar)) {
-                throw new CompilationException(ErrorCode.COMPILATION_ILLEGAL_USE_OF_IDENTIFIER, sourceLoc,
-                        SqlppVariableUtil.toUserDefinedVariableName(usedVar.getVar().getValue()).getValue());
-            } else {
-                // Rewrites to a reference to a single field in the group variable.
-                VariableExpr preGroupVar = VariableCheckAndRewriteVisitor.pickContextVar(preGroupContextVars, usedVar);
-                Identifier groupVarField = groupVarFieldMap.get(preGroupVar);
-                if (groupVarField == null) {
-                    throw new CompilationException(ErrorCode.COMPILATION_ILLEGAL_STATE, sourceLoc);
-                }
-                FieldAccessor faInner = new FieldAccessor(fromBindingVar, groupVarField);
-                faInner.setSourceLocation(usedVar.getSourceLocation());
-                Expression faOuter = VariableCheckAndRewriteVisitor.generateFieldAccess(faInner, usedVar.getVar(),
-                        usedVar.getSourceLocation());
-                varExprMap.put(usedVar, faOuter);
-            }
+        // Where clause if filter expression is present
+
+        List<AbstractClause> whereClauseList = null;
+        if (filterExpr != null) {
+            Expression newFilterExpr = rewriteAggregationArgumentExpr(filterExpr, groupItemVar, groupVarFieldMap,
+                    preGroupContextVars, preGroupUnmappedVars, outerVars, context);
+            WhereClause whereClause = new WhereClause(newFilterExpr);
+            whereClause.setSourceLocation(sourceLoc);
+            whereClauseList = new ArrayList<>(1);
+            whereClauseList.add(whereClause);
         }
 
         // Select clause.
-        SelectElement selectElement =
-                new SelectElement(SqlppRewriteUtil.substituteExpression(expr, varExprMap, context));
+        Expression newExpr = rewriteAggregationArgumentExpr(expr, groupItemVar, groupVarFieldMap, preGroupContextVars,
+                preGroupUnmappedVars, outerVars, context);
+
+        SelectElement selectElement = new SelectElement(newExpr);
         selectElement.setSourceLocation(sourceLoc);
         SelectClause selectClause = new SelectClause(selectElement, null, false);
         selectClause.setSourceLocation(sourceLoc);
 
         // Construct the select expression.
-        SelectBlock selectBlock = new SelectBlock(selectClause, fromClause, null, null, null);
+        SelectBlock selectBlock = new SelectBlock(selectClause, fromClause, whereClauseList, null, null);
         selectBlock.setSourceLocation(sourceLoc);
         SelectSetOperation selectSetOperation = new SelectSetOperation(new SetOperationInput(selectBlock, null), null);
         selectSetOperation.setSourceLocation(sourceLoc);
@@ -166,4 +171,42 @@
         selectExpr.setSourceLocation(sourceLoc);
         return selectExpr;
     }
+
+    private static Expression rewriteAggregationArgumentExpr(Expression expr, VariableExpr groupItemVar,
+            Map<VariableExpr, Identifier> groupVarFieldMap, Collection<VariableExpr> preGroupContextVars,
+            Collection<VariableExpr> preGroupUnmappedVars, Collection<VariableExpr> outerVars,
+            LangRewritingContext context) throws CompilationException {
+        // Maps field variable expressions to field accesses.
+        Set<VariableExpr> freeVars = SqlppRewriteUtil.getFreeVariable(expr);
+        Map<Expression, Expression> varExprMap = new HashMap<>();
+        for (VariableExpr usedVar : freeVars) {
+            // Reference to a field in the group variable.
+            if (groupVarFieldMap.containsKey(usedVar)) {
+                // Rewrites to a reference to a field in the group variable.
+                FieldAccessor fa =
+                        new FieldAccessor(groupItemVar, new VarIdentifier(groupVarFieldMap.get(usedVar).getValue()));
+                fa.setSourceLocation(usedVar.getSourceLocation());
+                varExprMap.put(usedVar, fa);
+            } else if (outerVars.contains(usedVar)) {
+                // Do nothing
+            } else if (preGroupUnmappedVars != null && preGroupUnmappedVars.contains(usedVar)) {
+                throw new CompilationException(ErrorCode.COMPILATION_ILLEGAL_USE_OF_IDENTIFIER,
+                        expr.getSourceLocation(),
+                        SqlppVariableUtil.toUserDefinedVariableName(usedVar.getVar().getValue()).getValue());
+            } else {
+                // Rewrites to a reference to a single field in the group variable.
+                VariableExpr preGroupVar = VariableCheckAndRewriteVisitor.pickContextVar(preGroupContextVars, usedVar);
+                Identifier groupVarField = groupVarFieldMap.get(preGroupVar);
+                if (groupVarField == null) {
+                    throw new CompilationException(ErrorCode.COMPILATION_ILLEGAL_STATE, expr.getSourceLocation());
+                }
+                FieldAccessor faInner = new FieldAccessor(groupItemVar, groupVarField);
+                faInner.setSourceLocation(usedVar.getSourceLocation());
+                Expression faOuter = VariableCheckAndRewriteVisitor.generateFieldAccess(faInner, usedVar.getVar(),
+                        usedVar.getSourceLocation());
+                varExprMap.put(usedVar, faOuter);
+            }
+        }
+        return SqlppRewriteUtil.substituteExpression(expr, varExprMap, context);
+    }
 }
diff --git a/asterixdb/asterix-lang-sqlpp/src/main/java/org/apache/asterix/lang/sqlpp/rewrites/visitor/SqlppCaseRewriteVisitor.java b/asterixdb/asterix-lang-sqlpp/src/main/java/org/apache/asterix/lang/sqlpp/rewrites/visitor/SqlppCaseRewriteVisitor.java
index 24a9c1f..cfe41ed 100644
--- a/asterixdb/asterix-lang-sqlpp/src/main/java/org/apache/asterix/lang/sqlpp/rewrites/visitor/SqlppCaseRewriteVisitor.java
+++ b/asterixdb/asterix-lang-sqlpp/src/main/java/org/apache/asterix/lang/sqlpp/rewrites/visitor/SqlppCaseRewriteVisitor.java
@@ -29,6 +29,7 @@
 import org.apache.asterix.lang.common.clause.GroupbyClause;
 import org.apache.asterix.lang.common.expression.CallExpr;
 import org.apache.asterix.lang.common.expression.IfExpr;
+import org.apache.asterix.lang.common.expression.VariableExpr;
 import org.apache.asterix.lang.common.rewrites.LangRewritingContext;
 import org.apache.asterix.lang.common.struct.VarIdentifier;
 import org.apache.asterix.lang.sqlpp.clause.FromClause;
@@ -87,9 +88,12 @@
         public Expression visit(CallExpr callExpr, ILangExpression arg) throws CompilationException {
             CallExpr resultExpr = (CallExpr) super.visit(callExpr, arg);
             if (FunctionMapUtil.isSql92AggregateFunction(resultExpr.getFunctionSignature())) {
-                Expression newExpr = extractExpression(resultExpr);
-                if (newExpr != null) {
-                    return newExpr;
+                StackElement stackElement = stack.peek();
+                if (stackElement != null && stackElement.getSelectBlock().hasGroupbyClause()) {
+                    VarIdentifier v = stackElement.addPendingLetClause(resultExpr);
+                    VariableExpr vExpr = new VariableExpr(v);
+                    vExpr.setSourceLocation(callExpr.getSourceLocation());
+                    return vExpr;
                 }
             }
             return resultExpr;
diff --git a/asterixdb/asterix-lang-sqlpp/src/main/java/org/apache/asterix/lang/sqlpp/rewrites/visitor/SqlppGroupingSetsVisitor.java b/asterixdb/asterix-lang-sqlpp/src/main/java/org/apache/asterix/lang/sqlpp/rewrites/visitor/SqlppGroupingSetsVisitor.java
index 4e517da..a6892c1 100644
--- a/asterixdb/asterix-lang-sqlpp/src/main/java/org/apache/asterix/lang/sqlpp/rewrites/visitor/SqlppGroupingSetsVisitor.java
+++ b/asterixdb/asterix-lang-sqlpp/src/main/java/org/apache/asterix/lang/sqlpp/rewrites/visitor/SqlppGroupingSetsVisitor.java
@@ -204,6 +204,10 @@
             throw new CompilationException(ErrorCode.COMPILATION_INVALID_NUM_OF_ARGS, callExpr.getSourceLocation(),
                     BuiltinFunctions.GROUPING.getName());
         }
+        if (callExpr.hasAggregateFilterExpr()) {
+            throw new CompilationException(ErrorCode.COMPILATION_ILLEGAL_USE_OF_FILTER_CLAUSE,
+                    callExpr.getSourceLocation());
+        }
         long result = 0;
         for (Expression argExpr : argList) {
             int v;
diff --git a/asterixdb/asterix-lang-sqlpp/src/main/java/org/apache/asterix/lang/sqlpp/rewrites/visitor/SqlppInlineUdfsVisitor.java b/asterixdb/asterix-lang-sqlpp/src/main/java/org/apache/asterix/lang/sqlpp/rewrites/visitor/SqlppInlineUdfsVisitor.java
index 513d33c..4fb17f0 100644
--- a/asterixdb/asterix-lang-sqlpp/src/main/java/org/apache/asterix/lang/sqlpp/rewrites/visitor/SqlppInlineUdfsVisitor.java
+++ b/asterixdb/asterix-lang-sqlpp/src/main/java/org/apache/asterix/lang/sqlpp/rewrites/visitor/SqlppInlineUdfsVisitor.java
@@ -270,6 +270,11 @@
             winExpr.setWindowFieldList(inlinedList.second);
             inlined |= inlinedList.first;
         }
+        if (winExpr.hasAggregateFilterExpr()) {
+            Pair<Boolean, Expression> inlinedExpr = inlineUdfsInExpr(winExpr.getAggregateFilterExpr(), funcs);
+            winExpr.setAggregateFilterExpr(inlinedExpr.second);
+            inlined |= inlinedExpr.first;
+        }
         Pair<Boolean, List<Expression>> inlinedList = inlineUdfsInExprList(winExpr.getExprList(), funcs);
         winExpr.setExprList(inlinedList.second);
         inlined |= inlinedList.first;
diff --git a/asterixdb/asterix-lang-sqlpp/src/main/java/org/apache/asterix/lang/sqlpp/rewrites/visitor/SqlppListInputFunctionRewriteVisitor.java b/asterixdb/asterix-lang-sqlpp/src/main/java/org/apache/asterix/lang/sqlpp/rewrites/visitor/SqlppListInputFunctionRewriteVisitor.java
index 2e65292..643bbb2 100644
--- a/asterixdb/asterix-lang-sqlpp/src/main/java/org/apache/asterix/lang/sqlpp/rewrites/visitor/SqlppListInputFunctionRewriteVisitor.java
+++ b/asterixdb/asterix-lang-sqlpp/src/main/java/org/apache/asterix/lang/sqlpp/rewrites/visitor/SqlppListInputFunctionRewriteVisitor.java
@@ -42,7 +42,7 @@
             newExprList.add(expr.accept(this, arg));
         }
         callExpr.setExprList(newExprList);
-        return FunctionMapUtil.normalizedListInputFunctions(callExpr);
+        FunctionMapUtil.normalizedListInputFunctions(callExpr);
+        return callExpr;
     }
-
 }
diff --git a/asterixdb/asterix-lang-sqlpp/src/main/java/org/apache/asterix/lang/sqlpp/rewrites/visitor/SqlppWindowAggregationSugarVisitor.java b/asterixdb/asterix-lang-sqlpp/src/main/java/org/apache/asterix/lang/sqlpp/rewrites/visitor/SqlppWindowAggregationSugarVisitor.java
index a5f43b6..6d8e52e 100644
--- a/asterixdb/asterix-lang-sqlpp/src/main/java/org/apache/asterix/lang/sqlpp/rewrites/visitor/SqlppWindowAggregationSugarVisitor.java
+++ b/asterixdb/asterix-lang-sqlpp/src/main/java/org/apache/asterix/lang/sqlpp/rewrites/visitor/SqlppWindowAggregationSugarVisitor.java
@@ -27,6 +27,7 @@
 import java.util.Set;
 
 import org.apache.asterix.common.exceptions.CompilationException;
+import org.apache.asterix.common.exceptions.ErrorCode;
 import org.apache.asterix.common.functions.FunctionSignature;
 import org.apache.asterix.lang.common.base.Expression;
 import org.apache.asterix.lang.common.base.ILangExpression;
@@ -89,17 +90,28 @@
             winExpr.setFunctionSignature(new FunctionSignature(winfi));
             if (BuiltinFunctions.builtinFunctionHasProperty(winfi,
                     BuiltinFunctions.WindowFunctionProperty.HAS_LIST_ARG)) {
-                wrapAggregationArguments(winExpr, 1);
+                if (winExpr.hasAggregateFilterExpr()) {
+                    throw new CompilationException(ErrorCode.COMPILATION_ILLEGAL_USE_OF_FILTER_CLAUSE,
+                            winExpr.getSourceLocation());
+                }
+                wrapAggregationArgument(winExpr, null);
             }
         } else if (FunctionMapUtil.isSql92AggregateFunction(signature)) {
+            if (winExpr.getExprList().size() != 1) {
+                // binary SQL-92 aggregate functions are not yet supported
+                throw new CompilationException(ErrorCode.COMPILATION_INVALID_PARAMETER_NUMBER,
+                        winExpr.getSourceLocation(), signature.getName(), winExpr.getExprList().size());
+            }
+            wrapAggregationArgument(winExpr, winExpr.getAggregateFilterExpr());
+            winExpr.setAggregateFilterExpr(null);
             winExpr.setFunctionSignature(FunctionMapUtil.sql92ToCoreAggregateFunction(signature));
-            wrapAggregationArguments(winExpr, winExpr.getExprList().size());
         }
 
         return super.visit(winExpr, arg);
     }
 
-    void wrapAggregationArguments(WindowExpression winExpr, int limit) throws CompilationException {
+    private void wrapAggregationArgument(WindowExpression winExpr, Expression aggFilterExpr)
+            throws CompilationException {
         VariableExpr winVar = winExpr.getWindowVar();
 
         Map<VariableExpr, Set<? extends Scope.SymbolAnnotation>> liveAnnotatedVars =
@@ -112,15 +124,15 @@
         Map<VariableExpr, Identifier> winVarFieldMap =
                 SqlppGroupByAggregationSugarVisitor.createGroupVarFieldMap(winFieldList);
 
+        //binary SQL-92 aggregates are not yet supported, so we just need to rewrite the first argument
         List<Expression> exprList = winExpr.getExprList();
-        int n = exprList.size();
-        List<Expression> newExprList = new ArrayList<>(n);
-        for (int i = 0; i < n; i++) {
-            Expression expr = exprList.get(i);
-            Expression newExpr = i < limit ? Sql92AggregateFunctionVisitor.wrapAggregationArgument(expr, winVar,
-                    winVarFieldMap, liveContextVars, null, liveVars, context) : expr;
-            newExprList.add(newExpr);
-        }
+        Expression aggArgExpr = exprList.get(0);
+        Expression newAggArgExpr = Sql92AggregateFunctionVisitor.wrapAggregationArgument(aggArgExpr, aggFilterExpr,
+                winVar, winVarFieldMap, liveContextVars, null, liveVars, context);
+
+        List<Expression> newExprList = new ArrayList<>(exprList);
+        newExprList.set(0, newAggArgExpr);
+
         winExpr.setExprList(newExprList);
     }
 
diff --git a/asterixdb/asterix-lang-sqlpp/src/main/java/org/apache/asterix/lang/sqlpp/rewrites/visitor/SqlppWindowRewriteVisitor.java b/asterixdb/asterix-lang-sqlpp/src/main/java/org/apache/asterix/lang/sqlpp/rewrites/visitor/SqlppWindowRewriteVisitor.java
index cbc3ac9..6e5ebba 100644
--- a/asterixdb/asterix-lang-sqlpp/src/main/java/org/apache/asterix/lang/sqlpp/rewrites/visitor/SqlppWindowRewriteVisitor.java
+++ b/asterixdb/asterix-lang-sqlpp/src/main/java/org/apache/asterix/lang/sqlpp/rewrites/visitor/SqlppWindowRewriteVisitor.java
@@ -75,24 +75,37 @@
         FunctionSignature signature = winExpr.getFunctionSignature();
         FunctionIdentifier winfi = FunctionMapUtil.getInternalWindowFunction(signature);
         if (winfi != null) {
+            if (winExpr.hasAggregateFilterExpr()) {
+                throw new CompilationException(ErrorCode.COMPILATION_ILLEGAL_USE_OF_FILTER_CLAUSE,
+                        winExpr.getSourceLocation());
+            }
             rewriteSpecificWindowFunctions(winfi, winExpr);
             if (BuiltinFunctions.builtinFunctionHasProperty(winfi,
                     BuiltinFunctions.WindowFunctionProperty.HAS_LIST_ARG)) {
-                List<Expression> newExprList = extractExpressionsFromList(winExpr.getExprList(), 1,
-                        SqlppWindowRewriteVisitor::isExtractableExpression);
-                if (newExprList == null) {
-                    throw new CompilationException(ErrorCode.COMPILATION_ERROR, winExpr.getSourceLocation(), "");
-                }
-                winExpr.setExprList(newExprList);
+                extractListArgument(winExpr);
             }
         } else if (FunctionMapUtil.isSql92AggregateFunction(signature)) {
-            List<Expression> newExprList = extractExpressionsFromList(winExpr.getExprList(),
-                    winExpr.getExprList().size(), SqlppWindowRewriteVisitor::isExtractableExpression);
-            if (newExprList == null) {
+            if (winExpr.hasAggregateFilterExpr()) {
+                Expression aggFilterExpr = winExpr.getAggregateFilterExpr();
+                if (isExtractableArgument(aggFilterExpr)) {
+                    VariableExpr newAggFilterExpr = extractExpression(aggFilterExpr);
+                    if (newAggFilterExpr == null) {
+                        throw new CompilationException(ErrorCode.COMPILATION_ERROR, winExpr.getSourceLocation(), "");
+                    }
+                    winExpr.setAggregateFilterExpr(newAggFilterExpr);
+                }
+            }
+            if (winExpr.getExprList().size() != 1) {
+                // binary SQL-92 aggregates are not yet supported
                 throw new CompilationException(ErrorCode.COMPILATION_ERROR, winExpr.getSourceLocation(), "");
             }
-            winExpr.setExprList(newExprList);
-        } else if (!FunctionMapUtil.isCoreAggregateFunction(signature)) {
+            extractListArgument(winExpr);
+        } else if (FunctionMapUtil.isCoreAggregateFunction(signature)) {
+            if (winExpr.hasAggregateFilterExpr()) {
+                throw new CompilationException(ErrorCode.COMPILATION_ILLEGAL_USE_OF_FILTER_CLAUSE,
+                        winExpr.getSourceLocation());
+            }
+        } else {
             throw new CompilationException(ErrorCode.COMPILATION_EXPECTED_WINDOW_FUNCTION, winExpr.getSourceLocation(),
                     signature.getName());
         }
@@ -100,30 +113,32 @@
         return winExpr;
     }
 
-    private List<Expression> extractExpressions(List<Expression> exprList, int limit) {
+    private void extractListArgument(WindowExpression winExpr) throws CompilationException {
+        List<Expression> argExprList = winExpr.getExprList();
+        Expression argExpr0 = argExprList.get(0);
+        if (isExtractableArgument(argExpr0)) {
+            VariableExpr newArgExpr0 = extractExpression(argExpr0);
+            if (newArgExpr0 == null) {
+                throw new CompilationException(ErrorCode.COMPILATION_ERROR, winExpr.getSourceLocation(), "");
+            }
+            List<Expression> newArgExprList = new ArrayList<>(argExprList);
+            newArgExprList.set(0, newArgExpr0);
+            winExpr.setExprList(newArgExprList);
+        }
+    }
+
+    private VariableExpr extractExpression(Expression expr) {
         StackElement stackElement = stack.peek();
         if (stackElement == null) {
             return null;
         }
-        int n = exprList.size();
-        List<Expression> newExprList = new ArrayList<>(n);
-        for (int i = 0; i < n; i++) {
-            Expression expr = exprList.get(i);
-            Expression newExpr;
-            if (i < limit && isExtractableExpression(expr)) {
-                VarIdentifier v = stackElement.addPendingLetClause(expr);
-                VariableExpr vExpr = new VariableExpr(v);
-                vExpr.setSourceLocation(expr.getSourceLocation());
-                newExpr = vExpr;
-            } else {
-                newExpr = expr;
-            }
-            newExprList.add(newExpr);
-        }
-        return newExprList;
+        VarIdentifier v = stackElement.addPendingLetClause(expr);
+        VariableExpr vExpr = new VariableExpr(v);
+        vExpr.setSourceLocation(expr.getSourceLocation());
+        return vExpr;
     }
 
-    protected static boolean isExtractableExpression(Expression expr) {
+    private boolean isExtractableArgument(Expression expr) {
         switch (expr.getKind()) {
             case LITERAL_EXPRESSION:
             case VARIABLE_EXPRESSION:
@@ -145,7 +160,7 @@
      * Add a copy of the first argument as the last argument for all functions
      * that have {@link BuiltinFunctions.WindowFunctionProperty#HAS_LIST_ARG} modifier.
      * The first argument will then be rewritten by
-     * {@link SqlppWindowAggregationSugarVisitor#wrapAggregationArguments(WindowExpression, int)}.
+     * {@link SqlppWindowAggregationSugarVisitor#wrapAggregationArgument(WindowExpression, int, Expression)}.
      * The new last argument will be handled by expression to plan translator
      * </li>
      * </ul>
diff --git a/asterixdb/asterix-lang-sqlpp/src/main/java/org/apache/asterix/lang/sqlpp/rewrites/visitor/VariableCheckAndRewriteVisitor.java b/asterixdb/asterix-lang-sqlpp/src/main/java/org/apache/asterix/lang/sqlpp/rewrites/visitor/VariableCheckAndRewriteVisitor.java
index 8190d3b..cf8469f 100644
--- a/asterixdb/asterix-lang-sqlpp/src/main/java/org/apache/asterix/lang/sqlpp/rewrites/visitor/VariableCheckAndRewriteVisitor.java
+++ b/asterixdb/asterix-lang-sqlpp/src/main/java/org/apache/asterix/lang/sqlpp/rewrites/visitor/VariableCheckAndRewriteVisitor.java
@@ -248,13 +248,14 @@
 
     @Override
     public Expression visit(WindowExpression winExpr, ILangExpression arg) throws CompilationException {
-        // skip variables inside list arguments of window functions (will be resolved by SqlppWindowExpressionVisitor)
+        // skip variables inside list and agg-filter arguments of window functions
+        // (will be resolved by SqlppWindowExpressionVisitor)
         FunctionSignature fs = winExpr.getFunctionSignature();
         FunctionIdentifier winfi = FunctionMapUtil.getInternalWindowFunction(fs);
         if (winfi != null) {
             if (BuiltinFunctions.builtinFunctionHasProperty(winfi,
                     BuiltinFunctions.WindowFunctionProperty.HAS_LIST_ARG)) {
-                visitWindowExpressionExcludingExprList(winExpr, arg);
+                visitWindowExpressionExcludingExprListAndAggFilter(winExpr, arg);
                 List<Expression> exprList = winExpr.getExprList();
                 List<Expression> newExprList = new ArrayList<>(exprList.size());
                 Iterator<Expression> i = exprList.iterator();
@@ -264,12 +265,15 @@
                 }
                 winExpr.setExprList(newExprList);
                 return winExpr;
+            } else {
+                return super.visit(winExpr, arg);
             }
         } else if (FunctionMapUtil.isSql92AggregateFunction(fs)) {
-            visitWindowExpressionExcludingExprList(winExpr, arg);
+            visitWindowExpressionExcludingExprListAndAggFilter(winExpr, arg);
             return winExpr;
+        } else {
+            return super.visit(winExpr, arg);
         }
-        return super.visit(winExpr, arg);
     }
 
     static VariableExpr pickContextVar(Collection<VariableExpr> contextVars, VariableExpr usedVar)
diff --git a/asterixdb/asterix-lang-sqlpp/src/main/java/org/apache/asterix/lang/sqlpp/util/FunctionMapUtil.java b/asterixdb/asterix-lang-sqlpp/src/main/java/org/apache/asterix/lang/sqlpp/util/FunctionMapUtil.java
index b092f4a..95ca9b1 100644
--- a/asterixdb/asterix-lang-sqlpp/src/main/java/org/apache/asterix/lang/sqlpp/util/FunctionMapUtil.java
+++ b/asterixdb/asterix-lang-sqlpp/src/main/java/org/apache/asterix/lang/sqlpp/util/FunctionMapUtil.java
@@ -162,20 +162,18 @@
      *
      * @param callExpr
      *            The input call expression.
-     * @return a new call expression that calls the corresponding AsterixDB internal function.
      */
-    public static CallExpr normalizedListInputFunctions(CallExpr callExpr) {
+    public static void normalizedListInputFunctions(CallExpr callExpr) {
         FunctionSignature fs = callExpr.getFunctionSignature();
         String internalFuncName = LIST_INPUT_FUNCTION_MAP.get(fs.getName().toLowerCase());
         if (internalFuncName == null) {
-            return callExpr;
+            return;
         }
         callExpr.setFunctionSignature(new FunctionSignature(FunctionConstants.ASTERIX_DV, internalFuncName, 1));
         ListConstructor listConstr =
                 new ListConstructor(ListConstructor.Type.ORDERED_LIST_CONSTRUCTOR, callExpr.getExprList());
         listConstr.setSourceLocation(callExpr.getSourceLocation());
         callExpr.setExprList(new ArrayList<>(Collections.singletonList(listConstr)));
-        return callExpr;
     }
 
     /**
diff --git a/asterixdb/asterix-lang-sqlpp/src/main/java/org/apache/asterix/lang/sqlpp/visitor/CheckSql92AggregateVisitor.java b/asterixdb/asterix-lang-sqlpp/src/main/java/org/apache/asterix/lang/sqlpp/visitor/CheckSql92AggregateVisitor.java
index b5107bc..a2dafbd 100644
--- a/asterixdb/asterix-lang-sqlpp/src/main/java/org/apache/asterix/lang/sqlpp/visitor/CheckSql92AggregateVisitor.java
+++ b/asterixdb/asterix-lang-sqlpp/src/main/java/org/apache/asterix/lang/sqlpp/visitor/CheckSql92AggregateVisitor.java
@@ -187,6 +187,9 @@
                 return true;
             }
         }
+        if (pf.hasAggregateFilterExpr() && pf.getAggregateFilterExpr().accept(this, parentSelectBlock)) {
+            return true;
+        }
         return false;
     }
 
@@ -307,6 +310,7 @@
                 || (winExpr.hasFrameStartExpr() && winExpr.getFrameStartExpr().accept(this, arg))
                 || (winExpr.hasFrameEndExpr() && winExpr.getFrameEndExpr().accept(this, arg))
                 || (winExpr.hasWindowFieldList() && visitFieldList(winExpr.getWindowFieldList(), arg))
+                || (winExpr.hasAggregateFilterExpr() && winExpr.getAggregateFilterExpr().accept(this, arg))
                 || visitExprList(winExpr.getExprList(), arg);
     }
 
diff --git a/asterixdb/asterix-lang-sqlpp/src/main/java/org/apache/asterix/lang/sqlpp/visitor/CheckSubqueryVisitor.java b/asterixdb/asterix-lang-sqlpp/src/main/java/org/apache/asterix/lang/sqlpp/visitor/CheckSubqueryVisitor.java
index 18c2b35..bc31e62 100644
--- a/asterixdb/asterix-lang-sqlpp/src/main/java/org/apache/asterix/lang/sqlpp/visitor/CheckSubqueryVisitor.java
+++ b/asterixdb/asterix-lang-sqlpp/src/main/java/org/apache/asterix/lang/sqlpp/visitor/CheckSubqueryVisitor.java
@@ -305,12 +305,14 @@
                 || (winExpr.hasFrameStartExpr() && visit(winExpr.getFrameStartExpr(), arg))
                 || (winExpr.hasFrameEndExpr() && visit(winExpr.getFrameEndExpr(), arg))
                 || (winExpr.hasWindowFieldList() && visitFieldList(winExpr.getWindowFieldList(), arg))
+                || (winExpr.hasAggregateFilterExpr() && winExpr.getAggregateFilterExpr().accept(this, arg))
                 || visitExprList(winExpr.getExprList(), arg);
     }
 
     @Override
     public Boolean visit(CallExpr callExpr, ILangExpression arg) throws CompilationException {
-        return visitExprList(callExpr.getExprList(), arg);
+        return visitExprList(callExpr.getExprList(), arg)
+                || (callExpr.hasAggregateFilterExpr() && visit(callExpr.getAggregateFilterExpr(), arg));
     }
 
     private boolean visit(ILangExpression expr, ILangExpression arg) throws CompilationException {
diff --git a/asterixdb/asterix-lang-sqlpp/src/main/java/org/apache/asterix/lang/sqlpp/visitor/DeepCopyVisitor.java b/asterixdb/asterix-lang-sqlpp/src/main/java/org/apache/asterix/lang/sqlpp/visitor/DeepCopyVisitor.java
index e661ac1..70ebcfc 100644
--- a/asterixdb/asterix-lang-sqlpp/src/main/java/org/apache/asterix/lang/sqlpp/visitor/DeepCopyVisitor.java
+++ b/asterixdb/asterix-lang-sqlpp/src/main/java/org/apache/asterix/lang/sqlpp/visitor/DeepCopyVisitor.java
@@ -441,7 +441,9 @@
         for (Expression expr : callExpr.getExprList()) {
             newExprList.add((Expression) expr.accept(this, arg));
         }
-        CallExpr copy = new CallExpr(callExpr.getFunctionSignature(), newExprList);
+        Expression newFilterExpr = callExpr.hasAggregateFilterExpr()
+                ? (Expression) callExpr.getAggregateFilterExpr().accept(this, arg) : null;
+        CallExpr copy = new CallExpr(callExpr.getFunctionSignature(), newExprList, newFilterExpr);
         copy.setSourceLocation(callExpr.getSourceLocation());
         copy.addHints(callExpr.getHints());
         return copy;
@@ -516,6 +518,8 @@
     @Override
     public ILangExpression visit(WindowExpression winExpr, Void arg) throws CompilationException {
         List<Expression> newExprList = copyExprList(winExpr.getExprList(), arg);
+        Expression newAggFilterExpr = winExpr.hasAggregateFilterExpr()
+                ? (Expression) winExpr.getAggregateFilterExpr().accept(this, arg) : null;
         List<Expression> newPartitionList =
                 winExpr.hasPartitionList() ? copyExprList(winExpr.getPartitionList(), arg) : null;
         List<Expression> newOrderbyList = winExpr.hasOrderByList() ? copyExprList(winExpr.getOrderbyList(), arg) : null;
@@ -529,10 +533,11 @@
                 winExpr.hasWindowVar() ? (VariableExpr) winExpr.getWindowVar().accept(this, arg) : null;
         List<Pair<Expression, Identifier>> newWindowFieldList =
                 winExpr.hasWindowFieldList() ? copyFieldList(winExpr.getWindowFieldList(), arg) : null;
-        WindowExpression copy = new WindowExpression(winExpr.getFunctionSignature(), newExprList, newPartitionList,
-                newOrderbyList, newOrderbyModifierList, winExpr.getFrameMode(), winExpr.getFrameStartKind(),
-                newFrameStartExpr, winExpr.getFrameEndKind(), newFrameEndExpr, winExpr.getFrameExclusionKind(),
-                newWindowVar, newWindowFieldList, winExpr.getIgnoreNulls(), winExpr.getFromLast());
+        WindowExpression copy =
+                new WindowExpression(winExpr.getFunctionSignature(), newExprList, newAggFilterExpr, newPartitionList,
+                        newOrderbyList, newOrderbyModifierList, winExpr.getFrameMode(), winExpr.getFrameStartKind(),
+                        newFrameStartExpr, winExpr.getFrameEndKind(), newFrameEndExpr, winExpr.getFrameExclusionKind(),
+                        newWindowVar, newWindowFieldList, winExpr.getIgnoreNulls(), winExpr.getFromLast());
         copy.setSourceLocation(winExpr.getSourceLocation());
         copy.addHints(winExpr.getHints());
         return copy;
diff --git a/asterixdb/asterix-lang-sqlpp/src/main/java/org/apache/asterix/lang/sqlpp/visitor/FreeVariableVisitor.java b/asterixdb/asterix-lang-sqlpp/src/main/java/org/apache/asterix/lang/sqlpp/visitor/FreeVariableVisitor.java
index e1ff4a7..0c14688 100644
--- a/asterixdb/asterix-lang-sqlpp/src/main/java/org/apache/asterix/lang/sqlpp/visitor/FreeVariableVisitor.java
+++ b/asterixdb/asterix-lang-sqlpp/src/main/java/org/apache/asterix/lang/sqlpp/visitor/FreeVariableVisitor.java
@@ -390,6 +390,9 @@
         for (Expression expr : callExpr.getExprList()) {
             expr.accept(this, freeVars);
         }
+        if (callExpr.hasAggregateFilterExpr()) {
+            callExpr.getAggregateFilterExpr().accept(this, freeVars);
+        }
         return null;
     }
 
@@ -461,6 +464,9 @@
             }
         }
         visit(winExpr.getExprList(), freeVars);
+        if (winExpr.hasAggregateFilterExpr()) {
+            winExpr.getAggregateFilterExpr().accept(this, freeVars);
+        }
         if (winExpr.hasWindowVar()) {
             freeVars.remove(winExpr.getWindowVar());
         }
diff --git a/asterixdb/asterix-lang-sqlpp/src/main/java/org/apache/asterix/lang/sqlpp/visitor/SqlppAstPrintVisitor.java b/asterixdb/asterix-lang-sqlpp/src/main/java/org/apache/asterix/lang/sqlpp/visitor/SqlppAstPrintVisitor.java
index d7ad1cb..7c9a4a7 100644
--- a/asterixdb/asterix-lang-sqlpp/src/main/java/org/apache/asterix/lang/sqlpp/visitor/SqlppAstPrintVisitor.java
+++ b/asterixdb/asterix-lang-sqlpp/src/main/java/org/apache/asterix/lang/sqlpp/visitor/SqlppAstPrintVisitor.java
@@ -250,21 +250,23 @@
     }
 
     @Override
-    public Void visit(CallExpr pf, Integer step) throws CompilationException {
-        FunctionSignature functionSignature = pf.getFunctionSignature();
-        FunctionSignature normalizedFunctionSignature =
-                FunctionMapUtil.normalizeBuiltinFunctionSignature(functionSignature, false, pf.getSourceLocation());
+    public Void visit(CallExpr callExpr, Integer step) throws CompilationException {
+        FunctionSignature functionSignature = callExpr.getFunctionSignature();
+        FunctionSignature normalizedFunctionSignature = FunctionMapUtil
+                .normalizeBuiltinFunctionSignature(functionSignature, false, callExpr.getSourceLocation());
         if (BuiltinFunctions.isBuiltinCompilerFunction(normalizedFunctionSignature, true)) {
             functionSignature = normalizedFunctionSignature;
         }
         //TODO(MULTI_PART_DATAVERSE_NAME):temporary workaround to preserve AST reference results
         if (FunctionUtil.isBuiltinDatasetFunction(functionSignature)) {
-            String singleArg = pf.getExprList().stream().map(LiteralExpr.class::cast).map(LiteralExpr::getValue)
+            String singleArg = callExpr.getExprList().stream().map(LiteralExpr.class::cast).map(LiteralExpr::getValue)
                     .map(StringLiteral.class::cast).map(StringLiteral::getValue).collect(Collectors.joining("."));
             printFunctionCall(functionSignature, 1,
-                    Collections.singletonList(new LiteralExpr(new StringLiteral(singleArg))), step);
+                    Collections.singletonList(new LiteralExpr(new StringLiteral(singleArg))),
+                    callExpr.getAggregateFilterExpr(), step);
         } else {
-            printFunctionCall(functionSignature, functionSignature.getArity(), pf.getExprList(), step);
+            printFunctionCall(functionSignature, functionSignature.getArity(), callExpr.getExprList(),
+                    callExpr.getAggregateFilterExpr(), step);
         }
         return null;
     }
@@ -376,6 +378,11 @@
                 printFieldList(step + 1, winExpr.getWindowFieldList());
             }
         }
+        if (winExpr.hasAggregateFilterExpr()) {
+            out.println(skip(step + 1) + "FILTER (WHERE");
+            winExpr.getAggregateFilterExpr().accept(this, step + 2);
+            out.println(skip(step + 1) + ')');
+        }
         out.println(skip(step) + "OVER (");
         if (winExpr.hasPartitionList()) {
             out.println(skip(step + 1) + "PARTITION BY");
diff --git a/asterixdb/asterix-lang-sqlpp/src/main/java/org/apache/asterix/lang/sqlpp/visitor/SqlppCloneAndSubstituteVariablesVisitor.java b/asterixdb/asterix-lang-sqlpp/src/main/java/org/apache/asterix/lang/sqlpp/visitor/SqlppCloneAndSubstituteVariablesVisitor.java
index 20fd0f5..418a960 100644
--- a/asterixdb/asterix-lang-sqlpp/src/main/java/org/apache/asterix/lang/sqlpp/visitor/SqlppCloneAndSubstituteVariablesVisitor.java
+++ b/asterixdb/asterix-lang-sqlpp/src/main/java/org/apache/asterix/lang/sqlpp/visitor/SqlppCloneAndSubstituteVariablesVisitor.java
@@ -406,6 +406,8 @@
             VariableSubstitutionEnvironment env) throws CompilationException {
         List<Expression> newExprList =
                 VariableCloneAndSubstitutionUtil.visitAndCloneExprList(winExpr.getExprList(), env, this);
+        Expression newAggFilterExpr = winExpr.hasAggregateFilterExpr()
+                ? (Expression) winExpr.getAggregateFilterExpr().accept(this, env).first : null;
         List<Expression> newPartitionList = winExpr.hasPartitionList()
                 ? VariableCloneAndSubstitutionUtil.visitAndCloneExprList(winExpr.getPartitionList(), env, this) : null;
         List<Expression> newOrderbyList = winExpr.hasOrderByList()
@@ -421,10 +423,10 @@
         List<Pair<Expression, Identifier>> newWindowFieldList = winExpr.hasWindowFieldList()
                 ? VariableCloneAndSubstitutionUtil.substInFieldList(winExpr.getWindowFieldList(), env, this) : null;
         WindowExpression newWinExpr =
-                new WindowExpression(winExpr.getFunctionSignature(), newExprList, newPartitionList, newOrderbyList,
-                        newOrderbyModifierList, winExpr.getFrameMode(), winExpr.getFrameStartKind(), newFrameStartExpr,
-                        winExpr.getFrameEndKind(), newFrameEndExpr, winExpr.getFrameExclusionKind(), newWindowVar,
-                        newWindowFieldList, winExpr.getIgnoreNulls(), winExpr.getFromLast());
+                new WindowExpression(winExpr.getFunctionSignature(), newExprList, newAggFilterExpr, newPartitionList,
+                        newOrderbyList, newOrderbyModifierList, winExpr.getFrameMode(), winExpr.getFrameStartKind(),
+                        newFrameStartExpr, winExpr.getFrameEndKind(), newFrameEndExpr, winExpr.getFrameExclusionKind(),
+                        newWindowVar, newWindowFieldList, winExpr.getIgnoreNulls(), winExpr.getFromLast());
         newWinExpr.setSourceLocation(winExpr.getSourceLocation());
         newWinExpr.addHints(winExpr.getHints());
         return new Pair<>(newWinExpr, env);
diff --git a/asterixdb/asterix-lang-sqlpp/src/main/java/org/apache/asterix/lang/sqlpp/visitor/SqlppFormatPrintVisitor.java b/asterixdb/asterix-lang-sqlpp/src/main/java/org/apache/asterix/lang/sqlpp/visitor/SqlppFormatPrintVisitor.java
index bf71c05..8798dfa 100644
--- a/asterixdb/asterix-lang-sqlpp/src/main/java/org/apache/asterix/lang/sqlpp/visitor/SqlppFormatPrintVisitor.java
+++ b/asterixdb/asterix-lang-sqlpp/src/main/java/org/apache/asterix/lang/sqlpp/visitor/SqlppFormatPrintVisitor.java
@@ -343,42 +343,43 @@
 
     @Override
     public Void visit(WindowExpression windowExpr, Integer step) throws CompilationException {
-        out.print(skip(step) + "window ");
-        out.print(generateFullName(windowExpr.getFunctionSignature().getDataverseName(),
+        out.println(skip(step) + "window " + generateFullName(windowExpr.getFunctionSignature().getDataverseName(),
                 windowExpr.getFunctionSignature().getName()) + "(");
-        printDelimitedExpressions(windowExpr.getExprList(), COMMA, step);
-        out.print(")");
-        out.print(skip(step) + " over ");
-        if (windowExpr.hasWindowVar()) {
-            windowExpr.getWindowVar().accept(this, step + 2);
-            out.print(skip(step) + "as ");
+        printDelimitedExpressions(windowExpr.getExprList(), COMMA, step + 2);
+        out.println(")");
+        if (windowExpr.hasAggregateFilterExpr()) {
+            out.println(skip(step + 1) + "filter where (");
+            windowExpr.getAggregateFilterExpr().accept(this, step + 2);
+            out.println();
         }
-        out.print("(");
+        out.print(skip(step + 1) + "over ");
+        if (windowExpr.hasWindowVar()) {
+            windowExpr.getWindowVar().accept(this, step + 1);
+            out.print(" as ");
+        }
+        out.println("(");
         if (windowExpr.hasPartitionList()) {
-            List<Expression> partitionList = windowExpr.getPartitionList();
-            for (int i = 0, ln = partitionList.size(); i < ln; i++) {
-                if (i > 0) {
-                    out.print(COMMA);
-                }
-                Expression partExpr = partitionList.get(i);
-                partExpr.accept(this, step + 2);
-            }
+            printDelimitedExpressions(windowExpr.getPartitionList(), COMMA, step + 2);
+            out.println();
         }
         if (windowExpr.hasOrderByList()) {
-            out.print(skip(step) + " order by ");
+            out.print(skip(step + 1) + "order by ");
             printDelimitedObyExpressions(windowExpr.getOrderbyList(), windowExpr.getOrderbyModifierList(), step + 2);
+            out.println();
         }
         if (windowExpr.hasFrameDefinition()) {
-            out.println(skip(step) + windowExpr.getFrameMode());
+            out.println(skip(step + 1) + windowExpr.getFrameMode());
             if (windowExpr.hasFrameStartExpr()) {
                 windowExpr.getFrameStartExpr().accept(this, step + 2);
+                out.println();
             }
-            out.println(skip(step) + windowExpr.getFrameStartKind());
+            out.println(skip(step + 1) + windowExpr.getFrameStartKind());
             if (windowExpr.hasFrameEndExpr()) {
                 windowExpr.getFrameEndExpr().accept(this, step + 2);
+                out.println();
             }
-            out.println(skip(step) + windowExpr.getFrameEndKind());
-            out.println(skip(step) + "exclude " + windowExpr.getFrameExclusionKind());
+            out.println(skip(step + 1) + windowExpr.getFrameEndKind());
+            out.println(skip(step + 1) + "exclude " + windowExpr.getFrameExclusionKind());
         }
         out.println(skip(step) + ")");
         return null;
diff --git a/asterixdb/asterix-lang-sqlpp/src/main/java/org/apache/asterix/lang/sqlpp/visitor/base/AbstractSqlppContainsExpressionVisitor.java b/asterixdb/asterix-lang-sqlpp/src/main/java/org/apache/asterix/lang/sqlpp/visitor/base/AbstractSqlppContainsExpressionVisitor.java
index 6495742..8e8e959 100644
--- a/asterixdb/asterix-lang-sqlpp/src/main/java/org/apache/asterix/lang/sqlpp/visitor/base/AbstractSqlppContainsExpressionVisitor.java
+++ b/asterixdb/asterix-lang-sqlpp/src/main/java/org/apache/asterix/lang/sqlpp/visitor/base/AbstractSqlppContainsExpressionVisitor.java
@@ -297,12 +297,14 @@
                 || (winExpr.hasFrameStartExpr() && visit(winExpr.getFrameStartExpr(), arg))
                 || (winExpr.hasFrameEndExpr() && visit(winExpr.getFrameEndExpr(), arg))
                 || (winExpr.hasWindowFieldList() && visitFieldList(winExpr.getWindowFieldList(), arg))
+                || (winExpr.hasAggregateFilterExpr() && visit(winExpr.getAggregateFilterExpr(), arg))
                 || visitExprList(winExpr.getExprList(), arg);
     }
 
     @Override
     public Boolean visit(CallExpr callExpr, T arg) throws CompilationException {
-        return visitExprList(callExpr.getExprList(), arg);
+        return visitExprList(callExpr.getExprList(), arg)
+                || (callExpr.hasAggregateFilterExpr() && visit(callExpr.getAggregateFilterExpr(), arg));
     }
 
     private boolean visit(ILangExpression expr, T arg) throws CompilationException {
diff --git a/asterixdb/asterix-lang-sqlpp/src/main/java/org/apache/asterix/lang/sqlpp/visitor/base/AbstractSqlppExpressionScopingVisitor.java b/asterixdb/asterix-lang-sqlpp/src/main/java/org/apache/asterix/lang/sqlpp/visitor/base/AbstractSqlppExpressionScopingVisitor.java
index db85d73..f0a9d9a 100644
--- a/asterixdb/asterix-lang-sqlpp/src/main/java/org/apache/asterix/lang/sqlpp/visitor/base/AbstractSqlppExpressionScopingVisitor.java
+++ b/asterixdb/asterix-lang-sqlpp/src/main/java/org/apache/asterix/lang/sqlpp/visitor/base/AbstractSqlppExpressionScopingVisitor.java
@@ -239,7 +239,7 @@
         // or an outer scope query) should still be visible.
         Scope newScope = new Scope(scopeChecker, scopeChecker.getPrecedingScope());
         // Puts all group-by variables into the symbol set of the new scope.
-        Set<VariableExpr> gbyKeyVars = new HashSet<>(); // bindings from prior grouping sets //TODO:FIXME:GBY:REVISIT
+        Set<VariableExpr> gbyKeyVars = new HashSet<>(); // bindings from prior grouping sets
         for (List<GbyVariableExpressionPair> gbyPairList : gc.getGbyPairList()) {
             for (GbyVariableExpressionPair gbyKeyVarExpr : gbyPairList) {
                 gbyKeyVarExpr.setExpr(visit(gbyKeyVarExpr.getExpr(), gc));
@@ -391,16 +391,22 @@
 
     @Override
     public Expression visit(WindowExpression winExpr, ILangExpression arg) throws CompilationException {
-        visitWindowExpressionExcludingExprList(winExpr, arg);
+        visitWindowExpressionExcludingExprListAndAggFilter(winExpr, arg);
         if (winExpr.hasWindowVar()) {
             Scope preScope = scopeChecker.getCurrentScope();
             Scope newScope = scopeChecker.extendCurrentScope();
             VariableExpr windowVar = winExpr.getWindowVar();
             addNewVarSymbolToScope(newScope, windowVar.getVar(), windowVar.getSourceLocation());
             winExpr.setExprList(visit(winExpr.getExprList(), arg));
+            if (winExpr.hasAggregateFilterExpr()) {
+                winExpr.setAggregateFilterExpr(visit(winExpr.getAggregateFilterExpr(), arg));
+            }
             scopeChecker.replaceCurrentScope(preScope);
         } else {
             winExpr.setExprList(visit(winExpr.getExprList(), arg));
+            if (winExpr.hasAggregateFilterExpr()) {
+                winExpr.setAggregateFilterExpr(visit(winExpr.getAggregateFilterExpr(), arg));
+            }
         }
         return winExpr;
     }
diff --git a/asterixdb/asterix-lang-sqlpp/src/main/java/org/apache/asterix/lang/sqlpp/visitor/base/AbstractSqlppSimpleExpressionVisitor.java b/asterixdb/asterix-lang-sqlpp/src/main/java/org/apache/asterix/lang/sqlpp/visitor/base/AbstractSqlppSimpleExpressionVisitor.java
index 3d39dc0..6dacea6 100644
--- a/asterixdb/asterix-lang-sqlpp/src/main/java/org/apache/asterix/lang/sqlpp/visitor/base/AbstractSqlppSimpleExpressionVisitor.java
+++ b/asterixdb/asterix-lang-sqlpp/src/main/java/org/apache/asterix/lang/sqlpp/visitor/base/AbstractSqlppSimpleExpressionVisitor.java
@@ -306,6 +306,9 @@
     @Override
     public Expression visit(CallExpr callExpr, ILangExpression arg) throws CompilationException {
         callExpr.setExprList(visit(callExpr.getExprList(), arg));
+        if (callExpr.hasAggregateFilterExpr()) {
+            callExpr.setAggregateFilterExpr(visit(callExpr.getAggregateFilterExpr(), arg));
+        }
         return callExpr;
     }
 
@@ -322,12 +325,15 @@
 
     @Override
     public Expression visit(WindowExpression winExpr, ILangExpression arg) throws CompilationException {
-        visitWindowExpressionExcludingExprList(winExpr, arg);
+        visitWindowExpressionExcludingExprListAndAggFilter(winExpr, arg);
         winExpr.setExprList(visit(winExpr.getExprList(), arg));
+        if (winExpr.hasAggregateFilterExpr()) {
+            winExpr.setAggregateFilterExpr(visit(winExpr.getAggregateFilterExpr(), arg));
+        }
         return winExpr;
     }
 
-    protected void visitWindowExpressionExcludingExprList(WindowExpression winExpr, ILangExpression arg)
+    protected void visitWindowExpressionExcludingExprListAndAggFilter(WindowExpression winExpr, ILangExpression arg)
             throws CompilationException {
         if (winExpr.hasPartitionList()) {
             winExpr.setPartitionList(visit(winExpr.getPartitionList(), arg));
diff --git a/asterixdb/asterix-lang-sqlpp/src/main/javacc/SQLPP.jj b/asterixdb/asterix-lang-sqlpp/src/main/javacc/SQLPP.jj
index ca5b1d8..7105a42 100644
--- a/asterixdb/asterix-lang-sqlpp/src/main/javacc/SQLPP.jj
+++ b/asterixdb/asterix-lang-sqlpp/src/main/javacc/SQLPP.jj
@@ -3375,50 +3375,58 @@
 
 Expression FunctionCallExpr() throws ParseException:
 {
-  Expression resultExpr;
-  CallExpr callExpr;
   List<Expression> argList = new ArrayList<Expression>();
-  Expression tmp = null;
-  int arity = 0;
+  Expression argExpr = null;
   FunctionName funcName = null;
   boolean star = false;
   boolean distinct = false;
+  Expression filterExpr = null;
+  WindowExpression windowExpr = null;
 }
 {
   funcName = FunctionName()
   <LEFTPAREN> (
     ( <DISTINCT> { distinct = true; } )?
-    ( tmp = Expression() | <MUL> { star = true; } )
+    ( argExpr = Expression() | <MUL> { star = true; } )
     {
-      if(star){
-        if(!funcName.function.equalsIgnoreCase("count")){
-           throw new SqlppParseException(getSourceLocation(token), "The parameter * can only be used in COUNT().");
+      if (star) {
+        if (funcName.function.equalsIgnoreCase(BuiltinFunctions.SCALAR_COUNT.getName())) {
+          argExpr = new LiteralExpr(new LongIntegerLiteral(1L));
+        } else {
+          throw new SqlppParseException(getSourceLocation(token),
+            "The parameter * can only be used in " + BuiltinFunctions.SCALAR_COUNT.getName() + "().");
         }
-        argList.add(new LiteralExpr(new LongIntegerLiteral(1L)));
-      } else {
-         argList.add(tmp);
       }
-      arity ++;
+      argList.add(argExpr);
     }
-  (<COMMA> tmp = Expression()
+  (<COMMA> argExpr = Expression()
     {
-      argList.add(tmp);
-      arity++;
+      argList.add(argExpr);
     }
   )*)? <RIGHTPAREN>
-    {
-      String name = funcName.function;
-      if (distinct) {
-        name += FunctionMapUtil.DISTINCT_AGGREGATE_SUFFIX;
-      }
-      // TODO use funcName.library
-      String fqFunctionName = funcName.library == null ? name : funcName.library + "#" + name;
-      FunctionSignature signature
-        = lookupFunctionSignature(funcName.dataverse, fqFunctionName, arity);
-      if (signature == null) {
-        signature = new FunctionSignature(funcName.dataverse, fqFunctionName, arity);
-      }
-      callExpr = FunctionMapUtil.normalizedListInputFunctions(new CallExpr(signature,argList));
+
+  {
+    String name = funcName.function;
+    if (distinct) {
+      name += FunctionMapUtil.DISTINCT_AGGREGATE_SUFFIX;
+    }
+    String fqFunctionName = funcName.library == null ? name : funcName.library + "#" + name;
+    int arity = argList.size();
+    FunctionSignature signature = lookupFunctionSignature(funcName.dataverse, fqFunctionName, arity);
+    if (signature == null) {
+      signature = new FunctionSignature(funcName.dataverse, fqFunctionName, arity);
+    }
+  }
+
+  ( <FILTER> <LEFTPAREN> <WHERE> filterExpr = Expression() <RIGHTPAREN> )?
+
+  ( LOOKAHEAD(5) windowExpr = WindowExpr(signature, argList, filterExpr) )?
+
+  {
+    if (windowExpr != null) {
+      return windowExpr;
+    } else {
+      CallExpr callExpr = new CallExpr(signature, argList, filterExpr);
       if (funcName.hint != null) {
         switch (funcName.hint) {
           case INDEXED_NESTED_LOOP_JOIN_HINT:
@@ -3429,18 +3437,15 @@
             break;
         }
       }
+      FunctionMapUtil.normalizedListInputFunctions(callExpr);
       callExpr.setSourceLocation(funcName.sourceLoc);
-      resultExpr = callExpr;
+      return callExpr;
     }
-
-  ( LOOKAHEAD(5) resultExpr = WindowExpr(callExpr.getFunctionSignature(), callExpr.getExprList()) )?
-
-  {
-     return resultExpr;
   }
 }
 
-WindowExpression WindowExpr(FunctionSignature signature, List<Expression> argList) throws ParseException:
+WindowExpression WindowExpr(FunctionSignature signature, List<Expression> argList, Expression aggFilterExpr)
+  throws ParseException:
 {
   Boolean fromLast = null, ignoreNulls = null;
 }
@@ -3481,12 +3486,12 @@
   )?
   <OVER>
   {
-    return OverClause(signature, argList, token, fromLast, ignoreNulls);
+    return OverClause(signature, argList, aggFilterExpr, token, fromLast, ignoreNulls);
   }
 }
 
-WindowExpression OverClause(FunctionSignature signature, List<Expression> argList, Token startToken, Boolean fromLast,
-    Boolean ignoreNulls) throws ParseException:
+WindowExpression OverClause(FunctionSignature signature, List<Expression> argList, Expression aggFilterExpr,
+    Token startToken, Boolean fromLast, Boolean ignoreNulls) throws ParseException:
 {
   Expression partitionExpr = null;
   List<Expression> partitionExprs = new ArrayList<Expression>();
@@ -3546,10 +3551,10 @@
   )?
   <RIGHTPAREN>
   {
-    WindowExpression winExp = new WindowExpression(signature, argList, partitionExprs, orderbyList, orderbyModifierList,
-      frameMode, frameStartKind, frameStartExpr, frameEndKind, frameEndExpr, frameExclusionKind, windowVar,
-      windowFieldList, ignoreNulls, fromLast);
-    return addSourceLocation(winExp, startToken);
+    WindowExpression winExpr = new WindowExpression(signature, argList, aggFilterExpr, partitionExprs, orderbyList,
+      orderbyModifierList, frameMode, frameStartKind, frameStartExpr, frameEndKind, frameEndExpr, frameExclusionKind,
+      windowVar, windowFieldList, ignoreNulls, fromLast);
+    return addSourceLocation(winExpr, startToken);
   }
 }