[NO ISSUE][RT] Support negative positions in path expression
- user model changes: no
- storage format changes: no
- interface changes: no
Details:
- Support negative array index in path expressions:
array[-1] addresses the last element,
array[-2] next to last, and so on
- Add testcase and update documentation
Change-Id: Ib2bdeab42d3bd37c21d860dda583a2762a74e2fb
Reviewed-on: https://asterix-gerrit.ics.uci.edu/c/asterixdb/+/5845
Integration-Tests: Jenkins <jenkins@fulliautomatix.ics.uci.edu>
Tested-by: Jenkins <jenkins@fulliautomatix.ics.uci.edu>
Reviewed-by: Dmitry Lychagin <dmitry.lychagin@couchbase.com>
Reviewed-by: Ali Alsuliman <ali.al.solaiman@gmail.com>
diff --git a/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/list/get-item_03/get-item_03.4.query.sqlpp b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/list/get-item_03/get-item_03.4.query.sqlpp
new file mode 100644
index 0000000..ab27331
--- /dev/null
+++ b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/list/get-item_03/get-item_03.4.query.sqlpp
@@ -0,0 +1,31 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+use test;
+
+{
+"ta1": [1, 2, 3][-1],
+"ta2": [1, 2, 3][-2],
+"ta3": [1, 2, 3][-3],
+"ta4": [1, 2, 3][-4] is missing,
+"tb1": [1, 2, 3][-((select value id from test where id = 1)[0])],
+"tb2": [1, 2, 3][-((select value id from test where id = 1)[0])-1],
+"tb3": [1, 2, 3][-((select value id from test where id = 1)[0])-2],
+"tb4": [1, 2, 3][-((select value id from test where id = 1)[0])-3] is missing
+};
diff --git a/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/misc/case_01/case_01.1.query.sqlpp b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/misc/case_01/case_01.1.query.sqlpp
index c2a4cea..7f9647b 100644
--- a/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/misc/case_01/case_01.1.query.sqlpp
+++ b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/misc/case_01/case_01.1.query.sqlpp
@@ -24,5 +24,5 @@
WHEN MISSING THEN -2
ELSE 2.0/t
END
-FROM [0, 1, 2, 4, NULL, [0][-1]] t;
+FROM [0, 1, 2, 4, NULL, MISSING] t;
diff --git a/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/misc/case_02/case_02.1.query.sqlpp b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/misc/case_02/case_02.1.query.sqlpp
index ab19bcb..095eb20 100644
--- a/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/misc/case_02/case_02.1.query.sqlpp
+++ b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/misc/case_02/case_02.1.query.sqlpp
@@ -24,6 +24,6 @@
WHEN t IS MISSING THEN -2
ELSE 2.0/t
END
-FROM [0, 1, 2, 4, NULL, [0][-1]] t;
+FROM [0, 1, 2, 4, NULL, MISSING] t;
diff --git a/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/misc/case_03/case_03.1.query.sqlpp b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/misc/case_03/case_03.1.query.sqlpp
index 7c3e566..12cd3f6 100644
--- a/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/misc/case_03/case_03.1.query.sqlpp
+++ b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/misc/case_03/case_03.1.query.sqlpp
@@ -24,5 +24,5 @@
WHEN t IS MISSING THEN (SELECT -2 AS r)
ELSE (SELECT -3 AS r)
END
-FROM [0, 1, 2, 4, NULL, [0][-1]] t;
+FROM [0, 1, 2, 4, NULL, MISSING] t;
diff --git a/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/misc/case_04/case_04.1.query.sqlpp b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/misc/case_04/case_04.1.query.sqlpp
index 154e611..e99da85 100644
--- a/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/misc/case_04/case_04.1.query.sqlpp
+++ b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/misc/case_04/case_04.1.query.sqlpp
@@ -24,5 +24,5 @@
WHEN t IS MISSING THEN (SELECT -2)
ELSE 2.0/t
END
-FROM [0, 1, 2, 4, NULL, [0][-1]] t;
+FROM [0, 1, 2, 4, NULL, MISSING] t;
diff --git a/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/misc/case_05/case_05.1.query.sqlpp b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/misc/case_05/case_05.1.query.sqlpp
index 32030a7..5940b19 100644
--- a/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/misc/case_05/case_05.1.query.sqlpp
+++ b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/misc/case_05/case_05.1.query.sqlpp
@@ -26,5 +26,5 @@
WHEN t=2 THEN MISSING
ELSE 2.0/t
END
-FROM [0, 1, 2, 4, NULL, [0][-1]] t;
+FROM [0, 1, 2, 4, NULL, MISSING] t;
diff --git a/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/misc/case_06/case_06.1.query.sqlpp b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/misc/case_06/case_06.1.query.sqlpp
index 5bf2786..8fbaa7a 100644
--- a/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/misc/case_06/case_06.1.query.sqlpp
+++ b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/misc/case_06/case_06.1.query.sqlpp
@@ -22,5 +22,5 @@
WHEN t = 0 THEN MISSING
ELSE NULL
END
-FROM [0, 1, 2, 4, NULL, [0][-1]] t;
+FROM [0, 1, 2, 4, NULL, MISSING] t;
diff --git a/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/misc/case_07/case_07.1.query.sqlpp b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/misc/case_07/case_07.1.query.sqlpp
index 4850047..27180b5 100644
--- a/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/misc/case_07/case_07.1.query.sqlpp
+++ b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/misc/case_07/case_07.1.query.sqlpp
@@ -27,4 +27,4 @@
WHEN t IS UNKNOWN THEN (SELECT -3) // Should never enter this THEN branch.
ELSE 2.0/t
END
-FROM [0, 1, 2, 4, NULL, [0][-1]] t;
+FROM [0, 1, 2, 4, NULL, MISSING] t;
diff --git a/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/numeric/add_double/add_double.1.query.sqlpp b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/numeric/add_double/add_double.1.query.sqlpp
index 366f2b1..a5b1e1b 100644
--- a/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/numeric/add_double/add_double.1.query.sqlpp
+++ b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/numeric/add_double/add_double.1.query.sqlpp
@@ -18,4 +18,4 @@
*/
-{'result1':(double('-6.5d') + tinyint('+1')),'result2':(double('-6.5d') + smallint('2')),'result3':(double('-6.5d') + integer('+3')),'result4':(double('-6.5d') + bigint('-4')),'result5':(double('-6.5d') + float('-5.5f')),'result6':(double('-6.5d') + double('-6.5d')),'result7':(double('-6.5d') + null), 'result8':double('-6.5d') + [1.0][-1]};
+{'result1':(double('-6.5d') + tinyint('+1')),'result2':(double('-6.5d') + smallint('2')),'result3':(double('-6.5d') + integer('+3')),'result4':(double('-6.5d') + bigint('-4')),'result5':(double('-6.5d') + float('-5.5f')),'result6':(double('-6.5d') + double('-6.5d')),'result7':(double('-6.5d') + null), 'result8':double('-6.5d') + MISSING};
diff --git a/asterixdb/asterix-app/src/test/resources/runtimets/results/list/get-item_03/get-item_03.1.adm b/asterixdb/asterix-app/src/test/resources/runtimets/results/list/get-item_03/get-item_03.3.adm
similarity index 100%
rename from asterixdb/asterix-app/src/test/resources/runtimets/results/list/get-item_03/get-item_03.1.adm
rename to asterixdb/asterix-app/src/test/resources/runtimets/results/list/get-item_03/get-item_03.3.adm
diff --git a/asterixdb/asterix-app/src/test/resources/runtimets/results/list/get-item_03/get-item_03.4.adm b/asterixdb/asterix-app/src/test/resources/runtimets/results/list/get-item_03/get-item_03.4.adm
new file mode 100644
index 0000000..27ec71f
--- /dev/null
+++ b/asterixdb/asterix-app/src/test/resources/runtimets/results/list/get-item_03/get-item_03.4.adm
@@ -0,0 +1 @@
+{ "ta1": 3, "ta2": 2, "ta3": 1, "ta4": true, "tb1": 3, "tb2": 2, "tb3": 1, "tb4": true }
diff --git a/asterixdb/asterix-app/src/test/resources/runtimets/results_parser_sqlpp/numeric/add_double/add_double.1.ast b/asterixdb/asterix-app/src/test/resources/runtimets/results_parser_sqlpp/numeric/add_double/add_double.1.ast
index 4550cfe..7e1443c 100644
--- a/asterixdb/asterix-app/src/test/resources/runtimets/results_parser_sqlpp/numeric/add_double/add_double.1.ast
+++ b/asterixdb/asterix-app/src/test/resources/runtimets/results_parser_sqlpp/numeric/add_double/add_double.1.ast
@@ -97,12 +97,7 @@
LiteralExpr [STRING] [-6.5d]
]
+
- IndexAccessor [
- OrderedListConstructor [
- LiteralExpr [DOUBLE] [1.0]
- ]
- Index: - LiteralExpr [LONG] [1]
- ]
+ LiteralExpr [MISSING]
]
)
]
diff --git a/asterixdb/asterix-doc/src/main/markdown/sqlpp/2_expr.md b/asterixdb/asterix-doc/src/main/markdown/sqlpp/2_expr.md
index 37e7f79..2e0b526 100644
--- a/asterixdb/asterix-doc/src/main/markdown/sqlpp/2_expr.md
+++ b/asterixdb/asterix-doc/src/main/markdown/sqlpp/2_expr.md
@@ -218,8 +218,9 @@
single element from an array, or a whole subset of an array. Accessing a single element is achieved by
providing a single index argument (zero-based element position), while obtaining a subset of an array is achieved by
providing the `start` and `end` (zero-based) index positions; the returned subset is from position `start` to position
-`end - 1`; the `end` position argument is optional. Multisets have similar behavior to arrays, except for retrieving
-arbitrary items as the order of items is not fixed in multisets.
+`end - 1`; the `end` position argument is optional. If a position argument is negative then the element position is
+counted from the end of the array (`-1` addresses the last element, `-2` next to last, and so on). Multisets have
+similar behavior to arrays, except for retrieving arbitrary items as the order of items is not fixed in multisets.
Attempts to access non-existent fields or out-of-bound array elements produce the special value `MISSING`. Type errors
will be raised for inappropriate use of a path expression, such as applying a field accessor to a numeric value.
@@ -232,12 +233,16 @@
({"name": "MyABCs", "array": [ "a", "b", "c"]}).array
(["a", "b", "c"])[2]
+
+ (["a", "b", "c"])[-1]
({"name": "MyABCs", "array": [ "a", "b", "c"]}).array[2]
(["a", "b", "c"])[0:2]
(["a", "b", "c"])[0:]
+
+ (["a", "b", "c"])[-2:-1]
## <a id="Primary_expressions">Primary Expressions</a>
diff --git a/asterixdb/asterix-runtime/src/main/java/org/apache/asterix/runtime/evaluators/functions/GetItemDescriptor.java b/asterixdb/asterix-runtime/src/main/java/org/apache/asterix/runtime/evaluators/functions/GetItemDescriptor.java
index f7bddf1..fb71eae 100644
--- a/asterixdb/asterix-runtime/src/main/java/org/apache/asterix/runtime/evaluators/functions/GetItemDescriptor.java
+++ b/asterixdb/asterix-runtime/src/main/java/org/apache/asterix/runtime/evaluators/functions/GetItemDescriptor.java
@@ -116,6 +116,10 @@
int itemIndex = ATypeHierarchy.getIntegerValue(BuiltinFunctions.GET_ITEM.getName(), 0,
indexBytes, indexOffset);
+ if (itemIndex < 0) {
+ itemIndex = itemCount + itemIndex;
+ }
+
if (itemIndex < 0 || itemIndex >= itemCount) {
// Out-of-bound index access should return MISSING.
result.set(missingBytes, 0, 1);