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/05/29 20:01:31 UTC

[asterixdb] branch master updated: [NO ISSUE][FUN] Support IGNORE NULLS in window functions

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 3346cea  [NO ISSUE][FUN] Support IGNORE NULLS in window functions
3346cea is described below

commit 3346cea1f72790858119c338cd05c658d0681e51
Author: Dmitry Lychagin <dm...@couchbase.com>
AuthorDate: Tue May 28 12:11:54 2019 -0700

    [NO ISSUE][FUN] Support IGNORE NULLS in window functions
    
    - user model changes: yes
    - storage format changes: no
    - interface changes: no
    
    Details:
    - Support IGNORE NULLS / RESPECT NULLS modifiers in window
      functions LEAD(), LAG(), FIRST_VALUE(), LAST_VALUE(), NTH_VALUE()
      (RESPECT NULLS is the default)
    - Add testcases for each function
    - Fix CollectionMemberResultType and PushAggregateIntoNestedSubplanRule
      to handle remaining non-list arguments
    
    Change-Id: I400c9c95bac9159efa17fca4e97047fd089eb931
    Reviewed-on: https://asterix-gerrit.ics.uci.edu/3407
    Contrib: Jenkins <je...@fulliautomatix.ics.uci.edu>
    Integration-Tests: Jenkins <je...@fulliautomatix.ics.uci.edu>
    Tested-by: Jenkins <je...@fulliautomatix.ics.uci.edu>
    Reviewed-by: Ali Alsuliman <al...@gmail.com>
---
 .../rules/PushAggregateIntoNestedSubplanRule.java  |   8 +-
 .../rules/SweepIllegalNonfunctionalFunctions.java  |  10 +-
 .../SqlppExpressionToPlanTranslator.java           | 157 ++++++++++++++++-----
 .../first_value_01/first_value_01.1.query.sqlpp    |  24 +++-
 .../window/lag_01/lag_01.1.query.sqlpp             |  32 ++++-
 .../window/lag_01/lag_01.2.query.sqlpp             |  32 ++++-
 .../window/lag_01/lag_01.3.query.sqlpp             |  32 ++++-
 .../last_value_01/last_value_01.1.query.sqlpp      |  28 +++-
 .../window/lead_01/lead_01.1.query.sqlpp           |  32 ++++-
 .../window/lead_01/lead_01.2.query.sqlpp           |  32 ++++-
 .../window/lead_01/lead_01.3.query.sqlpp           |  32 ++++-
 .../window/nth_value_01/nth_value_01.1.query.sqlpp |  28 +++-
 .../window/win_negative/win_negative.6.query.sqlpp |  12 +-
 .../window/win_negative/win_negative.7.query.sqlpp |  12 +-
 .../window/first_value_01/first_value_01.1.adm     |   4 +
 .../runtimets/results/window/lag_01/lag_01.1.adm   |  12 ++
 .../runtimets/results/window/lag_01/lag_01.2.adm   |  12 ++
 .../runtimets/results/window/lag_01/lag_01.3.adm   |  12 ++
 .../window/last_value_01/last_value_01.1.adm       |   8 ++
 .../runtimets/results/window/lead_01/lead_01.1.adm |  12 ++
 .../runtimets/results/window/lead_01/lead_01.2.adm |  12 ++
 .../runtimets/results/window/lead_01/lead_01.3.adm |  12 ++
 .../results/window/nth_value_01/nth_value_01.1.adm |   8 ++
 .../window/win_null_missing/win_null_missing.2.adm |  12 +-
 .../test/resources/runtimets/testsuite_sqlpp.xml   |  27 ++++
 .../asterix/common/exceptions/ErrorCode.java       |   1 +
 .../src/main/resources/asx_errormsg/en.properties  |   1 +
 .../lang/sqlpp/expression/WindowExpression.java    |  24 +++-
 .../visitor/SqlppWindowRewriteVisitor.java         |  13 +-
 .../lang/sqlpp/visitor/DeepCopyVisitor.java        |   2 +-
 .../SqlppCloneAndSubstituteVariablesVisitor.java   |   2 +-
 .../asterix-lang-sqlpp/src/main/javacc/SQLPP.jj    |  16 ++-
 .../asterix/om/functions/BuiltinFunctions.java     |  21 +--
 .../impl/CollectionMemberResultType.java           |   8 +-
 .../algebra/operators/logical/WindowOperator.java  |  54 ++++---
 .../visitors/IsomorphismOperatorVisitor.java       |   5 +-
 ...calOperatorDeepCopyWithNewVariablesVisitor.java |  11 +-
 .../logical/visitors/OperatorDeepCopyVisitor.java  |   8 +-
 .../visitors/SubstituteVariableVisitor.java        |  10 +-
 .../logical/visitors/UsedVariableVisitor.java      |  10 +-
 .../physical/AbstractWindowPOperator.java          |  17 ++-
 .../operators/physical/WindowPOperator.java        |  13 +-
 .../operators/physical/WindowStreamPOperator.java  |   3 +-
 .../LogicalOperatorPrettyPrintVisitor.java         |  11 +-
 .../LogicalOperatorPrettyPrintVisitorJson.java     |  12 +-
 .../core/utils/LogicalOperatorDotVisitor.java      |  12 +-
 .../algebricks/data/IBinaryBooleanInspector.java   |   1 -
 .../algebricks/data/IBinaryIntegerInspector.java   |   3 +-
 .../rules/ConsolidateWindowOperatorsRule.java      |   3 +-
 .../win/WindowNestedPlansPushRuntime.java          |  73 ++++++----
 .../win/WindowNestedPlansRuntimeFactory.java       |  13 +-
 51 files changed, 720 insertions(+), 229 deletions(-)

diff --git a/asterixdb/asterix-algebra/src/main/java/org/apache/asterix/optimizer/rules/PushAggregateIntoNestedSubplanRule.java b/asterixdb/asterix-algebra/src/main/java/org/apache/asterix/optimizer/rules/PushAggregateIntoNestedSubplanRule.java
index 3c97ec8..6497b88 100644
--- a/asterixdb/asterix-algebra/src/main/java/org/apache/asterix/optimizer/rules/PushAggregateIntoNestedSubplanRule.java
+++ b/asterixdb/asterix-algebra/src/main/java/org/apache/asterix/optimizer/rules/PushAggregateIntoNestedSubplanRule.java
@@ -331,10 +331,16 @@ public class PushAggregateIntoNestedSubplanRule implements IAlgebraicRewriteRule
                     AggregateFunctionCallExpression newAggFun = BuiltinFunctions
                             .makeAggregateFunctionExpression(aggFun.getFunctionIdentifier(), new ArrayList<>());
                     newAggFun.setSourceLocation(oldAggExpr.getSourceLocation());
