Dmitry Lychagin has submitted this change and it was merged. Change subject: [NO ISSUE][FUN] Add to_number() function ......................................................................
[NO ISSUE][FUN] Add to_number() function - user model changes: yes - storage format changes: no - interface changes: no Details: - add to_number() function - add testcases and documentation - fix type inference for to_bigint() and to_double() to return optional type in cases when NULL can be produced at runtime Change-Id: Id370aadcf16447f7c775c30d2bffc33fc6a96927 Reviewed-on: https://asterix-gerrit.ics.uci.edu/2449 Tested-by: Jenkins <[email protected]> Integration-Tests: Jenkins <[email protected]> Contrib: Jenkins <[email protected]> Reviewed-by: Till Westmann <[email protected]> --- A asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/types/to_number_01/to_number_01.1.query.sqlpp A asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/types/to_number_02/to_number_02.1.query.sqlpp A asterixdb/asterix-app/src/test/resources/runtimets/results/types/to_number_01/to_number_01.1.adm M asterixdb/asterix-app/src/test/resources/runtimets/testsuite_sqlpp.xml M asterixdb/asterix-doc/src/main/markdown/builtins/11_type.md M asterixdb/asterix-lang-common/src/main/java/org/apache/asterix/lang/common/util/CommonFunctionMapUtil.java M asterixdb/asterix-om/src/main/java/org/apache/asterix/om/functions/BuiltinFunctions.java A asterixdb/asterix-om/src/main/java/org/apache/asterix/om/typecomputer/impl/ToBigIntTypeComputer.java A asterixdb/asterix-om/src/main/java/org/apache/asterix/om/typecomputer/impl/ToDoubleTypeComputer.java A asterixdb/asterix-om/src/main/java/org/apache/asterix/om/typecomputer/impl/ToNumberTypeComputer.java M asterixdb/asterix-om/src/main/java/org/apache/asterix/om/types/AUnionType.java A asterixdb/asterix-runtime/src/main/java/org/apache/asterix/runtime/evaluators/common/NumberUtils.java M asterixdb/asterix-runtime/src/main/java/org/apache/asterix/runtime/evaluators/constructors/AFloatConstructorDescriptor.java M asterixdb/asterix-runtime/src/main/java/org/apache/asterix/runtime/evaluators/constructors/AbstractDoubleConstructorEvaluator.java M asterixdb/asterix-runtime/src/main/java/org/apache/asterix/runtime/evaluators/constructors/AbstractInt64ConstructorEvaluator.java M asterixdb/asterix-runtime/src/main/java/org/apache/asterix/runtime/evaluators/constructors/AbstractStringConstructorEvaluator.java M asterixdb/asterix-runtime/src/main/java/org/apache/asterix/runtime/evaluators/functions/ToBigIntDescriptor.java M asterixdb/asterix-runtime/src/main/java/org/apache/asterix/runtime/evaluators/functions/ToBooleanDescriptor.java M asterixdb/asterix-runtime/src/main/java/org/apache/asterix/runtime/evaluators/functions/ToDoubleDescriptor.java A asterixdb/asterix-runtime/src/main/java/org/apache/asterix/runtime/evaluators/functions/ToNumberDescriptor.java M asterixdb/asterix-runtime/src/main/java/org/apache/asterix/runtime/functions/FunctionCollection.java 21 files changed, 591 insertions(+), 127 deletions(-) Approvals: Anon. E. Moose #1000171: Till Westmann: Looks good to me, approved Jenkins: Verified; ; Verified Objections: Jenkins: Violations found diff --git a/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/types/to_number_01/to_number_01.1.query.sqlpp b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/types/to_number_01/to_number_01.1.query.sqlpp new file mode 100644 index 0000000..739a6a9 --- /dev/null +++ b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/types/to_number_01/to_number_01.1.query.sqlpp @@ -0,0 +1,42 @@ +/* + * 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. + */ + + { + "t1": tonumber(false), + "t2": to_number(true), + "t3": tonum(int8("8")), + "t4": to_number(int16("16")), + "t5": to_number(int32("32")), + "t6": to_number(int64("64")), + "t7": to_number(float("1.5")), + "t8": to_number(double("2.25")), + "t9": to_number("512"), + "t10": is_null(to_number("foo")), + "t11": is_null(to_number([])), + "t12": is_null(to_number({{}})), + "t13": is_null(to_number({})), + "t14": is_null(to_number(null)), + "t15": is_missing(to_number(missing)), + "t16": to_string(to_number(float("INF"))), + "t17": to_string(to_number(float("-INF"))), + "t18": to_string(to_number(float("NaN"))), + "t19": to_string(to_number(double("INF"))), + "t20": to_string(to_number(double("-INF"))), + "t21": to_string(to_number(double("NaN"))) + }; diff --git a/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/types/to_number_02/to_number_02.1.query.sqlpp b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/types/to_number_02/to_number_02.1.query.sqlpp new file mode 100644 index 0000000..837750e --- /dev/null +++ b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/types/to_number_02/to_number_02.1.query.sqlpp @@ -0,0 +1,21 @@ +/* + * 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. + */ +{ + "t": to_number(date("2017-06-30")) +} \ No newline at end of file diff --git a/asterixdb/asterix-app/src/test/resources/runtimets/results/types/to_number_01/to_number_01.1.adm b/asterixdb/asterix-app/src/test/resources/runtimets/results/types/to_number_01/to_number_01.1.adm new file mode 100644 index 0000000..72416e4 --- /dev/null +++ b/asterixdb/asterix-app/src/test/resources/runtimets/results/types/to_number_01/to_number_01.1.adm @@ -0,0 +1 @@ +{ "t1": 0, "t2": 1, "t3": 8, "t4": 16, "t5": 32, "t6": 64, "t7": 1.5, "t8": 2.25, "t9": 512, "t10": true, "t11": true, "t12": true, "t13": true, "t14": true, "t15": true, "t16": "INF", "t17": "-INF", "t18": "NaN", "t19": "INF", "t20": "-INF", "t21": "NaN" } \ 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 67980d0..6bb1886 100644 --- a/asterixdb/asterix-app/src/test/resources/runtimets/testsuite_sqlpp.xml +++ b/asterixdb/asterix-app/src/test/resources/runtimets/testsuite_sqlpp.xml @@ -9285,6 +9285,17 @@ <expected-error>ASX0002: Type mismatch</expected-error> </compilation-unit> </test-case> + <test-case FilePath="types"> + <compilation-unit name="to_number_01"> + <output-dir compare="Text">to_number_01</output-dir> + </compilation-unit> + </test-case> + <test-case FilePath="types"> + <compilation-unit name="to_number_02"> + <output-dir compare="Text">to_number_02</output-dir> + <expected-error>ASX0002: Type mismatch</expected-error> + </compilation-unit> + </test-case> </test-group> <test-group name="materialization"> <test-case FilePath="materialization"> diff --git a/asterixdb/asterix-doc/src/main/markdown/builtins/11_type.md b/asterixdb/asterix-doc/src/main/markdown/builtins/11_type.md index fd6a262..f972735 100644 --- a/asterixdb/asterix-doc/src/main/markdown/builtins/11_type.md +++ b/asterixdb/asterix-doc/src/main/markdown/builtins/11_type.md @@ -399,6 +399,40 @@ The function has an alias `todouble`. +### to_number ### + * Syntax: + + to_number(expr) + + * Converts input value to a numeric value + * Arguments: + * `expr` : an expression + * Return Value: + * if the argument is `missing` then `missing` is returned + * if the argument is `null` then `null` is returned + * if the argument is of numeric type then it is returned as is + * if the argument is of `boolean` type then `1` is returned if it is `true`, `0` if it is `false` + * if the argument is of `string` type and can be parsed as `bigint` then that `bigint` value is returned, + otherwise if it can be parsed as `double` then that `double` value is returned, + otherwise `null` is returned + * if the argument is of `array`/`multiset`/`object` type then `null` is returned + * type error is raised for all other input types + + * Example: + + { + "v1": to_number(false), + "v2": to_number(true), + "v3": to_number(10), + "v4": to_number(11.5), + "v5": to_number("12.5") + }; + + * The expected result is: + + { "v1": 0, "v2": 1, "v3": 10, "v4": 11.5, "v5": 12.5 } + + The function has an alias `tonumber`. ### to_string ### * Syntax: 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 e5d1643..0646224 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 @@ -74,6 +74,8 @@ FUNCTION_NAME_MAP.put("tostring", "to-string"); // tostring, internal: to-string FUNCTION_NAME_MAP.put("todouble", "to-double"); // todouble, internal: to-double FUNCTION_NAME_MAP.put("tobigint", "to-bigint"); // tobigint, internal: to-bigint + FUNCTION_NAME_MAP.put("tonumber", "to-number"); // tonumber, internal: to-number + FUNCTION_NAME_MAP.put("tonum", "to-number"); // tonum, internal: to-number // Object functions // record-merge, internal: object-merge 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 761ddba..3e3cf43 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 @@ -106,6 +106,9 @@ import org.apache.asterix.om.typecomputer.impl.SubsetCollectionTypeComputer; import org.apache.asterix.om.typecomputer.impl.SubstringTypeComputer; import org.apache.asterix.om.typecomputer.impl.SwitchCaseComputer; +import org.apache.asterix.om.typecomputer.impl.ToBigIntTypeComputer; +import org.apache.asterix.om.typecomputer.impl.ToDoubleTypeComputer; +import org.apache.asterix.om.typecomputer.impl.ToNumberTypeComputer; import org.apache.asterix.om.typecomputer.impl.UnaryBinaryInt64TypeComputer; import org.apache.asterix.om.typecomputer.impl.UnaryMinusTypeComputer; import org.apache.asterix.om.typecomputer.impl.UnaryStringInt64TypeComputer; @@ -871,6 +874,8 @@ new FunctionIdentifier(FunctionConstants.ASTERIX_NS, "to-double", 1); public static final FunctionIdentifier TO_BIGINT = new FunctionIdentifier(FunctionConstants.ASTERIX_NS, "to-bigint", 1); + public static final FunctionIdentifier TO_NUMBER = + new FunctionIdentifier(FunctionConstants.ASTERIX_NS, "to-number", 1); public static final FunctionIdentifier EXTERNAL_LOOKUP = new FunctionIdentifier(FunctionConstants.ASTERIX_NS, "external-lookup", FunctionIdentifier.VARARGS); @@ -1057,8 +1062,9 @@ addFunction(TO_BOOLEAN, ABooleanTypeComputer.INSTANCE, true); addFunction(TO_STRING, AStringTypeComputer.INSTANCE, true); - addFunction(TO_DOUBLE, ADoubleTypeComputer.INSTANCE, true); - addFunction(TO_BIGINT, AInt64TypeComputer.INSTANCE, true); + addFunction(TO_DOUBLE, ToDoubleTypeComputer.INSTANCE, true); + addFunction(TO_BIGINT, ToBigIntTypeComputer.INSTANCE, true); + addFunction(TO_NUMBER, ToNumberTypeComputer.INSTANCE, true); // Aggregate Functions addFunction(MAX, MinMaxAggTypeComputer.INSTANCE, true); diff --git a/asterixdb/asterix-om/src/main/java/org/apache/asterix/om/typecomputer/impl/ToBigIntTypeComputer.java b/asterixdb/asterix-om/src/main/java/org/apache/asterix/om/typecomputer/impl/ToBigIntTypeComputer.java new file mode 100644 index 0000000..e7b6f42 --- /dev/null +++ b/asterixdb/asterix-om/src/main/java/org/apache/asterix/om/typecomputer/impl/ToBigIntTypeComputer.java @@ -0,0 +1,53 @@ +/* + * 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 ToBigIntTypeComputer extends AbstractResultTypeComputer { + + public static final ToBigIntTypeComputer INSTANCE = new ToBigIntTypeComputer(); + + private ToBigIntTypeComputer() { + } + + @Override + protected IAType getResultType(ILogicalExpression expr, IAType... strippedInputTypes) throws AlgebricksException { + IAType strippedInputType = strippedInputTypes[0]; + switch (strippedInputType.getTypeTag()) { + case BOOLEAN: + case TINYINT: + case SMALLINT: + case INTEGER: + case BIGINT: + case FLOAT: + case DOUBLE: + // these types cannot result in NULL output + return BuiltinType.AINT64; + default: + return AUnionType.createNullableType(BuiltinType.AINT64); + } + } +} diff --git a/asterixdb/asterix-om/src/main/java/org/apache/asterix/om/typecomputer/impl/ToDoubleTypeComputer.java b/asterixdb/asterix-om/src/main/java/org/apache/asterix/om/typecomputer/impl/ToDoubleTypeComputer.java new file mode 100644 index 0000000..7794aab --- /dev/null +++ b/asterixdb/asterix-om/src/main/java/org/apache/asterix/om/typecomputer/impl/ToDoubleTypeComputer.java @@ -0,0 +1,53 @@ +/* + * 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 ToDoubleTypeComputer extends AbstractResultTypeComputer { + + public static final ToDoubleTypeComputer INSTANCE = new ToDoubleTypeComputer(); + + private ToDoubleTypeComputer() { + } + + @Override + protected IAType getResultType(ILogicalExpression expr, IAType... strippedInputTypes) throws AlgebricksException { + IAType strippedInputType = strippedInputTypes[0]; + switch (strippedInputType.getTypeTag()) { + case BOOLEAN: + case TINYINT: + case SMALLINT: + case INTEGER: + case BIGINT: + case FLOAT: + case DOUBLE: + // these types cannot result in NULL output + return BuiltinType.ADOUBLE; + default: + return AUnionType.createNullableType(BuiltinType.ADOUBLE); + } + } +} diff --git a/asterixdb/asterix-om/src/main/java/org/apache/asterix/om/typecomputer/impl/ToNumberTypeComputer.java b/asterixdb/asterix-om/src/main/java/org/apache/asterix/om/typecomputer/impl/ToNumberTypeComputer.java new file mode 100644 index 0000000..077a1eb --- /dev/null +++ b/asterixdb/asterix-om/src/main/java/org/apache/asterix/om/typecomputer/impl/ToNumberTypeComputer.java @@ -0,0 +1,52 @@ +/* + * 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.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 ToNumberTypeComputer extends AbstractResultTypeComputer { + public static final ToNumberTypeComputer INSTANCE = new ToNumberTypeComputer(); + + private ToNumberTypeComputer() { + } + + @Override + protected IAType getResultType(ILogicalExpression expr, IAType... strippedInputTypes) throws AlgebricksException { + IAType strippedInputType = strippedInputTypes[0]; + switch (strippedInputType.getTypeTag()) { + case BOOLEAN: + return BuiltinType.AINT64; + case TINYINT: + case SMALLINT: + case INTEGER: + case BIGINT: + case FLOAT: + case DOUBLE: + return strippedInputType; + default: + // STRING can be parsed as either BIGINT or DOUBLE + return BuiltinType.ANY; + } + } +} diff --git a/asterixdb/asterix-om/src/main/java/org/apache/asterix/om/types/AUnionType.java b/asterixdb/asterix-om/src/main/java/org/apache/asterix/om/types/AUnionType.java index 007f072..9fdaca1 100644 --- a/asterixdb/asterix-om/src/main/java/org/apache/asterix/om/types/AUnionType.java +++ b/asterixdb/asterix-om/src/main/java/org/apache/asterix/om/types/AUnionType.java @@ -99,6 +99,14 @@ return createMissableType(t, s == null ? null : s + "?"); } + public static IAType createNullableType(IAType t) { + if (t != null && t.getTypeTag() == ATypeTag.NULL) { + return t; + } + String s = t != null ? t.getTypeName() : null; + return createNullableType(t, s == null ? null : s + "?"); + } + public static IAType createNullableType(IAType type, String typeName) { if (type != null && type.getTypeTag() == ATypeTag.NULL) { return type; diff --git a/asterixdb/asterix-runtime/src/main/java/org/apache/asterix/runtime/evaluators/common/NumberUtils.java b/asterixdb/asterix-runtime/src/main/java/org/apache/asterix/runtime/evaluators/common/NumberUtils.java new file mode 100644 index 0000000..0b2e94c --- /dev/null +++ b/asterixdb/asterix-runtime/src/main/java/org/apache/asterix/runtime/evaluators/common/NumberUtils.java @@ -0,0 +1,119 @@ +/* + * 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.common; + +import org.apache.asterix.om.base.AMutableDouble; +import org.apache.asterix.om.base.AMutableInt64; +import org.apache.hyracks.data.std.primitive.UTF8StringPointable; + +/** + * Utility methods for number handling + */ +public final class NumberUtils { + + public static final UTF8StringPointable POSITIVE_INF = UTF8StringPointable.generateUTF8Pointable("INF"); + + public static final UTF8StringPointable NEGATIVE_INF = UTF8StringPointable.generateUTF8Pointable("-INF"); + + public static final UTF8StringPointable NAN = UTF8StringPointable.generateUTF8Pointable("NaN"); + + public static final long NAN_BITS = Double.doubleToLongBits(Double.NaN); + + public static final long POSITIVE_ZERO_BITS = Double.doubleToLongBits(+0.0d); + + public static final long NEGATIVE_ZERO_BITS = Double.doubleToLongBits(-0.0d); + + /** + * Parses string as double + * @param textPtr input string + * @param result placeholder for the result + * @return {@code true} if parsing was successful, {@code false} otherwise + */ + public static boolean parseDouble(UTF8StringPointable textPtr, AMutableDouble result) { + double v; + if (POSITIVE_INF.compareTo(textPtr) == 0) { + v = Double.POSITIVE_INFINITY; + } else if (NEGATIVE_INF.compareTo(textPtr) == 0) { + v = Double.NEGATIVE_INFINITY; + } else if (NAN.compareTo(textPtr) == 0) { + v = Double.NaN; + } else { + try { + v = Double.parseDouble(textPtr.toString()); + } catch (NumberFormatException e) { + return false; + } + } + result.setValue(v); + return true; + } + + /** + * Parses string as bigint + * @param textPtr input string + * @param result placeholder for the result + * @return {@code true} if parsing was successful, {@code false} otherwise + */ + public static boolean parseInt64(UTF8StringPointable textPtr, AMutableInt64 result) { + byte[] bytes = textPtr.getByteArray(); + int offset = textPtr.getCharStartOffset(); + //accumulating value in negative domain + //otherwise Long.MIN_VALUE = -(Long.MAX_VALUE + 1) would have caused overflow + long value = 0; + boolean positive = true; + long limit = -Long.MAX_VALUE; + if (bytes[offset] == '+') { + offset++; + } else if (bytes[offset] == '-') { + offset++; + positive = false; + limit = Long.MIN_VALUE; + } + int end = textPtr.getStartOffset() + textPtr.getLength(); + for (; offset < end; offset++) { + int digit; + if (bytes[offset] >= '0' && bytes[offset] <= '9') { + value *= 10; + digit = bytes[offset] - '0'; + } else if (bytes[offset] == 'i' && bytes[offset + 1] == '6' && bytes[offset + 2] == '4' + && offset + 3 == end) { + break; + } else { + return false; + } + if (value < limit + digit) { + return false; + } + value -= digit; + } + if (value > 0) { + return false; + } + if (value < 0 && positive) { + value *= -1; + } + + result.setValue(value); + return true; + } + + private NumberUtils() { + } +} 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 8c69671..b6f045e 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 @@ -21,7 +21,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.AFloat; import org.apache.asterix.om.base.AMutableFloat; @@ -31,13 +30,13 @@ import org.apache.asterix.om.types.ATypeTag; import org.apache.asterix.om.types.BuiltinType; import org.apache.asterix.runtime.evaluators.base.AbstractScalarFunctionDynamicDescriptor; +import org.apache.asterix.runtime.evaluators.common.NumberUtils; import org.apache.asterix.runtime.exceptions.InvalidDataFormatException; 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.algebricks.runtime.base.IScalarEvaluatorFactory; import org.apache.hyracks.api.context.IHyracksTaskContext; -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; @@ -45,7 +44,6 @@ 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 class AFloatConstructorDescriptor extends AbstractScalarFunctionDynamicDescriptor { private static final long serialVersionUID = 1L; @@ -89,14 +87,11 @@ resultStorage.reset(); int utf8offset = offset + 1; int utf8len = len - 1; - if (AbstractDoubleConstructorEvaluator.POSITIVE_INF.compareTo(serString, utf8offset, - utf8len) == 0) { + if (NumberUtils.POSITIVE_INF.compareTo(serString, utf8offset, utf8len) == 0) { aFloat.setValue(Float.POSITIVE_INFINITY); - } else if (AbstractDoubleConstructorEvaluator.NEGATIVE_INF.compareTo(serString, - utf8offset, utf8len) == 0) { + } else if (NumberUtils.NEGATIVE_INF.compareTo(serString, utf8offset, utf8len) == 0) { aFloat.setValue(Float.NEGATIVE_INFINITY); - } else if (AbstractDoubleConstructorEvaluator.NAN.compareTo(serString, utf8offset, - utf8len) == 0) { + } else if (NumberUtils.NAN.compareTo(serString, utf8offset, utf8len) == 0) { aFloat.setValue(Float.NaN); } else { utf8Ptr.set(serString, utf8offset, utf8len); 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 e10bf68..456735f 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 @@ -27,6 +27,7 @@ import org.apache.asterix.om.base.AMutableDouble; import org.apache.asterix.om.types.ATypeTag; import org.apache.asterix.om.types.BuiltinType; +import org.apache.asterix.runtime.evaluators.common.NumberUtils; import org.apache.asterix.runtime.exceptions.InvalidDataFormatException; import org.apache.asterix.runtime.exceptions.TypeMismatchException; import org.apache.hyracks.algebricks.core.algebra.functions.FunctionIdentifier; @@ -43,10 +44,6 @@ @SuppressWarnings("unchecked") protected static final ISerializerDeserializer<ADouble> DOUBLE_SERDE = SerializerDeserializerProvider.INSTANCE.getSerializerDeserializer(BuiltinType.ADOUBLE); - - 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; @@ -77,41 +74,25 @@ protected void evaluateImpl(IPointable result) throws IOException { byte[] bytes = inputArg.getByteArray(); - int offset = inputArg.getStartOffset(); - byte tt = bytes[offset]; + int startOffset = inputArg.getStartOffset(); + byte tt = bytes[startOffset]; if (tt == ATypeTag.SERIALIZED_DOUBLE_TYPE_TAG) { result.set(inputArg); } else if (tt == ATypeTag.SERIALIZED_STRING_TYPE_TAG) { - int len = inputArg.getLength(); - int utf8offset = offset + 1; - int utf8len = len - 1; - if (POSITIVE_INF.compareTo(bytes, utf8offset, utf8len) == 0) { - setDouble(result, Double.POSITIVE_INFINITY); - } else if (NEGATIVE_INF.compareTo(bytes, utf8offset, utf8len) == 0) { - setDouble(result, Double.NEGATIVE_INFINITY); - } else if (NAN.compareTo(bytes, utf8offset, utf8len) == 0) { - setDouble(result, Double.NaN); + utf8Ptr.set(bytes, startOffset + 1, inputArg.getLength() - 1); + if (NumberUtils.parseDouble(utf8Ptr, aDouble)) { + DOUBLE_SERDE.serialize(aDouble, out); + result.set(resultStorage); } else { - utf8Ptr.set(bytes, utf8offset, utf8len); - try { - setDouble(result, Double.parseDouble(utf8Ptr.toString())); - } catch (NumberFormatException e) { - handleUparseableString(result, e); - } + handleUparseableString(result); } } else { throw new TypeMismatchException(getIdentifier(), 0, tt, ATypeTag.SERIALIZED_STRING_TYPE_TAG); } } - protected void handleUparseableString(IPointable result, NumberFormatException e) throws HyracksDataException { - throw new InvalidDataFormatException(getIdentifier(), e, ATypeTag.SERIALIZED_DOUBLE_TYPE_TAG); - } - - protected void setDouble(IPointable result, double value) throws HyracksDataException { - aDouble.setValue(value); - DOUBLE_SERDE.serialize(aDouble, out); - result.set(resultStorage); + protected void handleUparseableString(IPointable result) throws HyracksDataException { + throw new InvalidDataFormatException(getIdentifier(), ATypeTag.SERIALIZED_DOUBLE_TYPE_TAG); } protected abstract FunctionIdentifier getIdentifier(); diff --git a/asterixdb/asterix-runtime/src/main/java/org/apache/asterix/runtime/evaluators/constructors/AbstractInt64ConstructorEvaluator.java b/asterixdb/asterix-runtime/src/main/java/org/apache/asterix/runtime/evaluators/constructors/AbstractInt64ConstructorEvaluator.java index 8cf9bed..2f9470d 100644 --- a/asterixdb/asterix-runtime/src/main/java/org/apache/asterix/runtime/evaluators/constructors/AbstractInt64ConstructorEvaluator.java +++ b/asterixdb/asterix-runtime/src/main/java/org/apache/asterix/runtime/evaluators/constructors/AbstractInt64ConstructorEvaluator.java @@ -27,6 +27,7 @@ import org.apache.asterix.om.base.AMutableInt64; import org.apache.asterix.om.types.ATypeTag; import org.apache.asterix.om.types.BuiltinType; +import org.apache.asterix.runtime.evaluators.common.NumberUtils; import org.apache.asterix.runtime.exceptions.InvalidDataFormatException; import org.apache.asterix.runtime.exceptions.TypeMismatchException; import org.apache.hyracks.algebricks.core.algebra.functions.FunctionIdentifier; @@ -79,49 +80,13 @@ if (tt == ATypeTag.SERIALIZED_INT64_TYPE_TAG) { result.set(inputArg); } else if (tt == ATypeTag.SERIALIZED_STRING_TYPE_TAG) { - int len = inputArg.getLength(); - utf8Ptr.set(bytes, startOffset + 1, len - 1); - int offset = utf8Ptr.getCharStartOffset(); - //accumulating value in negative domain - //otherwise Long.MIN_VALUE = -(Long.MAX_VALUE + 1) would have caused overflow - long value = 0; - boolean positive = true; - long limit = -Long.MAX_VALUE; - if (bytes[offset] == '+') { - offset++; - } else if (bytes[offset] == '-') { - offset++; - positive = false; - limit = Long.MIN_VALUE; - } - int end = startOffset + len; - for (; offset < end; offset++) { - int digit; - if (bytes[offset] >= '0' && bytes[offset] <= '9') { - value *= 10; - digit = bytes[offset] - '0'; - } else if (bytes[offset] == 'i' && bytes[offset + 1] == '6' && bytes[offset + 2] == '4' - && offset + 3 == end) { - break; - } else { - handleUnparseableString(result); - return; - } - if (value < limit + digit) { - handleUnparseableString(result); - } - value -= digit; - } - if (value > 0) { + utf8Ptr.set(bytes, startOffset + 1, inputArg.getLength() - 1); + if (NumberUtils.parseInt64(utf8Ptr, aInt64)) { + INT64_SERDE.serialize(aInt64, out); + result.set(resultStorage); + } else { handleUnparseableString(result); } - if (value < 0 && positive) { - value *= -1; - } - - aInt64.setValue(value); - INT64_SERDE.serialize(aInt64, out); - result.set(resultStorage); } else { throw new TypeMismatchException(getIdentifier(), 0, tt, ATypeTag.SERIALIZED_STRING_TYPE_TAG); } 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 11576aa..52d3bf6 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 @@ -30,6 +30,7 @@ import org.apache.asterix.dataflow.data.nontagged.serde.AInt64SerializerDeserializer; import org.apache.asterix.dataflow.data.nontagged.serde.AInt8SerializerDeserializer; import org.apache.asterix.om.types.ATypeTag; +import org.apache.asterix.runtime.evaluators.common.NumberUtils; import org.apache.asterix.runtime.exceptions.InvalidDataFormatException; import org.apache.asterix.runtime.exceptions.UnsupportedTypeException; import org.apache.hyracks.algebricks.core.algebra.functions.FunctionIdentifier; @@ -107,11 +108,11 @@ case DOUBLE: { double d = ADoubleSerializerDeserializer.getDouble(serString, startOffset); if (Double.isNaN(d)) { - builder.appendUtf8StringPointable(AbstractDoubleConstructorEvaluator.NAN); + builder.appendUtf8StringPointable(NumberUtils.NAN); } else if (d == Double.POSITIVE_INFINITY) { // NOSONAR - builder.appendUtf8StringPointable(AbstractDoubleConstructorEvaluator.POSITIVE_INF); + builder.appendUtf8StringPointable(NumberUtils.POSITIVE_INF); } else if (d == Double.NEGATIVE_INFINITY) { // NOSONAR - builder.appendUtf8StringPointable(AbstractDoubleConstructorEvaluator.NEGATIVE_INF); + builder.appendUtf8StringPointable(NumberUtils.NEGATIVE_INF); } else { builder.appendString(String.valueOf(d)); } @@ -120,11 +121,11 @@ case FLOAT: { float f = AFloatSerializerDeserializer.getFloat(serString, startOffset); if (Float.isNaN(f)) { - builder.appendUtf8StringPointable(AbstractDoubleConstructorEvaluator.NAN); + builder.appendUtf8StringPointable(NumberUtils.NAN); } else if (f == Float.POSITIVE_INFINITY) { // NOSONAR - builder.appendUtf8StringPointable(AbstractDoubleConstructorEvaluator.POSITIVE_INF); + builder.appendUtf8StringPointable(NumberUtils.POSITIVE_INF); } else if (f == Float.NEGATIVE_INFINITY) { // NOSONAR - builder.appendUtf8StringPointable(AbstractDoubleConstructorEvaluator.NEGATIVE_INF); + builder.appendUtf8StringPointable(NumberUtils.NEGATIVE_INF); } else { builder.appendString(String.valueOf(f)); } diff --git a/asterixdb/asterix-runtime/src/main/java/org/apache/asterix/runtime/evaluators/functions/ToBigIntDescriptor.java b/asterixdb/asterix-runtime/src/main/java/org/apache/asterix/runtime/evaluators/functions/ToBigIntDescriptor.java index 5cf9af7..2762d61 100644 --- a/asterixdb/asterix-runtime/src/main/java/org/apache/asterix/runtime/evaluators/functions/ToBigIntDescriptor.java +++ b/asterixdb/asterix-runtime/src/main/java/org/apache/asterix/runtime/evaluators/functions/ToBigIntDescriptor.java @@ -22,13 +22,10 @@ import java.io.IOException; import org.apache.asterix.dataflow.data.nontagged.serde.ABooleanSerializerDeserializer; -import org.apache.asterix.formats.nontagged.SerializerDeserializerProvider; -import org.apache.asterix.om.base.ANull; import org.apache.asterix.om.functions.BuiltinFunctions; import org.apache.asterix.om.functions.IFunctionDescriptor; import org.apache.asterix.om.functions.IFunctionDescriptorFactory; import org.apache.asterix.om.types.ATypeTag; -import org.apache.asterix.om.types.BuiltinType; import org.apache.asterix.om.types.hierachy.ATypeHierarchy; import org.apache.asterix.om.types.hierachy.ITypeConvertComputer; import org.apache.asterix.runtime.evaluators.base.AbstractScalarFunctionDynamicDescriptor; @@ -37,7 +34,6 @@ 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.dataflow.value.ISerializerDeserializer; import org.apache.hyracks.api.exceptions.HyracksDataException; import org.apache.hyracks.data.std.api.IPointable; @@ -58,10 +54,6 @@ @Override public IScalarEvaluator createScalarEvaluator(final IHyracksTaskContext ctx) throws HyracksDataException { return new AbstractInt64ConstructorEvaluator(args[0].createScalarEvaluator(ctx)) { - @SuppressWarnings("unchecked") - private final ISerializerDeserializer<ANull> nullSerde = - SerializerDeserializerProvider.INSTANCE.getSerializerDeserializer(BuiltinType.ANULL); - @Override protected void evaluateImpl(IPointable result) throws IOException { byte[] bytes = inputArg.getByteArray(); @@ -94,7 +86,7 @@ case ARRAY: case MULTISET: case OBJECT: - setNull(result); + PointableHelper.setNull(result); break; default: @@ -104,13 +96,8 @@ } @Override - protected void handleUnparseableString(IPointable result) throws HyracksDataException { - setNull(result); - } - - private void setNull(IPointable result) throws HyracksDataException { - nullSerde.serialize(ANull.NULL, out); - result.set(resultStorage); + protected void handleUnparseableString(IPointable result) { + PointableHelper.setNull(result); } @Override diff --git a/asterixdb/asterix-runtime/src/main/java/org/apache/asterix/runtime/evaluators/functions/ToBooleanDescriptor.java b/asterixdb/asterix-runtime/src/main/java/org/apache/asterix/runtime/evaluators/functions/ToBooleanDescriptor.java index 704630e..fccd6d1 100644 --- a/asterixdb/asterix-runtime/src/main/java/org/apache/asterix/runtime/evaluators/functions/ToBooleanDescriptor.java +++ b/asterixdb/asterix-runtime/src/main/java/org/apache/asterix/runtime/evaluators/functions/ToBooleanDescriptor.java @@ -33,6 +33,7 @@ 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.asterix.runtime.evaluators.common.NumberUtils; import org.apache.asterix.runtime.evaluators.constructors.AbstractBooleanConstructorEvaluator; import org.apache.hyracks.algebricks.core.algebra.functions.FunctionIdentifier; import org.apache.hyracks.algebricks.runtime.base.IScalarEvaluator; @@ -44,12 +45,6 @@ public class ToBooleanDescriptor extends AbstractScalarFunctionDynamicDescriptor { private static final long serialVersionUID = 1L; - - private static final long BITS_NAN = Double.doubleToLongBits(Double.NaN); - - private static final long BITS_ZERO_POS = Double.doubleToLongBits(+0.0d); - - private static final long BITS_ZERO_NEG = Double.doubleToLongBits(-0.0d); public static final IFunctionDescriptorFactory FACTORY = new IFunctionDescriptorFactory() { @Override @@ -117,7 +112,8 @@ private void setDouble(double v, IPointable result) throws HyracksDataException { long bits = Double.doubleToLongBits(v); - boolean zeroOrNaN = bits == BITS_ZERO_POS || bits == BITS_ZERO_NEG || bits == BITS_NAN; + boolean zeroOrNaN = bits == NumberUtils.POSITIVE_ZERO_BITS + || bits == NumberUtils.NEGATIVE_ZERO_BITS || bits == NumberUtils.NAN_BITS; setBoolean(result, !zeroOrNaN); } diff --git a/asterixdb/asterix-runtime/src/main/java/org/apache/asterix/runtime/evaluators/functions/ToDoubleDescriptor.java b/asterixdb/asterix-runtime/src/main/java/org/apache/asterix/runtime/evaluators/functions/ToDoubleDescriptor.java index 71cdbe5..4fa33de 100644 --- a/asterixdb/asterix-runtime/src/main/java/org/apache/asterix/runtime/evaluators/functions/ToDoubleDescriptor.java +++ b/asterixdb/asterix-runtime/src/main/java/org/apache/asterix/runtime/evaluators/functions/ToDoubleDescriptor.java @@ -22,13 +22,10 @@ import java.io.IOException; import org.apache.asterix.dataflow.data.nontagged.serde.ABooleanSerializerDeserializer; -import org.apache.asterix.formats.nontagged.SerializerDeserializerProvider; -import org.apache.asterix.om.base.ANull; import org.apache.asterix.om.functions.BuiltinFunctions; import org.apache.asterix.om.functions.IFunctionDescriptor; import org.apache.asterix.om.functions.IFunctionDescriptorFactory; import org.apache.asterix.om.types.ATypeTag; -import org.apache.asterix.om.types.BuiltinType; import org.apache.asterix.om.types.hierachy.ATypeHierarchy; import org.apache.asterix.om.types.hierachy.ITypeConvertComputer; import org.apache.asterix.runtime.evaluators.base.AbstractScalarFunctionDynamicDescriptor; @@ -37,7 +34,6 @@ 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.dataflow.value.ISerializerDeserializer; import org.apache.hyracks.api.exceptions.HyracksDataException; import org.apache.hyracks.data.std.api.IPointable; @@ -58,10 +54,6 @@ @Override public IScalarEvaluator createScalarEvaluator(final IHyracksTaskContext ctx) throws HyracksDataException { return new AbstractDoubleConstructorEvaluator(args[0].createScalarEvaluator(ctx)) { - @SuppressWarnings("unchecked") - private final ISerializerDeserializer<ANull> nullSerde = - SerializerDeserializerProvider.INSTANCE.getSerializerDeserializer(BuiltinType.ANULL); - @Override protected void evaluateImpl(IPointable result) throws IOException { byte[] bytes = inputArg.getByteArray(); @@ -88,7 +80,7 @@ case ARRAY: case MULTISET: case OBJECT: - setNull(result); + PointableHelper.setNull(result); break; default: @@ -98,14 +90,8 @@ } @Override - protected void handleUparseableString(IPointable result, NumberFormatException e) - throws HyracksDataException { - setNull(result); - } - - private void setNull(IPointable result) throws HyracksDataException { - nullSerde.serialize(ANull.NULL, out); - result.set(resultStorage); + protected void handleUparseableString(IPointable result) { + PointableHelper.setNull(result); } @Override diff --git a/asterixdb/asterix-runtime/src/main/java/org/apache/asterix/runtime/evaluators/functions/ToNumberDescriptor.java b/asterixdb/asterix-runtime/src/main/java/org/apache/asterix/runtime/evaluators/functions/ToNumberDescriptor.java new file mode 100644 index 0000000..d044744 --- /dev/null +++ b/asterixdb/asterix-runtime/src/main/java/org/apache/asterix/runtime/evaluators/functions/ToNumberDescriptor.java @@ -0,0 +1,149 @@ +/* + * 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 java.io.DataOutput; + +import org.apache.asterix.dataflow.data.nontagged.serde.ABooleanSerializerDeserializer; +import org.apache.asterix.formats.nontagged.SerializerDeserializerProvider; +import org.apache.asterix.om.base.ADouble; +import org.apache.asterix.om.base.AInt64; +import org.apache.asterix.om.base.AMutableDouble; +import org.apache.asterix.om.base.AMutableInt64; +import org.apache.asterix.om.functions.BuiltinFunctions; +import org.apache.asterix.om.functions.IFunctionDescriptor; +import org.apache.asterix.om.functions.IFunctionDescriptorFactory; +import org.apache.asterix.om.types.ATypeTag; +import org.apache.asterix.om.types.BuiltinType; +import org.apache.asterix.runtime.evaluators.base.AbstractScalarFunctionDynamicDescriptor; +import org.apache.asterix.runtime.evaluators.common.NumberUtils; +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.algebricks.runtime.base.IScalarEvaluatorFactory; +import org.apache.hyracks.api.context.IHyracksTaskContext; +import org.apache.hyracks.api.dataflow.value.ISerializerDeserializer; +import org.apache.hyracks.api.exceptions.HyracksDataException; +import org.apache.hyracks.data.std.api.IPointable; +import org.apache.hyracks.data.std.primitive.UTF8StringPointable; +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; + +public class ToNumberDescriptor extends AbstractScalarFunctionDynamicDescriptor { + private static final long serialVersionUID = 1L; + public static final IFunctionDescriptorFactory FACTORY = new IFunctionDescriptorFactory() { + @Override + public IFunctionDescriptor createFunctionDescriptor() { + return new ToNumberDescriptor(); + } + }; + + @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 { + + final IScalarEvaluator inputEval = args[0].createScalarEvaluator(ctx); + final IPointable inputArg = new VoidPointable(); + final ArrayBackedValueStorage resultStorage = new ArrayBackedValueStorage(); + final DataOutput out = resultStorage.getDataOutput(); + final AMutableInt64 aInt64 = new AMutableInt64(0); + final AMutableDouble aDouble = new AMutableDouble(0); + final UTF8StringPointable utf8Ptr = new UTF8StringPointable(); + + @SuppressWarnings("unchecked") + final ISerializerDeserializer<AInt64> INT64_SERDE = + SerializerDeserializerProvider.INSTANCE.getSerializerDeserializer(BuiltinType.AINT64); + + @SuppressWarnings("unchecked") + final ISerializerDeserializer<ADouble> DOUBLE_SERDE = + SerializerDeserializerProvider.INSTANCE.getSerializerDeserializer(BuiltinType.ADOUBLE); + + return new IScalarEvaluator() { + @Override + public void evaluate(IFrameTupleReference tuple, IPointable result) throws HyracksDataException { + inputEval.evaluate(tuple, inputArg); + + resultStorage.reset(); + + byte[] bytes = inputArg.getByteArray(); + int startOffset = inputArg.getStartOffset(); + ATypeTag tt = ATypeTag.VALUE_TYPE_MAPPING[bytes[startOffset]]; + switch (tt) { + case TINYINT: + case SMALLINT: + case INTEGER: + case BIGINT: + case FLOAT: + case DOUBLE: + result.set(inputArg); + break; + + case BOOLEAN: + boolean b = ABooleanSerializerDeserializer.getBoolean(bytes, startOffset + 1); + aInt64.setValue(b ? 1 : 0); + INT64_SERDE.serialize(aInt64, out); + result.set(resultStorage); + break; + + case STRING: + utf8Ptr.set(bytes, startOffset + 1, inputArg.getLength() - 1); + if (NumberUtils.parseInt64(utf8Ptr, aInt64)) { + INT64_SERDE.serialize(aInt64, out); + result.set(resultStorage); + } else if (NumberUtils.parseDouble(utf8Ptr, aDouble)) { + DOUBLE_SERDE.serialize(aDouble, out); + result.set(resultStorage); + } else { + PointableHelper.setNull(result); + } + break; + + case ARRAY: + case MULTISET: + case OBJECT: + PointableHelper.setNull(result); + break; + + default: + throw new TypeMismatchException(getIdentifier(), 0, bytes[startOffset], + ATypeTag.SERIALIZED_INT8_TYPE_TAG, ATypeTag.SERIALIZED_INT16_TYPE_TAG, + ATypeTag.SERIALIZED_INT32_TYPE_TAG, ATypeTag.SERIALIZED_INT64_TYPE_TAG, + ATypeTag.SERIALIZED_FLOAT_TYPE_TAG, ATypeTag.SERIALIZED_DOUBLE_TYPE_TAG, + ATypeTag.SERIALIZED_BOOLEAN_TYPE_TAG, ATypeTag.SERIALIZED_STRING_TYPE_TAG, + ATypeTag.SERIALIZED_ORDEREDLIST_TYPE_TAG, + ATypeTag.SERIALIZED_UNORDEREDLIST_TYPE_TAG, + ATypeTag.SERIALIZED_RECORD_TYPE_TAG); + } + } + }; + } + }; + } + + @Override + public FunctionIdentifier getIdentifier() { + return BuiltinFunctions.TO_NUMBER; + } +} 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 c47a4de..b94f55e 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 @@ -257,6 +257,7 @@ import org.apache.asterix.runtime.evaluators.functions.ToBigIntDescriptor; import org.apache.asterix.runtime.evaluators.functions.ToBooleanDescriptor; import org.apache.asterix.runtime.evaluators.functions.ToDoubleDescriptor; +import org.apache.asterix.runtime.evaluators.functions.ToNumberDescriptor; import org.apache.asterix.runtime.evaluators.functions.ToStringDescriptor; import org.apache.asterix.runtime.evaluators.functions.UUIDDescriptor; import org.apache.asterix.runtime.evaluators.functions.WordTokensDescriptor; @@ -728,6 +729,7 @@ fc.addGenerated(ToStringDescriptor.FACTORY); fc.addGenerated(ToDoubleDescriptor.FACTORY); fc.addGenerated(ToBigIntDescriptor.FACTORY); + fc.addGenerated(ToNumberDescriptor.FACTORY); // Cast function fc.addGenerated(CastTypeDescriptor.FACTORY); -- To view, visit https://asterix-gerrit.ics.uci.edu/2449 To unsubscribe, visit https://asterix-gerrit.ics.uci.edu/settings Gerrit-MessageType: merged Gerrit-Change-Id: Id370aadcf16447f7c775c30d2bffc33fc6a96927 Gerrit-PatchSet: 4 Gerrit-Project: asterixdb Gerrit-Branch: master Gerrit-Owner: Dmitry Lychagin <[email protected]> Gerrit-Reviewer: Anon. E. Moose #1000171 Gerrit-Reviewer: Dmitry Lychagin <[email protected]> Gerrit-Reviewer: Jenkins <[email protected]> Gerrit-Reviewer: Till Westmann <[email protected]>
