You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@asterixdb.apache.org by dl...@apache.org on 2019/06/04 15:39:22 UTC

[asterixdb] branch master updated: [NO ISSUE][FUN] Support FROM LAST in NTH_VALUE()

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

dlych pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/asterixdb.git


The following commit(s) were added to refs/heads/master by this push:
     new 5476f96  [NO ISSUE][FUN] Support FROM LAST in NTH_VALUE()
5476f96 is described below

commit 5476f96a1ad44487b1a2e0db2b7e9bb1d7dc4f20
Author: Dmitry Lychagin <dm...@couchbase.com>
AuthorDate: Fri May 31 16:03:11 2019 -0700

    [NO ISSUE][FUN] Support FROM LAST in NTH_VALUE()
    
    - user model changes: yes
    - storage format changes: no
    - interface changes: no
    
    Details:
    - Support FROM FIRST / FROM LAST modifiers in window
      function NTH_VALUE() with FROM FIRST being the default
    - Minor cleanup of SQL++ grammar
    - Update JavaCC version to 6.1.2
    
    Change-Id: Iceac579bd5a3e651bcd7707e324148690e020cf5
    Reviewed-on: https://asterix-gerrit.ics.uci.edu/3419
    Contrib: Jenkins <je...@fulliautomatix.ics.uci.edu>
    Tested-by: Jenkins <je...@fulliautomatix.ics.uci.edu>
    Integration-Tests: Jenkins <je...@fulliautomatix.ics.uci.edu>
    Reviewed-by: Ali Alsuliman <al...@gmail.com>
---
 .../SqlppExpressionToPlanTranslator.java           | 19 +++++-
 .../window/nth_value_01/nth_value_01.2.query.sqlpp | 43 ++++++++++++++
 .../window/nth_value_01/nth_value_01.3.query.sqlpp | 43 ++++++++++++++
 .../window/nth_value_01/nth_value_01.4.query.sqlpp | 46 +++++++++++++++
 .../window/nth_value_01/nth_value_01.5.query.sqlpp | 28 +++++++++
 .../window/win_negative/win_negative.8.query.sqlpp | 25 ++++++++
 .../window/win_negative/win_negative.9.query.sqlpp | 25 ++++++++
 .../results/window/nth_value_01/nth_value_01.2.adm |  8 +++
 .../results/window/nth_value_01/nth_value_01.3.adm |  8 +++
 .../results/window/nth_value_01/nth_value_01.4.adm |  8 +++
 .../results/window/nth_value_01/nth_value_01.5.adm |  2 +
 .../test/resources/runtimets/testsuite_sqlpp.xml   |  2 +
 .../lang/sqlpp/expression/WindowExpression.java    | 19 +++++-
 .../lang/sqlpp/visitor/DeepCopyVisitor.java        |  2 +-
 .../SqlppCloneAndSubstituteVariablesVisitor.java   |  9 +--
 .../asterix-lang-sqlpp/src/main/javacc/SQLPP.jj    | 68 +++++++++++++++++-----
 .../asterix/om/functions/BuiltinFunctions.java     |  7 ++-
 asterixdb/pom.xml                                  |  7 +++
 18 files changed, 341 insertions(+), 28 deletions(-)

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 9ba6d2c..988723f 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
@@ -1058,6 +1058,8 @@ public class SqlppExpressionToPlanTranslator extends LangExpressionToPlanTransla
                 BuiltinFunctions.WindowFunctionProperty.NO_FRAME_CLAUSE);
         boolean allowRespectIgnoreNulls = isWin && BuiltinFunctions.builtinFunctionHasProperty(fi,
                 BuiltinFunctions.WindowFunctionProperty.ALLOW_RESPECT_IGNORE_NULLS);
+        boolean allowFromFirstLast = isWin && BuiltinFunctions.builtinFunctionHasProperty(fi,
+                BuiltinFunctions.WindowFunctionProperty.ALLOW_FROM_FIRST_LAST);
 
         Mutable<ILogicalOperator> currentOpRef = tupSource;
 
@@ -1138,6 +1140,8 @@ public class SqlppExpressionToPlanTranslator extends LangExpressionToPlanTransla
 
         boolean respectNulls = !getBooleanModifier(winExpr.getIgnoreNulls(), false, allowRespectIgnoreNulls, sourceLoc,
                 "RESPECT/IGNORE NULLS", fs.getName());