-                    for (Mutable<ILogicalExpression> arg : oldAggExpr.getArguments()) {
+                    List<Mutable<ILogicalExpression>> oldAggArgs = oldAggExpr.getArguments();
+                    for (Mutable<ILogicalExpression> arg : oldAggArgs) {
                         ILogicalExpression cloned = arg.getValue().cloneExpression();
                         newAggFun.getArguments().add(new MutableObject<>(cloned));
                     }
+                    List<Mutable<ILogicalExpression>> aggFunArgs = aggFun.getArguments();
+                    for (int k = oldAggArgs.size(), ln = aggFunArgs.size(); k < ln; k++) {
+                        ILogicalExpression cloned = aggFunArgs.get(k).getValue().cloneExpression();
+                        newAggFun.getArguments().add(new MutableObject<>(cloned));
+                    }
                     aggOp.getVariables().add(newAggVar);
                     aggOp.getExpressions().add(new MutableObject<>(newAggFun));
                     context.computeAndSetTypeEnvironmentForOperator(aggOp);
diff --git a/asterixdb/asterix-algebra/src/main/java/org/apache/asterix/optimizer/rules/SweepIllegalNonfunctionalFunctions.java b/asterixdb/asterix-algebra/src/main/java/org/apache/asterix/optimizer/rules/SweepIllegalNonfunctionalFunctions.java
index 2d5e11e..9bfa8fb 100644
--- a/asterixdb/asterix-algebra/src/main/java/org/apache/asterix/optimizer/rules/SweepIllegalNonfunctionalFunctions.java
+++ b/asterixdb/asterix-algebra/src/main/java/org/apache/asterix/optimizer/rules/SweepIllegalNonfunctionalFunctions.java
@@ -340,9 +340,13 @@ public class SweepIllegalNonfunctionalFunctions implements IAlgebraicRewriteRule
             for (Mutable<ILogicalExpression> me : op.getFrameExcludeExpressions()) {
                 sweepExpression(me.getValue());
             }
-            ILogicalExpression frameOffset = op.getFrameOffset().getValue();
-            if (frameOffset != null) {
-                sweepExpression(frameOffset);
+            ILogicalExpression frameExcludeUnaryExpr = op.getFrameExcludeUnaryExpression().getValue();
+            if (frameExcludeUnaryExpr != null) {
+                sweepExpression(frameExcludeUnaryExpr);
+            }
+            ILogicalExpression frameOffsetExpr = op.getFrameOffsetExpression().getValue();
+            if (frameOffsetExpr != null) {
+                sweepExpression(frameOffsetExpr);
             }
             for (Mutable<ILogicalExpression> me : op.getExpressions()) {
                 ILogicalExpression expr = me.getValue();
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 a6b9f59..9ba6d2c 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
@@ -33,10 +33,12 @@ 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.AbstractExpression;
 import org.apache.asterix.lang.common.base.Clause.ClauseType;
 import org.apache.asterix.lang.common.base.Expression;
 import org.apache.asterix.lang.common.base.Expression.Kind;
 import org.apache.asterix.lang.common.base.ILangExpression;
+import org.apache.asterix.lang.common.base.Literal;
 import org.apache.asterix.lang.common.clause.GroupbyClause;
 import org.apache.asterix.lang.common.clause.LetClause;
 import org.apache.asterix.lang.common.clause.OrderbyClause;
@@ -1054,6 +1056,8 @@ public class SqlppExpressionToPlanTranslator extends LangExpressionToPlanTransla
                 BuiltinFunctions.WindowFunctionProperty.NO_ORDER_CLAUSE);
         boolean prohibitFrameClause = isWin && BuiltinFunctions.builtinFunctionHasProperty(fi,
                 BuiltinFunctions.WindowFunctionProperty.NO_FRAME_CLAUSE);
+        boolean allowRespectIgnoreNulls = isWin && BuiltinFunctions.builtinFunctionHasProperty(fi,
+                BuiltinFunctions.WindowFunctionProperty.ALLOW_RESPECT_IGNORE_NULLS);
 
         Mutable<ILogicalOperator> currentOpRef = tupSource;
 
@@ -1079,6 +1083,8 @@ public class SqlppExpressionToPlanTranslator extends LangExpressionToPlanTransla
         List<Mutable<ILogicalExpression>> frameEndValidationExprRefs = null;
         List<Mutable<ILogicalExpression>> frameExcludeExprRefs = null;
         int frameExcludeNotStartIdx = -1;
+        AbstractLogicalExpression frameExcludeUnaryExpr = null;
+        AbstractLogicalExpression frameOffsetExpr = null;
 
         if (winExpr.hasOrderByList()) {
             if (prohibitOrderClause) {
@@ -1107,8 +1113,8 @@ public class SqlppExpressionToPlanTranslator extends LangExpressionToPlanTransla
         WindowExpression.FrameExclusionKind winFrameExclusionKind = null;
         WindowExpression.FrameBoundaryKind winFrameStartKind = null, winFrameEndKind = null;
         Expression winFrameStartExpr = null, winFrameEndExpr = null;
-        Expression winFrameOffsetExpr = null;
-        int winFrameMaxOjbects = -1;
+        AbstractExpression winFrameExcludeUnaryExpr = null, winFrameOffsetExpr = null;
+        int winFrameMaxOjbects = WindowOperator.FRAME_MAX_OBJECTS_UNLIMITED;
 
         if (winExpr.hasFrameDefinition()) {
             if (prohibitFrameClause) {
@@ -1130,6 +1136,9 @@ public class SqlppExpressionToPlanTranslator extends LangExpressionToPlanTransla
             winFrameExclusionKind = WindowExpression.FrameExclusionKind.NO_OTHERS;
         }
 
+        boolean respectNulls = !getBooleanModifier(winExpr.getIgnoreNulls(), false, allowRespectIgnoreNulls, sourceLoc,
+                "RESPECT/IGNORE NULLS", fs.getName());
+
         boolean makeRunningAgg = false, makeNestedAgg = false;
         FunctionIdentifier runningAggFunc = null, nestedAggFunc = null, winResultFunc = null, postWinResultFunc = null;
         Expression postWinExpr = null;
@@ -1138,43 +1147,79 @@ public class SqlppExpressionToPlanTranslator extends LangExpressionToPlanTransla
 
         if (isWinAgg) {
             makeNestedAgg = true;
-            if (BuiltinFunctions.LEAD_IMPL.equals(fi) || BuiltinFunctions.LAG_IMPL.equals(fi)) {
+            nestedAggArgs = new ArrayList<>(fargs.size());
+            nestedAggArgs.add(fargs.get(0));
+
+            boolean isLead;
+            if ((isLead = BuiltinFunctions.LEAD_IMPL.equals(fi)) || BuiltinFunctions.LAG_IMPL.equals(fi)) {
+                boolean isLag = !isLead;
+
                 int argCount = fargs.size();
-                if (argCount < 1 || argCount > 3) {
+                // LEAD_IMPL() and LAG_IMPL() have one extra (last) argument introduced by SqlppWindowRewriteVisitor
+                // which is a copy of the original first argument
+                if (argCount < 2 || argCount > 4) {
                     throw new CompilationException(ErrorCode.COMPILATION_INVALID_NUM_OF_ARGS, sourceLoc, fi.getName());
                 }
+
                 winFrameMode = WindowExpression.FrameMode.ROWS;
                 winFrameExclusionKind = WindowExpression.FrameExclusionKind.NO_OTHERS;
-                winFrameStartKind = winFrameEndKind =
-                        BuiltinFunctions.LEAD_IMPL.equals(fi) ? WindowExpression.FrameBoundaryKind.BOUNDED_FOLLOWING
-                                : WindowExpression.FrameBoundaryKind.BOUNDED_PRECEDING;
-                winFrameStartExpr = argCount > 1 ? fargs.get(1) : new LiteralExpr(new IntegerLiteral(1));
-                winFrameEndExpr = (Expression) SqlppRewriteUtil.deepCopy(winFrameStartExpr);
-                // if lead/lag default expression is specified
-                // then use local-first-element() because it returns SYSTEM_NULL if the list is empty,
-                // otherwise (no default expression) use first-element() which returns NULL if the list is empty
-                if (argCount > 2) {
+
+                if (respectNulls) {
+                    winFrameStartKind = winFrameEndKind = isLead ? WindowExpression.FrameBoundaryKind.BOUNDED_FOLLOWING
+                            : WindowExpression.FrameBoundaryKind.BOUNDED_PRECEDING;
+                    winFrameStartExpr = argCount == 2 ? new LiteralExpr(new IntegerLiteral(1)) : fargs.get(1);
+                    winFrameEndExpr = (Expression) SqlppRewriteUtil.deepCopy(winFrameStartExpr);
+                    winFrameMaxOjbects = 1;
+                } else {
+                    // IGNORE NULLS
+                    if (isLag) {
+                        // reverse order for LAG()
+                        for (Pair<OrderOperator.IOrder, Mutable<ILogicalExpression>> orderExprPair : orderExprListOut) {
+                            orderExprPair.setFirst(reverseOrder(orderExprPair.getFirst()));
+                        }
+                    }
+                    winFrameStartKind = WindowExpression.FrameBoundaryKind.BOUNDED_FOLLOWING;
+                    winFrameStartExpr = new LiteralExpr(new IntegerLiteral(1));
+                    winFrameEndKind = WindowExpression.FrameBoundaryKind.UNBOUNDED_FOLLOWING;
+                    Expression fargLast = fargs.get(fargs.size() - 1);
+                    winFrameExcludeUnaryExpr = createCallExpr(BuiltinFunctions.IS_UNKNOWN, fargLast, sourceLoc);
+                    if (argCount > 2) {
+                        winFrameOffsetExpr =
+                                createOperatorExpr(fargs.get(1), OperatorType.MINUS, new IntegerLiteral(1), sourceLoc);
+                    }
+                }
+                if (argCount < 4) {
+                    nestedAggFunc = BuiltinFunctions.SCALAR_FIRST_ELEMENT;
+                } else {
+                    // return SYSTEM_NULL if required offset is not reached
                     nestedAggFunc = BuiltinFunctions.SCALAR_LOCAL_FIRST_ELEMENT;
                     postWinResultFunc = BuiltinFunctions.IF_SYSTEM_NULL;
                     postWinExpr = fargs.get(2);
-                } else {
-                    nestedAggFunc = BuiltinFunctions.SCALAR_FIRST_ELEMENT;
                 }
-                winFrameMaxOjbects = 1;
             } else if (BuiltinFunctions.FIRST_VALUE_IMPL.equals(fi)) {
                 nestedAggFunc = BuiltinFunctions.SCALAR_FIRST_ELEMENT;
-                winFrameMaxOjbects = 1;
+                if (respectNulls) {
+                    winFrameMaxOjbects = 1;
+                } else {
+                    Expression fargLast = fargs.get(fargs.size() - 1);
+                    winFrameExcludeUnaryExpr = createCallExpr(BuiltinFunctions.IS_UNKNOWN, fargLast, sourceLoc);
+                }
             } else if (BuiltinFunctions.LAST_VALUE_IMPL.equals(fi)) {
                 nestedAggFunc = BuiltinFunctions.SCALAR_LAST_ELEMENT;
+                if (!respectNulls) {
+                    Expression fargLast = fargs.get(fargs.size() - 1);
+                    winFrameExcludeUnaryExpr = createCallExpr(BuiltinFunctions.IS_UNKNOWN, fargLast, sourceLoc);
+                }
             } else if (BuiltinFunctions.NTH_VALUE_IMPL.equals(fi)) {
                 nestedAggFunc = BuiltinFunctions.SCALAR_FIRST_ELEMENT;
-                winFrameMaxOjbects = 1;
-                OperatorExpr opExpr = new OperatorExpr();
-                opExpr.addOperand(fargs.get(1));
-                opExpr.addOperator(OperatorType.MINUS);
-                opExpr.addOperand(new LiteralExpr(new IntegerLiteral(1)));
-                opExpr.setSourceLocation(sourceLoc);
-                winFrameOffsetExpr = opExpr;
+                if (respectNulls) {
+                    winFrameMaxOjbects = 1;
+                } else {
+                    Expression fargLast = fargs.get(fargs.size() - 1);
+                    winFrameExcludeUnaryExpr = createCallExpr(BuiltinFunctions.IS_UNKNOWN, fargLast, sourceLoc);
+                }
+                winFrameOffsetExpr =
+                        createOperatorExpr(fargs.get(1), OperatorType.MINUS, new IntegerLiteral(1), sourceLoc);
             } else if (BuiltinFunctions.RATIO_TO_REPORT_IMPL.equals(fi)) {
                 // ratio_to_report(x) over (...) --> x / sum(x) over (...)
                 nestedAggFunc = BuiltinFunctions.SCALAR_SQL_SUM;
@@ -1184,7 +1229,6 @@ public class SqlppExpressionToPlanTranslator extends LangExpressionToPlanTransla
             } else {
                 throw new CompilationException(ErrorCode.COMPILATION_ILLEGAL_STATE, sourceLoc, fi.getName());
             }
-            nestedAggArgs = mkSingletonArrayList(fargs.get(0));
         } else if (isWin) {
             makeRunningAgg = true;
             if (BuiltinFunctions.CUME_DIST_IMPL.equals(fi)) {
@@ -1253,19 +1297,28 @@ public class SqlppExpressionToPlanTranslator extends LangExpressionToPlanTransla
                     currentOpRef = new MutableObject<>(frameEndResult.first);
                 }
             }
-        }
 
-        AbstractLogicalExpression frameOffsetExpr = null;
-        if (winFrameOffsetExpr != null) {
-            Pair<ILogicalOperator, LogicalVariable> frameOffsetResult = winFrameOffsetExpr.accept(this, currentOpRef);
-            frameOffsetExpr = new VariableReferenceExpression(frameOffsetResult.second);
-            frameOffsetExpr.setSourceLocation(sourceLoc);
-            currentOpRef = new MutableObject<>(frameOffsetResult.first);
+            if (winFrameExcludeUnaryExpr != null) {
+                Pair<ILogicalOperator, LogicalVariable> frameExcludeUnaryResult =
+                        winFrameExcludeUnaryExpr.accept(this, currentOpRef);
+                frameExcludeUnaryExpr = new VariableReferenceExpression(frameExcludeUnaryResult.second);
+                frameExcludeUnaryExpr.setSourceLocation(sourceLoc);
+                currentOpRef = new MutableObject<>(frameExcludeUnaryResult.first);
+            }
+
+            if (winFrameOffsetExpr != null) {
+                Pair<ILogicalOperator, LogicalVariable> frameOffsetResult =
+                        winFrameOffsetExpr.accept(this, currentOpRef);
+                frameOffsetExpr = new VariableReferenceExpression(frameOffsetResult.second);
+                frameOffsetExpr.setSourceLocation(sourceLoc);
+                currentOpRef = new MutableObject<>(frameOffsetResult.first);
+            }
         }
 
         WindowOperator winOp = new WindowOperator(partExprListOut, orderExprListOut, frameValueExprRefs,
                 frameStartExprRefs, frameStartValidationExprRefs, frameEndExprRefs, frameEndValidationExprRefs,
-                frameExcludeExprRefs, frameExcludeNotStartIdx, frameOffsetExpr, winFrameMaxOjbects);
+                frameExcludeExprRefs, frameExcludeNotStartIdx, frameExcludeUnaryExpr, frameOffsetExpr,
+                winFrameMaxOjbects);
         winOp.setSourceLocation(sourceLoc);
 
         LogicalVariable runningAggResultVar = null, nestedAggResultVar = null;
@@ -1609,4 +1662,42 @@ public class SqlppExpressionToPlanTranslator extends LangExpressionToPlanTransla
         }
         return winOp;
     }
+
+    private boolean getBooleanModifier(Boolean value, boolean defaultValue, boolean isAllowed, SourceLocation sourceLoc,
+            String displayName, String funcName) throws CompilationException {
+        if (isAllowed) {
+            return value != null ? value : defaultValue;
+        }
+        if (value != null) {
+            throw new CompilationException(ErrorCode.INVALID_FUNCTION_MODIFIER, sourceLoc, displayName, funcName);
+        }
+        return defaultValue;
+    }
+
+    private CallExpr createCallExpr(FunctionIdentifier func, Expression arg, SourceLocation sourceLoc) {
+        CallExpr callExpr = new CallExpr(new FunctionSignature(func), mkSingletonArrayList(arg));
+        callExpr.setSourceLocation(sourceLoc);
+        return callExpr;
+    }
+
+    private AbstractExpression createOperatorExpr(Expression arg1, OperatorType opType, Literal arg2,
+            SourceLocation sourceLoc) {
+        OperatorExpr opExpr = new OperatorExpr();
+        opExpr.addOperand(arg1);
+        opExpr.addOperator(opType);
+        opExpr.addOperand(new LiteralExpr(arg2));
+        opExpr.setSourceLocation(sourceLoc);
+        return opExpr;
+    }
+
+    private static OrderOperator.IOrder reverseOrder(OrderOperator.IOrder order) throws CompilationException {
+        switch (order.getKind()) {
+            case ASC:
+                return OrderOperator.DESC_ORDER;
+            case DESC:
+                return OrderOperator.ASC_ORDER;
+            default:
+                throw new CompilationException(ErrorCode.COMPILATION_ERROR);
+        }
+    }
 }
diff --git a/hyracks-fullstack/algebricks/algebricks-data/src/main/java/org/apache/hyracks/algebricks/data/IBinaryBooleanInspector.java b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/window/first_value_01/first_value_01.1.query.sqlpp
similarity index 58%
copy from hyracks-fullstack/algebricks/algebricks-data/src/main/java/org/apache/hyracks/algebricks/data/IBinaryBooleanInspector.java
copy to asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/window/first_value_01/first_value_01.1.query.sqlpp
index d25ea82..5836ad0 100644
--- a/hyracks-fullstack/algebricks/algebricks-data/src/main/java/org/apache/hyracks/algebricks/data/IBinaryBooleanInspector.java
+++ b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/window/first_value_01/first_value_01.1.query.sqlpp
@@ -16,12 +16,22 @@
  * specific language governing permissions and limitations
  * under the License.
  */
-package org.apache.hyracks.algebricks.data;
-
-import org.apache.hyracks.api.exceptions.HyracksDataException;
+/*
+ * Description  : FIRST_VALUE() RESPECT / IGNORE NULLS
+ * Expected Res : SUCCESS
+ */
 
-@FunctionalInterface
-public interface IBinaryBooleanInspector {
+from [
+  {                   "y": 1, "p": 0 },
+  { "x": "a",         "y": 2, "p": 0 },
 
-    boolean getBooleanValue(byte[] bytes, int offset, int length) throws HyracksDataException;
-}
+  { "x": null,        "y": 3, "p": 1 },
+  { "x": "b",         "y": 4, "p": 1 }
+] t
+select
+  first_value(x) respect nulls over (partition by p order by y range between unbounded preceding and unbounded following)
+    as first_value_respect,
+  first_value(x) ignore nulls over (partition by p order by y range between unbounded preceding and unbounded following)
+    as first_value_ignore,
+  x, y, p
+order by y
\ No newline at end of file
diff --git a/hyracks-fullstack/algebricks/algebricks-data/src/main/java/org/apache/hyracks/algebricks/data/IBinaryBooleanInspector.java b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/window/lag_01/lag_01.1.query.sqlpp
similarity index 51%
copy from hyracks-fullstack/algebricks/algebricks-data/src/main/java/org/apache/hyracks/algebricks/data/IBinaryBooleanInspector.java
copy to asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/window/lag_01/lag_01.1.query.sqlpp
index d25ea82..b978d85 100644
--- a/hyracks-fullstack/algebricks/algebricks-data/src/main/java/org/apache/hyracks/algebricks/data/IBinaryBooleanInspector.java
+++ b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/window/lag_01/lag_01.1.query.sqlpp
@@ -16,12 +16,30 @@
  * specific language governing permissions and limitations
  * under the License.
  */
-package org.apache.hyracks.algebricks.data;
-
-import org.apache.hyracks.api.exceptions.HyracksDataException;
+/*
+ * Description  : LAG() RESPECT / IGNORE NULLS
+ * Expected Res : SUCCESS
+ */
 
-@FunctionalInterface
-public interface IBinaryBooleanInspector {
+from [
+  { "x": "a",         "y": 1,  "p": 0 },
+  {                   "y": 2,  "p": 0 },
+  { "x": "b",         "y": 3,  "p": 0 },
+  { "x": null,        "y": 4,  "p": 0 },
+  { "x": "c",         "y": 5,  "p": 0 },
+  {                   "y": 6,  "p": 0 },
 
-    boolean getBooleanValue(byte[] bytes, int offset, int length) throws HyracksDataException;
-}
+  { "x": "a",         "y": 7,  "p": 1 },
+  { "x": null,        "y": 8,  "p": 1 },
+  { "x": "b",         "y": 9,  "p": 1 },
+  {                   "y": 10, "p": 1 },
+  { "x": "c",         "y": 11, "p": 1 },
+  { "x": null,        "y": 12, "p": 1 }
+] t
+select
+  lag(x) RESPECT NULLS over (partition by p order by y)
+    as lag_1_respect,
+  lag(x) IGNORE NULLS over (partition by p order by y)
+    as lag_1_ignore,
+  x, y, p
+order by y
\ No newline at end of file
diff --git a/hyracks-fullstack/algebricks/algebricks-data/src/main/java/org/apache/hyracks/algebricks/data/IBinaryBooleanInspector.java b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/window/lag_01/lag_01.2.query.sqlpp
similarity index 51%
copy from hyracks-fullstack/algebricks/algebricks-data/src/main/java/org/apache/hyracks/algebricks/data/IBinaryBooleanInspector.java
copy to asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/window/lag_01/lag_01.2.query.sqlpp
index d25ea82..be265ff 100644
--- a/hyracks-fullstack/algebricks/algebricks-data/src/main/java/org/apache/hyracks/algebricks/data/IBinaryBooleanInspector.java
+++ b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/window/lag_01/lag_01.2.query.sqlpp
@@ -16,12 +16,30 @@
  * specific language governing permissions and limitations
  * under the License.
  */
-package org.apache.hyracks.algebricks.data;
-
-import org.apache.hyracks.api.exceptions.HyracksDataException;
+/*
+ * Description  : LAG() RESPECT / IGNORE NULLS
+ * Expected Res : SUCCESS
+ */
 
-@FunctionalInterface
-public interface IBinaryBooleanInspector {
+from [
+  { "x": "a",         "y": 1,  "p": 0 },
+  {                   "y": 2,  "p": 0 },
+  { "x": "b",         "y": 3,  "p": 0 },
+  { "x": null,        "y": 4,  "p": 0 },
+  { "x": "c",         "y": 5,  "p": 0 },
+  {                   "y": 6,  "p": 0 },
 
-    boolean getBooleanValue(byte[] bytes, int offset, int length) throws HyracksDataException;
-}
+  { "x": "a",         "y": 7,  "p": 1 },
+  { "x": null,        "y": 8,  "p": 1 },
+  { "x": "b",         "y": 9,  "p": 1 },
+  {                   "y": 10, "p": 1 },
+  { "x": "c",         "y": 11, "p": 1 },
+  { "x": null,        "y": 12, "p": 1 }
+] t
+select
+  lag(x, 2) RESPECT NULLS over (partition by p order by y)
+    as lag_2_respect,
+  lag(x, 2) IGNORE NULLS over (partition by p order by y)
+    as lag_2_ignore,
+  x, y, p
+order by y
\ No newline at end of file
diff --git a/hyracks-fullstack/algebricks/algebricks-data/src/main/java/org/apache/hyracks/algebricks/data/IBinaryBooleanInspector.java b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/window/lag_01/lag_01.3.query.sqlpp
similarity index 50%
copy from hyracks-fullstack/algebricks/algebricks-data/src/main/java/org/apache/hyracks/algebricks/data/IBinaryBooleanInspector.java
copy to asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/window/lag_01/lag_01.3.query.sqlpp
index d25ea82..c463937 100644
--- a/hyracks-fullstack/algebricks/algebricks-data/src/main/java/org/apache/hyracks/algebricks/data/IBinaryBooleanInspector.java
+++ b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/window/lag_01/lag_01.3.query.sqlpp
@@ -16,12 +16,30 @@
  * specific language governing permissions and limitations
  * under the License.
  */
-package org.apache.hyracks.algebricks.data;
-
-import org.apache.hyracks.api.exceptions.HyracksDataException;
+/*
+ * Description  : LAG() RESPECT / IGNORE NULLS
+ * Expected Res : SUCCESS
+ */
 
-@FunctionalInterface
-public interface IBinaryBooleanInspector {
+from [
+  { "x": "a",         "y": 1,  "p": 0 },
+  {                   "y": 2,  "p": 0 },
+  { "x": "b",         "y": 3,  "p": 0 },
+  { "x": null,        "y": 4,  "p": 0 },
+  { "x": "c",         "y": 5,  "p": 0 },
+  {                   "y": 6,  "p": 0 },
 
-    boolean getBooleanValue(byte[] bytes, int offset, int length) throws HyracksDataException;
-}
+  { "x": "a",         "y": 7,  "p": 1 },
+  { "x": null,        "y": 8,  "p": 1 },
+  { "x": "b",         "y": 9,  "p": 1 },
+  {                   "y": 10, "p": 1 },
+  { "x": "c",         "y": 11, "p": 1 },
+  { "x": null,        "y": 12, "p": 1 }
+] t
+select
+  lag(x, 2, "z") RESPECT NULLS over (partition by p order by y)
+    as lag_2_respect,
+  lag(x, 2, "z") IGNORE NULLS over (partition by p order by y)
+    as lag_2_ignore,
+  x, y, p
+order by y
\ No newline at end of file
diff --git a/hyracks-fullstack/algebricks/algebricks-data/src/main/java/org/apache/hyracks/algebricks/data/IBinaryBooleanInspector.java b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/window/last_value_01/last_value_01.1.query.sqlpp
similarity index 52%
copy from hyracks-fullstack/algebricks/algebricks-data/src/main/java/org/apache/hyracks/algebricks/data/IBinaryBooleanInspector.java
copy to asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/window/last_value_01/last_value_01.1.query.sqlpp
index d25ea82..2e2e5ff 100644
--- a/hyracks-fullstack/algebricks/algebricks-data/src/main/java/org/apache/hyracks/algebricks/data/IBinaryBooleanInspector.java
+++ b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/window/last_value_01/last_value_01.1.query.sqlpp
@@ -16,12 +16,26 @@
  * specific language governing permissions and limitations
  * under the License.
  */
-package org.apache.hyracks.algebricks.data;
-
-import org.apache.hyracks.api.exceptions.HyracksDataException;
+/*
+ * Description  : LAST_VALUE() RESPECT / IGNORE NULLS
+ * Expected Res : SUCCESS
+ */
 
-@FunctionalInterface
-public interface IBinaryBooleanInspector {
+from [
+  { "x": "a",         "y": 1, "p": 0 },
+  { "x": "b",         "y": 2, "p": 0 },
+  {                   "y": 3, "p": 0 },
+  { "x": null,        "y": 4, "p": 0 },
 
-    boolean getBooleanValue(byte[] bytes, int offset, int length) throws HyracksDataException;
-}
+  { "x": "a",         "y": 5, "p": 1 },
+  { "x": "b",         "y": 6, "p": 1 },
+  { "x": null,        "y": 7, "p": 1 },
+  {                   "y": 8, "p": 1 }
+] t
+select
+  last_value(x) respect nulls over (partition by p order by y range between unbounded preceding and unbounded following)
+    as last_value_respect,
+  last_value(x) ignore nulls over (partition by p order by y range between unbounded preceding and unbounded following)
+    as last_value_ignore,
+  x, y, p
+order by y
\ No newline at end of file
diff --git a/hyracks-fullstack/algebricks/algebricks-data/src/main/java/org/apache/hyracks/algebricks/data/IBinaryBooleanInspector.java b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/window/lead_01/lead_01.1.query.sqlpp
similarity index 51%
copy from hyracks-fullstack/algebricks/algebricks-data/src/main/java/org/apache/hyracks/algebricks/data/IBinaryBooleanInspector.java
copy to asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/window/lead_01/lead_01.1.query.sqlpp
index d25ea82..9226bf9 100644
--- a/hyracks-fullstack/algebricks/algebricks-data/src/main/java/org/apache/hyracks/algebricks/data/IBinaryBooleanInspector.java
+++ b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/window/lead_01/lead_01.1.query.sqlpp
@@ -16,12 +16,30 @@
  * specific language governing permissions and limitations
  * under the License.
  */
-package org.apache.hyracks.algebricks.data;
-
-import org.apache.hyracks.api.exceptions.HyracksDataException;
+/*
+ * Description  : LEAD() RESPECT / IGNORE NULLS
+ * Expected Res : SUCCESS
+ */
 
-@FunctionalInterface
-public interface IBinaryBooleanInspector {
+from [
+  { "x": "a",         "y": 1,  "p": 0 },
+  {                   "y": 2,  "p": 0 },
+  { "x": "b",         "y": 3,  "p": 0 },
+  { "x": null,        "y": 4,  "p": 0 },
+  { "x": "c",         "y": 5,  "p": 0 },
+  {                   "y": 6,  "p": 0 },
 
-    boolean getBooleanValue(byte[] bytes, int offset, int length) throws HyracksDataException;
-}
+  { "x": "a",         "y": 7,  "p": 1 },
+  { "x": null,        "y": 8,  "p": 1 },
+  { "x": "b",         "y": 9,  "p": 1 },
+  {                   "y": 10, "p": 1 },
+  { "x": "c",         "y": 11, "p": 1 },
+  { "x": null,        "y": 12, "p": 1 }
+] t
+select
+  lead(x) respect nulls over (partition by p order by y)
+    as lead_1_respect,
+  lead(x) ignore nulls over (partition by p order by y)
+    as lead_1_ignore,
+  x, y, p
+order by y
\ No newline at end of file
diff --git a/hyracks-fullstack/algebricks/algebricks-data/src/main/java/org/apache/hyracks/algebricks/data/IBinaryBooleanInspector.java b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/window/lead_01/lead_01.2.query.sqlpp
similarity index 51%
copy from hyracks-fullstack/algebricks/algebricks-data/src/main/java/org/apache/hyracks/algebricks/data/IBinaryBooleanInspector.java
copy to asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/window/lead_01/lead_01.2.query.sqlpp
index d25ea82..c9023b8 100644
--- a/hyracks-fullstack/algebricks/algebricks-data/src/main/java/org/apache/hyracks/algebricks/data/IBinaryBooleanInspector.java
+++ b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/window/lead_01/lead_01.2.query.sqlpp
@@ -16,12 +16,30 @@
  * specific language governing permissions and limitations
  * under the License.
  */
-package org.apache.hyracks.algebricks.data;
-
-import org.apache.hyracks.api.exceptions.HyracksDataException;
+/*
+ * Description  : LEAD() RESPECT / IGNORE NULLS
+ * Expected Res : SUCCESS
+ */
 
-@FunctionalInterface
-public interface IBinaryBooleanInspector {
+from [
+  { "x": "a",         "y": 1,  "p": 0 },
+  {                   "y": 2,  "p": 0 },
+  { "x": "b",         "y": 3,  "p": 0 },
+  { "x": null,        "y": 4,  "p": 0 },
+  { "x": "c",         "y": 5,  "p": 0 },
+  {                   "y": 6,  "p": 0 },
 
-    boolean getBooleanValue(byte[] bytes, int offset, int length) throws HyracksDataException;
-}
+  { "x": "a",         "y": 7,  "p": 1 },
+  { "x": null,        "y": 8,  "p": 1 },
+  { "x": "b",         "y": 9,  "p": 1 },
+  {                   "y": 10, "p": 1 },
+  { "x": "c",         "y": 11, "p": 1 },
+  { "x": null,        "y": 12, "p": 1 }
+] t
+select
+  lead(x, 2) respect nulls over (partition by p order by y)
+    as lead_2_respect,
+  lead(x, 2) ignore nulls over (partition by p order by y)
+    as lead_2_ignore,
+  x, y, p
+order by y
\ No newline at end of file
diff --git a/hyracks-fullstack/algebricks/algebricks-data/src/main/java/org/apache/hyracks/algebricks/data/IBinaryBooleanInspector.java b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/window/lead_01/lead_01.3.query.sqlpp
similarity index 50%
copy from hyracks-fullstack/algebricks/algebricks-data/src/main/java/org/apache/hyracks/algebricks/data/IBinaryBooleanInspector.java
copy to asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/window/lead_01/lead_01.3.query.sqlpp
index d25ea82..4947bcb 100644
--- a/hyracks-fullstack/algebricks/algebricks-data/src/main/java/org/apache/hyracks/algebricks/data/IBinaryBooleanInspector.java
+++ b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/window/lead_01/lead_01.3.query.sqlpp
@@ -16,12 +16,30 @@
  * specific language governing permissions and limitations
  * under the License.
  */
-package org.apache.hyracks.algebricks.data;
-
-import org.apache.hyracks.api.exceptions.HyracksDataException;
+/*
+ * Description  : LEAD() RESPECT / IGNORE NULLS
+ * Expected Res : SUCCESS
+ */
 
-@FunctionalInterface
-public interface IBinaryBooleanInspector {
+from [
+  { "x": "a",         "y": 1,  "p": 0 },
+  {                   "y": 2,  "p": 0 },
+  { "x": "b",         "y": 3,  "p": 0 },
+  { "x": null,        "y": 4,  "p": 0 },
+  { "x": "c",         "y": 5,  "p": 0 },
+  {                   "y": 6,  "p": 0 },
 
-    boolean getBooleanValue(byte[] bytes, int offset, int length) throws HyracksDataException;
-}
+  { "x": "a",         "y": 7,  "p": 1 },
+  { "x": null,        "y": 8,  "p": 1 },
+  { "x": "b",         "y": 9,  "p": 1 },
+  {                   "y": 10, "p": 1 },
+  { "x": "c",         "y": 11, "p": 1 },
+  { "x": null,        "y": 12, "p": 1 }
+] t
+select
+  lead(x, 2, "z") respect nulls over (partition by p order by y)
+    as lead_2_respect,
+  lead(x, 2, "z") ignore nulls over (partition by p order by y)
+    as lead_2_ignore,
+  x, y, p
+order by y
\ No newline at end of file
diff --git a/hyracks-fullstack/algebricks/algebricks-data/src/main/java/org/apache/hyracks/algebricks/data/IBinaryBooleanInspector.java b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/window/nth_value_01/nth_value_01.1.query.sqlpp
similarity index 52%
copy from hyracks-fullstack/algebricks/algebricks-data/src/main/java/org/apache/hyracks/algebricks/data/IBinaryBooleanInspector.java
copy to asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/window/nth_value_01/nth_value_01.1.query.sqlpp
index d25ea82..b1846d6 100644
--- a/hyracks-fullstack/algebricks/algebricks-data/src/main/java/org/apache/hyracks/algebricks/data/IBinaryBooleanInspector.java
+++ b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/window/nth_value_01/nth_value_01.1.query.sqlpp
@@ -16,12 +16,26 @@
  * specific language governing permissions and limitations
  * under the License.
  */
-package org.apache.hyracks.algebricks.data;
-
-import org.apache.hyracks.api.exceptions.HyracksDataException;
+/*
+ * Description  : NTH_VALUE() RESPECT / IGNORE NULLS
+ * Expected Res : SUCCESS
+ */
 
-@FunctionalInterface
-public interface IBinaryBooleanInspector {
+from [
+  { "x": "a",         "y": 1, "p": 0 },
+  {                   "y": 2, "p": 0 },
+  { "x": null,        "y": 3, "p": 0 },
+  { "x": "b",         "y": 4, "p": 0 },
 
-    boolean getBooleanValue(byte[] bytes, int offset, int length) throws HyracksDataException;
-}
+  { "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) respect nulls over (partition by p order by y range between unbounded preceding and unbounded following)
+    as nth_value_respect,
+  nth_value(x, 2) 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/hyracks-fullstack/algebricks/algebricks-data/src/main/java/org/apache/hyracks/algebricks/data/IBinaryIntegerInspector.java b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/window/win_negative/win_negative.6.query.sqlpp
similarity index 76%
copy from hyracks-fullstack/algebricks/algebricks-data/src/main/java/org/apache/hyracks/algebricks/data/IBinaryIntegerInspector.java
copy to asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/window/win_negative/win_negative.6.query.sqlpp
index 089619a..605c524 100644
--- a/hyracks-fullstack/algebricks/algebricks-data/src/main/java/org/apache/hyracks/algebricks/data/IBinaryIntegerInspector.java
+++ b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/window/win_negative/win_negative.6.query.sqlpp
@@ -16,10 +16,10 @@
  * specific language governing permissions and limitations
  * under the License.
  */
-package org.apache.hyracks.algebricks.data;
-
-import org.apache.hyracks.api.exceptions.HyracksDataException;
+/*
+ * Description  : Function that doesn't support RESPECT/IGNORE NULLS
+ * Expected Res : FAILURE
+ */
 
-public interface IBinaryIntegerInspector {
-    public int getIntegerValue(byte[] bytes, int offset, int length) throws HyracksDataException;
-}
+from range(1, 4) x
+select value rank() respect nulls over (order by x)
\ No newline at end of file
diff --git a/hyracks-fullstack/algebricks/algebricks-data/src/main/java/org/apache/hyracks/algebricks/data/IBinaryIntegerInspector.java b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/window/win_negative/win_negative.7.query.sqlpp
similarity index 76%
copy from hyracks-fullstack/algebricks/algebricks-data/src/main/java/org/apache/hyracks/algebricks/data/IBinaryIntegerInspector.java
copy to asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/window/win_negative/win_negative.7.query.sqlpp
index 089619a..f655d35 100644
--- a/hyracks-fullstack/algebricks/algebricks-data/src/main/java/org/apache/hyracks/algebricks/data/IBinaryIntegerInspector.java
+++ b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/window/win_negative/win_negative.7.query.sqlpp
@@ -16,10 +16,10 @@
  * specific language governing permissions and limitations
  * under the License.
  */
-package org.apache.hyracks.algebricks.data;
-
-import org.apache.hyracks.api.exceptions.HyracksDataException;
+/*
+ * Description  : Function that doesn't support RESPECT/IGNORE NULLS
+ * Expected Res : FAILURE
+ */
 
-public interface IBinaryIntegerInspector {
-    public int getIntegerValue(byte[] bytes, int offset, int length) throws HyracksDataException;
-}
+from range(1, 4) x
+select value dense_rank() ignore nulls over (order by x)
\ No newline at end of file
diff --git a/asterixdb/asterix-app/src/test/resources/runtimets/results/window/first_value_01/first_value_01.1.adm b/asterixdb/asterix-app/src/test/resources/runtimets/results/window/first_value_01/first_value_01.1.adm
new file mode 100644
index 0000000..5839925
--- /dev/null
+++ b/asterixdb/asterix-app/src/test/resources/runtimets/results/window/first_value_01/first_value_01.1.adm
@@ -0,0 +1,4 @@
+{ "first_value_ignore": "a", "y": 1, "p": 0 }
+{ "first_value_ignore": "a", "x": "a", "y": 2, "p": 0 }
+{ "first_value_respect": null, "first_value_ignore": "b", "x": null, "y": 3, "p": 1 }
+{ "first_value_respect": null, "first_value_ignore": "b", "x": "b", "y": 4, "p": 1 }
\ No newline at end of file
diff --git a/asterixdb/asterix-app/src/test/resources/runtimets/results/window/lag_01/lag_01.1.adm b/asterixdb/asterix-app/src/test/resources/runtimets/results/window/lag_01/lag_01.1.adm
new file mode 100644
index 0000000..48f50df
--- /dev/null
+++ b/asterixdb/asterix-app/src/test/resources/runtimets/results/window/lag_01/lag_01.1.adm
@@ -0,0 +1,12 @@
+{ "lag_1_respect": null, "lag_1_ignore": null, "x": "a", "y": 1, "p": 0 }
+{ "lag_1_respect": "a", "lag_1_ignore": "a", "y": 2, "p": 0 }
+{ "lag_1_ignore": "a", "x": "b", "y": 3, "p": 0 }
+{ "lag_1_respect": "b", "lag_1_ignore": "b", "x": null, "y": 4, "p": 0 }
+{ "lag_1_respect": null, "lag_1_ignore": "b", "x": "c", "y": 5, "p": 0 }
+{ "lag_1_respect": "c", "lag_1_ignore": "c", "y": 6, "p": 0 }
+{ "lag_1_respect": null, "lag_1_ignore": null, "x": "a", "y": 7, "p": 1 }
+{ "lag_1_respect": "a", "lag_1_ignore": "a", "x": null, "y": 8, "p": 1 }
+{ "lag_1_respect": null, "lag_1_ignore": "a", "x": "b", "y": 9, "p": 1 }
+{ "lag_1_respect": "b", "lag_1_ignore": "b", "y": 10, "p": 1 }
+{ "lag_1_ignore": "b", "x": "c", "y": 11, "p": 1 }
+{ "lag_1_respect": "c", "lag_1_ignore": "c", "x": null, "y": 12, "p": 1 }
\ No newline at end of file
diff --git a/asterixdb/asterix-app/src/test/resources/runtimets/results/window/lag_01/lag_01.2.adm b/asterixdb/asterix-app/src/test/resources/runtimets/results/window/lag_01/lag_01.2.adm
new file mode 100644
index 0000000..e6c4ff3
--- /dev/null
+++ b/asterixdb/asterix-app/src/test/resources/runtimets/results/window/lag_01/lag_01.2.adm
@@ -0,0 +1,12 @@
+{ "lag_2_respect": null, "lag_2_ignore": null, "x": "a", "y": 1, "p": 0 }
+{ "lag_2_respect": null, "lag_2_ignore": null, "y": 2, "p": 0 }
+{ "lag_2_respect": "a", "lag_2_ignore": null, "x": "b", "y": 3, "p": 0 }
+{ "lag_2_ignore": "a", "x": null, "y": 4, "p": 0 }
+{ "lag_2_respect": "b", "lag_2_ignore": "a", "x": "c", "y": 5, "p": 0 }
+{ "lag_2_respect": null, "lag_2_ignore": "b", "y": 6, "p": 0 }
+{ "lag_2_respect": null, "lag_2_ignore": null, "x": "a", "y": 7, "p": 1 }
+{ "lag_2_respect": null, "lag_2_ignore": null, "x": null, "y": 8, "p": 1 }
+{ "lag_2_respect": "a", "lag_2_ignore": null, "x": "b", "y": 9, "p": 1 }
+{ "lag_2_respect": null, "lag_2_ignore": "a", "y": 10, "p": 1 }
+{ "lag_2_respect": "b", "lag_2_ignore": "a", "x": "c", "y": 11, "p": 1 }
+{ "lag_2_ignore": "b", "x": null, "y": 12, "p": 1 }
\ No newline at end of file
diff --git a/asterixdb/asterix-app/src/test/resources/runtimets/results/window/lag_01/lag_01.3.adm b/asterixdb/asterix-app/src/test/resources/runtimets/results/window/lag_01/lag_01.3.adm
new file mode 100644
index 0000000..795627c
--- /dev/null
+++ b/asterixdb/asterix-app/src/test/resources/runtimets/results/window/lag_01/lag_01.3.adm
@@ -0,0 +1,12 @@
+{ "lag_2_respect": "z", "lag_2_ignore": "z", "x": "a", "y": 1, "p": 0 }
+{ "lag_2_respect": "z", "lag_2_ignore": "z", "y": 2, "p": 0 }
+{ "lag_2_respect": "a", "lag_2_ignore": "z", "x": "b", "y": 3, "p": 0 }
+{ "lag_2_ignore": "a", "x": null, "y": 4, "p": 0 }
+{ "lag_2_respect": "b", "lag_2_ignore": "a", "x": "c", "y": 5, "p": 0 }
+{ "lag_2_respect": null, "lag_2_ignore": "b", "y": 6, "p": 0 }
+{ "lag_2_respect": "z", "lag_2_ignore": "z", "x": "a", "y": 7, "p": 1 }
+{ "lag_2_respect": "z", "lag_2_ignore": "z", "x": null, "y": 8, "p": 1 }
+{ "lag_2_respect": "a", "lag_2_ignore": "z", "x": "b", "y": 9, "p": 1 }
+{ "lag_2_respect": null, "lag_2_ignore": "a", "y": 10, "p": 1 }
+{ "lag_2_respect": "b", "lag_2_ignore": "a", "x": "c", "y": 11, "p": 1 }
+{ "lag_2_ignore": "b", "x": null, "y": 12, "p": 1 }
\ No newline at end of file
diff --git a/asterixdb/asterix-app/src/test/resources/runtimets/results/window/last_value_01/last_value_01.1.adm b/asterixdb/asterix-app/src/test/resources/runtimets/results/window/last_value_01/last_value_01.1.adm
new file mode 100644
index 0000000..c7c8f7d
--- /dev/null
+++ b/asterixdb/asterix-app/src/test/resources/runtimets/results/window/last_value_01/last_value_01.1.adm
@@ -0,0 +1,8 @@
+{ "last_value_respect": null, "last_value_ignore": "b", "x": "a", "y": 1, "p": 0 }
+{ "last_value_respect": null, "last_value_ignore": "b", "x": "b", "y": 2, "p": 0 }
+{ "last_value_respect": null, "last_value_ignore": "b", "y": 3, "p": 0 }
+{ "last_value_respect": null, "last_value_ignore": "b", "x": null, "y": 4, "p": 0 }
+{ "last_value_ignore": "b", "x": "a", "y": 5, "p": 1 }
+{ "last_value_ignore": "b", "x": "b", "y": 6, "p": 1 }
+{ "last_value_ignore": "b", "x": null, "y": 7, "p": 1 }
+{ "last_value_ignore": "b", "y": 8, "p": 1 }
\ No newline at end of file
diff --git a/asterixdb/asterix-app/src/test/resources/runtimets/results/window/lead_01/lead_01.1.adm b/asterixdb/asterix-app/src/test/resources/runtimets/results/window/lead_01/lead_01.1.adm
new file mode 100644
index 0000000..8b0e3cf
--- /dev/null
+++ b/asterixdb/asterix-app/src/test/resources/runtimets/results/window/lead_01/lead_01.1.adm
@@ -0,0 +1,12 @@
+{ "lead_1_ignore": "b", "x": "a", "y": 1, "p": 0 }
+{ "lead_1_respect": "b", "lead_1_ignore": "b", "y": 2, "p": 0 }
+{ "lead_1_respect": null, "lead_1_ignore": "c", "x": "b", "y": 3, "p": 0 }
+{ "lead_1_respect": "c", "lead_1_ignore": "c", "x": null, "y": 4, "p": 0 }
+{ "lead_1_ignore": null, "x": "c", "y": 5, "p": 0 }
+{ "lead_1_respect": null, "lead_1_ignore": null, "y": 6, "p": 0 }
+{ "lead_1_respect": null, "lead_1_ignore": "b", "x": "a", "y": 7, "p": 1 }
+{ "lead_1_respect": "b", "lead_1_ignore": "b", "x": null, "y": 8, "p": 1 }
+{ "lead_1_ignore": "c", "x": "b", "y": 9, "p": 1 }
+{ "lead_1_respect": "c", "lead_1_ignore": "c", "y": 10, "p": 1 }
+{ "lead_1_respect": null, "lead_1_ignore": null, "x": "c", "y": 11, "p": 1 }
+{ "lead_1_respect": null, "lead_1_ignore": null, "x": null, "y": 12, "p": 1 }
\ No newline at end of file
diff --git a/asterixdb/asterix-app/src/test/resources/runtimets/results/window/lead_01/lead_01.2.adm b/asterixdb/asterix-app/src/test/resources/runtimets/results/window/lead_01/lead_01.2.adm
new file mode 100644
index 0000000..affe370
--- /dev/null
+++ b/asterixdb/asterix-app/src/test/resources/runtimets/results/window/lead_01/lead_01.2.adm
@@ -0,0 +1,12 @@
+{ "lead_2_respect": "b", "lead_2_ignore": "c", "x": "a", "y": 1, "p": 0 }
+{ "lead_2_respect": null, "lead_2_ignore": "c", "y": 2, "p": 0 }
+{ "lead_2_respect": "c", "lead_2_ignore": null, "x": "b", "y": 3, "p": 0 }
+{ "lead_2_ignore": null, "x": null, "y": 4, "p": 0 }
+{ "lead_2_respect": null, "lead_2_ignore": null, "x": "c", "y": 5, "p": 0 }
+{ "lead_2_respect": null, "lead_2_ignore": null, "y": 6, "p": 0 }
+{ "lead_2_respect": "b", "lead_2_ignore": "c", "x": "a", "y": 7, "p": 1 }
+{ "lead_2_ignore": "c", "x": null, "y": 8, "p": 1 }
+{ "lead_2_respect": "c", "lead_2_ignore": null, "x": "b", "y": 9, "p": 1 }
+{ "lead_2_respect": null, "lead_2_ignore": null, "y": 10, "p": 1 }
+{ "lead_2_respect": null, "lead_2_ignore": null, "x": "c", "y": 11, "p": 1 }
+{ "lead_2_respect": null, "lead_2_ignore": null, "x": null, "y": 12, "p": 1 }
\ No newline at end of file
diff --git a/asterixdb/asterix-app/src/test/resources/runtimets/results/window/lead_01/lead_01.3.adm b/asterixdb/asterix-app/src/test/resources/runtimets/results/window/lead_01/lead_01.3.adm
new file mode 100644
index 0000000..1875beb
--- /dev/null
+++ b/asterixdb/asterix-app/src/test/resources/runtimets/results/window/lead_01/lead_01.3.adm
@@ -0,0 +1,12 @@
+{ "lead_2_respect": "b", "lead_2_ignore": "c", "x": "a", "y": 1, "p": 0 }
+{ "lead_2_respect": null, "lead_2_ignore": "c", "y": 2, "p": 0 }
+{ "lead_2_respect": "c", "lead_2_ignore": "z", "x": "b", "y": 3, "p": 0 }
+{ "lead_2_ignore": "z", "x": null, "y": 4, "p": 0 }
+{ "lead_2_respect": "z", "lead_2_ignore": "z", "x": "c", "y": 5, "p": 0 }
+{ "lead_2_respect": "z", "lead_2_ignore": "z", "y": 6, "p": 0 }
+{ "lead_2_respect": "b", "lead_2_ignore": "c", "x": "a", "y": 7, "p": 1 }
+{ "lead_2_ignore": "c", "x": null, "y": 8, "p": 1 }
+{ "lead_2_respect": "c", "lead_2_ignore": "z", "x": "b", "y": 9, "p": 1 }
+{ "lead_2_respect": null, "lead_2_ignore": "z", "y": 10, "p": 1 }
+{ "lead_2_respect": "z", "lead_2_ignore": "z", "x": "c", "y": 11, "p": 1 }
+{ "lead_2_respect": "z", "lead_2_ignore": "z", "x": null, "y": 12, "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.1.adm b/asterixdb/asterix-app/src/test/resources/runtimets/results/window/nth_value_01/nth_value_01.1.adm
new file mode 100644
index 0000000..0c47a99
--- /dev/null
+++ b/asterixdb/asterix-app/src/test/resources/runtimets/results/window/nth_value_01/nth_value_01.1.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_respect": null, "nth_value_ignore": "b", "x": "a", "y": 5, "p": 1 }
+{ "nth_value_respect": null, "nth_value_ignore": "b", "x": null, "y": 6, "p": 1 }
+{ "nth_value_respect": null, "nth_value_ignore": "b", "y": 7, "p": 1 }
+{ "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/win_null_missing/win_null_missing.2.adm b/asterixdb/asterix-app/src/test/resources/runtimets/results/window/win_null_missing/win_null_missing.2.adm
index 456c327..9818aa3 100644
--- a/asterixdb/asterix-app/src/test/resources/runtimets/results/window/win_null_missing/win_null_missing.2.adm
+++ b/asterixdb/asterix-app/src/test/resources/runtimets/results/window/win_null_missing/win_null_missing.2.adm
@@ -1,6 +1,6 @@
-{ "w0": "m", "w1": "m", "w2": "n", "w3": "i", "w4": "s", "w5": "a", "w6": "o", "y": "m" }
-{ "w0": "m", "w1": "m", "w2": "n", "w3": "i", "w4": "s", "w5": "a", "w6": "o", "x": null, "y": "n" }
-{ "w0": "m", "w1": "m", "w2": "n", "w3": "i", "w4": "s", "w5": "a", "w6": "o", "x": 1, "y": "i" }
-{ "w0": "m", "w1": "m", "w2": "n", "w3": "i", "w4": "s", "w5": "a", "w6": "o", "x": "a", "y": "s" }
-{ "w0": "m", "w1": "m", "w2": "n", "w3": "i", "w4": "s", "w5": "a", "w6": "o", "x": [ "b" ], "y": "a" }
-{ "w0": "m", "w1": "m", "w2": "n", "w3": "i", "w4": "s", "w5": "a", "w6": "o", "x": { "c": 1 }, "y": "o" }
+{ "w0": null, "w1": "m", "w2": "n", "w3": "i", "w4": "s", "w5": "a", "w6": "o", "y": "m" }
+{ "w0": null, "w1": "m", "w2": "n", "w3": "i", "w4": "s", "w5": "a", "w6": "o", "x": null, "y": "n" }
+{ "w0": null, "w1": "m", "w2": "n", "w3": "i", "w4": "s", "w5": "a", "w6": "o", "x": 1, "y": "i" }
+{ "w0": null, "w1": "m", "w2": "n", "w3": "i", "w4": "s", "w5": "a", "w6": "o", "x": "a", "y": "s" }
+{ "w0": null, "w1": "m", "w2": "n", "w3": "i", "w4": "s", "w5": "a", "w6": "o", "x": [ "b" ], "y": "a" }
+{ "w0": null, "w1": "m", "w2": "n", "w3": "i", "w4": "s", "w5": "a", "w6": "o", "x": { "c": 1 }, "y": "o" }
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 fae2795..d5fa013 100644
--- a/asterixdb/asterix-app/src/test/resources/runtimets/testsuite_sqlpp.xml
+++ b/asterixdb/asterix-app/src/test/resources/runtimets/testsuite_sqlpp.xml
@@ -10355,11 +10355,36 @@
       </compilation-unit>
     </test-case>
     <test-case FilePath="window">
+      <compilation-unit name="first_value_01">
+        <output-dir compare="Text">first_value_01</output-dir>
+      </compilation-unit>
+    </test-case>
+    <test-case FilePath="window">
+      <compilation-unit name="lag_01">
+        <output-dir compare="Text">lag_01</output-dir>
+      </compilation-unit>
+    </test-case>
+    <test-case FilePath="window">
+      <compilation-unit name="last_value_01">
+        <output-dir compare="Text">last_value_01</output-dir>
+      </compilation-unit>
+    </test-case>
+    <test-case FilePath="window">
+      <compilation-unit name="lead_01">
+        <output-dir compare="Text">lead_01</output-dir>
+      </compilation-unit>
+    </test-case>
+    <test-case FilePath="window">
       <compilation-unit name="misc_01">
         <output-dir compare="Text">misc_01</output-dir>
       </compilation-unit>
     </test-case>
     <test-case FilePath="window">
+      <compilation-unit name="nth_value_01">
+        <output-dir compare="Text">nth_value_01</output-dir>
+      </compilation-unit>
+    </test-case>
+    <test-case FilePath="window">
       <compilation-unit name="ntile_01">
         <output-dir compare="Text">ntile_01</output-dir>
       </compilation-unit>
@@ -10397,6 +10422,8 @@
         <expected-error>ASX1037: Invalid query parameter compiler.windowmemory</expected-error>
         <expected-error>ASX1102: Expected window or aggregate function, got: unknown_func</expected-error>
         <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>
         <source-location>false</source-location>
       </compilation-unit>
     </test-case>
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 0e81bf7..9902afc 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
@@ -184,6 +184,7 @@ public class ErrorCode {
     public static final int COMPILATION_UNEXPECTED_WINDOW_ORDERBY = 1101;
     public static final int COMPILATION_EXPECTED_WINDOW_FUNCTION = 1102;
     public static final int COMPILATION_ILLEGAL_USE_OF_IDENTIFIER = 1103;
+    public static final int INVALID_FUNCTION_MODIFIER = 1104;
 
     // 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 36a2021..49a9821 100644
--- a/asterixdb/asterix-common/src/main/resources/asx_errormsg/en.properties
+++ b/asterixdb/asterix-common/src/main/resources/asx_errormsg/en.properties
@@ -179,6 +179,7 @@
 1101 = Unexpected ORDER BY clause in window expression
 1102 = Expected window or aggregate function, got: %1$s
 1103 = Illegal use of identifier: %1$s
+1104 = Invalid modifier %1$s for function %2$s
 
 # Feed Errors
 3001 = Illegal state.
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 42b5605..5a9173b 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
@@ -54,12 +54,14 @@ public class WindowExpression extends AbstractExpression {
     private VariableExpr windowVar;
     private List<Pair<Expression, Identifier>> windowFieldList;
 
+    private Boolean ignoreNulls;
+
     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) {
+            List<Pair<Expression, Identifier>> windowFieldList, Boolean ignoreNulls) {
         if (functionSignature == null || exprList == null) {
             throw new NullPointerException();
         }
@@ -76,6 +78,7 @@ public class WindowExpression extends AbstractExpression {
         this.frameExclusionKind = frameExclusionKind;
         this.windowVar = windowVar;
         this.windowFieldList = windowFieldList;
+        this.ignoreNulls = ignoreNulls;
     }
 
     @Override
@@ -221,12 +224,20 @@ public class WindowExpression extends AbstractExpression {
         this.windowFieldList = windowFieldList;
     }
 
+    public Boolean getIgnoreNulls() {
+        return ignoreNulls;
+    }
+
+    public void setIgnoreNulls(Boolean ignoreNulls) {
+        this.ignoreNulls = ignoreNulls;
+    }
+
     @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));
+                ExpressionUtils.emptyIfNull(windowFieldList), ignoreNulls);
     }
 
     @Override
@@ -251,7 +262,8 @@ public class WindowExpression extends AbstractExpression {
                 && Objects.equals(frameEndExpr, target.frameEndExpr) && frameExclusionKind == target.frameExclusionKind
                 && Objects.equals(windowVar, target.windowVar)
                 && Objects.equals(ExpressionUtils.emptyIfNull(windowFieldList),
-                        ExpressionUtils.emptyIfNull(target.windowFieldList));
+                        ExpressionUtils.emptyIfNull(target.windowFieldList))
+                && Objects.equals(ignoreNulls, target.ignoreNulls);
     }
 
     @Override
@@ -261,7 +273,11 @@ public class WindowExpression extends AbstractExpression {
         sb.append(functionSignature);
         sb.append('(');
         sb.append(StringUtils.join(exprList, ','));
-        sb.append(") OVER ");
+        sb.append(')');
+        if (ignoreNulls != null && ignoreNulls) {
+            sb.append(" IGNORE NULLS");
+        }
+        sb.append(" OVER ");
         if (hasWindowVar()) {
             sb.append(windowVar);
             if (hasWindowFieldList()) {
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 b18cc02..ba1e7f9 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
@@ -109,23 +109,24 @@ public final class SqlppWindowRewriteVisitor extends AbstractSqlppExpressionExtr
      * Apply rewritings for specific window functions:
      * <ul>
      * <li>
-     * {@code ratio_to_report(x) -> ratio_to_report_impl(x, x)}.
+     * 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)}.
-     * The remaining rewriting to {@code x/sum(x)} will be done by the expression to plan translator
+     * The new last argument will be handled by expression to plan translator
      * </li>
      * </ul>
      */
     private void rewriteSpecificWindowFunctions(FunctionIdentifier winfi, WindowExpression winExpr)
             throws CompilationException {
-        if (BuiltinFunctions.RATIO_TO_REPORT_IMPL.equals(winfi)) {
-            duplicateLastArgument(winExpr);
+        if (BuiltinFunctions.builtinFunctionHasProperty(winfi, BuiltinFunctions.WindowFunctionProperty.HAS_LIST_ARG)) {
+            duplicateFirstArgument(winExpr);
         }
     }
 
-    private void duplicateLastArgument(WindowExpression winExpr) throws CompilationException {
+    private void duplicateFirstArgument(WindowExpression winExpr) throws CompilationException {
         List<Expression> exprList = winExpr.getExprList();
-        Expression arg = exprList.get(exprList.size() - 1);
+        Expression arg = exprList.get(0);
         exprList.add((Expression) SqlppRewriteUtil.deepCopy(arg));
     }
 }
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 370ca4a..3e07177 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);
+                newWindowVar, newWindowFieldList, winExpr.getIgnoreNulls());
         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 dc3e565..4b76b56 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
@@ -423,7 +423,7 @@ public class SqlppCloneAndSubstituteVariablesVisitor extends CloneAndSubstituteV
         WindowExpression newWinExpr = new WindowExpression(winExpr.getFunctionSignature(), newExprList,
                 newPartitionList, newOrderbyList, newOrderbyModifierList, winExpr.getFrameMode(),
                 winExpr.getFrameStartKind(), newFrameStartExpr, winExpr.getFrameEndKind(), newFrameEndExpr,
-                winExpr.getFrameExclusionKind(), newWindowVar, newWindowFieldList);
+                winExpr.getFrameExclusionKind(), newWindowVar, newWindowFieldList, winExpr.getIgnoreNulls());
         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 7120686..c0f1725 100644
--- a/asterixdb/asterix-lang-sqlpp/src/main/javacc/SQLPP.jj
+++ b/asterixdb/asterix-lang-sqlpp/src/main/javacc/SQLPP.jj
@@ -193,11 +193,14 @@ class SQLPPParser extends ScopeChecker implements IParser {
     private static final String EXCLUDE = "EXCLUDE";
     private static final String FOLLOWING = "FOLLOWING";
     private static final String GROUPS = "GROUPS";
+    private static final String IGNORE = "IGNORE";
     private static final String NO = "NO";
+    private static final String NULLS = "NULLS";
     private static final String OTHERS = "OTHERS";
     private static final String PARTITION = "PARTITION";
     private static final String PRECEDING = "PRECEDING";
     private static final String RANGE = "RANGE";
+    private static final String RESPECT = "RESPECT";
     private static final String ROW = "ROW";
     private static final String ROWS = "ROWS";
     private static final String TIES = "TIES";
@@ -2746,7 +2749,7 @@ Expression FunctionCallExpr() throws ParseException:
       resultExpr = callExpr;
     }
 
-  ( <OVER> resultExpr = WindowExpr(callExpr.getFunctionSignature(), callExpr.getExprList(), token) )?
+  ( resultExpr = WindowExpr(callExpr.getFunctionSignature(), callExpr.getExprList(), token) )?
 
   {
      return resultExpr;
@@ -2768,9 +2771,18 @@ 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;
@@ -2815,7 +2827,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);
+      windowFieldList, ignoreNulls);
     return addSourceLocation(winExp, startToken);
   }
 }
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 b5105b4..3bf3514 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_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;
 import static org.apache.asterix.om.functions.BuiltinFunctions.WindowFunctionProperty.MATERIALIZE_PARTITION;
