[ASTERIXDB-2291][FUN] Implement if_inf,if_nan,if_nan_or_inf
- user model changes: yes
- storage format changes: no
- interface changes: no
Details:
- Implement functions: if_inf(), if_nan(), if_nan_or_inf()
- Add testcases and documentation
- INF to string conversion should produce "INF" instead of "Infinity"
- Move if_missing(), if_null() testcases to 'null-missing' folder
Change-Id: I12dccd848a1d6b4ba102bea3b7602c2cbd8aaef5
Reviewed-on: https://asterix-gerrit.ics.uci.edu/2411
Tested-by: Jenkins <jenkins@fulliautomatix.ics.uci.edu>
Integration-Tests: Jenkins <jenkins@fulliautomatix.ics.uci.edu>
Contrib: Jenkins <jenkins@fulliautomatix.ics.uci.edu>
Reviewed-by: Till Westmann <tillw@apache.org>
diff --git a/asterixdb/asterix-app/src/test/resources/runtimets/queries/types/to_string_01/to_string_01.1.query.aql b/asterixdb/asterix-app/src/test/resources/runtimets/queries/types/to_string_01/to_string_01.1.query.aql
index 2abf4c3..ee5ed6f 100644
--- a/asterixdb/asterix-app/src/test/resources/runtimets/queries/types/to_string_01/to_string_01.1.query.aql
+++ b/asterixdb/asterix-app/src/test/resources/runtimets/queries/types/to_string_01/to_string_01.1.query.aql
@@ -30,5 +30,11 @@
"t11": is_null(to_string({{}})),
"t12": is_null(to_string({})),
"t13": is_null(to_string(null)),
- "t14": is_missing(to_string(missing))
+ "t14": is_missing(to_string(missing)),
+ "t15": to_string(double("NaN")),
+ "t16": to_string(float("NaN")),
+ "t17": to_string(double("INF")),
+ "t18": to_string(float("INF")),
+ "t19": to_string(double("-INF")),
+ "t20": to_string(float("-INF"))
}
\ No newline at end of file
diff --git a/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/types/ifmissing/ifmissing.1.query.sqlpp b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/null-missing/ifmissing/ifmissing.1.query.sqlpp
similarity index 100%
rename from asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/types/ifmissing/ifmissing.1.query.sqlpp
rename to asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/null-missing/ifmissing/ifmissing.1.query.sqlpp
diff --git a/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/types/ifmissingornull/ifmissingornull.1.query.sqlpp b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/null-missing/ifmissingornull/ifmissingornull.1.query.sqlpp
similarity index 100%
rename from asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/types/ifmissingornull/ifmissingornull.1.query.sqlpp
rename to asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/null-missing/ifmissingornull/ifmissingornull.1.query.sqlpp
diff --git a/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/types/ifnull/ifnull.1.query.sqlpp b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/null-missing/ifnull/ifnull.1.query.sqlpp
similarity index 100%
rename from asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/types/ifnull/ifnull.1.query.sqlpp
rename to asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/null-missing/ifnull/ifnull.1.query.sqlpp
diff --git a/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/numeric/ifinf/ifinf.1.query.sqlpp b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/numeric/ifinf/ifinf.1.query.sqlpp
new file mode 100644
index 0000000..3b12ead
--- /dev/null
+++ b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/numeric/ifinf/ifinf.1.query.sqlpp
@@ -0,0 +1,44 @@
+/*
+ * 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.
+ */
+
+select value t
+from [
+ [ 0, isnull(ifinf()) ],
+ [ 1, isnull(ifinf(null)) ],
+ [ 2, ismissing(ifinf(missing)) ],
+ [ 3, isnull(ifinf(double("INF"))) ],
+ [ 4, isnull(ifinf(double("-INF"))) ],
+ [ 5, isnull(ifinf(float("INF"))) ],
+ [ 6, isnull(ifinf(float("-INF"))) ],
+ [ 7, ifinf(tinyint("1")) ],
+ [ 8, ifinf(smallint("2")) ],
+ [ 9, ifinf(integer("3")) ],
+ [ 10, ifinf(bigint("4")) ],
+ [ 11, ifinf(float("1.5")) ],
+ [ 12, ifinf(double("2.5")) ],
+ [ 13, ifinf(2, null, missing) ],
+ [ 14, isnull(ifinf(null, missing, 2)) ],
+ [ 15, ismissing(ifinf(missing, null, 2)) ],
+ [ 16, ifinf(double("INF"), float("-INF"), 2) ],
+ [ 17, ifinf(float("INF"), double("-INF"), 2) ],
+ [ 18, isnull(ifinf(double("INF"), double("-INF"), [], 2)) ],
+ [ 19, ismissing(if_inf(double("INF"), double("-INF"), missing, 2)) ],
+ [ 20, tostring(ifinf(float("INF"), float("NaN"), 2)) ]
+] t
+order by t[0]
\ No newline at end of file
diff --git a/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/numeric/ifnan/ifnan.1.query.sqlpp b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/numeric/ifnan/ifnan.1.query.sqlpp
new file mode 100644
index 0000000..40a55d1
--- /dev/null
+++ b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/numeric/ifnan/ifnan.1.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.
+ */
+
+select value t
+from [
+ [ 0, isnull(ifnan()) ],
+ [ 1, isnull(ifnan(null)) ],
+ [ 2, ismissing(ifnan(missing)) ],
+ [ 3, isnull(ifnan(double("NaN"))) ],
+ [ 5, isnull(ifnan(float("NaN"))) ],
+ [ 7, ifnan(tinyint("1")) ],
+ [ 8, ifnan(smallint("2")) ],
+ [ 9, ifnan(integer("3")) ],
+ [ 10, ifnan(bigint("4")) ],
+ [ 11, ifnan(float("1.5")) ],
+ [ 12, ifnan(double("2.5")) ],
+ [ 13, ifnan(2, null, missing) ],
+ [ 14, isnull(ifnan(null, missing, 2)) ],
+ [ 15, ismissing(ifnan(missing, null, 2)) ],
+ [ 16, ifnan(double("NaN"), 2) ],
+ [ 17, ifnan(float("NaN"), 2) ],
+ [ 18, isnull(ifnan(double("NaN"), [], 2)) ],
+ [ 19, ismissing(if_nan(double("NaN"), missing, 2)) ],
+ [ 20, tostring(ifnan(double("NaN"), double("INF"), 2)) ],
+ [ 21, tostring(ifnan(float("NaN"), float("-INF"), 2)) ]
+] t
+order by t[0]
\ No newline at end of file
diff --git a/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/numeric/ifnanorinf/ifnanorinf.1.query.sqlpp b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/numeric/ifnanorinf/ifnanorinf.1.query.sqlpp
new file mode 100644
index 0000000..d4b1863
--- /dev/null
+++ b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/numeric/ifnanorinf/ifnanorinf.1.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.
+ */
+
+select value t
+from [
+ [ 0, isnull(ifnanorinf()) ],
+ [ 1, isnull(ifnanorinf(null)) ],
+ [ 2, ismissing(ifnanorinf(missing)) ],
+ [ 3, isnull(ifnanorinf(double("NaN"))) ],
+ [ 5, isnull(ifnanorinf(float("INF"))) ],
+ [ 7, ifnanorinf(tinyint("1")) ],
+ [ 8, ifnanorinf(smallint("2")) ],
+ [ 9, ifnanorinf(integer("3")) ],
+ [ 10, ifnanorinf(bigint("4")) ],
+ [ 11, ifnanorinf(float("1.5")) ],
+ [ 12, ifnanorinf(double("2.5")) ],
+ [ 13, ifnanorinf(2, null, missing) ],
+ [ 14, isnull(ifnanorinf(null, missing, 2)) ],
+ [ 15, ismissing(ifnanorinf(missing, null, 2)) ],
+ [ 16, ifnanorinf(double("-INF"), 2) ],
+ [ 17, ifnanorinf(float("NaN"), 2) ],
+ [ 18, isnull(ifnanorinf(double("NaN"), [], 2)) ],
+ [ 19, ismissing(if_nan_or_inf(double("NaN"), missing, 2)) ],
+ [ 20, ifnanorinf(double("NaN"), double("INF"), 2) ],
+ [ 21, ifnanorinf(float("NaN"), float("-INF"), 2) ]
+] t
+order by t[0]
\ No newline at end of file
diff --git a/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/types/to_string_01/to_string_01.1.query.sqlpp b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/types/to_string_01/to_string_01.1.query.sqlpp
index 8582521..f759e16 100644
--- a/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/types/to_string_01/to_string_01.1.query.sqlpp
+++ b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/types/to_string_01/to_string_01.1.query.sqlpp
@@ -30,5 +30,11 @@
"t11": is_null(to_string({{}})),
"t12": is_null(to_string({})),
"t13": is_null(to_string(null)),
- "t14": is_missing(to_string(missing))
+ "t14": is_missing(to_string(missing)),
+ "t15": to_string(double("NaN")),
+ "t16": to_string(float("NaN")),
+ "t17": to_string(double("INF")),
+ "t18": to_string(float("INF")),
+ "t19": to_string(double("-INF")),
+ "t20": to_string(float("-INF"))
}
\ No newline at end of file
diff --git a/asterixdb/asterix-app/src/test/resources/runtimets/results/types/ifmissing/ifmissing.1.adm b/asterixdb/asterix-app/src/test/resources/runtimets/results/null-missing/ifmissing/ifmissing.1.adm
similarity index 100%
rename from asterixdb/asterix-app/src/test/resources/runtimets/results/types/ifmissing/ifmissing.1.adm
rename to asterixdb/asterix-app/src/test/resources/runtimets/results/null-missing/ifmissing/ifmissing.1.adm
diff --git a/asterixdb/asterix-app/src/test/resources/runtimets/results/types/ifmissingornull/ifmissingornull.1.adm b/asterixdb/asterix-app/src/test/resources/runtimets/results/null-missing/ifmissingornull/ifmissingornull.1.adm
similarity index 100%
rename from asterixdb/asterix-app/src/test/resources/runtimets/results/types/ifmissingornull/ifmissingornull.1.adm
rename to asterixdb/asterix-app/src/test/resources/runtimets/results/null-missing/ifmissingornull/ifmissingornull.1.adm
diff --git a/asterixdb/asterix-app/src/test/resources/runtimets/results/types/ifnull/ifnull.1.adm b/asterixdb/asterix-app/src/test/resources/runtimets/results/null-missing/ifnull/ifnull.1.adm
similarity index 100%
rename from asterixdb/asterix-app/src/test/resources/runtimets/results/types/ifnull/ifnull.1.adm
rename to asterixdb/asterix-app/src/test/resources/runtimets/results/null-missing/ifnull/ifnull.1.adm
diff --git a/asterixdb/asterix-app/src/test/resources/runtimets/results/numeric/ifinf/ifinf.1.adm b/asterixdb/asterix-app/src/test/resources/runtimets/results/numeric/ifinf/ifinf.1.adm
new file mode 100644
index 0000000..c45efa6
--- /dev/null
+++ b/asterixdb/asterix-app/src/test/resources/runtimets/results/numeric/ifinf/ifinf.1.adm
@@ -0,0 +1,21 @@
+[ 0, true ]
+[ 1, true ]
+[ 2, true ]
+[ 3, true ]
+[ 4, true ]
+[ 5, true ]
+[ 6, true ]
+[ 7, 1 ]
+[ 8, 2 ]
+[ 9, 3 ]
+[ 10, 4 ]
+[ 11, 1.5 ]
+[ 12, 2.5 ]
+[ 13, 2 ]
+[ 14, true ]
+[ 15, true ]
+[ 16, 2 ]
+[ 17, 2 ]
+[ 18, true ]
+[ 19, true ]
+[ 20, "NaN" ]
\ No newline at end of file
diff --git a/asterixdb/asterix-app/src/test/resources/runtimets/results/numeric/ifnan/ifnan.1.adm b/asterixdb/asterix-app/src/test/resources/runtimets/results/numeric/ifnan/ifnan.1.adm
new file mode 100644
index 0000000..ec02746
--- /dev/null
+++ b/asterixdb/asterix-app/src/test/resources/runtimets/results/numeric/ifnan/ifnan.1.adm
@@ -0,0 +1,20 @@
+[ 0, true ]
+[ 1, true ]
+[ 2, true ]
+[ 3, true ]
+[ 5, true ]
+[ 7, 1 ]
+[ 8, 2 ]
+[ 9, 3 ]
+[ 10, 4 ]
+[ 11, 1.5 ]
+[ 12, 2.5 ]
+[ 13, 2 ]
+[ 14, true ]
+[ 15, true ]
+[ 16, 2 ]
+[ 17, 2 ]
+[ 18, true ]
+[ 19, true ]
+[ 20, "INF" ]
+[ 21, "-INF" ]
\ No newline at end of file
diff --git a/asterixdb/asterix-app/src/test/resources/runtimets/results/numeric/ifnanorinf/ifnanorinf.1.adm b/asterixdb/asterix-app/src/test/resources/runtimets/results/numeric/ifnanorinf/ifnanorinf.1.adm
new file mode 100644
index 0000000..34476cc
--- /dev/null
+++ b/asterixdb/asterix-app/src/test/resources/runtimets/results/numeric/ifnanorinf/ifnanorinf.1.adm
@@ -0,0 +1,20 @@
+[ 0, true ]
+[ 1, true ]
+[ 2, true ]
+[ 3, true ]
+[ 5, true ]
+[ 7, 1 ]
+[ 8, 2 ]
+[ 9, 3 ]
+[ 10, 4 ]
+[ 11, 1.5 ]
+[ 12, 2.5 ]
+[ 13, 2 ]
+[ 14, true ]
+[ 15, true ]
+[ 16, 2 ]
+[ 17, 2 ]
+[ 18, true ]
+[ 19, true ]
+[ 20, 2 ]
+[ 21, 2 ]
\ No newline at end of file
diff --git a/asterixdb/asterix-app/src/test/resources/runtimets/results/types/to_string_01/to_string_01.1.adm b/asterixdb/asterix-app/src/test/resources/runtimets/results/types/to_string_01/to_string_01.1.adm
index 83dee03..11a3abb 100644
--- a/asterixdb/asterix-app/src/test/resources/runtimets/results/types/to_string_01/to_string_01.1.adm
+++ b/asterixdb/asterix-app/src/test/resources/runtimets/results/types/to_string_01/to_string_01.1.adm
@@ -1 +1 @@
-{ "t1": "false", "t2": "true", "t3": "8", "t4": "16", "t5": "32", "t6": "64", "t7": "128.0", "t8": "256.0", "t9": "foo", "t10": true, "t11": true, "t12": true, "t13": true, "t14": true }
\ No newline at end of file
+{ "t1": "false", "t2": "true", "t3": "8", "t4": "16", "t5": "32", "t6": "64", "t7": "128.0", "t8": "256.0", "t9": "foo", "t10": true, "t11": true, "t12": true, "t13": true, "t14": true, "t15": "NaN", "t16": "NaN", "t17": "INF", "t18": "INF", "t19": "-INF", "t20": "-INF" }
\ 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 a869b22..bb0d473 100644
--- a/asterixdb/asterix-app/src/test/resources/runtimets/testsuite_sqlpp.xml
+++ b/asterixdb/asterix-app/src/test/resources/runtimets/testsuite_sqlpp.xml
@@ -4648,6 +4648,21 @@
</compilation-unit>
</test-case>
<test-case FilePath="null-missing">
+ <compilation-unit name="ifmissing">
+ <output-dir compare="Text">ifmissing</output-dir>
+ </compilation-unit>
+ </test-case>
+ <test-case FilePath="null-missing">
+ <compilation-unit name="ifnull">
+ <output-dir compare="Text">ifnull</output-dir>
+ </compilation-unit>
+ </test-case>
+ <test-case FilePath="null-missing">
+ <compilation-unit name="ifmissingornull">
+ <output-dir compare="Text">ifmissingornull</output-dir>
+ </compilation-unit>
+ </test-case>
+ <test-case FilePath="null-missing">
<compilation-unit name="query-ASTERIXDB-1689">
<output-dir compare="Text">query-ASTERIXDB-1689</output-dir>
</compilation-unit>
@@ -4805,6 +4820,21 @@
</compilation-unit>
</test-case>
<test-case FilePath="numeric">
+ <compilation-unit name="ifinf">
+ <output-dir compare="Text">ifinf</output-dir>
+ </compilation-unit>
+ </test-case>
+ <test-case FilePath="numeric">
+ <compilation-unit name="ifnan">
+ <output-dir compare="Text">ifnan</output-dir>
+ </compilation-unit>
+ </test-case>
+ <test-case FilePath="numeric">
+ <compilation-unit name="ifnanorinf">
+ <output-dir compare="Text">ifnanorinf</output-dir>
+ </compilation-unit>
+ </test-case>
+ <test-case FilePath="numeric">
<compilation-unit name="infinity">
<output-dir compare="Text">infinity</output-dir>
</compilation-unit>
@@ -9182,21 +9212,6 @@
</compilation-unit>
</test-case>
<test-case FilePath="types">
- <compilation-unit name="ifmissing">
- <output-dir compare="Text">ifmissing</output-dir>
- </compilation-unit>
- </test-case>
- <test-case FilePath="types">
- <compilation-unit name="ifnull">
- <output-dir compare="Text">ifnull</output-dir>
- </compilation-unit>
- </test-case>
- <test-case FilePath="types">
- <compilation-unit name="ifmissingornull">
- <output-dir compare="Text">ifmissingornull</output-dir>
- </compilation-unit>
- </test-case>
- <test-case FilePath="types">
<compilation-unit name="to_boolean_01">
<output-dir compare="Text">to_boolean_01</output-dir>
</compilation-unit>
diff --git a/asterixdb/asterix-doc/src/main/markdown/builtins/13_conditional.md b/asterixdb/asterix-doc/src/main/markdown/builtins/13_conditional.md
index 202f7e4..ed9551e 100644
--- a/asterixdb/asterix-doc/src/main/markdown/builtins/13_conditional.md
+++ b/asterixdb/asterix-doc/src/main/markdown/builtins/13_conditional.md
@@ -101,3 +101,101 @@
{ "a": null, "b": null, "c": "asterixdb" }
The function has an alias `ifmissingornull`.
+
+### if_inf (ifinf) ###
+
+ * Syntax:
+
+ if_inf(expression1, expression2, ... expressionN)
+
+ * Finds first argument which is a non-infinite (`INF` or`-INF`) number
+ * Arguments:
+ * `expressionI` : an expression (any type is allowed).
+ * Return Value:
+ * a `missing` if `missing` argument was encountered before the first non-infinite number argument
+ * a `null` if `null` argument or any other non-number argument was encountered before the first non-infinite number argument
+ * the first non-infinite number argument otherwise
+
+ * Example:
+
+ {
+ "a": is_null(if_inf(null)),
+ "b": is_missing(if_inf(missing)),
+ "c": is_null(if_inf(double("INF"))),
+ "d": if_inf(1, null, missing) ],
+ "e": is_null(if_inf(null, missing, 1)) ],
+ "f": is_missing(if_inf(missing, null, 1)) ],
+ "g": if_inf(float("INF"), 1) ],
+ "h": to_string(if_inf(float("INF"), double("NaN"), 1)) ]
+ };
+
+ * The expected result is:
+
+ { "a": true, "b": true, "c": true, "d": 1, "e": true, "f": true, "g": 1, "h": "NaN" }
+
+ The function has an alias `ifinf`.
+
+### if_nan (ifnan) ###
+
+ * Syntax:
+
+ if_nan(expression1, expression2, ... expressionN)
+
+ * Finds first argument which is a non-`NaN` number
+ * Arguments:
+ * `expressionI` : an expression (any type is allowed).
+ * Return Value:
+ * a `missing` if `missing` argument was encountered before the first non-`NaN` number argument
+ * a `null` if `null` argument or any other non-number argument was encountered before the first non-`NaN` number argument
+ * the first non-`NaN` number argument otherwise
+
+ * Example:
+
+ {
+ "a": is_null(if_nan(null)),
+ "b": is_missing(if_nan(missing)),
+ "c": is_null(if_nan(double("NaN"))),
+ "d": if_nan(1, null, missing) ],
+ "e": is_null(if_nan(null, missing, 1)) ],
+ "f": is_missing(if_nan(missing, null, 1)) ],
+ "g": if_nan(float("NaN"), 1) ],
+ "h": to_string(if_nan(float("NaN"), double("INF"), 1)) ]
+ };
+
+ * The expected result is:
+
+ { "a": true, "b": true, "c": true, "d": 1, "e": true, "f": true, "g": 1, "h": "INF" }
+
+ The function has an alias `ifnan`.
+
+### if_nan_or_inf (ifnanorinf) ###
+
+ * Syntax:
+
+ if_nan_or_inf(expression1, expression2, ... expressionN)
+
+ * Finds first argument which is a non-infinite (`INF` or`-INF`) and non-`NaN` number
+ * Arguments:
+ * `expressionI` : an expression (any type is allowed).
+ * Return Value:
+ * a `missing` if `missing` argument was encountered before the first non-infinite and non-`NaN` number argument
+ * a `null` if `null` argument or any other non-number argument was encountered before the first non-infinite and non-`NaN` number argument
+ * the first non-infinite and non-`NaN` number argument otherwise
+
+ * Example:
+
+ {
+ "a": is_null(if_nan_or_inf(null)),
+ "b": is_missing(if_nan_or_inf(missing)),
+ "c": is_null(if_nan_or_inf(double("NaN"), double("INF"))),
+ "d": if_nan_or_inf(1, null, missing) ],
+ "e": is_null(if_nan_or_inf(null, missing, 1)) ],
+ "f": is_missing(if_nan_or_inf(missing, null, 1)) ],
+ "g": if_nan_or_inf(float("NaN"), float("INF"), 1) ],
+ };
+
+ * The expected result is:
+
+ { "a": true, "b": true, "c": true, "d": 1, "e": true, "f": true, "g": 1 }
+
+ The function has an alias `ifnanorinf`.
diff --git a/asterixdb/asterix-lang-common/src/main/java/org/apache/asterix/lang/common/util/CommonFunctionMapUtil.java b/asterixdb/asterix-lang-common/src/main/java/org/apache/asterix/lang/common/util/CommonFunctionMapUtil.java
index b78ee9f..5556ea9 100644
--- a/asterixdb/asterix-lang-common/src/main/java/org/apache/asterix/lang/common/util/CommonFunctionMapUtil.java
+++ b/asterixdb/asterix-lang-common/src/main/java/org/apache/asterix/lang/common/util/CommonFunctionMapUtil.java
@@ -66,7 +66,10 @@
FUNCTION_NAME_MAP.put("isobj", "is-object"); // isobj, internal: is-object
FUNCTION_NAME_MAP.put("ifmissing", "if-missing"); // ifmissing, internal: if-missing
FUNCTION_NAME_MAP.put("ifnull", "if-null"); // ifnull, internal: if-null
- FUNCTION_NAME_MAP.put("ifmissingornull", "if-missing-or-null"); // ifmissingornull, internal: is-missing-or-null
+ FUNCTION_NAME_MAP.put("ifmissingornull", "if-missing-or-null"); // ifmissingornull, internal: if-missing-or-null
+ FUNCTION_NAME_MAP.put("ifinf", "if-inf"); // ifinf, internal: if-inf
+ FUNCTION_NAME_MAP.put("ifnan", "if-nan"); // ifnan, internal: if-nan
+ FUNCTION_NAME_MAP.put("ifnanorinf", "if-nan-or-inf"); // ifnanorinf, internal: if-nan-or-inf
FUNCTION_NAME_MAP.put("toboolean", "to-boolean"); // toboolean, internal: to-boolean
FUNCTION_NAME_MAP.put("tostring", "to-string"); // tostring, internal: to-string
FUNCTION_NAME_MAP.put("todouble", "to-double"); // todouble, internal: to-double
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 153ea45..761ddba 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
@@ -66,6 +66,7 @@
import org.apache.asterix.om.typecomputer.impl.FieldAccessNestedResultType;
import org.apache.asterix.om.typecomputer.impl.FullTextContainsResultTypeComputer;
import org.apache.asterix.om.typecomputer.impl.GetOverlappingInvervalTypeComputer;
+import org.apache.asterix.om.typecomputer.impl.IfNanOrInfTypeComputer;
import org.apache.asterix.om.typecomputer.impl.IfMissingOrNullTypeComputer;
import org.apache.asterix.om.typecomputer.impl.IfMissingTypeComputer;
import org.apache.asterix.om.typecomputer.impl.IfNullTypeComputer;
@@ -855,6 +856,12 @@
new FunctionIdentifier(FunctionConstants.ASTERIX_NS, "if-null", FunctionIdentifier.VARARGS);
public static final FunctionIdentifier IF_MISSING_OR_NULL =
new FunctionIdentifier(FunctionConstants.ASTERIX_NS, "if-missing-or-null", FunctionIdentifier.VARARGS);
+ public static final FunctionIdentifier IF_INF =
+ new FunctionIdentifier(FunctionConstants.ASTERIX_NS, "if-inf", FunctionIdentifier.VARARGS);
+ public static final FunctionIdentifier IF_NAN =
+ new FunctionIdentifier(FunctionConstants.ASTERIX_NS, "if-nan", FunctionIdentifier.VARARGS);
+ public static final FunctionIdentifier IF_NAN_OR_INF =
+ new FunctionIdentifier(FunctionConstants.ASTERIX_NS, "if-nan-or-inf", FunctionIdentifier.VARARGS);
public static final FunctionIdentifier TO_BOOLEAN =
new FunctionIdentifier(FunctionConstants.ASTERIX_NS, "to-boolean", 1);
@@ -992,6 +999,9 @@
addFunction(NUMERIC_ROUND_HALF_TO_EVEN, NumericUnaryFunctionTypeComputer.INSTANCE, true);
addFunction(NUMERIC_ROUND_HALF_TO_EVEN2, NumericRound2TypeComputer.INSTANCE, true);
addFunction(NUMERIC_TRUNC, NumericRound2TypeComputer.INSTANCE, true);
+ addFunction(IF_INF, IfNanOrInfTypeComputer.INSTANCE, true);
+ addFunction(IF_NAN, IfNanOrInfTypeComputer.INSTANCE, true);
+ addFunction(IF_NAN_OR_INF, IfNanOrInfTypeComputer.INSTANCE, true);
addFunction(BINARY_LENGTH, UnaryBinaryInt64TypeComputer.INSTANCE, true);
addFunction(PARSE_BINARY, ABinaryTypeComputer.INSTANCE, true);
diff --git a/asterixdb/asterix-om/src/main/java/org/apache/asterix/om/typecomputer/base/AbstractResultTypeComputer.java b/asterixdb/asterix-om/src/main/java/org/apache/asterix/om/typecomputer/base/AbstractResultTypeComputer.java
index f33db4b..4d6b7e6 100644
--- a/asterixdb/asterix-om/src/main/java/org/apache/asterix/om/typecomputer/base/AbstractResultTypeComputer.java
+++ b/asterixdb/asterix-om/src/main/java/org/apache/asterix/om/typecomputer/base/AbstractResultTypeComputer.java
@@ -40,7 +40,7 @@
AbstractFunctionCallExpression functionCallExpression = (AbstractFunctionCallExpression) expression;
String funcName = functionCallExpression.getFunctionIdentifier().getName();
return TypeComputeUtils.resolveResultType(expression, env, (index, type) -> checkArgType(funcName, index, type),
- this::getResultType, true);
+ this::getResultType, propagateNullAndMissing());
}
/**
@@ -70,4 +70,12 @@
*/
protected abstract IAType getResultType(ILogicalExpression expr, IAType... strippedInputTypes)
throws AlgebricksException;
+
+ /**
+ * Whether the expression follows MISSING-in-MISSING-out and NULL-in-NULL-out semantics
+ * @return said value
+ */
+ protected boolean propagateNullAndMissing() {
+ return true;
+ }
}
diff --git a/asterixdb/asterix-om/src/main/java/org/apache/asterix/om/typecomputer/impl/IfNanOrInfTypeComputer.java b/asterixdb/asterix-om/src/main/java/org/apache/asterix/om/typecomputer/impl/IfNanOrInfTypeComputer.java
new file mode 100644
index 0000000..bb9fa38
--- /dev/null
+++ b/asterixdb/asterix-om/src/main/java/org/apache/asterix/om/typecomputer/impl/IfNanOrInfTypeComputer.java
@@ -0,0 +1,74 @@
+/*
+ * 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.
+ */
+
+package org.apache.asterix.om.typecomputer.impl;
+
+import org.apache.asterix.om.typecomputer.base.AbstractResultTypeComputer;
+import org.apache.asterix.om.types.AUnionType;
+import org.apache.asterix.om.types.BuiltinType;
+import org.apache.asterix.om.types.IAType;
+import org.apache.hyracks.algebricks.common.exceptions.AlgebricksException;
+import org.apache.hyracks.algebricks.core.algebra.base.ILogicalExpression;
+
+public class IfNanOrInfTypeComputer extends AbstractResultTypeComputer {
+
+ public static final IfNanOrInfTypeComputer INSTANCE = new IfNanOrInfTypeComputer();
+
+ @Override
+ protected IAType getResultType(ILogicalExpression expr, IAType... strippedInputTypes) throws AlgebricksException {
+ if (strippedInputTypes.length == 0) {
+ return BuiltinType.ANULL;
+ }
+
+ boolean any = false;
+ IAType currentType = null;
+ for (IAType type : strippedInputTypes) {
+ if (currentType != null && !type.equals(currentType)) {
+ any = true;
+ break;
+ }
+ currentType = type;
+ }
+ if (any || currentType == null) {
+ return BuiltinType.ANY;
+ }
+
+ switch (currentType.getTypeTag()) {
+ case ANY:
+ case MISSING:
+ case BIGINT:
+ case INTEGER:
+ case SMALLINT:
+ case TINYINT:
+ return currentType;
+
+ case DOUBLE:
+ case FLOAT:
+ return AUnionType.createNullableType(currentType, null);
+
+ default:
+ return BuiltinType.ANULL;
+ }
+ }
+
+ @Override
+ protected boolean propagateNullAndMissing() {
+ return false;
+ }
+}
diff --git a/asterixdb/asterix-runtime/src/main/java/org/apache/asterix/runtime/evaluators/constructors/AFloatConstructorDescriptor.java b/asterixdb/asterix-runtime/src/main/java/org/apache/asterix/runtime/evaluators/constructors/AFloatConstructorDescriptor.java
index ffc982a..8c69671 100644
--- a/asterixdb/asterix-runtime/src/main/java/org/apache/asterix/runtime/evaluators/constructors/AFloatConstructorDescriptor.java
+++ b/asterixdb/asterix-runtime/src/main/java/org/apache/asterix/runtime/evaluators/constructors/AFloatConstructorDescriptor.java
@@ -68,11 +68,6 @@
private DataOutput out = resultStorage.getDataOutput();
private IPointable inputArg = new VoidPointable();
private IScalarEvaluator eval = args[0].createScalarEvaluator(ctx);
- private final byte[] POSITIVE_INF = UTF8StringUtil.writeStringToBytes("INF");
- private final byte[] NEGATIVE_INF = UTF8StringUtil.writeStringToBytes("-INF");
- private final byte[] NAN = UTF8StringUtil.writeStringToBytes("NaN");
- private IBinaryComparator utf8BinaryComparator =
- BinaryComparatorFactoryProvider.UTF8STRING_POINTABLE_INSTANCE.createBinaryComparator();
private AMutableFloat aFloat = new AMutableFloat(0);
@SuppressWarnings("unchecked")
private ISerializerDeserializer<AFloat> floatSerde =
@@ -92,17 +87,19 @@
result.set(inputArg);
} else if (tt == ATypeTag.SERIALIZED_STRING_TYPE_TAG) {
resultStorage.reset();
- if (utf8BinaryComparator.compare(serString, offset + 1, len - 1, POSITIVE_INF, 0,
- 5) == 0) {
+ int utf8offset = offset + 1;
+ int utf8len = len - 1;
+ if (AbstractDoubleConstructorEvaluator.POSITIVE_INF.compareTo(serString, utf8offset,
+ utf8len) == 0) {
aFloat.setValue(Float.POSITIVE_INFINITY);
- } else if (utf8BinaryComparator.compare(serString, offset + 1, len - 1, NEGATIVE_INF, 0,
- 6) == 0) {
+ } else if (AbstractDoubleConstructorEvaluator.NEGATIVE_INF.compareTo(serString,
+ utf8offset, utf8len) == 0) {
aFloat.setValue(Float.NEGATIVE_INFINITY);
- } else if (utf8BinaryComparator.compare(serString, offset + 1, len - 1, NAN, 0,
- 5) == 0) {
+ } else if (AbstractDoubleConstructorEvaluator.NAN.compareTo(serString, utf8offset,
+ utf8len) == 0) {
aFloat.setValue(Float.NaN);
} else {
- utf8Ptr.set(serString, offset + 1, len - 1);
+ utf8Ptr.set(serString, utf8offset, utf8len);
aFloat.setValue(Float.parseFloat(utf8Ptr.toString()));
}
floatSerde.serialize(aFloat, out);
diff --git a/asterixdb/asterix-runtime/src/main/java/org/apache/asterix/runtime/evaluators/constructors/AbstractDoubleConstructorEvaluator.java b/asterixdb/asterix-runtime/src/main/java/org/apache/asterix/runtime/evaluators/constructors/AbstractDoubleConstructorEvaluator.java
index 332bfc2..e10bf68 100644
--- a/asterixdb/asterix-runtime/src/main/java/org/apache/asterix/runtime/evaluators/constructors/AbstractDoubleConstructorEvaluator.java
+++ b/asterixdb/asterix-runtime/src/main/java/org/apache/asterix/runtime/evaluators/constructors/AbstractDoubleConstructorEvaluator.java
@@ -22,7 +22,6 @@
import java.io.DataOutput;
import java.io.IOException;
-import org.apache.asterix.formats.nontagged.BinaryComparatorFactoryProvider;
import org.apache.asterix.formats.nontagged.SerializerDeserializerProvider;
import org.apache.asterix.om.base.ADouble;
import org.apache.asterix.om.base.AMutableDouble;
@@ -32,7 +31,6 @@
import org.apache.asterix.runtime.exceptions.TypeMismatchException;
import org.apache.hyracks.algebricks.core.algebra.functions.FunctionIdentifier;
import org.apache.hyracks.algebricks.runtime.base.IScalarEvaluator;
-import org.apache.hyracks.api.dataflow.value.IBinaryComparator;
import org.apache.hyracks.api.dataflow.value.ISerializerDeserializer;
import org.apache.hyracks.api.exceptions.HyracksDataException;
import org.apache.hyracks.data.std.api.IPointable;
@@ -40,19 +38,15 @@
import org.apache.hyracks.data.std.primitive.VoidPointable;
import org.apache.hyracks.data.std.util.ArrayBackedValueStorage;
import org.apache.hyracks.dataflow.common.data.accessors.IFrameTupleReference;
-import org.apache.hyracks.util.string.UTF8StringUtil;
public abstract class AbstractDoubleConstructorEvaluator implements IScalarEvaluator {
@SuppressWarnings("unchecked")
protected static final ISerializerDeserializer<ADouble> DOUBLE_SERDE =
SerializerDeserializerProvider.INSTANCE.getSerializerDeserializer(BuiltinType.ADOUBLE);
- protected static final IBinaryComparator UTF8_BINARY_CMP =
- BinaryComparatorFactoryProvider.UTF8STRING_POINTABLE_INSTANCE.createBinaryComparator();
-
- protected static final byte[] POSITIVE_INF = UTF8StringUtil.writeStringToBytes("INF");
- protected static final byte[] NEGATIVE_INF = UTF8StringUtil.writeStringToBytes("-INF");
- protected static final byte[] NAN = UTF8StringUtil.writeStringToBytes("NaN");
+ protected static final UTF8StringPointable POSITIVE_INF = UTF8StringPointable.generateUTF8Pointable("INF");
+ protected static final UTF8StringPointable NEGATIVE_INF = UTF8StringPointable.generateUTF8Pointable("-INF");
+ protected static final UTF8StringPointable NAN = UTF8StringPointable.generateUTF8Pointable("NaN");
protected final IScalarEvaluator inputEval;
protected final ArrayBackedValueStorage resultStorage;
@@ -89,14 +83,16 @@
result.set(inputArg);
} else if (tt == ATypeTag.SERIALIZED_STRING_TYPE_TAG) {
int len = inputArg.getLength();
- if (UTF8_BINARY_CMP.compare(bytes, offset + 1, len - 1, POSITIVE_INF, 0, 5) == 0) {
+ int utf8offset = offset + 1;
+ int utf8len = len - 1;
+ if (POSITIVE_INF.compareTo(bytes, utf8offset, utf8len) == 0) {
setDouble(result, Double.POSITIVE_INFINITY);
- } else if (UTF8_BINARY_CMP.compare(bytes, offset + 1, len - 1, NEGATIVE_INF, 0, 6) == 0) {
+ } else if (NEGATIVE_INF.compareTo(bytes, utf8offset, utf8len) == 0) {
setDouble(result, Double.NEGATIVE_INFINITY);
- } else if (UTF8_BINARY_CMP.compare(bytes, offset + 1, len - 1, NAN, 0, 5) == 0) {
+ } else if (NAN.compareTo(bytes, utf8offset, utf8len) == 0) {
setDouble(result, Double.NaN);
} else {
- utf8Ptr.set(bytes, offset + 1, len - 1);
+ utf8Ptr.set(bytes, utf8offset, utf8len);
try {
setDouble(result, Double.parseDouble(utf8Ptr.toString()));
} catch (NumberFormatException e) {
diff --git a/asterixdb/asterix-runtime/src/main/java/org/apache/asterix/runtime/evaluators/constructors/AbstractStringConstructorEvaluator.java b/asterixdb/asterix-runtime/src/main/java/org/apache/asterix/runtime/evaluators/constructors/AbstractStringConstructorEvaluator.java
index 6918bb9..11576aa 100644
--- a/asterixdb/asterix-runtime/src/main/java/org/apache/asterix/runtime/evaluators/constructors/AbstractStringConstructorEvaluator.java
+++ b/asterixdb/asterix-runtime/src/main/java/org/apache/asterix/runtime/evaluators/constructors/AbstractStringConstructorEvaluator.java
@@ -106,12 +106,28 @@
}
case DOUBLE: {
double d = ADoubleSerializerDeserializer.getDouble(serString, startOffset);
- builder.appendString(String.valueOf(d));
+ if (Double.isNaN(d)) {
+ builder.appendUtf8StringPointable(AbstractDoubleConstructorEvaluator.NAN);
+ } else if (d == Double.POSITIVE_INFINITY) { // NOSONAR
+ builder.appendUtf8StringPointable(AbstractDoubleConstructorEvaluator.POSITIVE_INF);
+ } else if (d == Double.NEGATIVE_INFINITY) { // NOSONAR
+ builder.appendUtf8StringPointable(AbstractDoubleConstructorEvaluator.NEGATIVE_INF);
+ } else {
+ builder.appendString(String.valueOf(d));
+ }
break;
}
case FLOAT: {
float f = AFloatSerializerDeserializer.getFloat(serString, startOffset);
- builder.appendString(String.valueOf(f));
+ if (Float.isNaN(f)) {
+ builder.appendUtf8StringPointable(AbstractDoubleConstructorEvaluator.NAN);
+ } else if (f == Float.POSITIVE_INFINITY) { // NOSONAR
+ builder.appendUtf8StringPointable(AbstractDoubleConstructorEvaluator.POSITIVE_INF);
+ } else if (f == Float.NEGATIVE_INFINITY) { // NOSONAR
+ builder.appendUtf8StringPointable(AbstractDoubleConstructorEvaluator.NEGATIVE_INF);
+ } else {
+ builder.appendString(String.valueOf(f));
+ }
break;
}
case BOOLEAN: {
diff --git a/asterixdb/asterix-runtime/src/main/java/org/apache/asterix/runtime/evaluators/functions/IfInfDescriptor.java b/asterixdb/asterix-runtime/src/main/java/org/apache/asterix/runtime/evaluators/functions/IfInfDescriptor.java
new file mode 100644
index 0000000..0ff08a4
--- /dev/null
+++ b/asterixdb/asterix-runtime/src/main/java/org/apache/asterix/runtime/evaluators/functions/IfInfDescriptor.java
@@ -0,0 +1,67 @@
+/*
+ * 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.
+ */
+
+package org.apache.asterix.runtime.evaluators.functions;
+
+import org.apache.asterix.om.functions.BuiltinFunctions;
+import org.apache.asterix.om.functions.IFunctionDescriptorFactory;
+import org.apache.asterix.runtime.evaluators.base.AbstractScalarFunctionDynamicDescriptor;
+import org.apache.hyracks.algebricks.core.algebra.functions.FunctionIdentifier;
+import org.apache.hyracks.algebricks.runtime.base.IScalarEvaluator;
+import org.apache.hyracks.algebricks.runtime.base.IScalarEvaluatorFactory;
+import org.apache.hyracks.api.context.IHyracksTaskContext;
+import org.apache.hyracks.api.exceptions.HyracksDataException;
+
+public class IfInfDescriptor extends AbstractScalarFunctionDynamicDescriptor {
+
+ private static final long serialVersionUID = 1L;
+ public static final IFunctionDescriptorFactory FACTORY = IfInfDescriptor::new;
+
+ @Override
+ public IScalarEvaluatorFactory createEvaluatorFactory(final IScalarEvaluatorFactory[] args) {
+ return new IScalarEvaluatorFactory() {
+ private static final long serialVersionUID = 1L;
+
+ @Override
+ public IScalarEvaluator createScalarEvaluator(final IHyracksTaskContext ctx) throws HyracksDataException {
+ return new IfNanOrInfDescriptor.AbstractIfInfOrNanEval(ctx, args) {
+ @Override
+ protected boolean skipDouble(double d) {
+ return Double.isInfinite(d);
+ }
+
+ @Override
+ protected boolean skipFloat(float f) {
+ return Float.isInfinite(f);
+ }
+
+ @Override
+ protected FunctionIdentifier getIdentifier() {
+ return IfInfDescriptor.this.getIdentifier();
+ }
+ };
+ }
+ };
+ }
+
+ @Override
+ public FunctionIdentifier getIdentifier() {
+ return BuiltinFunctions.IF_INF;
+ }
+}
diff --git a/asterixdb/asterix-runtime/src/main/java/org/apache/asterix/runtime/evaluators/functions/IfMissingDescriptor.java b/asterixdb/asterix-runtime/src/main/java/org/apache/asterix/runtime/evaluators/functions/IfMissingDescriptor.java
index 984b692..c3c017c 100644
--- a/asterixdb/asterix-runtime/src/main/java/org/apache/asterix/runtime/evaluators/functions/IfMissingDescriptor.java
+++ b/asterixdb/asterix-runtime/src/main/java/org/apache/asterix/runtime/evaluators/functions/IfMissingDescriptor.java
@@ -40,7 +40,7 @@
@Override
public IScalarEvaluator createScalarEvaluator(final IHyracksTaskContext ctx) throws HyracksDataException {
- return new IfMissingOrNullDescriptor.AbstractIfEvaluator(ctx, args) {
+ return new IfMissingOrNullDescriptor.AbstractIfMissingOrNullEval(ctx, args) {
@Override
protected boolean skip(byte argTypeTag) {
return argTypeTag == ATypeTag.SERIALIZED_MISSING_TYPE_TAG;
diff --git a/asterixdb/asterix-runtime/src/main/java/org/apache/asterix/runtime/evaluators/functions/IfMissingOrNullDescriptor.java b/asterixdb/asterix-runtime/src/main/java/org/apache/asterix/runtime/evaluators/functions/IfMissingOrNullDescriptor.java
index 28db4d6..3166c40 100644
--- a/asterixdb/asterix-runtime/src/main/java/org/apache/asterix/runtime/evaluators/functions/IfMissingOrNullDescriptor.java
+++ b/asterixdb/asterix-runtime/src/main/java/org/apache/asterix/runtime/evaluators/functions/IfMissingOrNullDescriptor.java
@@ -45,7 +45,7 @@
@Override
public IScalarEvaluator createScalarEvaluator(final IHyracksTaskContext ctx) throws HyracksDataException {
- return new AbstractIfEvaluator(ctx, args) {
+ return new AbstractIfMissingOrNullEval(ctx, args) {
@Override
protected boolean skip(byte argTypeTag) {
return argTypeTag == ATypeTag.SERIALIZED_MISSING_TYPE_TAG
@@ -61,15 +61,14 @@
return BuiltinFunctions.IF_MISSING_OR_NULL;
}
- public static abstract class AbstractIfEvaluator implements IScalarEvaluator {
-
- private static final byte[] nullBytes = new byte[] { ATypeTag.SERIALIZED_NULL_TYPE_TAG };
+ public static abstract class AbstractIfMissingOrNullEval implements IScalarEvaluator {
private final IScalarEvaluator[] argEvals;
private final IPointable argPtr;
- AbstractIfEvaluator(IHyracksTaskContext ctx, IScalarEvaluatorFactory[] args) throws HyracksDataException {
+ AbstractIfMissingOrNullEval(IHyracksTaskContext ctx, IScalarEvaluatorFactory[] args)
+ throws HyracksDataException {
argEvals = new IScalarEvaluator[args.length];
for (int i = 0; i < argEvals.length; i++) {
argEvals[i] = args[i].createScalarEvaluator(ctx);
@@ -88,7 +87,7 @@
return;
}
}
- result.set(nullBytes, 0, nullBytes.length);
+ PointableHelper.setNull(result);
}
protected abstract boolean skip(byte argTypeTag);
diff --git a/asterixdb/asterix-runtime/src/main/java/org/apache/asterix/runtime/evaluators/functions/IfNanDescriptor.java b/asterixdb/asterix-runtime/src/main/java/org/apache/asterix/runtime/evaluators/functions/IfNanDescriptor.java
new file mode 100644
index 0000000..9c1f024
--- /dev/null
+++ b/asterixdb/asterix-runtime/src/main/java/org/apache/asterix/runtime/evaluators/functions/IfNanDescriptor.java
@@ -0,0 +1,67 @@
+/*
+ * 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.
+ */
+
+package org.apache.asterix.runtime.evaluators.functions;
+
+import org.apache.asterix.om.functions.BuiltinFunctions;
+import org.apache.asterix.om.functions.IFunctionDescriptorFactory;
+import org.apache.asterix.runtime.evaluators.base.AbstractScalarFunctionDynamicDescriptor;
+import org.apache.hyracks.algebricks.core.algebra.functions.FunctionIdentifier;
+import org.apache.hyracks.algebricks.runtime.base.IScalarEvaluator;
+import org.apache.hyracks.algebricks.runtime.base.IScalarEvaluatorFactory;
+import org.apache.hyracks.api.context.IHyracksTaskContext;
+import org.apache.hyracks.api.exceptions.HyracksDataException;
+
+public class IfNanDescriptor extends AbstractScalarFunctionDynamicDescriptor {
+
+ private static final long serialVersionUID = 1L;
+ public static final IFunctionDescriptorFactory FACTORY = IfNanDescriptor::new;
+
+ @Override
+ public IScalarEvaluatorFactory createEvaluatorFactory(final IScalarEvaluatorFactory[] args) {
+ return new IScalarEvaluatorFactory() {
+ private static final long serialVersionUID = 1L;
+
+ @Override
+ public IScalarEvaluator createScalarEvaluator(final IHyracksTaskContext ctx) throws HyracksDataException {
+ return new IfNanOrInfDescriptor.AbstractIfInfOrNanEval(ctx, args) {
+ @Override
+ protected boolean skipDouble(double d) {
+ return Double.isNaN(d);
+ }
+
+ @Override
+ protected boolean skipFloat(float f) {
+ return Float.isNaN(f);
+ }
+
+ @Override
+ protected FunctionIdentifier getIdentifier() {
+ return IfNanDescriptor.this.getIdentifier();
+ }
+ };
+ }
+ };
+ }
+
+ @Override
+ public FunctionIdentifier getIdentifier() {
+ return BuiltinFunctions.IF_NAN;
+ }
+}
diff --git a/asterixdb/asterix-runtime/src/main/java/org/apache/asterix/runtime/evaluators/functions/IfNanOrInfDescriptor.java b/asterixdb/asterix-runtime/src/main/java/org/apache/asterix/runtime/evaluators/functions/IfNanOrInfDescriptor.java
new file mode 100644
index 0000000..bf61922
--- /dev/null
+++ b/asterixdb/asterix-runtime/src/main/java/org/apache/asterix/runtime/evaluators/functions/IfNanOrInfDescriptor.java
@@ -0,0 +1,135 @@
+/*
+ * 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.
+ */
+
+package org.apache.asterix.runtime.evaluators.functions;
+
+import org.apache.asterix.common.exceptions.ErrorCode;
+import org.apache.asterix.common.exceptions.RuntimeDataException;
+import org.apache.asterix.om.functions.BuiltinFunctions;
+import org.apache.asterix.om.functions.IFunctionDescriptorFactory;
+import org.apache.asterix.om.types.ATypeTag;
+import org.apache.asterix.runtime.evaluators.base.AbstractScalarFunctionDynamicDescriptor;
+import org.apache.hyracks.algebricks.core.algebra.functions.FunctionIdentifier;
+import org.apache.hyracks.algebricks.runtime.base.IScalarEvaluator;
+import org.apache.hyracks.algebricks.runtime.base.IScalarEvaluatorFactory;
+import org.apache.hyracks.api.context.IHyracksTaskContext;
+import org.apache.hyracks.api.exceptions.HyracksDataException;
+import org.apache.hyracks.data.std.api.IPointable;
+import org.apache.hyracks.data.std.primitive.DoublePointable;
+import org.apache.hyracks.data.std.primitive.FloatPointable;
+import org.apache.hyracks.data.std.primitive.VoidPointable;
+import org.apache.hyracks.dataflow.common.data.accessors.IFrameTupleReference;
+
+public class IfNanOrInfDescriptor extends AbstractScalarFunctionDynamicDescriptor {
+
+ private static final long serialVersionUID = 1L;
+ public static final IFunctionDescriptorFactory FACTORY = IfNanOrInfDescriptor::new;
+
+ @Override
+ public IScalarEvaluatorFactory createEvaluatorFactory(final IScalarEvaluatorFactory[] args) {
+ return new IScalarEvaluatorFactory() {
+ private static final long serialVersionUID = 1L;
+
+ @Override
+ public IScalarEvaluator createScalarEvaluator(final IHyracksTaskContext ctx) throws HyracksDataException {
+ return new AbstractIfInfOrNanEval(ctx, args) {
+ @Override
+ protected boolean skipDouble(double d) {
+ return Double.isInfinite(d) || Double.isNaN(d);
+ }
+
+ @Override
+ protected boolean skipFloat(float f) {
+ return Float.isInfinite(f) || Float.isNaN(f);
+ }
+
+ @Override
+ protected FunctionIdentifier getIdentifier() {
+ return IfNanOrInfDescriptor.this.getIdentifier();
+ }
+ };
+ }
+ };
+ }
+
+ @Override
+ public FunctionIdentifier getIdentifier() {
+ return BuiltinFunctions.IF_NAN_OR_INF;
+ }
+
+ public static abstract class AbstractIfInfOrNanEval implements IScalarEvaluator {
+
+ private final IScalarEvaluator[] argEvals;
+
+ private final IPointable argPtr;
+
+ AbstractIfInfOrNanEval(IHyracksTaskContext ctx, IScalarEvaluatorFactory[] args) throws HyracksDataException {
+ argEvals = new IScalarEvaluator[args.length];
+ for (int i = 0; i < argEvals.length; i++) {
+ argEvals[i] = args[i].createScalarEvaluator(ctx);
+ }
+ argPtr = new VoidPointable();
+ }
+
+ @Override
+ public void evaluate(IFrameTupleReference tuple, IPointable result) throws HyracksDataException {
+ for (int i = 0; i < argEvals.length; i++) {
+ argEvals[i].evaluate(tuple, argPtr);
+ ATypeTag argTypeTag = PointableHelper.getTypeTag(argPtr);
+ if (argTypeTag == null) {
+ throw new RuntimeDataException(ErrorCode.INVALID_FORMAT, getIdentifier(), i);
+ }
+ switch (argTypeTag) {
+ case DOUBLE:
+ double d = DoublePointable.getDouble(argPtr.getByteArray(), argPtr.getStartOffset() + 1);
+ if (skipDouble(d)) {
+ continue;
+ }
+ result.set(argPtr);
+ return;
+ case FLOAT:
+ float f = FloatPointable.getFloat(argPtr.getByteArray(), argPtr.getStartOffset() + 1);
+ if (skipFloat(f)) {
+ continue;
+ }
+ result.set(argPtr);
+ return;
+ case BIGINT:
+ case INTEGER:
+ case SMALLINT:
+ case TINYINT:
+ case MISSING:
+ result.set(argPtr);
+ return;
+ default:
+ PointableHelper.setNull(result);
+ return;
+ }
+ }
+
+ PointableHelper.setNull(result);
+ }
+
+ protected abstract FunctionIdentifier getIdentifier();
+
+ protected abstract boolean skipDouble(double d);
+
+ protected abstract boolean skipFloat(float f);
+ }
+}
diff --git a/asterixdb/asterix-runtime/src/main/java/org/apache/asterix/runtime/evaluators/functions/IfNullDescriptor.java b/asterixdb/asterix-runtime/src/main/java/org/apache/asterix/runtime/evaluators/functions/IfNullDescriptor.java
index 11e464a..e5316cb 100644
--- a/asterixdb/asterix-runtime/src/main/java/org/apache/asterix/runtime/evaluators/functions/IfNullDescriptor.java
+++ b/asterixdb/asterix-runtime/src/main/java/org/apache/asterix/runtime/evaluators/functions/IfNullDescriptor.java
@@ -41,7 +41,7 @@
@Override
public IScalarEvaluator createScalarEvaluator(final IHyracksTaskContext ctx) throws HyracksDataException {
- return new IfMissingOrNullDescriptor.AbstractIfEvaluator(ctx, args) {
+ return new IfMissingOrNullDescriptor.AbstractIfMissingOrNullEval(ctx, args) {
@Override
protected boolean skip(byte argTypeTag) {
return argTypeTag == ATypeTag.SERIALIZED_NULL_TYPE_TAG;
diff --git a/asterixdb/asterix-runtime/src/main/java/org/apache/asterix/runtime/evaluators/functions/PointableHelper.java b/asterixdb/asterix-runtime/src/main/java/org/apache/asterix/runtime/evaluators/functions/PointableHelper.java
index df8fc75..1c69d4c 100644
--- a/asterixdb/asterix-runtime/src/main/java/org/apache/asterix/runtime/evaluators/functions/PointableHelper.java
+++ b/asterixdb/asterix-runtime/src/main/java/org/apache/asterix/runtime/evaluators/functions/PointableHelper.java
@@ -28,6 +28,7 @@
import org.apache.hyracks.api.exceptions.HyracksDataException;
import org.apache.hyracks.data.std.accessors.PointableBinaryComparatorFactory;
import org.apache.hyracks.data.std.api.IMutableValueStorage;
+import org.apache.hyracks.data.std.api.IPointable;
import org.apache.hyracks.data.std.api.IValueReference;
import org.apache.hyracks.data.std.primitive.UTF8StringPointable;
import org.apache.hyracks.util.string.UTF8StringWriter;
@@ -40,6 +41,9 @@
*/
public class PointableHelper {
+
+ private static final byte[] NULL_BYTES = new byte[] { ATypeTag.SERIALIZED_NULL_TYPE_TAG };
+
private static final IBinaryComparator STRING_BINARY_COMPARATOR =
PointableBinaryComparatorFactory.of(UTF8StringPointable.FACTORY).createBinaryComparator();
private final UTF8StringWriter utf8Writer;
@@ -123,4 +127,8 @@
throw new HyracksDataException("Could not serialize " + str);
}
}
+
+ public static void setNull(IPointable pointable) {
+ pointable.set(NULL_BYTES, 0, NULL_BYTES.length);
+ }
}
diff --git a/asterixdb/asterix-runtime/src/main/java/org/apache/asterix/runtime/functions/FunctionCollection.java b/asterixdb/asterix-runtime/src/main/java/org/apache/asterix/runtime/functions/FunctionCollection.java
index 2f1fdcb..c47a4de 100644
--- a/asterixdb/asterix-runtime/src/main/java/org/apache/asterix/runtime/functions/FunctionCollection.java
+++ b/asterixdb/asterix-runtime/src/main/java/org/apache/asterix/runtime/functions/FunctionCollection.java
@@ -162,8 +162,11 @@
import org.apache.asterix.runtime.evaluators.functions.GramTokensDescriptor;
import org.apache.asterix.runtime.evaluators.functions.HashedGramTokensDescriptor;
import org.apache.asterix.runtime.evaluators.functions.HashedWordTokensDescriptor;
+import org.apache.asterix.runtime.evaluators.functions.IfInfDescriptor;
+import org.apache.asterix.runtime.evaluators.functions.IfNanOrInfDescriptor;
import org.apache.asterix.runtime.evaluators.functions.IfMissingDescriptor;
import org.apache.asterix.runtime.evaluators.functions.IfMissingOrNullDescriptor;
+import org.apache.asterix.runtime.evaluators.functions.IfNanDescriptor;
import org.apache.asterix.runtime.evaluators.functions.IfNullDescriptor;
import org.apache.asterix.runtime.evaluators.functions.InjectFailureDescriptor;
import org.apache.asterix.runtime.evaluators.functions.IsArrayDescriptor;
@@ -483,6 +486,9 @@
fc.addGenerated(GetItemDescriptor.FACTORY);
// Numeric functions
+ fc.add(IfInfDescriptor.FACTORY);
+ fc.add(IfNanDescriptor.FACTORY);
+ fc.add(IfNanOrInfDescriptor.FACTORY);
fc.addGenerated(NumericUnaryMinusDescriptor.FACTORY);
fc.addGenerated(NumericAddDescriptor.FACTORY);
fc.addGenerated(NumericDivideDescriptor.FACTORY);