+        boolean fromLast = getBooleanModifier(winExpr.getFromLast(), false, allowFromFirstLast, sourceLoc,
+                "FROM FIRST/LAST", fs.getName());
 
         boolean makeRunningAgg = false, makeNestedAgg = false;
         FunctionIdentifier runningAggFunc = null, nestedAggFunc = null, winResultFunc = null, postWinResultFunc = null;
@@ -1174,9 +1178,7 @@ public class SqlppExpressionToPlanTranslator extends LangExpressionToPlanTransla
                     // IGNORE NULLS
                     if (isLag) {
                         // reverse order for LAG()
-                        for (Pair<OrderOperator.IOrder, Mutable<ILogicalExpression>> orderExprPair : orderExprListOut) {
-                            orderExprPair.setFirst(reverseOrder(orderExprPair.getFirst()));
-                        }
+                        reverseOrder(orderExprListOut);
                     }
                     winFrameStartKind = WindowExpression.FrameBoundaryKind.BOUNDED_FOLLOWING;
                     winFrameStartExpr = new LiteralExpr(new IntegerLiteral(1));
@@ -1212,6 +1214,10 @@ public class SqlppExpressionToPlanTranslator extends LangExpressionToPlanTransla
                 }
             } else if (BuiltinFunctions.NTH_VALUE_IMPL.equals(fi)) {
                 nestedAggFunc = BuiltinFunctions.SCALAR_FIRST_ELEMENT;
+                if (fromLast) {
+                    // reverse order if FROM LAST modifier is present
+                    reverseOrder(orderExprListOut);
+                }
                 if (respectNulls) {
                     winFrameMaxOjbects = 1;
                 } else {
@@ -1690,6 +1696,13 @@ public class SqlppExpressionToPlanTranslator extends LangExpressionToPlanTransla
         return opExpr;
     }
 
+    private static void reverseOrder(List<Pair<OrderOperator.IOrder, Mutable<ILogicalExpression>>> orderExprList)
+            throws CompilationException {
+        for (Pair<OrderOperator.IOrder, Mutable<ILogicalExpression>> orderExprPair : orderExprList) {
+            orderExprPair.setFirst(reverseOrder(orderExprPair.getFirst()));
+        }
+    }
+
     private static OrderOperator.IOrder reverseOrder(OrderOperator.IOrder order) throws CompilationException {
         switch (order.getKind()) {
             case ASC:
diff --git a/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/window/nth_value_01/nth_value_01.2.query.sqlpp b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/window/nth_value_01/nth_value_01.2.query.sqlpp
new file mode 100644
index 0000000..5d7ed88
--- /dev/null
+++ b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/window/nth_value_01/nth_value_01.2.query.sqlpp
@@ -0,0 +1,43 @@
+/*
+ * 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.
+ */
+/*
+ * Description  : NTH_VALUE() FROM FIRST (default), RESPECT/IGNORE NULLS
+ * Expected Res : SUCCESS
+ */
+
+from [
+  { "x": "a",         "y": 1, "p": 0 },
+  {                   "y": 2, "p": 0 },
+  { "x": null,        "y": 3, "p": 0 },
+  { "x": "b",         "y": 4, "p": 0 },
+
+  { "x": "a",         "y": 5, "p": 1 },
+  { "x": null,        "y": 6, "p": 1 },
+  {                   "y": 7, "p": 1 },
+  { "x": "b",         "y": 8, "p": 1 }
+] t
+select
+  nth_value(x, 2) from first over (partition by p order by y range between unbounded preceding and unbounded following)
+    as nth_value_default_respect,
+  nth_value(x, 2) from first RESPECT NULLS over (partition by p order by y range between unbounded preceding and unbounded following)
+    as nth_value_respect,
+  nth_value(x, 2) FROM FIRST IGNORE NULLS over (partition by p order by y range between unbounded preceding and unbounded following)
+    as nth_value_ignore,
+  x, y, p
+order by y
\ No newline at end of file
diff --git a/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/window/nth_value_01/nth_value_01.3.query.sqlpp b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/window/nth_value_01/nth_value_01.3.query.sqlpp
new file mode 100644
index 0000000..51f315e
--- /dev/null
+++ b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/window/nth_value_01/nth_value_01.3.query.sqlpp
@@ -0,0 +1,43 @@
+/*
+ * 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.
+ */
+/*
+ * Description  : NTH_VALUE() FROM LAST, RESPECT/IGNORE NULLS
+ * Expected Res : SUCCESS
+ */
+
+from [
+  { "x": "a",         "y": 1, "p": 0 },
+  {                   "y": 2, "p": 0 },
+  { "x": null,        "y": 3, "p": 0 },
+  { "x": "b",         "y": 4, "p": 0 },
+
+  { "x": "a",         "y": 5, "p": 1 },
+  { "x": null,        "y": 6, "p": 1 },
+  {                   "y": 7, "p": 1 },
+  { "x": "b",         "y": 8, "p": 1 }
+] t
+select
+  nth_value(x, 2) from last over (partition by p order by y range between unbounded preceding and unbounded following)
+    as nth_value_default_respect,
+  nth_value(x, 2) from last RESPECT NULLS over (partition by p order by y range between unbounded preceding and unbounded following)
+    as nth_value_respect,
+  nth_value(x, 2) FROM LAST IGNORE NULLS over (partition by p order by y range between unbounded preceding and unbounded following)
+    as nth_value_ignore,
+  x, y, p
+order by y
\ No newline at end of file
diff --git a/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/window/nth_value_01/nth_value_01.4.query.sqlpp b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/window/nth_value_01/nth_value_01.4.query.sqlpp
new file mode 100644
index 0000000..e17155a
--- /dev/null
+++ b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/window/nth_value_01/nth_value_01.4.query.sqlpp
@@ -0,0 +1,46 @@
+/*
+ * 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.
+ */
+/*
+ * Description  : NTH_VALUE() FROM FIRST/LAST, RESPECT/IGNORE NULLS
+ *              : that exceeds frame boundary
+ * Expected Res : SUCCESS
+ */
+
+from [
+  { "x": "a",         "y": 1, "p": 0 },
+  {                   "y": 2, "p": 0 },
+  { "x": null,        "y": 3, "p": 0 },
+  { "x": "b",         "y": 4, "p": 0 },
+
+  { "x": "a",         "y": 5, "p": 1 },
+  { "x": null,        "y": 6, "p": 1 },
+  {                   "y": 7, "p": 1 },
+  { "x": "b",         "y": 8, "p": 1 }
+] t
+select
+  nth_value(x, 5) from first respect nulls over (partition by p order by y range between unbounded preceding and unbounded following)
+    as nth_value_first_respect,
+  nth_value(x, 3) from first ignore nulls over (partition by p order by y range between unbounded preceding and unbounded following)
+    as nth_value_first_ignore,
+  nth_value(x, 5) from last respect nulls over (partition by p order by y range between unbounded preceding and unbounded following)
+    as nth_value_last_respect,
+  nth_value(x, 3) from last ignore nulls over (partition by p order by y range between unbounded preceding and unbounded following)
+    as nth_value_last_ignore,
+  x, y, p
+order by y
\ No newline at end of file
diff --git a/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/window/nth_value_01/nth_value_01.5.query.sqlpp b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/window/nth_value_01/nth_value_01.5.query.sqlpp
new file mode 100644
index 0000000..7c62cdb
--- /dev/null
+++ b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/window/nth_value_01/nth_value_01.5.query.sqlpp
@@ -0,0 +1,28 @@
+/*
+ * 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.
+ */
+/*
+ * Description  : Test FROM FIRST/LAST outside of a window function call
+ * Expected Res : SUCCESS
+ */
+
+with first as [1, 2, 3], last as [1, 2, 1, 2]
+select count(`over`) c, sum(`over`) from first `over`
+union all
+select count(`over`) c, sum(`over`) from last `over`
+order by c
diff --git a/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/window/win_negative/win_negative.8.query.sqlpp b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/window/win_negative/win_negative.8.query.sqlpp
new file mode 100644
index 0000000..73344ce
--- /dev/null
+++ b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/window/win_negative/win_negative.8.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.
+ */
+/*
+ * Description  : Function that doesn't support FROM FIRST/LAST
+ * Expected Res : FAILURE
+ */
+
+from range(1, 4) x
+select value first_value(x) from first over (order by x)
\ No newline at end of file
diff --git a/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/window/win_negative/win_negative.9.query.sqlpp b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/window/win_negative/win_negative.9.query.sqlpp
new file mode 100644
index 0000000..c03e763
--- /dev/null
+++ b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/window/win_negative/win_negative.9.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.
+ */
+/*
+ * Description  : Function that doesn't support FROM FIRST/LAST
+ * Expected Res : FAILURE
+ */
+
+from range(1, 4) x
+select value first_value(x) from last over (order by x)
\ No newline at end of file
diff --git a/asterixdb/asterix-app/src/test/resources/runtimets/results/window/nth_value_01/nth_value_01.2.adm b/asterixdb/asterix-app/src/test/resources/runtimets/results/window/nth_value_01/nth_value_01.2.adm
new file mode 100644
index 0000000..55ac6c6
--- /dev/null
+++ b/asterixdb/asterix-app/src/test/resources/runtimets/results/window/nth_value_01/nth_value_01.2.adm
@@ -0,0 +1,8 @@
+{ "nth_value_ignore": "b", "x": "a", "y": 1, "p": 0 }
+{ "nth_value_ignore": "b", "y": 2, "p": 0 }
+{ "nth_value_ignore": "b", "x": null, "y": 3, "p": 0 }
+{ "nth_value_ignore": "b", "x": "b", "y": 4, "p": 0 }
+{ "nth_value_default_respect": null, "nth_value_respect": null, "nth_value_ignore": "b", "x": "a", "y": 5, "p": 1 }
+{ "nth_value_default_respect": null, "nth_value_respect": null, "nth_value_ignore": "b", "x": null, "y": 6, "p": 1 }
+{ "nth_value_default_respect": null, "nth_value_respect": null, "nth_value_ignore": "b", "y": 7, "p": 1 }
+{ "nth_value_default_respect": null, "nth_value_respect": null, "nth_value_ignore": "b", "x": "b", "y": 8, "p": 1 }
\ No newline at end of file
diff --git a/asterixdb/asterix-app/src/test/resources/runtimets/results/window/nth_value_01/nth_value_01.3.adm b/asterixdb/asterix-app/src/test/resources/runtimets/results/window/nth_value_01/nth_value_01.3.adm
new file mode 100644
index 0000000..7ccb3f9
--- /dev/null
+++ b/asterixdb/asterix-app/src/test/resources/runtimets/results/window/nth_value_01/nth_value_01.3.adm
@@ -0,0 +1,8 @@
+{ "nth_value_default_respect": null, "nth_value_respect": null, "nth_value_ignore": "a", "x": "a", "y": 1, "p": 0 }
+{ "nth_value_default_respect": null, "nth_value_respect": null, "nth_value_ignore": "a", "y": 2, "p": 0 }
+{ "nth_value_default_respect": null, "nth_value_respect": null, "nth_value_ignore": "a", "x": null, "y": 3, "p": 0 }
+{ "nth_value_default_respect": null, "nth_value_respect": null, "nth_value_ignore": "a", "x": "b", "y": 4, "p": 0 }
+{ "nth_value_ignore": "a", "x": "a", "y": 5, "p": 1 }
+{ "nth_value_ignore": "a", "x": null, "y": 6, "p": 1 }
+{ "nth_value_ignore": "a", "y": 7, "p": 1 }
+{ "nth_value_ignore": "a", "x": "b", "y": 8, "p": 1 }
\ No newline at end of file
diff --git a/asterixdb/asterix-app/src/test/resources/runtimets/results/window/nth_value_01/nth_value_01.4.adm b/asterixdb/asterix-app/src/test/resources/runtimets/results/window/nth_value_01/nth_value_01.4.adm
new file mode 100644
index 0000000..4faf9b1
--- /dev/null
+++ b/asterixdb/asterix-app/src/test/resources/runtimets/results/window/nth_value_01/nth_value_01.4.adm
@@ -0,0 +1,8 @@
+{ "nth_value_first_respect": null, "nth_value_first_ignore": null, "nth_value_last_respect": null, "nth_value_last_ignore": null, "x": "a", "y": 1, "p": 0 }
+{ "nth_value_first_respect": null, "nth_value_first_ignore": null, "nth_value_last_respect": null, "nth_value_last_ignore": null, "y": 2, "p": 0 }
+{ "nth_value_first_respect": null, "nth_value_first_ignore": null, "nth_value_last_respect": null, "nth_value_last_ignore": null, "x": null, "y": 3, "p": 0 }
+{ "nth_value_first_respect": null, "nth_value_first_ignore": null, "nth_value_last_respect": null, "nth_value_last_ignore": null, "x": "b", "y": 4, "p": 0 }
+{ "nth_value_first_respect": null, "nth_value_first_ignore": null, "nth_value_last_respect": null, "nth_value_last_ignore": null, "x": "a", "y": 5, "p": 1 }
+{ "nth_value_first_respect": null, "nth_value_first_ignore": null, "nth_value_last_respect": null, "nth_value_last_ignore": null, "x": null, "y": 6, "p": 1 }
+{ "nth_value_first_respect": null, "nth_value_first_ignore": null, "nth_value_last_respect": null, "nth_value_last_ignore": null, "y": 7, "p": 1 }
+{ "nth_value_first_respect": null, "nth_value_first_ignore": null, "nth_value_last_respect": null, "nth_value_last_ignore": null, "x": "b", "y": 8, "p": 1 }
\ No newline at end of file
diff --git a/asterixdb/asterix-app/src/test/resources/runtimets/results/window/nth_value_01/nth_value_01.5.adm b/asterixdb/asterix-app/src/test/resources/runtimets/results/window/nth_value_01/nth_value_01.5.adm
new file mode 100644
index 0000000..e062cda
--- /dev/null
+++ b/asterixdb/asterix-app/src/test/resources/runtimets/results/window/nth_value_01/nth_value_01.5.adm
@@ -0,0 +1,2 @@
+{ "c": 3, "$1": 6 }
+{ "c": 4, "$2": 6 }
\ No newline at end of file
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 d5fa013..d1098dc 100644
--- a/asterixdb/asterix-app/src/test/resources/runtimets/testsuite_sqlpp.xml
+++ b/asterixdb/asterix-app/src/test/resources/runtimets/testsuite_sqlpp.xml
@@ -10424,6 +10424,8 @@
         <expected-error>ASX1079: Compilation error: count is a SQL-92 aggregate function</expected-error>
         <expected-error>ASX1104: Invalid modifier RESPECT/IGNORE NULLS for function</expected-error>
         <expected-error>ASX1104: Invalid modifier RESPECT/IGNORE NULLS for function</expected-error>
+        <expected-error>ASX1104: Invalid modifier FROM FIRST/LAST for function</expected-error>
+        <expected-error>ASX1104: Invalid modifier FROM FIRST/LAST for function</expected-error>
         <source-location>false</source-location>
       </compilation-unit>
     </test-case>
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 5a9173b..e0a6c6b 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
@@ -55,13 +55,14 @@ public class WindowExpression extends AbstractExpression {
     private List<Pair<Expression, Identifier>> windowFieldList;
 
     private Boolean ignoreNulls;
+    private Boolean fromLast;
 
     public WindowExpression(FunctionSignature functionSignature, List<Expression> exprList,
             List<Expression> partitionList, List<Expression> orderbyList,
             List<OrderbyClause.OrderModifier> orderbyModifierList, FrameMode frameMode,
             FrameBoundaryKind frameStartKind, Expression frameStartExpr, FrameBoundaryKind frameEndKind,
             Expression frameEndExpr, FrameExclusionKind frameExclusionKind, VariableExpr windowVar,
-            List<Pair<Expression, Identifier>> windowFieldList, Boolean ignoreNulls) {
+            List<Pair<Expression, Identifier>> windowFieldList, Boolean ignoreNulls, Boolean fromLast) {
         if (functionSignature == null || exprList == null) {
             throw new NullPointerException();
         }
@@ -79,6 +80,7 @@ public class WindowExpression extends AbstractExpression {
         this.windowVar = windowVar;
         this.windowFieldList = windowFieldList;
         this.ignoreNulls = ignoreNulls;
+        this.fromLast = fromLast;
     }
 
     @Override
@@ -232,12 +234,20 @@ public class WindowExpression extends AbstractExpression {
         this.ignoreNulls = ignoreNulls;
     }
 
+    public Boolean getFromLast() {
+        return fromLast;
+    }
+
+    public void setFromLast(Boolean fromLast) {
+        this.fromLast = fromLast;
+    }
+
     @Override
     public int hashCode() {
         return Objects.hash(functionSignature, exprList, ExpressionUtils.emptyIfNull(partitionList),
                 ExpressionUtils.emptyIfNull(orderbyList), ExpressionUtils.emptyIfNull(orderbyModifierList), frameMode,
                 frameStartKind, frameStartExpr, frameEndKind, frameEndExpr, frameExclusionKind, windowVar,
-                ExpressionUtils.emptyIfNull(windowFieldList), ignoreNulls);
+                ExpressionUtils.emptyIfNull(windowFieldList), ignoreNulls, fromLast);
     }
 
     @Override
@@ -263,7 +273,7 @@ public class WindowExpression extends AbstractExpression {
                 && Objects.equals(windowVar, target.windowVar)
                 && Objects.equals(ExpressionUtils.emptyIfNull(windowFieldList),
                         ExpressionUtils.emptyIfNull(target.windowFieldList))
-                && Objects.equals(ignoreNulls, target.ignoreNulls);
+                && Objects.equals(ignoreNulls, target.ignoreNulls) && Objects.equals(fromLast, target.fromLast);
     }
 
     @Override
@@ -274,6 +284,9 @@ public class WindowExpression extends AbstractExpression {
         sb.append('(');
         sb.append(StringUtils.join(exprList, ','));
         sb.append(')');
+        if (fromLast != null && fromLast) {
+            sb.append(" FROM LAST");
+        }
         if (ignoreNulls != null && ignoreNulls) {
             sb.append(" IGNORE NULLS");
         }
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 3e07177..6e28d36 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
@@ -525,7 +525,7 @@ public class DeepCopyVisitor extends AbstractSqlppQueryExpressionVisitor<ILangEx
         WindowExpression copy = new WindowExpression(winExpr.getFunctionSignature(), newExprList, newPartitionList,
                 newOrderbyList, newOrderbyModifierList, winExpr.getFrameMode(), winExpr.getFrameStartKind(),
                 newFrameStartExpr, winExpr.getFrameEndKind(), newFrameEndExpr, winExpr.getFrameExclusionKind(),
-                newWindowVar, newWindowFieldList, winExpr.getIgnoreNulls());
+                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/SqlppCloneAndSubstituteVariablesVisitor.java b/asterixdb/asterix-lang-sqlpp/src/main/java/org/apache/asterix/lang/sqlpp/visitor/SqlppCloneAndSubstituteVariablesVisitor.java
index 4b76b56..20fd0f5 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
@@ -420,10 +420,11 @@ public class SqlppCloneAndSubstituteVariablesVisitor extends CloneAndSubstituteV
                 winExpr.hasWindowVar() ? (VariableExpr) winExpr.getWindowVar().accept(this, env).first : null;
         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());
+        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());
         newWinExpr.setSourceLocation(winExpr.getSourceLocation());
         newWinExpr.addHints(winExpr.getHints());
         return new Pair<>(newWinExpr, env);
diff --git a/asterixdb/asterix-lang-sqlpp/src/main/javacc/SQLPP.jj b/asterixdb/asterix-lang-sqlpp/src/main/javacc/SQLPP.jj
index c0f1725..25ad2ab 100644
--- a/asterixdb/asterix-lang-sqlpp/src/main/javacc/SQLPP.jj
+++ b/asterixdb/asterix-lang-sqlpp/src/main/javacc/SQLPP.jj
@@ -191,9 +191,11 @@ class SQLPPParser extends ScopeChecker implements IParser {
     // tokens parsed as identifiers
     private static final String CURRENT = "CURRENT";
     private static final String EXCLUDE = "EXCLUDE";
+    private static final String FIRST = "FIRST";
     private static final String FOLLOWING = "FOLLOWING";
     private static final String GROUPS = "GROUPS";
     private static final String IGNORE = "IGNORE";
+    private static final String LAST = "LAST";
     private static final String NO = "NO";
     private static final String NULLS = "NULLS";
     private static final String OTHERS = "OTHERS";
@@ -1973,7 +1975,7 @@ Expression NotExpr() throws ParseException:
    Token startToken = null;
 }
 {
-  (<NOT> { not = true; startToken = token; } )? inputExpr = RelExpr()
+  (LOOKAHEAD(2) <NOT> { not = true; startToken = token; } )? inputExpr = RelExpr()
   {
     if(not) {
         FunctionSignature signature = new FunctionSignature(BuiltinFunctions.NOT);
@@ -2749,14 +2751,60 @@ Expression FunctionCallExpr() throws ParseException:
       resultExpr = callExpr;
     }
 
-  ( resultExpr = WindowExpr(callExpr.getFunctionSignature(), callExpr.getExprList(), token) )?
+  ( LOOKAHEAD(5) resultExpr = WindowExpr(callExpr.getFunctionSignature(), callExpr.getExprList()) )?
 
   {
      return resultExpr;
   }
 }
 
-WindowExpression WindowExpr(FunctionSignature signature, List<Expression> argList, Token startToken) throws ParseException:
+WindowExpression WindowExpr(FunctionSignature signature, List<Expression> argList) throws ParseException:
+{
+  Boolean fromLast = null, ignoreNulls = null;
+}
+{
+  (
+    // FROM ( FIRST | LAST ) ( ( RESPECT | IGNORE ) NULLS )? OVER
+    LOOKAHEAD(5, <FROM> <IDENTIFIER> ( <IDENTIFIER> <IDENTIFIER> )? <OVER>)
+    <FROM> <IDENTIFIER>
+    {
+      if (isToken(FIRST)) {
+        fromLast = false;
+      } else if (isToken(LAST)) {
+        fromLast = true;
+      } else {
+        throw createUnexpectedTokenError();
+      }
+    }
+  )?
+  (
+    // ( RESPECT | IGNORE ) NULLS OVER
+    LOOKAHEAD(3, <IDENTIFIER> <IDENTIFIER> <OVER>)
+    <IDENTIFIER>
+    {
+      if (isToken(RESPECT)) {
+        ignoreNulls = false;
+      } else if (isToken(IGNORE)) {
+        ignoreNulls = true;
+      } else {
+        throw createUnexpectedTokenError();
+      }
+    }
+    <IDENTIFIER>
+    {
+      if (!isToken(NULLS)) {
+        throw createUnexpectedTokenError();
+      }
+    }
+  )?
+  <OVER>
+  {
+    return OverClause(signature, argList, token, fromLast, ignoreNulls);
+  }
+}
+
+WindowExpression OverClause(FunctionSignature signature, List<Expression> argList, Token startToken, Boolean fromLast,
+    Boolean ignoreNulls) throws ParseException:
 {
   Expression partitionExpr = null;
   List<Expression> partitionExprs = new ArrayList<Expression>();
@@ -2771,18 +2819,9 @@ WindowExpression WindowExpr(FunctionSignature signature, List<Expression> argLis
   Pair<VariableExpr, List<Pair<Expression, Identifier>>> windowVarWithFieldList = null;
   VariableExpr windowVar = null;
   List<Pair<Expression, Identifier>> windowFieldList = null;
-  Boolean ignoreNulls = null;
 }
 {
   (
-    (
-      LOOKAHEAD({ laIdentifier(RESPECT) }) <IDENTIFIER> { ignoreNulls = false; }
-      | LOOKAHEAD({ laIdentifier(IGNORE) }) <IDENTIFIER> { ignoreNulls = true; }
-    )
-    LOOKAHEAD({ laIdentifier(NULLS) }) <IDENTIFIER>
-  )?
-  <OVER>
-  (
     windowVarWithFieldList = VariableWithFieldMap() <AS>
     {
       windowVar = windowVarWithFieldList.first;
@@ -2827,7 +2866,7 @@ WindowExpression WindowExpr(FunctionSignature signature, List<Expression> argLis
   {
     WindowExpression winExp = new WindowExpression(signature, argList, partitionExprs, orderbyList, orderbyModifierList,
       frameMode, frameStartKind, frameStartExpr, frameEndKind, frameEndExpr, frameExclusionKind, windowVar,
-      windowFieldList, ignoreNulls);
+      windowFieldList, ignoreNulls, fromLast);
     return addSourceLocation(winExp, startToken);
   }
 }
@@ -2857,8 +2896,7 @@ Pair<WindowExpression.FrameBoundaryKind, Expression> WindowFrameBoundary() throw
 }
 {
   (
-    LOOKAHEAD({ laIdentifier(CURRENT) }) <IDENTIFIER> { current = true; }
-    | LOOKAHEAD({ laIdentifier(UNBOUNDED) }) <IDENTIFIER>
+    LOOKAHEAD({ laIdentifier(CURRENT) || laIdentifier(UNBOUNDED) }) <IDENTIFIER> { current = isToken(CURRENT); }
     | expr = Expression()
   )
   <IDENTIFIER>
diff --git a/asterixdb/asterix-om/src/main/java/org/apache/asterix/om/functions/BuiltinFunctions.java b/asterixdb/asterix-om/src/main/java/org/apache/asterix/om/functions/BuiltinFunctions.java
index 3bf3514..06dcb89 100644
--- a/asterixdb/asterix-om/src/main/java/org/apache/asterix/om/functions/BuiltinFunctions.java
+++ b/asterixdb/asterix-om/src/main/java/org/apache/asterix/om/functions/BuiltinFunctions.java
@@ -18,6 +18,7 @@
  */
 package org.apache.asterix.om.functions;
 
+import static org.apache.asterix.om.functions.BuiltinFunctions.WindowFunctionProperty.ALLOW_FROM_FIRST_LAST;
 import static org.apache.asterix.om.functions.BuiltinFunctions.WindowFunctionProperty.ALLOW_RESPECT_IGNORE_NULLS;
 import static org.apache.asterix.om.functions.BuiltinFunctions.WindowFunctionProperty.HAS_LIST_ARG;
 import static org.apache.asterix.om.functions.BuiltinFunctions.WindowFunctionProperty.INJECT_ORDER_ARGS;
@@ -3030,8 +3031,10 @@ public class BuiltinFunctions {
         INJECT_ORDER_ARGS,
         /** Whether a running aggregate requires partition materialization runtime */
         MATERIALIZE_PARTITION,
+        /** Whether FROM (FIRST | LAST) modifier is allowed */
+        ALLOW_FROM_FIRST_LAST,
         /** Whether (RESPECT | IGNORE) NULLS modifier is allowed */
-        ALLOW_RESPECT_IGNORE_NULLS,
+        ALLOW_RESPECT_IGNORE_NULLS
     }
 
     static {
@@ -3042,7 +3045,7 @@ public class BuiltinFunctions {
         addWindowFunction(LAG, LAG_IMPL, NO_FRAME_CLAUSE, HAS_LIST_ARG, ALLOW_RESPECT_IGNORE_NULLS);
         addWindowFunction(LAST_VALUE, LAST_VALUE_IMPL, HAS_LIST_ARG, ALLOW_RESPECT_IGNORE_NULLS);
         addWindowFunction(LEAD, LEAD_IMPL, NO_FRAME_CLAUSE, HAS_LIST_ARG, ALLOW_RESPECT_IGNORE_NULLS);
-        addWindowFunction(NTH_VALUE, NTH_VALUE_IMPL, HAS_LIST_ARG, ALLOW_RESPECT_IGNORE_NULLS);
+        addWindowFunction(NTH_VALUE, NTH_VALUE_IMPL, HAS_LIST_ARG, ALLOW_FROM_FIRST_LAST, ALLOW_RESPECT_IGNORE_NULLS);
         addWindowFunction(NTILE, NTILE_IMPL, NO_FRAME_CLAUSE, MATERIALIZE_PARTITION);
         addWindowFunction(PERCENT_RANK, PERCENT_RANK_IMPL, NO_FRAME_CLAUSE, INJECT_ORDER_ARGS, MATERIALIZE_PARTITION);
         addWindowFunction(RANK, RANK_IMPL, NO_FRAME_CLAUSE, INJECT_ORDER_ARGS);
diff --git a/asterixdb/pom.xml b/asterixdb/pom.xml
index 3e65e5f..0149810 100644
--- a/asterixdb/pom.xml
+++ b/asterixdb/pom.xml
@@ -568,6 +568,13 @@
           <groupId>org.codehaus.mojo</groupId>
           <artifactId>javacc-maven-plugin</artifactId>
           <version>2.6</version>
+          <dependencies>
+            <dependency>
+              <groupId>net.java.dev.javacc</groupId>
+              <artifactId>javacc</artifactId>
+              <version>6.1.2</version>
+            </dependency>
+          </dependencies>
         </plugin>
         <plugin>
           <groupId>net.revelc.code.formatter</groupId>