@@ -1011,7 +1012,7 @@ public class BuiltinFunctions {
     public static final FunctionIdentifier FIRST_VALUE =
             new FunctionIdentifier(FunctionConstants.ASTERIX_NS, "first_value", 1);
     public static final FunctionIdentifier FIRST_VALUE_IMPL =
-            new FunctionIdentifier(FunctionConstants.ASTERIX_NS, "first-value-impl", 1);
+            new FunctionIdentifier(FunctionConstants.ASTERIX_NS, "first-value-impl", 2);
     public static final FunctionIdentifier LAG =
             new FunctionIdentifier(FunctionConstants.ASTERIX_NS, "lag", FunctionIdentifier.VARARGS);
     public static final FunctionIdentifier LAG_IMPL =
@@ -1019,7 +1020,7 @@ public class BuiltinFunctions {
     public static final FunctionIdentifier LAST_VALUE =
             new FunctionIdentifier(FunctionConstants.ASTERIX_NS, "last_value", 1);
     public static final FunctionIdentifier LAST_VALUE_IMPL =
-            new FunctionIdentifier(FunctionConstants.ASTERIX_NS, "last-value-impl", 1);
+            new FunctionIdentifier(FunctionConstants.ASTERIX_NS, "last-value-impl", 2);
     public static final FunctionIdentifier LEAD =
             new FunctionIdentifier(FunctionConstants.ASTERIX_NS, "lead", FunctionIdentifier.VARARGS);
     public static final FunctionIdentifier LEAD_IMPL =
@@ -1027,7 +1028,7 @@ public class BuiltinFunctions {
     public static final FunctionIdentifier NTH_VALUE =
             new FunctionIdentifier(FunctionConstants.ASTERIX_NS, "nth_value", 2);
     public static final FunctionIdentifier NTH_VALUE_IMPL =
-            new FunctionIdentifier(FunctionConstants.ASTERIX_NS, "nth-value-impl", 2);
+            new FunctionIdentifier(FunctionConstants.ASTERIX_NS, "nth-value-impl", 3);
     public static final FunctionIdentifier NTILE = new FunctionIdentifier(FunctionConstants.ASTERIX_NS, "ntile", 1);
     public static final FunctionIdentifier NTILE_IMPL =
             new FunctionIdentifier(FunctionConstants.ASTERIX_NS, "ntile-impl", FunctionIdentifier.VARARGS);
@@ -3028,18 +3029,20 @@ public class BuiltinFunctions {
         /** Whether order by expressions must be injected as arguments */
         INJECT_ORDER_ARGS,
         /** Whether a running aggregate requires partition materialization runtime */
-        MATERIALIZE_PARTITION
+        MATERIALIZE_PARTITION,
+        /** Whether (RESPECT | IGNORE) NULLS modifier is allowed */
+        ALLOW_RESPECT_IGNORE_NULLS,
     }
 
     static {
         // Window functions
         addWindowFunction(CUME_DIST, CUME_DIST_IMPL, NO_FRAME_CLAUSE, MATERIALIZE_PARTITION);
         addWindowFunction(DENSE_RANK, DENSE_RANK_IMPL, NO_FRAME_CLAUSE, INJECT_ORDER_ARGS);
-        addWindowFunction(FIRST_VALUE, FIRST_VALUE_IMPL, HAS_LIST_ARG);
-        addWindowFunction(LAG, LAG_IMPL, NO_FRAME_CLAUSE, HAS_LIST_ARG);
-        addWindowFunction(LAST_VALUE, LAST_VALUE_IMPL, HAS_LIST_ARG);
-        addWindowFunction(LEAD, LEAD_IMPL, NO_FRAME_CLAUSE, HAS_LIST_ARG);
-        addWindowFunction(NTH_VALUE, NTH_VALUE_IMPL, HAS_LIST_ARG);
+        addWindowFunction(FIRST_VALUE, FIRST_VALUE_IMPL, HAS_LIST_ARG, ALLOW_RESPECT_IGNORE_NULLS);
+        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(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/asterix-om/src/main/java/org/apache/asterix/om/typecomputer/impl/CollectionMemberResultType.java b/asterixdb/asterix-om/src/main/java/org/apache/asterix/om/typecomputer/impl/CollectionMemberResultType.java
index 9ad6af2..a81baf5 100644
--- a/asterixdb/asterix-om/src/main/java/org/apache/asterix/om/typecomputer/impl/CollectionMemberResultType.java
+++ b/asterixdb/asterix-om/src/main/java/org/apache/asterix/om/typecomputer/impl/CollectionMemberResultType.java
@@ -49,9 +49,11 @@ public class CollectionMemberResultType extends AbstractResultTypeComputer {
     @Override
     protected void checkArgType(FunctionIdentifier funcId, int argIndex, IAType type, SourceLocation sourceLoc)
             throws AlgebricksException {
-        ATypeTag actualTypeTag = type.getTypeTag();
-        if (!type.getTypeTag().isListType()) {
-            throw new TypeMismatchException(sourceLoc, actualTypeTag, ATypeTag.MULTISET, ATypeTag.ARRAY);
+        if (argIndex == 0) {
+            ATypeTag actualTypeTag = type.getTypeTag();
+            if (!type.getTypeTag().isListType()) {
+                throw new TypeMismatchException(sourceLoc, actualTypeTag, ATypeTag.MULTISET, ATypeTag.ARRAY);
+            }
         }
     }
 
diff --git a/hyracks-fullstack/algebricks/algebricks-core/src/main/java/org/apache/hyracks/algebricks/core/algebra/operators/logical/WindowOperator.java b/hyracks-fullstack/algebricks/algebricks-core/src/main/java/org/apache/hyracks/algebricks/core/algebra/operators/logical/WindowOperator.java
index db2290c..6733775 100644
--- a/hyracks-fullstack/algebricks/algebricks-core/src/main/java/org/apache/hyracks/algebricks/core/algebra/operators/logical/WindowOperator.java
+++ b/hyracks-fullstack/algebricks/algebricks-core/src/main/java/org/apache/hyracks/algebricks/core/algebra/operators/logical/WindowOperator.java
@@ -47,15 +47,17 @@ import org.apache.hyracks.algebricks.core.algebra.visitors.ILogicalOperatorVisit
  *     Each must be a variable reference</li>
  * <li>{@link #orderExpressions} - define how data inside these partitions must be ordered.
  *     Each must be a variable reference</li>
- * <li>{@link #frameValueExpressions} - value expressions for comparing against frame start / end boundaries and frame exclusion.
- *     Each must be a variable reference</li>
+ * <li>{@link #frameValueExpressions} - value expressions for comparing against frame start / end boundaries and frame
+ *     exclusion. Each must be a variable reference</li>
  * <li>{@link #frameStartExpressions} - frame start boundary</li>
  * <li>{@link #frameStartValidationExpressions} - frame start boundary validators</li>
  * <li>{@link #frameEndExpressions} - frame end boundary</li>
  * <li>{@link #frameEndValidationExpressions} - frame end boundary validators</li>
- * <li>{@link #frameExcludeExpressions} - define values to be excluded from the frame</li>
- * <li>{@link #frameOffset} - sets how many tuples to skip inside each frame</li>
- * <li>{@link #frameMaxObjects} - limits number of tuples to be returned for each frame ({@code -1} = unlimited)</li>
+ * <li>{@link #frameExcludeExpressions} and {@link #frameExcludeUnaryExpression} - define values to be excluded from
+ *     the frame</li>
+ * <li>{@link #frameOffsetExpression} - sets how many tuples to skip inside each frame after exclusion is applied</li>
+ * <li>{@link #frameMaxObjects} - limits number of tuples to be returned for each frame
+ * ({@link #FRAME_MAX_OBJECTS_UNLIMITED} = unlimited)</li>
  * <li>{@link #variables} - output variables containing return values of these functions</li>
  * <li>{@link #expressions} - window function expressions (running aggregates)</li>
  * </ul>
@@ -84,7 +86,9 @@ public class WindowOperator extends AbstractOperatorWithNestedPlans {
 
     private int frameExcludeNegationStartIdx;
 
-    private final Mutable<ILogicalExpression> frameOffset;
+    private final Mutable<ILogicalExpression> frameExcludeUnaryExpression;
+
+    private final Mutable<ILogicalExpression> frameOffsetExpression;
 
     private int frameMaxObjects;
 
@@ -94,7 +98,7 @@ public class WindowOperator extends AbstractOperatorWithNestedPlans {
 
     public WindowOperator(List<Mutable<ILogicalExpression>> partitionExpressions,
             List<Pair<OrderOperator.IOrder, Mutable<ILogicalExpression>>> orderExpressions) {
-        this(partitionExpressions, orderExpressions, null, null, null, null, null, null, -1, null, -1);
+        this(partitionExpressions, orderExpressions, null, null, null, null, null, null, -1, null, null, -1);
     }
 
     public WindowOperator(List<Mutable<ILogicalExpression>> partitionExpressions,
@@ -105,7 +109,8 @@ public class WindowOperator extends AbstractOperatorWithNestedPlans {
             List<Mutable<ILogicalExpression>> frameEndExpressions,
             List<Mutable<ILogicalExpression>> frameEndValidationExpressions,
             List<Mutable<ILogicalExpression>> frameExcludeExpressions, int frameExcludeNegationStartIdx,
-            ILogicalExpression frameOffset, int frameMaxObjects) {
+            ILogicalExpression frameExcludeUnaryExpression, ILogicalExpression frameOffsetExpression,
+            int frameMaxObjects) {
         this.partitionExpressions = new ArrayList<>();
         if (partitionExpressions != null) {
             this.partitionExpressions.addAll(partitionExpressions);
@@ -139,7 +144,8 @@ public class WindowOperator extends AbstractOperatorWithNestedPlans {
             this.frameExcludeExpressions.addAll(frameExcludeExpressions);
         }
         this.frameExcludeNegationStartIdx = frameExcludeNegationStartIdx;
-        this.frameOffset = new MutableObject<>(frameOffset);
+        this.frameExcludeUnaryExpression = new MutableObject<>(frameExcludeUnaryExpression);
+        this.frameOffsetExpression = new MutableObject<>(frameOffsetExpression);
         this.variables = new ArrayList<>();
         this.expressions = new ArrayList<>();
         setFrameMaxObjects(frameMaxObjects);
@@ -153,11 +159,13 @@ public class WindowOperator extends AbstractOperatorWithNestedPlans {
             List<Mutable<ILogicalExpression>> frameEndExpressions,
             List<Mutable<ILogicalExpression>> frameEndValidationExpressions,
             List<Mutable<ILogicalExpression>> frameExcludeExpressions, int frameExcludeNegationStartIdx,
-            ILogicalExpression frameOffset, int frameMaxObjects, List<LogicalVariable> variables,
-            List<Mutable<ILogicalExpression>> expressions, List<ILogicalPlan> nestedPlans) {
+            ILogicalExpression frameExcludeUnaryExpression, ILogicalExpression frameOffsetExpression,
+            int frameMaxObjects, List<LogicalVariable> variables, List<Mutable<ILogicalExpression>> expressions,
+            List<ILogicalPlan> nestedPlans) {
         this(partitionExpressions, orderExpressions, frameValueExpressions, frameStartExpressions,
                 frameStartValidationExpressions, frameEndExpressions, frameEndValidationExpressions,
-                frameExcludeExpressions, frameExcludeNegationStartIdx, frameOffset, frameMaxObjects);
+                frameExcludeExpressions, frameExcludeNegationStartIdx, frameExcludeUnaryExpression,
+                frameOffsetExpression, frameMaxObjects);
         if (variables != null) {
             this.variables.addAll(variables);
         }
@@ -214,8 +222,12 @@ public class WindowOperator extends AbstractOperatorWithNestedPlans {
         this.frameExcludeNegationStartIdx = value;
     }
 
-    public Mutable<ILogicalExpression> getFrameOffset() {
-        return frameOffset;
+    public Mutable<ILogicalExpression> getFrameExcludeUnaryExpression() {
+        return frameExcludeUnaryExpression;
+    }
+
+    public Mutable<ILogicalExpression> getFrameOffsetExpression() {
+        return frameOffsetExpression;
     }
 
     public int getFrameMaxObjects() {
@@ -292,8 +304,11 @@ public class WindowOperator extends AbstractOperatorWithNestedPlans {
         for (Mutable<ILogicalExpression> excludeExpr : frameExcludeExpressions) {
             mod |= visitor.transform(excludeExpr);
         }
-        if (frameOffset.getValue() != null) {
-            mod |= visitor.transform(frameOffset);
+        if (frameExcludeUnaryExpression.getValue() != null) {
+            mod |= visitor.transform(frameExcludeUnaryExpression);
+        }
+        if (frameOffsetExpression.getValue() != null) {
+            mod |= visitor.transform(frameOffsetExpression);
         }
         for (Mutable<ILogicalExpression> expr : expressions) {
             mod |= visitor.transform(expr);
@@ -360,8 +375,11 @@ public class WindowOperator extends AbstractOperatorWithNestedPlans {
         for (Mutable<ILogicalExpression> excludeExpr : frameExcludeExpressions) {
             excludeExpr.getValue().getUsedVariables(vars);
         }
-        if (frameOffset != null) {
-            frameOffset.getValue().getUsedVariables(vars);
+        if (frameExcludeUnaryExpression.getValue() != null) {
+            frameExcludeUnaryExpression.getValue().getUsedVariables(vars);
+        }
+        if (frameOffsetExpression.getValue() != null) {
+            frameOffsetExpression.getValue().getUsedVariables(vars);
         }
         for (Mutable<ILogicalExpression> expr : expressions) {
             expr.getValue().getUsedVariables(vars);
diff --git a/hyracks-fullstack/algebricks/algebricks-core/src/main/java/org/apache/hyracks/algebricks/core/algebra/operators/logical/visitors/IsomorphismOperatorVisitor.java b/hyracks-fullstack/algebricks/algebricks-core/src/main/java/org/apache/hyracks/algebricks/core/algebra/operators/logical/visitors/IsomorphismOperatorVisitor.java
index 09358dd..3a1d085 100644
--- a/hyracks-fullstack/algebricks/algebricks-core/src/main/java/org/apache/hyracks/algebricks/core/algebra/operators/logical/visitors/IsomorphismOperatorVisitor.java
+++ b/hyracks-fullstack/algebricks/algebricks-core/src/main/java/org/apache/hyracks/algebricks/core/algebra/operators/logical/visitors/IsomorphismOperatorVisitor.java
@@ -663,7 +663,10 @@ public class IsomorphismOperatorVisitor implements ILogicalOperatorVisitor<Boole
                         winOp2.getFrameEndValidationExpressions())
                 && compareExpressions(winOp1.getFrameExcludeExpressions(), winOp2.getFrameExcludeExpressions())
                 && winOp1.getFrameExcludeNegationStartIdx() == winOp2.getFrameExcludeNegationStartIdx()
-                && Objects.equals(winOp1.getFrameOffset().getValue(), winOp2.getFrameOffset().getValue());
+                && Objects.equals(winOp1.getFrameExcludeUnaryExpression().getValue(),
+                        winOp2.getFrameExcludeUnaryExpression().getValue())
+                && Objects.equals(winOp1.getFrameOffsetExpression().getValue(),
+                        winOp2.getFrameOffsetExpression().getValue());
         // do not include WindowOperator.getFrameMaxObjects()
     }
 
diff --git a/hyracks-fullstack/algebricks/algebricks-core/src/main/java/org/apache/hyracks/algebricks/core/algebra/operators/logical/visitors/LogicalOperatorDeepCopyWithNewVariablesVisitor.java b/hyracks-fullstack/algebricks/algebricks-core/src/main/java/org/apache/hyracks/algebricks/core/algebra/operators/logical/visitors/LogicalOperatorDeepCopyWithNewVariablesVisitor.java
index 34b0ae6..609f1fb 100644
--- a/hyracks-fullstack/algebricks/algebricks-core/src/main/java/org/apache/hyracks/algebricks/core/algebra/operators/logical/visitors/LogicalOperatorDeepCopyWithNewVariablesVisitor.java
+++ b/hyracks-fullstack/algebricks/algebricks-core/src/main/java/org/apache/hyracks/algebricks/core/algebra/operators/logical/visitors/LogicalOperatorDeepCopyWithNewVariablesVisitor.java
@@ -628,17 +628,19 @@ public class LogicalOperatorDeepCopyWithNewVariablesVisitor
                 exprDeepCopyVisitor.deepCopyExpressionReferenceList(op.getFrameEndExpressions());
         List<Mutable<ILogicalExpression>> frameEndValidationExprCopy =
                 exprDeepCopyVisitor.deepCopyExpressionReferenceList(op.getFrameEndValidationExpressions());
-        List<Mutable<ILogicalExpression>> frameExclusionExprCopy =
+        List<Mutable<ILogicalExpression>> frameExcludeExprCopy =
                 exprDeepCopyVisitor.deepCopyExpressionReferenceList(op.getFrameExcludeExpressions());
-        ILogicalExpression frameOffsetCopy = exprDeepCopyVisitor.deepCopy(op.getFrameOffset().getValue());
+        ILogicalExpression frameExcludeUnaryExprCopy =
+                exprDeepCopyVisitor.deepCopy(op.getFrameExcludeUnaryExpression().getValue());
+        ILogicalExpression frameOffsetExprCopy = exprDeepCopyVisitor.deepCopy(op.getFrameOffsetExpression().getValue());
         List<LogicalVariable> varCopy = deepCopyVariableList(op.getVariables());
         List<Mutable<ILogicalExpression>> exprCopy =
                 exprDeepCopyVisitor.deepCopyExpressionReferenceList(op.getExpressions());
         List<ILogicalPlan> nestedPlansCopy = new ArrayList<>();
         WindowOperator opCopy = new WindowOperator(partitionExprCopy, orderExprCopy, frameValueExprCopy,
                 frameStartExprCopy, frameStartValidationExprCopy, frameEndExprCopy, frameEndValidationExprCopy,
-                frameExclusionExprCopy, op.getFrameExcludeNegationStartIdx(), frameOffsetCopy, op.getFrameMaxObjects(),
-                varCopy, exprCopy, nestedPlansCopy);
+                frameExcludeExprCopy, op.getFrameExcludeNegationStartIdx(), frameExcludeUnaryExprCopy,
+                frameOffsetExprCopy, op.getFrameMaxObjects(), varCopy, exprCopy, nestedPlansCopy);
         deepCopyInputsAnnotationsAndExecutionMode(op, arg, opCopy);
         deepCopyPlanList(op.getNestedPlans(), nestedPlansCopy, opCopy);
         return opCopy;
@@ -647,5 +649,4 @@ public class LogicalOperatorDeepCopyWithNewVariablesVisitor
     public LinkedHashMap<LogicalVariable, LogicalVariable> getInputToOutputVariableMapping() {
         return inputVarToOutputVarMapping;
     }
-
 }
diff --git a/hyracks-fullstack/algebricks/algebricks-core/src/main/java/org/apache/hyracks/algebricks/core/algebra/operators/logical/visitors/OperatorDeepCopyVisitor.java b/hyracks-fullstack/algebricks/algebricks-core/src/main/java/org/apache/hyracks/algebricks/core/algebra/operators/logical/visitors/OperatorDeepCopyVisitor.java
index fd4009f..1d64ad0 100644
--- a/hyracks-fullstack/algebricks/algebricks-core/src/main/java/org/apache/hyracks/algebricks/core/algebra/operators/logical/visitors/OperatorDeepCopyVisitor.java
+++ b/hyracks-fullstack/algebricks/algebricks-core/src/main/java/org/apache/hyracks/algebricks/core/algebra/operators/logical/visitors/OperatorDeepCopyVisitor.java
@@ -426,7 +426,9 @@ public class OperatorDeepCopyVisitor implements ILogicalOperatorVisitor<ILogical
         deepCopyExpressionRefs(newFrameEndValidationExprs, op.getFrameEndValidationExpressions());
         List<Mutable<ILogicalExpression>> newFrameExclusionExprs = new ArrayList<>();
         deepCopyExpressionRefs(newFrameExclusionExprs, op.getFrameExcludeExpressions());
-        ILogicalExpression newFrameOffset = deepCopyExpressionRef(op.getFrameOffset()).getValue();
+        ILogicalExpression newFrameExcludeUnaryExpr =
+                deepCopyExpressionRef(op.getFrameExcludeUnaryExpression()).getValue();
+        ILogicalExpression newFrameOffsetExpr = deepCopyExpressionRef(op.getFrameOffsetExpression()).getValue();
         List<LogicalVariable> newVariables = new ArrayList<>();
         deepCopyVars(newVariables, op.getVariables());
         List<Mutable<ILogicalExpression>> newExpressions = new ArrayList<>();
@@ -434,8 +436,8 @@ public class OperatorDeepCopyVisitor implements ILogicalOperatorVisitor<ILogical
         List<ILogicalPlan> newNestedPlans = new ArrayList<>();
         WindowOperator newWinOp = new WindowOperator(newPartitionExprs, newOrderExprs, newFrameValueExprs,
                 newFrameStartExprs, newFrameStartValidationExprs, newFrameEndExprs, newFrameEndValidationExprs,
-                newFrameExclusionExprs, op.getFrameExcludeNegationStartIdx(), newFrameOffset, op.getFrameMaxObjects(),
-                newVariables, newExpressions, newNestedPlans);
+                newFrameExclusionExprs, op.getFrameExcludeNegationStartIdx(), newFrameExcludeUnaryExpr,
+                newFrameOffsetExpr, op.getFrameMaxObjects(), newVariables, newExpressions, newNestedPlans);
         for (ILogicalPlan nestedPlan : op.getNestedPlans()) {
             newNestedPlans.add(OperatorManipulationUtil.deepCopy(nestedPlan, newWinOp));
         }
diff --git a/hyracks-fullstack/algebricks/algebricks-core/src/main/java/org/apache/hyracks/algebricks/core/algebra/operators/logical/visitors/SubstituteVariableVisitor.java b/hyracks-fullstack/algebricks/algebricks-core/src/main/java/org/apache/hyracks/algebricks/core/algebra/operators/logical/visitors/SubstituteVariableVisitor.java
index 028bf9f..dfd88ec 100644
--- a/hyracks-fullstack/algebricks/algebricks-core/src/main/java/org/apache/hyracks/algebricks/core/algebra/operators/logical/visitors/SubstituteVariableVisitor.java
+++ b/hyracks-fullstack/algebricks/algebricks-core/src/main/java/org/apache/hyracks/algebricks/core/algebra/operators/logical/visitors/SubstituteVariableVisitor.java
@@ -529,9 +529,13 @@ public class SubstituteVariableVisitor
         for (Mutable<ILogicalExpression> expr : op.getFrameExcludeExpressions()) {
             expr.getValue().substituteVar(pair.first, pair.second);
         }
-        ILogicalExpression frameOffset = op.getFrameOffset().getValue();
-        if (frameOffset != null) {
-            frameOffset.substituteVar(pair.first, pair.second);
+        ILogicalExpression frameExcludeUnaryExpr = op.getFrameExcludeUnaryExpression().getValue();
+        if (frameExcludeUnaryExpr != null) {
+            frameExcludeUnaryExpr.substituteVar(pair.first, pair.second);
+        }
+        ILogicalExpression frameOffsetExpr = op.getFrameOffsetExpression().getValue();
+        if (frameOffsetExpr != null) {
+            frameOffsetExpr.substituteVar(pair.first, pair.second);
         }
         substAssignVariables(op.getVariables(), op.getExpressions(), pair);
         substInNestedPlans(pair.first, pair.second, op);
diff --git a/hyracks-fullstack/algebricks/algebricks-core/src/main/java/org/apache/hyracks/algebricks/core/algebra/operators/logical/visitors/UsedVariableVisitor.java b/hyracks-fullstack/algebricks/algebricks-core/src/main/java/org/apache/hyracks/algebricks/core/algebra/operators/logical/visitors/UsedVariableVisitor.java
index 845a853..e298200 100644
--- a/hyracks-fullstack/algebricks/algebricks-core/src/main/java/org/apache/hyracks/algebricks/core/algebra/operators/logical/visitors/UsedVariableVisitor.java
+++ b/hyracks-fullstack/algebricks/algebricks-core/src/main/java/org/apache/hyracks/algebricks/core/algebra/operators/logical/visitors/UsedVariableVisitor.java
@@ -492,9 +492,13 @@ public class UsedVariableVisitor implements ILogicalOperatorVisitor<Void, Void>
         for (Mutable<ILogicalExpression> exprRef : op.getFrameExcludeExpressions()) {
             exprRef.getValue().getUsedVariables(usedVariables);
         }
-        ILogicalExpression frameOffset = op.getFrameOffset().getValue();
-        if (frameOffset != null) {
-            frameOffset.getUsedVariables(usedVariables);
+        ILogicalExpression frameExcludeUnaryExpr = op.getFrameExcludeUnaryExpression().getValue();
+        if (frameExcludeUnaryExpr != null) {
+            frameExcludeUnaryExpr.getUsedVariables(usedVariables);
+        }
+        ILogicalExpression frameOffsetExpr = op.getFrameOffsetExpression().getValue();
+        if (frameOffsetExpr != null) {
+            frameOffsetExpr.getUsedVariables(usedVariables);
         }
         for (Mutable<ILogicalExpression> exprRef : op.getExpressions()) {
             exprRef.getValue().getUsedVariables(usedVariables);
diff --git a/hyracks-fullstack/algebricks/algebricks-core/src/main/java/org/apache/hyracks/algebricks/core/algebra/operators/physical/AbstractWindowPOperator.java b/hyracks-fullstack/algebricks/algebricks-core/src/main/java/org/apache/hyracks/algebricks/core/algebra/operators/physical/AbstractWindowPOperator.java
index 7065b70..0b3a79c 100644
--- a/hyracks-fullstack/algebricks/algebricks-core/src/main/java/org/apache/hyracks/algebricks/core/algebra/operators/physical/AbstractWindowPOperator.java
+++ b/hyracks-fullstack/algebricks/algebricks-core/src/main/java/org/apache/hyracks/algebricks/core/algebra/operators/physical/AbstractWindowPOperator.java
@@ -172,8 +172,15 @@ public abstract class AbstractWindowPOperator extends AbstractPhysicalOperator {
                 createEvaluatorAndComparatorFactories(frameExcludeExprList, v -> v, v -> OrderOperator.ASC_ORDER,
                         inputSchemas, inputTypeEnv, exprRuntimeProvider, binaryComparatorFactoryProvider, context);
 
+        IScalarEvaluatorFactory frameExcludeUnaryEval = null;
+        ILogicalExpression frameExcludeUnaryExpr = winOp.getFrameExcludeUnaryExpression().getValue();
+        if (frameExcludeUnaryExpr != null) {
+            frameExcludeUnaryEval = exprRuntimeProvider.createEvaluatorFactory(frameExcludeUnaryExpr, inputTypeEnv,
+                    inputSchemas, context);
+        }
+
         IScalarEvaluatorFactory frameOffsetExprEval = null;
-        ILogicalExpression frameOffsetExpr = winOp.getFrameOffset().getValue();
+        ILogicalExpression frameOffsetExpr = winOp.getFrameOffsetExpression().getValue();
         if (frameOffsetExpr != null) {
             frameOffsetExprEval =
                     exprRuntimeProvider.createEvaluatorFactory(frameOffsetExpr, inputTypeEnv, inputSchemas, context);
@@ -207,8 +214,9 @@ public abstract class AbstractWindowPOperator extends AbstractPhysicalOperator {
                 partitionComparatorFactories, orderComparatorFactories, frameValueExprEvalsAndComparators.first,
                 frameValueExprEvalsAndComparators.second, frameStartExprEvals, frameStartValidationExprEvals,
                 frameEndExprEvals, frameEndValidationExprEvals, frameExcludeExprEvalsAndComparators.first,
-                frameExcludeExprEvalsAndComparators.second, frameOffsetExprEval, projectionColumnsExcludingSubplans,
-                runningAggOutColumns, runningAggFactories, nestedAggOutSchemaSize, nestedAggFactory, context);
+                frameExcludeExprEvalsAndComparators.second, frameExcludeUnaryEval, frameOffsetExprEval,
+                projectionColumnsExcludingSubplans, runningAggOutColumns, runningAggFactories, nestedAggOutSchemaSize,
+                nestedAggFactory, context);
         runtime.setSourceLocation(winOp.getSourceLocation());
 
         // contribute one Asterix framewriter
@@ -225,7 +233,8 @@ public abstract class AbstractWindowPOperator extends AbstractPhysicalOperator {
             IBinaryComparatorFactory[] frameValueComparatorFactories, IScalarEvaluatorFactory[] frameStartExprEvals,
             IScalarEvaluatorFactory[] frameStartValidationExprEvals, IScalarEvaluatorFactory[] frameEndExprEvals,
             IScalarEvaluatorFactory[] frameEndValidationExprEvals, IScalarEvaluatorFactory[] frameExcludeExprEvals,
-            IBinaryComparatorFactory[] frameExcludeComparatorFactories, IScalarEvaluatorFactory frameOffsetExprEval,
+            IBinaryComparatorFactory[] frameExcludeComparatorFactories,
+            IScalarEvaluatorFactory frameExcludeUnaryExprEval, IScalarEvaluatorFactory frameOffsetExprEval,
             int[] projectionColumnsExcludingSubplans, int[] runningAggOutColumns,
             IRunningAggregateEvaluatorFactory[] runningAggFactories, int nestedAggOutSchemaSize,
             WindowAggregatorDescriptorFactory nestedAggFactory, JobGenContext context);
diff --git a/hyracks-fullstack/algebricks/algebricks-core/src/main/java/org/apache/hyracks/algebricks/core/algebra/operators/physical/WindowPOperator.java b/hyracks-fullstack/algebricks/algebricks-core/src/main/java/org/apache/hyracks/algebricks/core/algebra/operators/physical/WindowPOperator.java
index 2a0b052..0e9191f 100644
--- a/hyracks-fullstack/algebricks/algebricks-core/src/main/java/org/apache/hyracks/algebricks/core/algebra/operators/physical/WindowPOperator.java
+++ b/hyracks-fullstack/algebricks/algebricks-core/src/main/java/org/apache/hyracks/algebricks/core/algebra/operators/physical/WindowPOperator.java
@@ -75,7 +75,8 @@ public final class WindowPOperator extends AbstractWindowPOperator {
             IBinaryComparatorFactory[] frameValueComparatorFactories, IScalarEvaluatorFactory[] frameStartExprEvals,
             IScalarEvaluatorFactory[] frameStartValidationExprEvals, IScalarEvaluatorFactory[] frameEndExprEvals,
             IScalarEvaluatorFactory[] frameEndValidationExprEvals, IScalarEvaluatorFactory[] frameExcludeExprEvals,
-            IBinaryComparatorFactory[] frameExcludeComparatorFactories, IScalarEvaluatorFactory frameOffsetExprEval,
+            IBinaryComparatorFactory[] frameExcludeComparatorFactories,
+            IScalarEvaluatorFactory frameExcludeUnaryExprEval, IScalarEvaluatorFactory frameOffsetExprEval,
             int[] projectionColumnsExcludingSubplans, int[] runningAggOutColumns,
             IRunningAggregateEvaluatorFactory[] runningAggFactories, int nestedAggOutSchemaSize,
             WindowAggregatorDescriptorFactory nestedAggFactory, JobGenContext context) {
@@ -92,8 +93,9 @@ public final class WindowPOperator extends AbstractWindowPOperator {
         boolean hasFrameStart = frameStartExprEvals != null && frameStartExprEvals.length > 0;
         boolean hasFrameEnd = frameEndExprEvals != null && frameEndExprEvals.length > 0;
         boolean hasFrameExclude = frameExcludeExprEvals != null && frameExcludeExprEvals.length > 0;
+        boolean hasFrameExcludeUnary = frameExcludeUnaryExprEval != null;
         boolean hasFrameOffset = frameOffsetExprEval != null;
-        if (!hasFrameStart && !hasFrameExclude && !hasFrameOffset) {
+        if (!hasFrameStart && !hasFrameExclude && !hasFrameExcludeUnary && !hasFrameOffset) {
             if (!hasFrameEnd) {
                 // special case #1: frame == whole partition, no exclusions, no offset
                 return new WindowNestedPlansUnboundedRuntimeFactory(partitionColumnsList, partitionComparatorFactories,
@@ -118,8 +120,9 @@ public final class WindowPOperator extends AbstractWindowPOperator {
                 orderComparatorFactories, frameValueExprEvals, frameValueComparatorFactories, frameStartExprEvals,
                 frameStartValidationExprEvals, frameStartIsMonotonic, frameEndExprEvals, frameEndValidationExprEvals,
                 frameExcludeExprEvals, winOp.getFrameExcludeNegationStartIdx(), frameExcludeComparatorFactories,
-                frameOffsetExprEval, winOp.getFrameMaxObjects(), context.getBinaryBooleanInspectorFactory(),
-                context.getBinaryIntegerInspectorFactory(), projectionColumnsExcludingSubplans, runningAggOutColumns,
-                runningAggFactories, nestedAggOutSchemaSize, nestedAggFactory, memSizeInFrames);
+                frameExcludeUnaryExprEval, frameOffsetExprEval, winOp.getFrameMaxObjects(),
+                context.getBinaryBooleanInspectorFactory(), context.getBinaryIntegerInspectorFactory(),
+                projectionColumnsExcludingSubplans, runningAggOutColumns, runningAggFactories, nestedAggOutSchemaSize,
+                nestedAggFactory, memSizeInFrames);
     }
 }
diff --git a/hyracks-fullstack/algebricks/algebricks-core/src/main/java/org/apache/hyracks/algebricks/core/algebra/operators/physical/WindowStreamPOperator.java b/hyracks-fullstack/algebricks/algebricks-core/src/main/java/org/apache/hyracks/algebricks/core/algebra/operators/physical/WindowStreamPOperator.java
index 68476d1..78983b9 100644
--- a/hyracks-fullstack/algebricks/algebricks-core/src/main/java/org/apache/hyracks/algebricks/core/algebra/operators/physical/WindowStreamPOperator.java
+++ b/hyracks-fullstack/algebricks/algebricks-core/src/main/java/org/apache/hyracks/algebricks/core/algebra/operators/physical/WindowStreamPOperator.java
@@ -61,7 +61,8 @@ public final class WindowStreamPOperator extends AbstractWindowPOperator {
             IBinaryComparatorFactory[] frameValueComparatorFactories, IScalarEvaluatorFactory[] frameStartExprEvals,
             IScalarEvaluatorFactory[] frameStartValidationExprEvals, IScalarEvaluatorFactory[] frameEndExprEvals,
             IScalarEvaluatorFactory[] frameEndValidationExprEvals, IScalarEvaluatorFactory[] frameExcludeExprEvals,
-            IBinaryComparatorFactory[] frameExcludeComparatorFactories, IScalarEvaluatorFactory frameOffsetExprEval,
+            IBinaryComparatorFactory[] frameExcludeComparatorFactories,
+            IScalarEvaluatorFactory frameExcludeUnaryExprEval, IScalarEvaluatorFactory frameOffsetExprEval,
             int[] projectionColumnsExcludingSubplans, int[] runningAggOutColumns,
             IRunningAggregateEvaluatorFactory[] runningAggFactories, int nestedAggOutSchemaSize,
             WindowAggregatorDescriptorFactory nestedAggFactory, JobGenContext context) {
diff --git a/hyracks-fullstack/algebricks/algebricks-core/src/main/java/org/apache/hyracks/algebricks/core/algebra/prettyprint/LogicalOperatorPrettyPrintVisitor.java b/hyracks-fullstack/algebricks/algebricks-core/src/main/java/org/apache/hyracks/algebricks/core/algebra/prettyprint/LogicalOperatorPrettyPrintVisitor.java
index 16afd50..7b49117 100644
--- a/hyracks-fullstack/algebricks/algebricks-core/src/main/java/org/apache/hyracks/algebricks/core/algebra/prettyprint/LogicalOperatorPrettyPrintVisitor.java
+++ b/hyracks-fullstack/algebricks/algebricks-core/src/main/java/org/apache/hyracks/algebricks/core/algebra/prettyprint/LogicalOperatorPrettyPrintVisitor.java
@@ -536,10 +536,15 @@ public class LogicalOperatorPrettyPrintVisitor extends AbstractLogicalOperatorPr
                     pprintExprList(frameExcludeExpressions, indent);
                 }
             }
-            Mutable<ILogicalExpression> frameOffset = op.getFrameOffset();
-            if (frameOffset.getValue() != null) {
+            Mutable<ILogicalExpression> frameExcludeUnaryExpression = op.getFrameExcludeUnaryExpression();
+            if (frameExcludeUnaryExpression.getValue() != null) {
+                buffer.append(" exclude unary ");
+                buffer.append(frameExcludeUnaryExpression.getValue().accept(exprVisitor, indent));
+            }
+            Mutable<ILogicalExpression> frameOffsetExpression = op.getFrameOffsetExpression();
+            if (frameOffsetExpression.getValue() != null) {
                 buffer.append(" offset ");
-                buffer.append(frameOffset.getValue().accept(exprVisitor, indent));
+                buffer.append(frameOffsetExpression.getValue().accept(exprVisitor, indent));
             }
             int frameMaxObjects = op.getFrameMaxObjects();
             if (frameMaxObjects != -1) {
diff --git a/hyracks-fullstack/algebricks/algebricks-core/src/main/java/org/apache/hyracks/algebricks/core/algebra/prettyprint/LogicalOperatorPrettyPrintVisitorJson.java b/hyracks-fullstack/algebricks/algebricks-core/src/main/java/org/apache/hyracks/algebricks/core/algebra/prettyprint/LogicalOperatorPrettyPrintVisitorJson.java
index 7e8a289..266cfd4 100644
--- a/hyracks-fullstack/algebricks/algebricks-core/src/main/java/org/apache/hyracks/algebricks/core/algebra/prettyprint/LogicalOperatorPrettyPrintVisitorJson.java
+++ b/hyracks-fullstack/algebricks/algebricks-core/src/main/java/org/apache/hyracks/algebricks/core/algebra/prettyprint/LogicalOperatorPrettyPrintVisitorJson.java
@@ -742,11 +742,17 @@ public class LogicalOperatorPrettyPrintVisitorJson extends AbstractLogicalOperat
                 addIndent(indent).append("\"frame-exclude-negation-start\": ")
                         .append(String.valueOf(op.getFrameExcludeNegationStartIdx()));
             }
-            Mutable<ILogicalExpression> frameOffset = op.getFrameOffset();
-            if (frameOffset.getValue() != null) {
+            Mutable<ILogicalExpression> frameExcludeUnaryExpression = op.getFrameExcludeUnaryExpression();
+            if (frameExcludeUnaryExpression.getValue() != null) {
+                buffer.append(",\n");
+                addIndent(indent).append("\"frame-exclude-unary\": ");
+                pprintExpr(frameExcludeUnaryExpression, fldIndent);
+            }
+            Mutable<ILogicalExpression> frameOffsetExpression = op.getFrameOffsetExpression();
+            if (frameOffsetExpression.getValue() != null) {
                 buffer.append(",\n");
                 addIndent(indent).append("\"frame-offset\": ");
-                pprintExpr(frameOffset, fldIndent);
+                pprintExpr(frameOffsetExpression, fldIndent);
             }
             int frameMaxObjects = op.getFrameMaxObjects();
             if (frameMaxObjects != -1) {
diff --git a/hyracks-fullstack/algebricks/algebricks-core/src/main/java/org/apache/hyracks/algebricks/core/utils/LogicalOperatorDotVisitor.java b/hyracks-fullstack/algebricks/algebricks-core/src/main/java/org/apache/hyracks/algebricks/core/utils/LogicalOperatorDotVisitor.java
index 795fb10..799e74b 100644
--- a/hyracks-fullstack/algebricks/algebricks-core/src/main/java/org/apache/hyracks/algebricks/core/utils/LogicalOperatorDotVisitor.java
+++ b/hyracks-fullstack/algebricks/algebricks-core/src/main/java/org/apache/hyracks/algebricks/core/utils/LogicalOperatorDotVisitor.java
@@ -635,10 +635,16 @@ public class LogicalOperatorDotVisitor implements ILogicalOperatorVisitor<String
                 stringBuilder.append(" (negation start: ").append(op.getFrameExcludeNegationStartIdx()).append(") ");
                 printExprList(frameExcludeExpressions);
             }
-            Mutable<ILogicalExpression> frameOffset = op.getFrameOffset();
-            if (frameOffset.getValue() != null) {
+            Mutable<ILogicalExpression> frameExcludeUnaryExpression = op.getFrameExcludeUnaryExpression();
+            if (frameExcludeUnaryExpression.getValue() != null) {
+                stringBuilder.append(") frame exclude unary (");
+                stringBuilder.append(frameExcludeUnaryExpression.getValue());
+                stringBuilder.append(") ");
+            }
+            Mutable<ILogicalExpression> frameOffsetExpression = op.getFrameOffsetExpression();
+            if (frameOffsetExpression.getValue() != null) {
                 stringBuilder.append(") frame offset (");
-                stringBuilder.append(frameOffset.getValue());
+                stringBuilder.append(frameOffsetExpression.getValue());
                 stringBuilder.append(") ");
             }
             int frameMaxObjects = op.getFrameMaxObjects();
diff --git a/hyracks-fullstack/algebricks/algebricks-data/src/main/java/org/apache/hyracks/algebricks/data/IBinaryBooleanInspector.java b/hyracks-fullstack/algebricks/algebricks-data/src/main/java/org/apache/hyracks/algebricks/data/IBinaryBooleanInspector.java
index d25ea82..96f66dc 100644
--- a/hyracks-fullstack/algebricks/algebricks-data/src/main/java/org/apache/hyracks/algebricks/data/IBinaryBooleanInspector.java
+++ b/hyracks-fullstack/algebricks/algebricks-data/src/main/java/org/apache/hyracks/algebricks/data/IBinaryBooleanInspector.java
@@ -22,6 +22,5 @@ import org.apache.hyracks.api.exceptions.HyracksDataException;
 
 @FunctionalInterface
 public interface IBinaryBooleanInspector {
-
     boolean getBooleanValue(byte[] bytes, int offset, int length) throws HyracksDataException;
 }
diff --git a/hyracks-fullstack/algebricks/algebricks-data/src/main/java/org/apache/hyracks/algebricks/data/IBinaryIntegerInspector.java b/hyracks-fullstack/algebricks/algebricks-data/src/main/java/org/apache/hyracks/algebricks/data/IBinaryIntegerInspector.java
index 089619a..e6098fa 100644
--- a/hyracks-fullstack/algebricks/algebricks-data/src/main/java/org/apache/hyracks/algebricks/data/IBinaryIntegerInspector.java
+++ b/hyracks-fullstack/algebricks/algebricks-data/src/main/java/org/apache/hyracks/algebricks/data/IBinaryIntegerInspector.java
@@ -20,6 +20,7 @@ package org.apache.hyracks.algebricks.data;
 
 import org.apache.hyracks.api.exceptions.HyracksDataException;
 
+@FunctionalInterface
 public interface IBinaryIntegerInspector {
-    public int getIntegerValue(byte[] bytes, int offset, int length) throws HyracksDataException;
+    int getIntegerValue(byte[] bytes, int offset, int length) throws HyracksDataException;
 }
diff --git a/hyracks-fullstack/algebricks/algebricks-rewriter/src/main/java/org/apache/hyracks/algebricks/rewriter/rules/ConsolidateWindowOperatorsRule.java b/hyracks-fullstack/algebricks/algebricks-rewriter/src/main/java/org/apache/hyracks/algebricks/rewriter/rules/ConsolidateWindowOperatorsRule.java
index ee19eaa..2ec0654 100644
--- a/hyracks-fullstack/algebricks/algebricks-rewriter/src/main/java/org/apache/hyracks/algebricks/rewriter/rules/ConsolidateWindowOperatorsRule.java
+++ b/hyracks-fullstack/algebricks/algebricks-rewriter/src/main/java/org/apache/hyracks/algebricks/rewriter/rules/ConsolidateWindowOperatorsRule.java
@@ -138,7 +138,8 @@ public class ConsolidateWindowOperatorsRule implements IAlgebraicRewriteRule {
             setAll(winOpTo.getFrameEndValidationExpressions(), winOpFrom.getFrameEndValidationExpressions());
             setAll(winOpTo.getFrameExcludeExpressions(), winOpFrom.getFrameExcludeExpressions());
             winOpTo.setFrameExcludeNegationStartIdx(winOpFrom.getFrameExcludeNegationStartIdx());
-            winOpTo.getFrameOffset().setValue(winOpFrom.getFrameOffset().getValue());
+            winOpTo.getFrameExcludeUnaryExpression().setValue(winOpFrom.getFrameExcludeUnaryExpression().getValue());
+            winOpTo.getFrameOffsetExpression().setValue(winOpFrom.getFrameOffsetExpression().getValue());
             winOpTo.setFrameMaxObjects(winOpFrom.getFrameMaxObjects());
         }
         return true;
diff --git a/hyracks-fullstack/algebricks/algebricks-runtime/src/main/java/org/apache/hyracks/algebricks/runtime/operators/win/WindowNestedPlansPushRuntime.java b/hyracks-fullstack/algebricks/algebricks-runtime/src/main/java/org/apache/hyracks/algebricks/runtime/operators/win/WindowNestedPlansPushRuntime.java
index 1a57e27..a5f0bb4 100644
--- a/hyracks-fullstack/algebricks/algebricks-runtime/src/main/java/org/apache/hyracks/algebricks/runtime/operators/win/WindowNestedPlansPushRuntime.java
+++ b/hyracks-fullstack/algebricks/algebricks-runtime/src/main/java/org/apache/hyracks/algebricks/runtime/operators/win/WindowNestedPlansPushRuntime.java
@@ -110,20 +110,22 @@ class WindowNestedPlansPushRuntime extends AbstractWindowNestedPlansPushRuntime
 
     private PointableTupleReference frameExcludePointables;
 
-    private IPointable frameExcludePointable2;
-
     private final IBinaryComparatorFactory[] frameExcludeComparatorFactories;
 
     private IBinaryComparator[] frameExcludeComparators;
 
+    private final boolean frameExcludeUnaryExists;
+
+    private final IScalarEvaluatorFactory frameExcludeUnaryEvalFactory;
+
+    private IScalarEvaluator frameExcludeUnaryEval;
+
     private final boolean frameOffsetExists;
 
     private final IScalarEvaluatorFactory frameOffsetEvalFactory;
 
     private IScalarEvaluator frameOffsetEval;
 
-    private IPointable frameOffsetPointable;
-
     private final int frameMaxObjects;
 
     private final IBinaryBooleanInspectorFactory booleanAccessorFactory;
@@ -134,6 +136,8 @@ class WindowNestedPlansPushRuntime extends AbstractWindowNestedPlansPushRuntime
 
     private IBinaryIntegerInspector integerAccessor;
 
+    private IPointable tmpPointable;
+
     private FrameTupleAccessor tAccess2;
 
     private FrameTupleReference tRef2;
@@ -148,7 +152,8 @@ class WindowNestedPlansPushRuntime extends AbstractWindowNestedPlansPushRuntime
             IScalarEvaluatorFactory[] frameStartValidationEvalFactories, boolean frameStartIsMonotonic,
             IScalarEvaluatorFactory[] frameEndEvalFactories, IScalarEvaluatorFactory[] frameEndValidationEvalFactories,
             IScalarEvaluatorFactory[] frameExcludeEvalFactories, int frameExcludeNegationStartIdx,
-            IBinaryComparatorFactory[] frameExcludeComparatorFactories, IScalarEvaluatorFactory frameOffsetEvalFactory,
+            IBinaryComparatorFactory[] frameExcludeComparatorFactories,
+            IScalarEvaluatorFactory frameExcludeUnaryEvalFactory, IScalarEvaluatorFactory frameOffsetEvalFactory,
             int frameMaxObjects, IBinaryBooleanInspectorFactory booleanAccessorFactory,
             IBinaryIntegerInspectorFactory integerAccessorFactory, int[] projectionColumns, int[] runningAggOutColumns,
             IRunningAggregateEvaluatorFactory[] runningAggFactories, int nestedAggOutSchemaSize,
@@ -175,6 +180,8 @@ class WindowNestedPlansPushRuntime extends AbstractWindowNestedPlansPushRuntime
         this.frameExcludeExists = frameExcludeEvalFactories != null && frameExcludeEvalFactories.length > 0;
         this.frameExcludeComparatorFactories = frameExcludeComparatorFactories;
         this.frameExcludeNegationStartIdx = frameExcludeNegationStartIdx;
+        this.frameExcludeUnaryExists = frameExcludeUnaryEvalFactory != null;
+        this.frameExcludeUnaryEvalFactory = frameExcludeUnaryEvalFactory;
         this.frameOffsetExists = frameOffsetEvalFactory != null;
         this.frameOffsetEvalFactory = frameOffsetEvalFactory;
         this.frameMaxObjects = frameMaxObjects;
@@ -215,16 +222,22 @@ class WindowNestedPlansPushRuntime extends AbstractWindowNestedPlansPushRuntime
             frameExcludeComparators = createBinaryComparators(frameExcludeComparatorFactories);
             frameExcludePointables =
                     PointableTupleReference.create(frameExcludeEvalFactories.length, VoidPointable.FACTORY);
-            frameExcludePointable2 = VoidPointable.FACTORY.createPointable();
+        }
+        if (frameExcludeUnaryExists) {
+            frameExcludeUnaryEval = frameExcludeUnaryEvalFactory.createScalarEvaluator(ctx);
         }
         if (frameOffsetExists) {
             frameOffsetEval = frameOffsetEvalFactory.createScalarEvaluator(ctx);
-            frameOffsetPointable = VoidPointable.FACTORY.createPointable();
-            integerAccessor = integerAccessorFactory.createBinaryIntegerInspector(ctx);
         }
-        if (frameStartValidationExists || frameEndValidationExists) {
+        if (frameExcludeExists || frameExcludeUnaryExists || frameOffsetExists) {
+            tmpPointable = VoidPointable.FACTORY.createPointable();
+        }
+        if (frameStartValidationExists || frameEndValidationExists || frameExcludeUnaryExists) {
             booleanAccessor = booleanAccessorFactory.createBinaryBooleanInspector(ctx);
         }
+        if (frameOffsetExists) {
+            integerAccessor = integerAccessorFactory.createBinaryIntegerInspector(ctx);
+        }
         tAccess2 = new FrameTupleAccessor(inputRecordDesc);
         tRef2 = new FrameTupleReference();
     }
@@ -259,37 +272,39 @@ class WindowNestedPlansPushRuntime extends AbstractWindowNestedPlansPushRuntime
             nestedAggInit();
 
             // frame boundaries
-            boolean frameStartValid = true;
+            boolean frameValid = true;
             if (frameStartExists) {
                 if (frameStartValidationExists) {
                     evaluate(frameStartValidationEvals, tRef, frameStartValidationPointables);
-                    frameStartValid = allTrue(frameStartValidationPointables, booleanAccessor);
+                    frameValid = allTrue(frameStartValidationPointables, booleanAccessor);
                 }
-                if (frameStartValid) {
+                if (frameValid) {
                     evaluate(frameStartEvals, tRef, frameStartPointables);
                 }
             }
-            boolean frameEndValid = true;
-            if (frameEndExists) {
+
+            if (frameValid && frameEndExists) {
                 if (frameEndValidationExists) {
                     evaluate(frameEndValidationEvals, tRef, frameEndValidationPointables);
-                    frameEndValid = allTrue(frameEndValidationPointables, booleanAccessor);
+                    frameValid = allTrue(frameEndValidationPointables, booleanAccessor);
                 }
-                if (frameEndValid) {
+                if (frameValid) {
                     evaluate(frameEndEvals, tRef, frameEndPointables);
                 }
             }
 
-            if (frameStartValid && frameEndValid) {
+            int toSkip = 0;
+            if (frameValid && frameOffsetExists) {
+                frameOffsetEval.evaluate(tRef, tmpPointable);
+                toSkip = integerAccessor.getIntegerValue(tmpPointable.getByteArray(), tmpPointable.getStartOffset(),
+                        tmpPointable.getLength());
+                frameValid = toSkip >= 0;
+            }
+
+            if (frameValid) {
                 if (frameExcludeExists) {
                     evaluate(frameExcludeEvals, tRef, frameExcludePointables);
                 }
-                int toSkip = 0;
-                if (frameOffsetExists) {
-                    frameOffsetEval.evaluate(tRef, frameOffsetPointable);
-                    toSkip = integerAccessor.getIntegerValue(frameOffsetPointable.getByteArray(),
-                            frameOffsetPointable.getStartOffset(), frameOffsetPointable.getLength());
-                }
                 int toWrite = frameMaxObjects;
 
                 boolean frameStartForward = frameStartIsMonotonic && chunkIdxFrameStartGlobal >= 0;
@@ -346,7 +361,7 @@ class WindowNestedPlansPushRuntime extends AbstractWindowNestedPlansPushRuntime
                                 break frame_loop;
                             }
                         }
-                        if (frameExcludeExists && isExcluded()) {
+                        if ((frameExcludeExists && isExcluded()) || (frameExcludeUnaryExists && isExcludedUnary())) {
                             // skip if excluded
                             continue;
                         }
@@ -390,8 +405,8 @@ class WindowNestedPlansPushRuntime extends AbstractWindowNestedPlansPushRuntime
 
     private boolean isExcluded() throws HyracksDataException {
         for (int i = 0; i < frameExcludeEvals.length; i++) {
-            frameExcludeEvals[i].evaluate(tRef2, frameExcludePointable2);
-            boolean b = DataUtils.compare(frameExcludePointables.getField(i), frameExcludePointable2,
+            frameExcludeEvals[i].evaluate(tRef2, tmpPointable);
+            boolean b = DataUtils.compare(frameExcludePointables.getField(i), tmpPointable,
                     frameExcludeComparators[i]) != 0;
             if (i >= frameExcludeNegationStartIdx) {
                 b = !b;
@@ -403,6 +418,12 @@ class WindowNestedPlansPushRuntime extends AbstractWindowNestedPlansPushRuntime
         return true;
     }
 
+    private boolean isExcludedUnary() throws HyracksDataException {
+        frameExcludeUnaryEval.evaluate(tRef2, tmpPointable);
+        return booleanAccessor.getBooleanValue(tmpPointable.getByteArray(), tmpPointable.getStartOffset(),
+                tmpPointable.getLength());
+    }
+
     @Override
     protected int getPartitionReaderSlotCount() {
         return PARTITION_READER_SLOT_COUNT;
diff --git a/hyracks-fullstack/algebricks/algebricks-runtime/src/main/java/org/apache/hyracks/algebricks/runtime/operators/win/WindowNestedPlansRuntimeFactory.java b/hyracks-fullstack/algebricks/algebricks-runtime/src/main/java/org/apache/hyracks/algebricks/runtime/operators/win/WindowNestedPlansRuntimeFactory.java
index 43b1bf3..174f574 100644
--- a/hyracks-fullstack/algebricks/algebricks-runtime/src/main/java/org/apache/hyracks/algebricks/runtime/operators/win/WindowNestedPlansRuntimeFactory.java
+++ b/hyracks-fullstack/algebricks/algebricks-runtime/src/main/java/org/apache/hyracks/algebricks/runtime/operators/win/WindowNestedPlansRuntimeFactory.java
@@ -57,6 +57,8 @@ public class WindowNestedPlansRuntimeFactory extends AbstractWindowNestedPlansRu
 
     private final IBinaryComparatorFactory[] frameExcludeComparatorFactories;
 
+    private final IScalarEvaluatorFactory frameExcludeUnaryEvalFactory;
+
     private final IScalarEvaluatorFactory frameOffsetEvalFactory;
 
     private final int frameMaxObjects;
@@ -72,7 +74,8 @@ public class WindowNestedPlansRuntimeFactory extends AbstractWindowNestedPlansRu
             IScalarEvaluatorFactory[] frameStartValidationEvalFactories, boolean frameStartIsMonotonic,
             IScalarEvaluatorFactory[] frameEndEvalFactories, IScalarEvaluatorFactory[] frameEndValidationEvalFactories,
             IScalarEvaluatorFactory[] frameExcludeEvalFactories, int frameExcludeNegationStartIdx,
-            IBinaryComparatorFactory[] frameExcludeComparatorFactories, IScalarEvaluatorFactory frameOffsetEvalFactory,
+            IBinaryComparatorFactory[] frameExcludeComparatorFactories,
+            IScalarEvaluatorFactory frameExcludeUnaryEvalFactory, IScalarEvaluatorFactory frameOffsetEvalFactory,
             int frameMaxObjects, IBinaryBooleanInspectorFactory booleanAccessorFactory,
             IBinaryIntegerInspectorFactory integerAccessorFactory, int[] projectionColumnsExcludingSubplans,
             int[] runningAggOutColumns, IRunningAggregateEvaluatorFactory[] runningAggFactories,
@@ -90,6 +93,7 @@ public class WindowNestedPlansRuntimeFactory extends AbstractWindowNestedPlansRu
         this.frameExcludeEvalFactories = frameExcludeEvalFactories;
         this.frameExcludeComparatorFactories = frameExcludeComparatorFactories;
         this.frameExcludeNegationStartIdx = frameExcludeNegationStartIdx;
+        this.frameExcludeUnaryEvalFactory = frameExcludeUnaryEvalFactory;
         this.frameOffsetEvalFactory = frameOffsetEvalFactory;
         this.frameMaxObjects = frameMaxObjects;
         this.booleanAccessorFactory = booleanAccessorFactory;
@@ -102,9 +106,10 @@ public class WindowNestedPlansRuntimeFactory extends AbstractWindowNestedPlansRu
                 orderComparatorFactories, frameValueEvalFactories, frameValueComparatorFactories,
                 frameStartEvalFactories, frameStartValidatinoEvalFactories, frameStartIsMonotonic,
                 frameEndEvalFactories, frameEndValidationEvalFactories, frameExcludeEvalFactories,
-                frameExcludeNegationStartIdx, frameExcludeComparatorFactories, frameOffsetEvalFactory, frameMaxObjects,
-                booleanAccessorFactory, integerAccessorFactory, projectionList, runningAggOutColumns,
-                runningAggFactories, nestedAggOutSchemaSize, nestedAggFactory, ctx, memSizeInFrames, sourceLoc);
+                frameExcludeNegationStartIdx, frameExcludeComparatorFactories, frameExcludeUnaryEvalFactory,
+                frameOffsetEvalFactory, frameMaxObjects, booleanAccessorFactory, integerAccessorFactory, projectionList,
+                runningAggOutColumns, runningAggFactories, nestedAggOutSchemaSize, nestedAggFactory, ctx,
+                memSizeInFrames, sourceLoc);
     }
 
     @Override