[NO ISSUE][FUN] Support FROM LAST in NTH_VALUE()
- user model changes: yes
- storage format changes: no
- interface changes: no
Details:
- Support FROM FIRST / FROM LAST modifiers in window
function NTH_VALUE() with FROM FIRST being the default
- Minor cleanup of SQL++ grammar
- Update JavaCC version to 6.1.2
Change-Id: Iceac579bd5a3e651bcd7707e324148690e020cf5
Reviewed-on: https://asterix-gerrit.ics.uci.edu/3419
Contrib: Jenkins <jenkins@fulliautomatix.ics.uci.edu>
Tested-by: Jenkins <jenkins@fulliautomatix.ics.uci.edu>
Integration-Tests: Jenkins <jenkins@fulliautomatix.ics.uci.edu>
Reviewed-by: Ali Alsuliman <ali.al.solaiman@gmail.com>
diff --git a/asterixdb/asterix-algebra/src/main/java/org/apache/asterix/translator/SqlppExpressionToPlanTranslator.java b/asterixdb/asterix-algebra/src/main/java/org/apache/asterix/translator/SqlppExpressionToPlanTranslator.java
index 9ba6d2c..988723f 100644
--- a/asterixdb/asterix-algebra/src/main/java/org/apache/asterix/translator/SqlppExpressionToPlanTranslator.java
+++ b/asterixdb/asterix-algebra/src/main/java/org/apache/asterix/translator/SqlppExpressionToPlanTranslator.java
@@ -1058,6 +1058,8 @@
BuiltinFunctions.WindowFunctionProperty.NO_FRAME_CLAUSE);
boolean allowRespectIgnoreNulls = isWin && BuiltinFunctions.builtinFunctionHasProperty(fi,
BuiltinFunctions.WindowFunctionProperty.ALLOW_RESPECT_IGNORE_NULLS);
+ boolean allowFromFirstLast = isWin && BuiltinFunctions.builtinFunctionHasProperty(fi,
+ BuiltinFunctions.WindowFunctionProperty.ALLOW_FROM_FIRST_LAST);
Mutable<ILogicalOperator> currentOpRef = tupSource;
@@ -1138,6 +1140,8 @@
boolean respectNulls = !getBooleanModifier(winExpr.getIgnoreNulls(), false, allowRespectIgnoreNulls, sourceLoc,
"RESPECT/IGNORE NULLS", fs.getName());
+ boolean fromLast = getBooleanModifier(winExpr.getFromLast(), false, allowFromFirstLast, sourceLoc,
+ "FROM FIRST/LAST", fs.getName());
boolean makeRunningAgg = false, makeNestedAgg = false;
FunctionIdentifier runningAggFunc = null, nestedAggFunc = null, winResultFunc = null, postWinResultFunc = null;
@@ -1174,9 +1178,7 @@
// IGNORE NULLS
if (isLag) {
// reverse order for LAG()
- for (Pair<OrderOperator.IOrder, Mutable<ILogicalExpression>> orderExprPair : orderExprListOut) {
- orderExprPair.setFirst(reverseOrder(orderExprPair.getFirst()));
- }
+ reverseOrder(orderExprListOut);
}
winFrameStartKind = WindowExpression.FrameBoundaryKind.BOUNDED_FOLLOWING;
winFrameStartExpr = new LiteralExpr(new IntegerLiteral(1));
@@ -1212,6 +1214,10 @@
}
} else if (BuiltinFunctions.NTH_VALUE_IMPL.equals(fi)) {
nestedAggFunc = BuiltinFunctions.SCALAR_FIRST_ELEMENT;
+ if (fromLast) {
+ // reverse order if FROM LAST modifier is present
+ reverseOrder(orderExprListOut);
+ }
if (respectNulls) {
winFrameMaxOjbects = 1;
} else {
@@ -1690,6 +1696,13 @@
return opExpr;
}
+ private static void reverseOrder(List<Pair<OrderOperator.IOrder, Mutable<ILogicalExpression>>> orderExprList)
+ throws CompilationException {
+ for (Pair<OrderOperator.IOrder, Mutable<ILogicalExpression>> orderExprPair : orderExprList) {
+ orderExprPair.setFirst(reverseOrder(orderExprPair.getFirst()));
+ }
+ }
+
private static OrderOperator.IOrder reverseOrder(OrderOperator.IOrder order) throws CompilationException {
switch (order.getKind()) {
case ASC:
diff --git a/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/window/nth_value_01/nth_value_01.2.query.sqlpp b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/window/nth_value_01/nth_value_01.2.query.sqlpp
new file mode 100644
index 0000000..5d7ed88
--- /dev/null
+++ b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/window/nth_value_01/nth_value_01.2.query.sqlpp
@@ -0,0 +1,43 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+/*
+ * Description : NTH_VALUE() FROM FIRST (default), RESPECT/IGNORE NULLS
+ * Expected Res : SUCCESS
+ */
+
+from [
+ { "x": "a", "y": 1, "p": 0 },
+ { "y": 2, "p": 0 },
+ { "x": null, "y": 3, "p": 0 },
+ { "x": "b", "y": 4, "p": 0 },
+
+ { "x": "a", "y": 5, "p": 1 },
+ { "x": null, "y": 6, "p": 1 },
+ { "y": 7, "p": 1 },
+ { "x": "b", "y": 8, "p": 1 }
+] t
+select
+ nth_value(x, 2) from first over (partition by p order by y range between unbounded preceding and unbounded following)
+ as nth_value_default_respect,
+ nth_value(x, 2) from first RESPECT NULLS over (partition by p order by y range between unbounded preceding and unbounded following)
+ as nth_value_respect,
+ nth_value(x, 2) FROM FIRST IGNORE NULLS over (partition by p order by y range between unbounded preceding and unbounded following)
+ as nth_value_ignore,
+ x, y, p
+order by y
\ No newline at end of file
diff --git a/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/window/nth_value_01/nth_value_01.3.query.sqlpp b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/window/nth_value_01/nth_value_01.3.query.sqlpp
new file mode 100644
index 0000000..51f315e
--- /dev/null
+++ b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/window/nth_value_01/nth_value_01.3.query.sqlpp
@@ -0,0 +1,43 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+/*
+ * Description : NTH_VALUE() FROM LAST, RESPECT/IGNORE NULLS
+ * Expected Res : SUCCESS
+ */
+
+from [
+ { "x": "a", "y": 1, "p": 0 },
+ { "y": 2, "p": 0 },
+ { "x": null, "y": 3, "p": 0 },
+ { "x": "b", "y": 4, "p": 0 },
+
+ { "x": "a", "y": 5, "p": 1 },
+ { "x": null, "y": 6, "p": 1 },
+ { "y": 7, "p": 1 },
+ { "x": "b", "y": 8, "p": 1 }
+] t
+select
+ nth_value(x, 2) from last over (partition by p order by y range between unbounded preceding and unbounded following)
+ as nth_value_default_respect,
+ nth_value(x, 2) from last RESPECT NULLS over (partition by p order by y range between unbounded preceding and unbounded following)
+ as nth_value_respect,
+ nth_value(x, 2) FROM LAST IGNORE NULLS over (partition by p order by y range between unbounded preceding and unbounded following)
+ as nth_value_ignore,
+ x, y, p
+order by y
\ No newline at end of file
diff --git a/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/window/nth_value_01/nth_value_01.4.query.sqlpp b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/window/nth_value_01/nth_value_01.4.query.sqlpp
new file mode 100644
index 0000000..e17155a
--- /dev/null
+++ b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/window/nth_value_01/nth_value_01.4.query.sqlpp
@@ -0,0 +1,46 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+/*
+ * Description : NTH_VALUE() FROM FIRST/LAST, RESPECT/IGNORE NULLS
+ * : that exceeds frame boundary
+ * Expected Res : SUCCESS
+ */
+
+from [
+ { "x": "a", "y": 1, "p": 0 },
+ { "y": 2, "p": 0 },
+ { "x": null, "y": 3, "p": 0 },
+ { "x": "b", "y": 4, "p": 0 },
+
+ { "x": "a", "y": 5, "p": 1 },
+ { "x": null, "y": 6, "p": 1 },
+ { "y": 7, "p": 1 },
+ { "x": "b", "y": 8, "p": 1 }
+] t
+select
+ nth_value(x, 5) from first respect nulls over (partition by p order by y range between unbounded preceding and unbounded following)
+ as nth_value_first_respect,
+ nth_value(x, 3) from first ignore nulls over (partition by p order by y range between unbounded preceding and unbounded following)
+ as nth_value_first_ignore,
+ nth_value(x, 5) from last respect nulls over (partition by p order by y range between unbounded preceding and unbounded following)
+ as nth_value_last_respect,
+ nth_value(x, 3) from last ignore nulls over (partition by p order by y range between unbounded preceding and unbounded following)
+ as nth_value_last_ignore,
+ x, y, p
+order by y
\ No newline at end of file
diff --git a/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/window/nth_value_01/nth_value_01.5.query.sqlpp b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/window/nth_value_01/nth_value_01.5.query.sqlpp
new file mode 100644
index 0000000..7c62cdb
--- /dev/null
+++ b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/window/nth_value_01/nth_value_01.5.query.sqlpp
@@ -0,0 +1,28 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+/*
+ * Description : Test FROM FIRST/LAST outside of a window function call
+ * Expected Res : SUCCESS
+ */
+
+with first as [1, 2, 3], last as [1, 2, 1, 2]
+select count(`over`) c, sum(`over`) from first `over`
+union all
+select count(`over`) c, sum(`over`) from last `over`
+order by c
diff --git a/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/window/win_negative/win_negative.8.query.sqlpp b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/window/win_negative/win_negative.8.query.sqlpp
new file mode 100644
index 0000000..73344ce
--- /dev/null
+++ b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/window/win_negative/win_negative.8.query.sqlpp
@@ -0,0 +1,25 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+/*
+ * Description : Function that doesn't support FROM FIRST/LAST
+ * Expected Res : FAILURE
+ */
+
+from range(1, 4) x
+select value first_value(x) from first over (order by x)
\ No newline at end of file
diff --git a/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/window/win_negative/win_negative.9.query.sqlpp b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/window/win_negative/win_negative.9.query.sqlpp
new file mode 100644
index 0000000..c03e763
--- /dev/null
+++ b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/window/win_negative/win_negative.9.query.sqlpp
@@ -0,0 +1,25 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+/*
+ * Description : Function that doesn't support FROM FIRST/LAST
+ * Expected Res : FAILURE
+ */
+
+from range(1, 4) x
+select value first_value(x) from last over (order by x)
\ No newline at end of file
diff --git a/asterixdb/asterix-app/src/test/resources/runtimets/results/window/nth_value_01/nth_value_01.2.adm b/asterixdb/asterix-app/src/test/resources/runtimets/results/window/nth_value_01/nth_value_01.2.adm
new file mode 100644
index 0000000..55ac6c6
--- /dev/null
+++ b/asterixdb/asterix-app/src/test/resources/runtimets/results/window/nth_value_01/nth_value_01.2.adm
@@ -0,0 +1,8 @@
+{ "nth_value_ignore": "b", "x": "a", "y": 1, "p": 0 }
+{ "nth_value_ignore": "b", "y": 2, "p": 0 }
+{ "nth_value_ignore": "b", "x": null, "y": 3, "p": 0 }
+{ "nth_value_ignore": "b", "x": "b", "y": 4, "p": 0 }
+{ "nth_value_default_respect": null, "nth_value_respect": null, "nth_value_ignore": "b", "x": "a", "y": 5, "p": 1 }
+{ "nth_value_default_respect": null, "nth_value_respect": null, "nth_value_ignore": "b", "x": null, "y": 6, "p": 1 }
+{ "nth_value_default_respect": null, "nth_value_respect": null, "nth_value_ignore": "b", "y": 7, "p": 1 }
+{ "nth_value_default_respect": null, "nth_value_respect": null, "nth_value_ignore": "b", "x": "b", "y": 8, "p": 1 }
\ No newline at end of file
diff --git a/asterixdb/asterix-app/src/test/resources/runtimets/results/window/nth_value_01/nth_value_01.3.adm b/asterixdb/asterix-app/src/test/resources/runtimets/results/window/nth_value_01/nth_value_01.3.adm
new file mode 100644
index 0000000..7ccb3f9
--- /dev/null
+++ b/asterixdb/asterix-app/src/test/resources/runtimets/results/window/nth_value_01/nth_value_01.3.adm
@@ -0,0 +1,8 @@
+{ "nth_value_default_respect": null, "nth_value_respect": null, "nth_value_ignore": "a", "x": "a", "y": 1, "p": 0 }
+{ "nth_value_default_respect": null, "nth_value_respect": null, "nth_value_ignore": "a", "y": 2, "p": 0 }
+{ "nth_value_default_respect": null, "nth_value_respect": null, "nth_value_ignore": "a", "x": null, "y": 3, "p": 0 }
+{ "nth_value_default_respect": null, "nth_value_respect": null, "nth_value_ignore": "a", "x": "b", "y": 4, "p": 0 }
+{ "nth_value_ignore": "a", "x": "a", "y": 5, "p": 1 }
+{ "nth_value_ignore": "a", "x": null, "y": 6, "p": 1 }
+{ "nth_value_ignore": "a", "y": 7, "p": 1 }
+{ "nth_value_ignore": "a", "x": "b", "y": 8, "p": 1 }
\ No newline at end of file
diff --git a/asterixdb/asterix-app/src/test/resources/runtimets/results/window/nth_value_01/nth_value_01.4.adm b/asterixdb/asterix-app/src/test/resources/runtimets/results/window/nth_value_01/nth_value_01.4.adm
new file mode 100644
index 0000000..4faf9b1
--- /dev/null
+++ b/asterixdb/asterix-app/src/test/resources/runtimets/results/window/nth_value_01/nth_value_01.4.adm
@@ -0,0 +1,8 @@
+{ "nth_value_first_respect": null, "nth_value_first_ignore": null, "nth_value_last_respect": null, "nth_value_last_ignore": null, "x": "a", "y": 1, "p": 0 }
+{ "nth_value_first_respect": null, "nth_value_first_ignore": null, "nth_value_last_respect": null, "nth_value_last_ignore": null, "y": 2, "p": 0 }
+{ "nth_value_first_respect": null, "nth_value_first_ignore": null, "nth_value_last_respect": null, "nth_value_last_ignore": null, "x": null, "y": 3, "p": 0 }
+{ "nth_value_first_respect": null, "nth_value_first_ignore": null, "nth_value_last_respect": null, "nth_value_last_ignore": null, "x": "b", "y": 4, "p": 0 }
+{ "nth_value_first_respect": null, "nth_value_first_ignore": null, "nth_value_last_respect": null, "nth_value_last_ignore": null, "x": "a", "y": 5, "p": 1 }
+{ "nth_value_first_respect": null, "nth_value_first_ignore": null, "nth_value_last_respect": null, "nth_value_last_ignore": null, "x": null, "y": 6, "p": 1 }
+{ "nth_value_first_respect": null, "nth_value_first_ignore": null, "nth_value_last_respect": null, "nth_value_last_ignore": null, "y": 7, "p": 1 }
+{ "nth_value_first_respect": null, "nth_value_first_ignore": null, "nth_value_last_respect": null, "nth_value_last_ignore": null, "x": "b", "y": 8, "p": 1 }
\ No newline at end of file
diff --git a/asterixdb/asterix-app/src/test/resources/runtimets/results/window/nth_value_01/nth_value_01.5.adm b/asterixdb/asterix-app/src/test/resources/runtimets/results/window/nth_value_01/nth_value_01.5.adm
new file mode 100644
index 0000000..e062cda
--- /dev/null
+++ b/asterixdb/asterix-app/src/test/resources/runtimets/results/window/nth_value_01/nth_value_01.5.adm
@@ -0,0 +1,2 @@
+{ "c": 3, "$1": 6 }
+{ "c": 4, "$2": 6 }
\ No newline at end of file
diff --git a/asterixdb/asterix-app/src/test/resources/runtimets/testsuite_sqlpp.xml b/asterixdb/asterix-app/src/test/resources/runtimets/testsuite_sqlpp.xml
index d5fa013..d1098dc 100644
--- a/asterixdb/asterix-app/src/test/resources/runtimets/testsuite_sqlpp.xml
+++ b/asterixdb/asterix-app/src/test/resources/runtimets/testsuite_sqlpp.xml
@@ -10424,6 +10424,8 @@
<expected-error>ASX1079: Compilation error: count is a SQL-92 aggregate function</expected-error>
<expected-error>ASX1104: Invalid modifier RESPECT/IGNORE NULLS for function</expected-error>
<expected-error>ASX1104: Invalid modifier RESPECT/IGNORE NULLS for function</expected-error>
+ <expected-error>ASX1104: Invalid modifier FROM FIRST/LAST for function</expected-error>
+ <expected-error>ASX1104: Invalid modifier FROM FIRST/LAST for function</expected-error>
<source-location>false</source-location>
</compilation-unit>
</test-case>
diff --git a/asterixdb/asterix-lang-sqlpp/src/main/java/org/apache/asterix/lang/sqlpp/expression/WindowExpression.java b/asterixdb/asterix-lang-sqlpp/src/main/java/org/apache/asterix/lang/sqlpp/expression/WindowExpression.java
index 5a9173b..e0a6c6b 100644
--- a/asterixdb/asterix-lang-sqlpp/src/main/java/org/apache/asterix/lang/sqlpp/expression/WindowExpression.java
+++ b/asterixdb/asterix-lang-sqlpp/src/main/java/org/apache/asterix/lang/sqlpp/expression/WindowExpression.java
@@ -55,13 +55,14 @@
private List<Pair<Expression, Identifier>> windowFieldList;
private Boolean ignoreNulls;
+ private Boolean fromLast;
public WindowExpression(FunctionSignature functionSignature, List<Expression> exprList,
List<Expression> partitionList, List<Expression> orderbyList,
List<OrderbyClause.OrderModifier> orderbyModifierList, FrameMode frameMode,
FrameBoundaryKind frameStartKind, Expression frameStartExpr, FrameBoundaryKind frameEndKind,
Expression frameEndExpr, FrameExclusionKind frameExclusionKind, VariableExpr windowVar,
- List<Pair<Expression, Identifier>> windowFieldList, Boolean ignoreNulls) {
+ List<Pair<Expression, Identifier>> windowFieldList, Boolean ignoreNulls, Boolean fromLast) {
if (functionSignature == null || exprList == null) {
throw new NullPointerException();
}
@@ -79,6 +80,7 @@
this.windowVar = windowVar;
this.windowFieldList = windowFieldList;
this.ignoreNulls = ignoreNulls;
+ this.fromLast = fromLast;
}
@Override
@@ -232,12 +234,20 @@
this.ignoreNulls = ignoreNulls;
}
+ public Boolean getFromLast() {
+ return fromLast;
+ }
+
+ public void setFromLast(Boolean fromLast) {
+ this.fromLast = fromLast;
+ }
+
@Override
public int hashCode() {
return Objects.hash(functionSignature, exprList, ExpressionUtils.emptyIfNull(partitionList),
ExpressionUtils.emptyIfNull(orderbyList), ExpressionUtils.emptyIfNull(orderbyModifierList), frameMode,
frameStartKind, frameStartExpr, frameEndKind, frameEndExpr, frameExclusionKind, windowVar,
- ExpressionUtils.emptyIfNull(windowFieldList), ignoreNulls);
+ ExpressionUtils.emptyIfNull(windowFieldList), ignoreNulls, fromLast);
}
@Override
@@ -263,7 +273,7 @@
&& Objects.equals(windowVar, target.windowVar)
&& Objects.equals(ExpressionUtils.emptyIfNull(windowFieldList),
ExpressionUtils.emptyIfNull(target.windowFieldList))
- && Objects.equals(ignoreNulls, target.ignoreNulls);
+ && Objects.equals(ignoreNulls, target.ignoreNulls) && Objects.equals(fromLast, target.fromLast);
}
@Override
@@ -274,6 +284,9 @@
sb.append('(');
sb.append(StringUtils.join(exprList, ','));
sb.append(')');
+ if (fromLast != null && fromLast) {
+ sb.append(" FROM LAST");
+ }
if (ignoreNulls != null && ignoreNulls) {
sb.append(" IGNORE NULLS");
}
diff --git a/asterixdb/asterix-lang-sqlpp/src/main/java/org/apache/asterix/lang/sqlpp/visitor/DeepCopyVisitor.java b/asterixdb/asterix-lang-sqlpp/src/main/java/org/apache/asterix/lang/sqlpp/visitor/DeepCopyVisitor.java
index 3e07177..6e28d36 100644
--- a/asterixdb/asterix-lang-sqlpp/src/main/java/org/apache/asterix/lang/sqlpp/visitor/DeepCopyVisitor.java
+++ b/asterixdb/asterix-lang-sqlpp/src/main/java/org/apache/asterix/lang/sqlpp/visitor/DeepCopyVisitor.java
@@ -525,7 +525,7 @@
WindowExpression copy = new WindowExpression(winExpr.getFunctionSignature(), newExprList, newPartitionList,
newOrderbyList, newOrderbyModifierList, winExpr.getFrameMode(), winExpr.getFrameStartKind(),
newFrameStartExpr, winExpr.getFrameEndKind(), newFrameEndExpr, winExpr.getFrameExclusionKind(),
- newWindowVar, newWindowFieldList, winExpr.getIgnoreNulls());
+ newWindowVar, newWindowFieldList, winExpr.getIgnoreNulls(), winExpr.getFromLast());
copy.setSourceLocation(winExpr.getSourceLocation());
copy.addHints(winExpr.getHints());
return copy;
diff --git a/asterixdb/asterix-lang-sqlpp/src/main/java/org/apache/asterix/lang/sqlpp/visitor/SqlppCloneAndSubstituteVariablesVisitor.java b/asterixdb/asterix-lang-sqlpp/src/main/java/org/apache/asterix/lang/sqlpp/visitor/SqlppCloneAndSubstituteVariablesVisitor.java
index 4b76b56..20fd0f5 100644
--- a/asterixdb/asterix-lang-sqlpp/src/main/java/org/apache/asterix/lang/sqlpp/visitor/SqlppCloneAndSubstituteVariablesVisitor.java
+++ b/asterixdb/asterix-lang-sqlpp/src/main/java/org/apache/asterix/lang/sqlpp/visitor/SqlppCloneAndSubstituteVariablesVisitor.java
@@ -420,10 +420,11 @@
winExpr.hasWindowVar() ? (VariableExpr) winExpr.getWindowVar().accept(this, env).first : null;
List<Pair<Expression, Identifier>> newWindowFieldList = winExpr.hasWindowFieldList()
? VariableCloneAndSubstitutionUtil.substInFieldList(winExpr.getWindowFieldList(), env, this) : null;
- WindowExpression newWinExpr = new WindowExpression(winExpr.getFunctionSignature(), newExprList,
- newPartitionList, newOrderbyList, newOrderbyModifierList, winExpr.getFrameMode(),
- winExpr.getFrameStartKind(), newFrameStartExpr, winExpr.getFrameEndKind(), newFrameEndExpr,
- winExpr.getFrameExclusionKind(), newWindowVar, newWindowFieldList, winExpr.getIgnoreNulls());
+ WindowExpression newWinExpr =
+ new WindowExpression(winExpr.getFunctionSignature(), newExprList, newPartitionList, newOrderbyList,
+ newOrderbyModifierList, winExpr.getFrameMode(), winExpr.getFrameStartKind(), newFrameStartExpr,
+ winExpr.getFrameEndKind(), newFrameEndExpr, winExpr.getFrameExclusionKind(), newWindowVar,
+ newWindowFieldList, winExpr.getIgnoreNulls(), winExpr.getFromLast());
newWinExpr.setSourceLocation(winExpr.getSourceLocation());
newWinExpr.addHints(winExpr.getHints());
return new Pair<>(newWinExpr, env);
diff --git a/asterixdb/asterix-lang-sqlpp/src/main/javacc/SQLPP.jj b/asterixdb/asterix-lang-sqlpp/src/main/javacc/SQLPP.jj
index c0f1725..25ad2ab 100644
--- a/asterixdb/asterix-lang-sqlpp/src/main/javacc/SQLPP.jj
+++ b/asterixdb/asterix-lang-sqlpp/src/main/javacc/SQLPP.jj
@@ -191,9 +191,11 @@
// tokens parsed as identifiers
private static final String CURRENT = "CURRENT";
private static final String EXCLUDE = "EXCLUDE";
+ private static final String FIRST = "FIRST";
private static final String FOLLOWING = "FOLLOWING";
private static final String GROUPS = "GROUPS";
private static final String IGNORE = "IGNORE";
+ private static final String LAST = "LAST";
private static final String NO = "NO";
private static final String NULLS = "NULLS";
private static final String OTHERS = "OTHERS";
@@ -1973,7 +1975,7 @@
Token startToken = null;
}
{
- (<NOT> { not = true; startToken = token; } )? inputExpr = RelExpr()
+ (LOOKAHEAD(2) <NOT> { not = true; startToken = token; } )? inputExpr = RelExpr()
{
if(not) {
FunctionSignature signature = new FunctionSignature(BuiltinFunctions.NOT);
@@ -2749,14 +2751,60 @@
resultExpr = callExpr;
}
- ( resultExpr = WindowExpr(callExpr.getFunctionSignature(), callExpr.getExprList(), token) )?
+ ( LOOKAHEAD(5) resultExpr = WindowExpr(callExpr.getFunctionSignature(), callExpr.getExprList()) )?
{
return resultExpr;
}
}
-WindowExpression WindowExpr(FunctionSignature signature, List<Expression> argList, Token startToken) throws ParseException:
+WindowExpression WindowExpr(FunctionSignature signature, List<Expression> argList) throws ParseException:
+{
+ Boolean fromLast = null, ignoreNulls = null;
+}
+{
+ (
+ // FROM ( FIRST | LAST ) ( ( RESPECT | IGNORE ) NULLS )? OVER
+ LOOKAHEAD(5, <FROM> <IDENTIFIER> ( <IDENTIFIER> <IDENTIFIER> )? <OVER>)
+ <FROM> <IDENTIFIER>
+ {
+ if (isToken(FIRST)) {
+ fromLast = false;
+ } else if (isToken(LAST)) {
+ fromLast = true;
+ } else {
+ throw createUnexpectedTokenError();
+ }
+ }
+ )?
+ (
+ // ( RESPECT | IGNORE ) NULLS OVER
+ LOOKAHEAD(3, <IDENTIFIER> <IDENTIFIER> <OVER>)
+ <IDENTIFIER>
+ {
+ if (isToken(RESPECT)) {
+ ignoreNulls = false;
+ } else if (isToken(IGNORE)) {
+ ignoreNulls = true;
+ } else {
+ throw createUnexpectedTokenError();
+ }
+ }
+ <IDENTIFIER>
+ {
+ if (!isToken(NULLS)) {
+ throw createUnexpectedTokenError();
+ }
+ }
+ )?
+ <OVER>
+ {
+ return OverClause(signature, argList, token, fromLast, ignoreNulls);
+ }
+}
+
+WindowExpression OverClause(FunctionSignature signature, List<Expression> argList, Token startToken, Boolean fromLast,
+ Boolean ignoreNulls) throws ParseException:
{
Expression partitionExpr = null;
List<Expression> partitionExprs = new ArrayList<Expression>();
@@ -2771,18 +2819,9 @@
Pair<VariableExpr, List<Pair<Expression, Identifier>>> windowVarWithFieldList = null;
VariableExpr windowVar = null;
List<Pair<Expression, Identifier>> windowFieldList = null;
- Boolean ignoreNulls = null;
}
{
(
- (
- LOOKAHEAD({ laIdentifier(RESPECT) }) <IDENTIFIER> { ignoreNulls = false; }
- | LOOKAHEAD({ laIdentifier(IGNORE) }) <IDENTIFIER> { ignoreNulls = true; }
- )
- LOOKAHEAD({ laIdentifier(NULLS) }) <IDENTIFIER>
- )?
- <OVER>
- (
windowVarWithFieldList = VariableWithFieldMap() <AS>
{
windowVar = windowVarWithFieldList.first;
@@ -2827,7 +2866,7 @@
{
WindowExpression winExp = new WindowExpression(signature, argList, partitionExprs, orderbyList, orderbyModifierList,
frameMode, frameStartKind, frameStartExpr, frameEndKind, frameEndExpr, frameExclusionKind, windowVar,
- windowFieldList, ignoreNulls);
+ windowFieldList, ignoreNulls, fromLast);
return addSourceLocation(winExp, startToken);
}
}
@@ -2857,8 +2896,7 @@
}
{
(
- LOOKAHEAD({ laIdentifier(CURRENT) }) <IDENTIFIER> { current = true; }
- | LOOKAHEAD({ laIdentifier(UNBOUNDED) }) <IDENTIFIER>
+ LOOKAHEAD({ laIdentifier(CURRENT) || laIdentifier(UNBOUNDED) }) <IDENTIFIER> { current = isToken(CURRENT); }
| expr = Expression()
)
<IDENTIFIER>
diff --git a/asterixdb/asterix-om/src/main/java/org/apache/asterix/om/functions/BuiltinFunctions.java b/asterixdb/asterix-om/src/main/java/org/apache/asterix/om/functions/BuiltinFunctions.java
index 3bf3514..06dcb89 100644
--- a/asterixdb/asterix-om/src/main/java/org/apache/asterix/om/functions/BuiltinFunctions.java
+++ b/asterixdb/asterix-om/src/main/java/org/apache/asterix/om/functions/BuiltinFunctions.java
@@ -18,6 +18,7 @@
*/
package org.apache.asterix.om.functions;
+import static org.apache.asterix.om.functions.BuiltinFunctions.WindowFunctionProperty.ALLOW_FROM_FIRST_LAST;
import static org.apache.asterix.om.functions.BuiltinFunctions.WindowFunctionProperty.ALLOW_RESPECT_IGNORE_NULLS;
import static org.apache.asterix.om.functions.BuiltinFunctions.WindowFunctionProperty.HAS_LIST_ARG;
import static org.apache.asterix.om.functions.BuiltinFunctions.WindowFunctionProperty.INJECT_ORDER_ARGS;
@@ -3030,8 +3031,10 @@
INJECT_ORDER_ARGS,
/** Whether a running aggregate requires partition materialization runtime */
MATERIALIZE_PARTITION,
+ /** Whether FROM (FIRST | LAST) modifier is allowed */
+ ALLOW_FROM_FIRST_LAST,
/** Whether (RESPECT | IGNORE) NULLS modifier is allowed */
- ALLOW_RESPECT_IGNORE_NULLS,
+ ALLOW_RESPECT_IGNORE_NULLS
}
static {
@@ -3042,7 +3045,7 @@
addWindowFunction(LAG, LAG_IMPL, NO_FRAME_CLAUSE, HAS_LIST_ARG, ALLOW_RESPECT_IGNORE_NULLS);
addWindowFunction(LAST_VALUE, LAST_VALUE_IMPL, HAS_LIST_ARG, ALLOW_RESPECT_IGNORE_NULLS);
addWindowFunction(LEAD, LEAD_IMPL, NO_FRAME_CLAUSE, HAS_LIST_ARG, ALLOW_RESPECT_IGNORE_NULLS);
- addWindowFunction(NTH_VALUE, NTH_VALUE_IMPL, HAS_LIST_ARG, ALLOW_RESPECT_IGNORE_NULLS);
+ addWindowFunction(NTH_VALUE, NTH_VALUE_IMPL, HAS_LIST_ARG, ALLOW_FROM_FIRST_LAST, ALLOW_RESPECT_IGNORE_NULLS);
addWindowFunction(NTILE, NTILE_IMPL, NO_FRAME_CLAUSE, MATERIALIZE_PARTITION);
addWindowFunction(PERCENT_RANK, PERCENT_RANK_IMPL, NO_FRAME_CLAUSE, INJECT_ORDER_ARGS, MATERIALIZE_PARTITION);
addWindowFunction(RANK, RANK_IMPL, NO_FRAME_CLAUSE, INJECT_ORDER_ARGS);
diff --git a/asterixdb/pom.xml b/asterixdb/pom.xml
index 3e65e5f..0149810 100644
--- a/asterixdb/pom.xml
+++ b/asterixdb/pom.xml
@@ -568,6 +568,13 @@
<groupId>org.codehaus.mojo</groupId>
<artifactId>javacc-maven-plugin</artifactId>
<version>2.6</version>
+ <dependencies>
+ <dependency>
+ <groupId>net.java.dev.javacc</groupId>
+ <artifactId>javacc</artifactId>
+ <version>6.1.2</version>
+ </dependency>
+ </dependencies>
</plugin>
<plugin>
<groupId>net.revelc.code.formatter</groupId>