This is an automated email from the ASF dual-hosted git repository. mblow pushed a commit to branch master in repository https://gitbox.apache.org/repos/asf/asterixdb.git
commit e54e4178fec325e8812f5f042c3cdba81ed49fb1 Author: Ali Alsuliman <[email protected]> AuthorDate: Thu Oct 3 15:50:17 2019 -0700 [ASTERIXDB-2634][COMP] String functions return null on data/type errors - user model changes: no - storage format changes: no - interface changes: no Details: Functions changed: substring(string, start_idx), repeat(string, num_times), string_join([string], string_separator), replace(string, search_string, string_replacement, num_times), regexp_replace(string, string_pattern, string_replacement, num_times) Those functions (except string_join) can return NULL even if the arguments types are valid since the argument values could be not valid at runtime. Their type computer is always nullable. - int argument can be double/float on the condition that its value is integer - clean-ups: UnaryStringInt64TypeComputer & AbstractStringTypeComputer. ExceptionUtil: - changed signature of toExpectedTypeString(). Overloading with varargs with Object made it confusing. - changed some args to Supplier to allow code sharing. - removed StringIntToStringTypeComputer since now the instances are not being used by the above functions. - reorganized/renamed test cases Change-Id: Ia85a0d08888021ae439a1d9f2f5858bcd52c79f3 Reviewed-on: https://asterix-gerrit.ics.uci.edu/c/asterixdb/+/3605 Tested-by: Jenkins <[email protected]> Integration-Tests: Jenkins <[email protected]> Reviewed-by: Ali Alsuliman <[email protected]> Reviewed-by: Michael Blow <[email protected]> --- .../string_fun_001/string_fun_001.01.ddl.sqlpp} | 0 .../string_fun_001/string_fun_001.02.update.sqlpp} | 0 .../string_fun_001/string_fun_001.03.query.sqlpp} | 0 .../string_fun_001/string_fun_001.04.query.sqlpp} | 0 .../string_fun_001/string_fun_001.05.query.sqlpp} | 0 .../string_fun_001/string_fun_001.06.query.sqlpp} | 0 .../string_fun_001/string_fun_001.07.query.sqlpp} | 0 .../string_fun_001/string_fun_001.08.query.sqlpp} | 0 .../string_fun_001/string_fun_001.09.ddl.sqlpp} | 0 .../string_fun_002/string_fun_002.01.query.sqlpp} | 0 .../string_fun_003/string_fun_003.01.ddl.sqlpp} | 0 .../string_fun_003/string_fun_003.02.update.sqlpp} | 8 +- .../string_fun_003/string_fun_003.03.query.sqlpp} | 27 ++--- .../string_fun_003/string_fun_003.04.query.sqlpp} | 27 ++--- .../string_fun_003/string_fun_003.05.query.sqlpp} | 27 ++--- .../string_fun_003/string_fun_003.06.ddl.sqlpp} | 0 .../string/repeat_error/repeat_error.1.query.sqlpp | 20 ---- .../string_fun_001/string_fun_001.03.adm} | 0 .../string_fun_001/string_fun_001.04.adm} | 0 .../string_fun_001/string_fun_001.05.adm} | 0 .../string_fun_001/string_fun_001.06.adm} | 0 .../string_fun_001/string_fun_001.07.adm} | 0 .../string_fun_001/string_fun_001.08.adm} | 0 .../string_fun_002/string_fun_002.01.adm} | 0 .../string_fun_003/string_fun_003.01.adm | 2 + .../string_fun_003/string_fun_003.02.adm | 2 + .../string_fun_003/string_fun_003.03.adm | 1 + .../test/resources/runtimets/testsuite_sqlpp.xml | 45 +++++--- .../asterix/common/exceptions/ErrorCode.java | 1 + .../src/main/resources/asx_errormsg/en.properties | 3 +- .../asterix/om/exceptions/ExceptionUtil.java | 44 ++++++-- .../asterix/om/functions/BuiltinFunctions.java | 15 ++- .../om/typecomputer/impl/AStringTypeComputer.java | 12 ++- .../impl/AbstractStringTypeComputer.java | 14 +-- .../impl/StringIntToStringTypeComputer.java | 116 --------------------- ...peComputer.java => StringJoinTypeComputer.java} | 23 +++- .../impl/UnaryStringInt64TypeComputer.java | 2 +- .../hierachy/DoubleToInt32TypeConvertComputer.java | 2 +- .../std/AbstractMinMaxAggregateFunction.java | 2 +- .../runtime/evaluators/common/ArgumentUtils.java | 76 ++++++++++++++ .../functions/AbstractQuadStringStringEval.java | 20 ++-- .../AbstractStringStringStringIntEval.java | 57 +++++----- .../evaluators/functions/StringJoinDescriptor.java | 54 ++++------ .../StringRegExpReplaceWithFlagDescriptor.java | 44 +++++--- .../functions/StringRepeatDescriptor.java | 36 +++---- .../StringReplaceWithLimitDescriptor.java | 12 +-- .../evaluators/functions/Substring2Descriptor.java | 47 ++++----- 47 files changed, 360 insertions(+), 379 deletions(-) diff --git a/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/fun_return_null/fun_return_null_01/fun_return_null_01.01.ddl.sqlpp b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/fun_return_null/string_fun/string_fun_001/string_fun_001.01.ddl.sqlpp similarity index 100% copy from asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/fun_return_null/fun_return_null_01/fun_return_null_01.01.ddl.sqlpp copy to asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/fun_return_null/string_fun/string_fun_001/string_fun_001.01.ddl.sqlpp diff --git a/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/fun_return_null/fun_return_null_01/fun_return_null_01.02.update.sqlpp b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/fun_return_null/string_fun/string_fun_001/string_fun_001.02.update.sqlpp similarity index 100% copy from asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/fun_return_null/fun_return_null_01/fun_return_null_01.02.update.sqlpp copy to asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/fun_return_null/string_fun/string_fun_001/string_fun_001.02.update.sqlpp diff --git a/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/fun_return_null/fun_return_null_01/fun_return_null_01.03.query.sqlpp b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/fun_return_null/string_fun/string_fun_001/string_fun_001.03.query.sqlpp similarity index 100% copy from asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/fun_return_null/fun_return_null_01/fun_return_null_01.03.query.sqlpp copy to asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/fun_return_null/string_fun/string_fun_001/string_fun_001.03.query.sqlpp diff --git a/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/fun_return_null/fun_return_null_01/fun_return_null_01.04.query.sqlpp b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/fun_return_null/string_fun/string_fun_001/string_fun_001.04.query.sqlpp similarity index 100% copy from asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/fun_return_null/fun_return_null_01/fun_return_null_01.04.query.sqlpp copy to asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/fun_return_null/string_fun/string_fun_001/string_fun_001.04.query.sqlpp diff --git a/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/fun_return_null/fun_return_null_01/fun_return_null_01.05.query.sqlpp b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/fun_return_null/string_fun/string_fun_001/string_fun_001.05.query.sqlpp similarity index 100% rename from asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/fun_return_null/fun_return_null_01/fun_return_null_01.05.query.sqlpp rename to asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/fun_return_null/string_fun/string_fun_001/string_fun_001.05.query.sqlpp diff --git a/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/fun_return_null/fun_return_null_01/fun_return_null_01.06.query.sqlpp b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/fun_return_null/string_fun/string_fun_001/string_fun_001.06.query.sqlpp similarity index 100% rename from asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/fun_return_null/fun_return_null_01/fun_return_null_01.06.query.sqlpp rename to asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/fun_return_null/string_fun/string_fun_001/string_fun_001.06.query.sqlpp diff --git a/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/fun_return_null/fun_return_null_01/fun_return_null_01.07.query.sqlpp b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/fun_return_null/string_fun/string_fun_001/string_fun_001.07.query.sqlpp similarity index 100% rename from asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/fun_return_null/fun_return_null_01/fun_return_null_01.07.query.sqlpp rename to asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/fun_return_null/string_fun/string_fun_001/string_fun_001.07.query.sqlpp diff --git a/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/fun_return_null/fun_return_null_01/fun_return_null_01.08.query.sqlpp b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/fun_return_null/string_fun/string_fun_001/string_fun_001.08.query.sqlpp similarity index 100% rename from asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/fun_return_null/fun_return_null_01/fun_return_null_01.08.query.sqlpp rename to asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/fun_return_null/string_fun/string_fun_001/string_fun_001.08.query.sqlpp diff --git a/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/fun_return_null/fun_return_null_01/fun_return_null_01.09.ddl.sqlpp b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/fun_return_null/string_fun/string_fun_001/string_fun_001.09.ddl.sqlpp similarity index 100% copy from asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/fun_return_null/fun_return_null_01/fun_return_null_01.09.ddl.sqlpp copy to asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/fun_return_null/string_fun/string_fun_001/string_fun_001.09.ddl.sqlpp diff --git a/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/fun_return_null/fun_return_null_02/fun_return_null_02.01.query.sqlpp b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/fun_return_null/string_fun/string_fun_002/string_fun_002.01.query.sqlpp similarity index 100% rename from asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/fun_return_null/fun_return_null_02/fun_return_null_02.01.query.sqlpp rename to asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/fun_return_null/string_fun/string_fun_002/string_fun_002.01.query.sqlpp diff --git a/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/fun_return_null/fun_return_null_01/fun_return_null_01.01.ddl.sqlpp b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/fun_return_null/string_fun/string_fun_003/string_fun_003.01.ddl.sqlpp similarity index 100% rename from asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/fun_return_null/fun_return_null_01/fun_return_null_01.01.ddl.sqlpp rename to asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/fun_return_null/string_fun/string_fun_003/string_fun_003.01.ddl.sqlpp diff --git a/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/fun_return_null/fun_return_null_01/fun_return_null_01.02.update.sqlpp b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/fun_return_null/string_fun/string_fun_003/string_fun_003.02.update.sqlpp similarity index 58% rename from asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/fun_return_null/fun_return_null_01/fun_return_null_01.02.update.sqlpp rename to asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/fun_return_null/string_fun/string_fun_003/string_fun_003.02.update.sqlpp index b0caa32..c81b5a9 100644 --- a/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/fun_return_null/fun_return_null_01/fun_return_null_01.02.update.sqlpp +++ b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/fun_return_null/string_fun/string_fun_003/string_fun_003.02.update.sqlpp @@ -20,11 +20,11 @@ use test; insert into closedDS([ -{"id": 1, "i8": int8("2"), "i16": int16("10"), "i32": int32("21"), "i64": int64("87"), "f": float("4.2"), "d": double("5.3"), "str1": "foo", "mixed": 7}, -{"id": 2, "i8": int8("2"), "i16": int16("10"), "i32": int32("21"), "i64": int64("87"), "f": float("4.2"), "d": double("5.3"), "str1": "foo", "mixed": "m"} +{"id": 1, "i8": int8("-2"), "i16": int16("10"), "i32": int32("21"), "i64": int64("87"), "f": float("4.2"), "d": double("5.0"), "str1": "foo", "mixed": 7}, +{"id": 2, "i8": int8("0"), "i16": int16("-10"), "i32": int32("21"), "i64": int64("87"), "f": float("4.2"), "d": double("5.3"), "str1": "foo", "mixed": "m"} ]); insert into openDS([ -{"id": 1, "i8": int8("2"), "i16": int16("10"), "i32": int32("21"), "i64": int64("87"), "f": float("4.2"), "d": double("5.3"), "str1": "foo", "mixed": 7}, -{"id": 2, "i8": int8("2"), "i16": int16("10"), "i32": int32("21"), "i64": int64("87"), "f": float("4.2"), "d": double("5.3"), "str1": "foo", "mixed": "m"} +{"id": 1, "i8": int8("-2"), "i16": int16("10"), "i32": int32("21"), "i64": int64("87"), "f": float("4.2"), "d": double("5.0"), "str1": "foo", "mixed": 7}, +{"id": 2, "i8": int8("0"), "i16": int16("-10"), "i32": int32("21"), "i64": int64("87"), "f": float("4.2"), "d": double("5.3"), "str1": "foo", "mixed": "m"} ]); \ No newline at end of file diff --git a/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/fun_return_null/fun_return_null_01/fun_return_null_01.03.query.sqlpp b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/fun_return_null/string_fun/string_fun_003/string_fun_003.03.query.sqlpp similarity index 67% rename from asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/fun_return_null/fun_return_null_01/fun_return_null_01.03.query.sqlpp rename to asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/fun_return_null/string_fun/string_fun_003/string_fun_003.03.query.sqlpp index 6dec119..3c7ebf2 100644 --- a/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/fun_return_null/fun_return_null_01/fun_return_null_01.03.query.sqlpp +++ b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/fun_return_null/string_fun/string_fun_003/string_fun_003.03.query.sqlpp @@ -18,30 +18,21 @@ */ /* - * Description: tests reporting type mismatch for string functions + * Description: tests that string functions do not throw exception on type/data errors */ // requesttype=application/json // param max-warnings:json=1000 use test; -set `import-private-functions` "true"; - from closedDS as ds select -`LIKE`(ds.str1, ds.i8), -`LIKE`(ds.i8, ds.str1), -contains(ds.str1, ds.i8), -string_to_codepoint(ds.i64), -length(ds.i32), -lowercase(ds.i16), -uppercase(ds.i16), -initcap(ds.i64), -trim(ds.i32), -trim(ds.i64, ds.str1), -ltrim(ds.i16), -ltrim(ds.str1, ds.i8), -rtrim(ds.i32), -rtrim(ds.i64, ds.str1), -position(ds.i8, ds.str1) +repeat(ds.str1, ds.i8), +repeat(ds.str1, ds.d), +string_join(["str", "str"], ds.mixed), +replace("hello world he", "he", "be", ds.i8), +replace("hello world he", "he", "be", ds.d), +regexp_replace("hello world he", "he", "be", ds.i8), +regexp_replace("hello world he", "he", "be", ds.d), +substring(ds.mixed, 0) order by ds.id; \ No newline at end of file diff --git a/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/fun_return_null/fun_return_null_01/fun_return_null_01.04.query.sqlpp b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/fun_return_null/string_fun/string_fun_003/string_fun_003.04.query.sqlpp similarity index 67% copy from asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/fun_return_null/fun_return_null_01/fun_return_null_01.04.query.sqlpp copy to asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/fun_return_null/string_fun/string_fun_003/string_fun_003.04.query.sqlpp index bbfb127..92747a9 100644 --- a/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/fun_return_null/fun_return_null_01/fun_return_null_01.04.query.sqlpp +++ b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/fun_return_null/string_fun/string_fun_003/string_fun_003.04.query.sqlpp @@ -18,30 +18,21 @@ */ /* - * Description: tests reporting type mismatch for string functions + * Description: tests that string functions do not throw exception on type/data errors */ // requesttype=application/json // param max-warnings:json=1000 use test; -set `import-private-functions` "true"; - from openDS as ds select -`LIKE`(ds.str1, ds.i8), -`LIKE`(ds.i8, ds.str1), -contains(ds.str1, ds.i8), -string_to_codepoint(ds.i64), -length(ds.i32), -lowercase(ds.i16), -uppercase(ds.i16), -initcap(ds.i64), -trim(ds.i32), -trim(ds.i64, ds.str1), -ltrim(ds.i16), -ltrim(ds.str1, ds.i8), -rtrim(ds.i32), -rtrim(ds.i64, ds.str1), -position(ds.i8, ds.str1) +repeat(ds.str1, ds.i8), +repeat(ds.str1, ds.d), +string_join(["str", "str"], ds.mixed), +replace("hello world he", "he", "be", ds.i8), +replace("hello world he", "he", "be", ds.d), +regexp_replace("hello world he", "he", "be", ds.i8), +regexp_replace("hello world he", "he", "be", ds.d), +substring(ds.mixed, 0) order by ds.id; \ No newline at end of file diff --git a/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/fun_return_null/fun_return_null_01/fun_return_null_01.04.query.sqlpp b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/fun_return_null/string_fun/string_fun_003/string_fun_003.05.query.sqlpp similarity index 66% rename from asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/fun_return_null/fun_return_null_01/fun_return_null_01.04.query.sqlpp rename to asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/fun_return_null/string_fun/string_fun_003/string_fun_003.05.query.sqlpp index bbfb127..f1867fc 100644 --- a/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/fun_return_null/fun_return_null_01/fun_return_null_01.04.query.sqlpp +++ b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/fun_return_null/string_fun/string_fun_003/string_fun_003.05.query.sqlpp @@ -18,30 +18,17 @@ */ /* - * Description: tests reporting type mismatch for string functions + * Description: tests that string functions do not throw exception on type/data errors */ // requesttype=application/json // param max-warnings:json=1000 use test; -set `import-private-functions` "true"; - -from openDS as ds select -`LIKE`(ds.str1, ds.i8), -`LIKE`(ds.i8, ds.str1), -contains(ds.str1, ds.i8), -string_to_codepoint(ds.i64), -length(ds.i32), -lowercase(ds.i16), -uppercase(ds.i16), -initcap(ds.i64), -trim(ds.i32), -trim(ds.i64, ds.str1), -ltrim(ds.i16), -ltrim(ds.str1, ds.i8), -rtrim(ds.i32), -rtrim(ds.i64, ds.str1), -position(ds.i8, ds.str1) -order by ds.id; \ No newline at end of file +repeat("str", double("INF")), +replace("hello world he", "he", "be", double("-INF")), +replace("hello world he", "he", "be", double("NaN")), +regexp_replace("hello world he", "he", "be", double("NaN")), +regexp_replace("hello world he", "he", "be", double("-INF")), +substring("hello world", double("INF")); \ No newline at end of file diff --git a/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/fun_return_null/fun_return_null_01/fun_return_null_01.09.ddl.sqlpp b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/fun_return_null/string_fun/string_fun_003/string_fun_003.06.ddl.sqlpp similarity index 100% rename from asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/fun_return_null/fun_return_null_01/fun_return_null_01.09.ddl.sqlpp rename to asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/fun_return_null/string_fun/string_fun_003/string_fun_003.06.ddl.sqlpp diff --git a/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/string/repeat_error/repeat_error.1.query.sqlpp b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/string/repeat_error/repeat_error.1.query.sqlpp deleted file mode 100644 index 0701ce3..0000000 --- a/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/string/repeat_error/repeat_error.1.query.sqlpp +++ /dev/null @@ -1,20 +0,0 @@ -/* - * 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 repeat(" new ", -1); diff --git a/asterixdb/asterix-app/src/test/resources/runtimets/results/fun_return_null/fun_return_null_01/fun_return_null_01.07.adm b/asterixdb/asterix-app/src/test/resources/runtimets/results/fun_return_null/string_fun/string_fun_001/string_fun_001.03.adm similarity index 100% rename from asterixdb/asterix-app/src/test/resources/runtimets/results/fun_return_null/fun_return_null_01/fun_return_null_01.07.adm rename to asterixdb/asterix-app/src/test/resources/runtimets/results/fun_return_null/string_fun/string_fun_001/string_fun_001.03.adm diff --git a/asterixdb/asterix-app/src/test/resources/runtimets/results/fun_return_null/fun_return_null_01/fun_return_null_01.06.adm b/asterixdb/asterix-app/src/test/resources/runtimets/results/fun_return_null/string_fun/string_fun_001/string_fun_001.04.adm similarity index 100% rename from asterixdb/asterix-app/src/test/resources/runtimets/results/fun_return_null/fun_return_null_01/fun_return_null_01.06.adm rename to asterixdb/asterix-app/src/test/resources/runtimets/results/fun_return_null/string_fun/string_fun_001/string_fun_001.04.adm diff --git a/asterixdb/asterix-app/src/test/resources/runtimets/results/fun_return_null/fun_return_null_01/fun_return_null_01.05.adm b/asterixdb/asterix-app/src/test/resources/runtimets/results/fun_return_null/string_fun/string_fun_001/string_fun_001.05.adm similarity index 100% rename from asterixdb/asterix-app/src/test/resources/runtimets/results/fun_return_null/fun_return_null_01/fun_return_null_01.05.adm rename to asterixdb/asterix-app/src/test/resources/runtimets/results/fun_return_null/string_fun/string_fun_001/string_fun_001.05.adm diff --git a/asterixdb/asterix-app/src/test/resources/runtimets/results/fun_return_null/fun_return_null_01/fun_return_null_01.04.adm b/asterixdb/asterix-app/src/test/resources/runtimets/results/fun_return_null/string_fun/string_fun_001/string_fun_001.06.adm similarity index 100% rename from asterixdb/asterix-app/src/test/resources/runtimets/results/fun_return_null/fun_return_null_01/fun_return_null_01.04.adm rename to asterixdb/asterix-app/src/test/resources/runtimets/results/fun_return_null/string_fun/string_fun_001/string_fun_001.06.adm diff --git a/asterixdb/asterix-app/src/test/resources/runtimets/results/fun_return_null/fun_return_null_01/fun_return_null_01.03.adm b/asterixdb/asterix-app/src/test/resources/runtimets/results/fun_return_null/string_fun/string_fun_001/string_fun_001.07.adm similarity index 100% rename from asterixdb/asterix-app/src/test/resources/runtimets/results/fun_return_null/fun_return_null_01/fun_return_null_01.03.adm rename to asterixdb/asterix-app/src/test/resources/runtimets/results/fun_return_null/string_fun/string_fun_001/string_fun_001.07.adm diff --git a/asterixdb/asterix-app/src/test/resources/runtimets/results/fun_return_null/fun_return_null_01/fun_return_null_01.08.adm b/asterixdb/asterix-app/src/test/resources/runtimets/results/fun_return_null/string_fun/string_fun_001/string_fun_001.08.adm similarity index 100% rename from asterixdb/asterix-app/src/test/resources/runtimets/results/fun_return_null/fun_return_null_01/fun_return_null_01.08.adm rename to asterixdb/asterix-app/src/test/resources/runtimets/results/fun_return_null/string_fun/string_fun_001/string_fun_001.08.adm diff --git a/asterixdb/asterix-app/src/test/resources/runtimets/results/fun_return_null/fun_return_null_02/fun_return_null_02.01.adm b/asterixdb/asterix-app/src/test/resources/runtimets/results/fun_return_null/string_fun/string_fun_002/string_fun_002.01.adm similarity index 100% rename from asterixdb/asterix-app/src/test/resources/runtimets/results/fun_return_null/fun_return_null_02/fun_return_null_02.01.adm rename to asterixdb/asterix-app/src/test/resources/runtimets/results/fun_return_null/string_fun/string_fun_002/string_fun_002.01.adm diff --git a/asterixdb/asterix-app/src/test/resources/runtimets/results/fun_return_null/string_fun/string_fun_003/string_fun_003.01.adm b/asterixdb/asterix-app/src/test/resources/runtimets/results/fun_return_null/string_fun/string_fun_003/string_fun_003.01.adm new file mode 100644 index 0000000..382af83 --- /dev/null +++ b/asterixdb/asterix-app/src/test/resources/runtimets/results/fun_return_null/string_fun/string_fun_003/string_fun_003.01.adm @@ -0,0 +1,2 @@ +{ "$1": null, "$2": "foofoofoofoofoo", "$3": null, "$4": "bello world be", "$5": "bello world be", "$6": "bello world be", "$7": "bello world be", "$8": null } +{ "$1": "", "$2": null, "$3": "strmstr", "$4": "hello world he", "$5": null, "$6": "hello world he", "$7": null, "$8": "m" } \ No newline at end of file diff --git a/asterixdb/asterix-app/src/test/resources/runtimets/results/fun_return_null/string_fun/string_fun_003/string_fun_003.02.adm b/asterixdb/asterix-app/src/test/resources/runtimets/results/fun_return_null/string_fun/string_fun_003/string_fun_003.02.adm new file mode 100644 index 0000000..382af83 --- /dev/null +++ b/asterixdb/asterix-app/src/test/resources/runtimets/results/fun_return_null/string_fun/string_fun_003/string_fun_003.02.adm @@ -0,0 +1,2 @@ +{ "$1": null, "$2": "foofoofoofoofoo", "$3": null, "$4": "bello world be", "$5": "bello world be", "$6": "bello world be", "$7": "bello world be", "$8": null } +{ "$1": "", "$2": null, "$3": "strmstr", "$4": "hello world he", "$5": null, "$6": "hello world he", "$7": null, "$8": "m" } \ No newline at end of file diff --git a/asterixdb/asterix-app/src/test/resources/runtimets/results/fun_return_null/string_fun/string_fun_003/string_fun_003.03.adm b/asterixdb/asterix-app/src/test/resources/runtimets/results/fun_return_null/string_fun/string_fun_003/string_fun_003.03.adm new file mode 100644 index 0000000..c89e546 --- /dev/null +++ b/asterixdb/asterix-app/src/test/resources/runtimets/results/fun_return_null/string_fun/string_fun_003/string_fun_003.03.adm @@ -0,0 +1 @@ +{ "$1": null, "$2": null, "$3": null, "$4": null, "$5": null, "$6": null } \ 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 1ca022a..abbb411 100644 --- a/asterixdb/asterix-app/src/test/resources/runtimets/testsuite_sqlpp.xml +++ b/asterixdb/asterix-app/src/test/resources/runtimets/testsuite_sqlpp.xml @@ -8722,12 +8722,6 @@ </compilation-unit> </test-case> <test-case FilePath="string"> - <compilation-unit name="repeat_error"> - <output-dir compare="Text">repeat</output-dir> - <expected-error>Invalid value: function repeat expects its 1 input parameter to be a non-negative value, but gets -1</expected-error> - </compilation-unit> - </test-case> - <test-case FilePath="string"> <compilation-unit name="replace"> <output-dir compare="Text">replace</output-dir> </compilation-unit> @@ -12513,10 +12507,10 @@ </compilation-unit> </test-case> </test-group> - <test-group name="fun_return_null"> - <test-case FilePath="fun_return_null" check-warnings="true"> - <compilation-unit name="fun_return_null_01"> - <output-dir compare="Text">fun_return_null_01</output-dir> + <test-group name="fun_return_null/string_fun"> + <test-case FilePath="fun_return_null/string_fun" check-warnings="true"> + <compilation-unit name="string_fun_001"> + <output-dir compare="Text">string_fun_001</output-dir> <expected-warn>Type mismatch: function trim expects its 1st input parameter to be of type string, but the actual input type is bigint (in line 41, at column 1)</expected-warn> <expected-warn>Type mismatch: function like expects its 2nd input parameter to be of type string, but the actual input type is tinyint (in line 32, at column 1)</expected-warn> <expected-warn>Type mismatch: function uppercase expects its 1st input parameter to be of type string, but the actual input type is smallint (in line 38, at column 1)</expected-warn> @@ -12614,9 +12608,9 @@ <expected-warn>Type mismatch: function ends-with expects its 1st input parameter to be of type string, but the actual input type is bigint (in line 33, at column 1)</expected-warn> </compilation-unit> </test-case> - <test-case FilePath="fun_return_null" check-warnings="true"> - <compilation-unit name="fun_return_null_02"> - <output-dir compare="Text">fun_return_null_02</output-dir> + <test-case FilePath="fun_return_null/string_fun" check-warnings="true"> + <compilation-unit name="string_fun_002"> + <output-dir compare="Text">string_fun_002</output-dir> <expected-warn>Type mismatch: function rtrim expects its 1st input parameter to be of type string, but the actual input type is bigint (in line 42, at column 1)</expected-warn> <expected-warn>Type mismatch: function regexp-replace expects its 2nd input parameter to be of type string, but the actual input type is bigint (in line 52, at column 1)</expected-warn> <expected-warn>Type mismatch: function regexp-like expects its 1st input parameter to be of type string, but the actual input type is bigint (in line 48, at column 1)</expected-warn> @@ -12649,5 +12643,30 @@ <expected-warn>Type mismatch: function regexp-position expects its 1st input parameter to be of type string, but the actual input type is integer (in line 50, at column 1)</expected-warn> </compilation-unit> </test-case> + <test-case FilePath="fun_return_null/string_fun" check-warnings="true"> + <compilation-unit name="string_fun_003"> + <output-dir compare="Text">string_fun_003</output-dir> + <expected-warn>Invalid value: function repeat expects its 2nd input parameter to be an integer value, got 5.3 (in line 31, at column 1)</expected-warn> + <expected-warn>Invalid value: function regexp-replace expects its 4th input parameter to be an integer value, got 5.3 (in line 36, at column 1)</expected-warn> + <expected-warn>Type mismatch: function substring expects its 1st input parameter to be of type string, but the actual input type is bigint (in line 37, at column 1)</expected-warn> + <expected-warn>Invalid value: function replace expects its 4th input parameter to be an integer value, got 5.3 (in line 34, at column 1)</expected-warn> + <expected-warn>Invalid value: function repeat expects its 2nd input parameter to be a non-negative value, got -2.0 (in line 30, at column 1)</expected-warn> + <expected-warn>Type mismatch: function string-join expects its 2nd input parameter to be of type string, but the actual input type is bigint (in line 32, at column 1)</expected-warn> + + <expected-warn>Invalid value: function repeat expects its 2nd input parameter to be an integer value, got 5.3 (in line 31, at column 1)</expected-warn> + <expected-warn>Invalid value: function regexp-replace expects its 4th input parameter to be an integer value, got 5.3 (in line 36, at column 1)</expected-warn> + <expected-warn>Type mismatch: function substring expects its 1st input parameter to be of type string, but the actual input type is bigint (in line 37, at column 1)</expected-warn> + <expected-warn>Invalid value: function replace expects its 4th input parameter to be an integer value, got 5.3 (in line 34, at column 1)</expected-warn> + <expected-warn>Invalid value: function repeat expects its 2nd input parameter to be a non-negative value, got -2.0 (in line 30, at column 1)</expected-warn> + <expected-warn>Type mismatch: function string-join expects its 2nd input parameter to be of type string, but the actual input type is bigint (in line 32, at column 1)</expected-warn> + + <expected-warn>Invalid value: function repeat expects its 2nd input parameter to be an integer value, got Infinity (in line 29, at column 1)</expected-warn> + <expected-warn>Invalid value: function replace expects its 4th input parameter to be an integer value, got -Infinity (in line 30, at column 1)</expected-warn> + <expected-warn>Invalid value: function replace expects its 4th input parameter to be an integer value, got NaN (in line 31, at column 1)</expected-warn> + <expected-warn>Invalid value: function regexp-replace expects its 4th input parameter to be an integer value, got NaN (in line 32, at column 1)</expected-warn> + <expected-warn>Invalid value: function regexp-replace expects its 4th input parameter to be an integer value, got -Infinity (in line 33, at column 1)</expected-warn> + <expected-warn>Invalid value: function substring expects its 2nd input parameter to be an integer value, got Infinity (in line 34, at column 1)</expected-warn> + </compilation-unit> + </test-case> </test-group> </test-suite> diff --git a/asterixdb/asterix-common/src/main/java/org/apache/asterix/common/exceptions/ErrorCode.java b/asterixdb/asterix-common/src/main/java/org/apache/asterix/common/exceptions/ErrorCode.java index 8803214..609e3a6 100644 --- a/asterixdb/asterix-common/src/main/java/org/apache/asterix/common/exceptions/ErrorCode.java +++ b/asterixdb/asterix-common/src/main/java/org/apache/asterix/common/exceptions/ErrorCode.java @@ -77,6 +77,7 @@ public class ErrorCode { public static final int TPCDS_INVALID_TABLE_NAME = 42; public static final int VALUE_OUT_OF_RANGE = 43; public static final int PROHIBITED_STATEMENT_CATEGORY = 44; + public static final int INTEGER_VALUE_EXPECTED_FUNCTION = 45; public static final int UNSUPPORTED_JRE = 100; diff --git a/asterixdb/asterix-common/src/main/resources/asx_errormsg/en.properties b/asterixdb/asterix-common/src/main/resources/asx_errormsg/en.properties index cb48e19..661a220 100644 --- a/asterixdb/asterix-common/src/main/resources/asx_errormsg/en.properties +++ b/asterixdb/asterix-common/src/main/resources/asx_errormsg/en.properties @@ -47,7 +47,7 @@ 7 = Overflow in %1$s 8 = Underflow in %1$s 9 = Injected failure in %1$s -10 = Invalid value: function %1$s expects its %2$s input parameter to be a non-negative value, but gets %3$s +10 = Invalid value: function %1$s expects its %2$s input parameter to be a non-negative value, got %3$s 11 = Index out of bound in %1$s: %2$s 12 = Invalid implicit scalar to collection coercion in %1$s 14 = Property %1$s not set @@ -79,6 +79,7 @@ 42 = %1$s: \"%2$s\" is not a TPC-DS table name 43 = Value out of range, function %1$s expects its %2$s input parameter value to be between %3$s and %4$s, received %5$s 44 = %1$s statement is prohibited by this request +45 = Invalid value: function %1$s expects its %2$s input parameter to be an integer value, got %3$s 100 = Unsupported JRE: %1$s diff --git a/asterixdb/asterix-om/src/main/java/org/apache/asterix/om/exceptions/ExceptionUtil.java b/asterixdb/asterix-om/src/main/java/org/apache/asterix/om/exceptions/ExceptionUtil.java index 0791576..b5d599b 100644 --- a/asterixdb/asterix-om/src/main/java/org/apache/asterix/om/exceptions/ExceptionUtil.java +++ b/asterixdb/asterix-om/src/main/java/org/apache/asterix/om/exceptions/ExceptionUtil.java @@ -19,6 +19,8 @@ package org.apache.asterix.om.exceptions; +import java.util.function.Supplier; + import org.apache.asterix.common.exceptions.ErrorCode; import org.apache.asterix.common.exceptions.WarningUtil; import org.apache.asterix.om.types.ATypeTag; @@ -28,12 +30,12 @@ import org.apache.hyracks.algebricks.runtime.base.IEvaluatorContext; import org.apache.hyracks.api.exceptions.IWarningCollector; import org.apache.hyracks.api.exceptions.SourceLocation; -public class ExceptionUtil { +public final class ExceptionUtil { private ExceptionUtil() { } - public static String toExpectedTypeString(Object... expectedItems) { + public static String toExpectedTypeString(Object[] expectedItems) { StringBuilder expectedTypes = new StringBuilder(); int numCandidateTypes = expectedItems.length; for (int index = 0; index < numCandidateTypes; ++index) { @@ -49,7 +51,7 @@ public class ExceptionUtil { return expectedTypes.toString(); } - public static String toExpectedTypeString(byte... expectedTypeTags) { + public static String toExpectedTypeString(byte[] expectedTypeTags) { StringBuilder expectedTypes = new StringBuilder(); int numCandidateTypes = expectedTypeTags.length; for (int index = 0; index < numCandidateTypes; ++index) { @@ -88,20 +90,20 @@ public class ExceptionUtil { public static void warnTypeMismatch(IEvaluatorContext ctx, SourceLocation srcLoc, FunctionIdentifier fid, byte actualType, int argIdx, ATypeTag expectedType) { - IWarningCollector warningCollector = ctx.getWarningCollector(); - if (warningCollector.shouldWarn()) { - warningCollector.warn(WarningUtil.forAsterix(srcLoc, ErrorCode.TYPE_MISMATCH_FUNCTION, fid.getName(), - indexToPosition(argIdx), expectedType, - EnumDeserializer.ATYPETAGDESERIALIZER.deserialize(actualType))); - } + warnTypeMismatch(ctx, srcLoc, fid, actualType, argIdx, expectedType::toString); } public static void warnTypeMismatch(IEvaluatorContext ctx, SourceLocation srcLoc, FunctionIdentifier fid, - byte actualType, int argIdx, byte... expectedTypes) { + byte actualType, int argIdx, byte[] expectedTypes) { + warnTypeMismatch(ctx, srcLoc, fid, actualType, argIdx, () -> toExpectedTypeString(expectedTypes)); + } + + private static void warnTypeMismatch(IEvaluatorContext ctx, SourceLocation srcLoc, FunctionIdentifier fid, + byte actualType, int argIdx, Supplier<String> expectedTypesString) { IWarningCollector warningCollector = ctx.getWarningCollector(); if (warningCollector.shouldWarn()) { warningCollector.warn(WarningUtil.forAsterix(srcLoc, ErrorCode.TYPE_MISMATCH_FUNCTION, fid.getName(), - indexToPosition(argIdx), toExpectedTypeString(expectedTypes), + indexToPosition(argIdx), expectedTypesString.get(), EnumDeserializer.ATYPETAGDESERIALIZER.deserialize(actualType))); } } @@ -121,4 +123,24 @@ public class ExceptionUtil { warningCollector.warn(WarningUtil.forAsterix(srcLoc, ErrorCode.TYPE_UNSUPPORTED, funName, unsupportedType)); } } + + /** For functions that accept an integer value (no fractions) of any numeric type including double & float */ + public static void warnNonInteger(IEvaluatorContext ctx, SourceLocation srcLoc, FunctionIdentifier fid, int argIdx, + double argValue) { + warnInvalidValue(ctx, srcLoc, fid, argIdx, argValue, ErrorCode.INTEGER_VALUE_EXPECTED_FUNCTION); + } + + public static void warnNegativeValue(IEvaluatorContext ctx, SourceLocation srcLoc, FunctionIdentifier fid, + int argIdx, double argValue) { + warnInvalidValue(ctx, srcLoc, fid, argIdx, argValue, ErrorCode.NEGATIVE_VALUE); + } + + private static void warnInvalidValue(IEvaluatorContext ctx, SourceLocation srcLoc, FunctionIdentifier fid, + int argIdx, double argValue, int errorCode) { + IWarningCollector warningCollector = ctx.getWarningCollector(); + if (warningCollector.shouldWarn()) { + warningCollector.warn(WarningUtil.forAsterix(srcLoc, errorCode, fid.getName(), indexToPosition(argIdx), + Double.toString(argValue))); + } + } } 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 eba5100..5f42dfb 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 @@ -126,7 +126,7 @@ import org.apache.asterix.om.typecomputer.impl.ScalarVersionOfAggregateResultTyp import org.apache.asterix.om.typecomputer.impl.SleepTypeComputer; import org.apache.asterix.om.typecomputer.impl.StringBooleanTypeComputer; import org.apache.asterix.om.typecomputer.impl.StringInt32TypeComputer; -import org.apache.asterix.om.typecomputer.impl.StringIntToStringTypeComputer; +import org.apache.asterix.om.typecomputer.impl.StringJoinTypeComputer; import org.apache.asterix.om.typecomputer.impl.StringStringTypeComputer; import org.apache.asterix.om.typecomputer.impl.StringToInt64ListTypeComputer; import org.apache.asterix.om.typecomputer.impl.StringToStringListTypeComputer; @@ -1703,7 +1703,8 @@ public class BuiltinFunctions { addFunction(STRING_TO_CODEPOINT, StringToInt64ListTypeComputer.INSTANCE, true); addFunction(CODEPOINT_TO_STRING, AStringTypeComputer.INSTANCE, true); // TODO addFunction(STRING_CONCAT, ConcatTypeComputer.INSTANCE_STRING, true); // TODO - addFunction(SUBSTRING2, StringIntToStringTypeComputer.INSTANCE_NULLABLE, true); // TODO + addFunction(SUBSTRING, SubstringTypeComputer.INSTANCE, true); // TODO + addFunction(SUBSTRING2, AStringTypeComputer.INSTANCE_NULLABLE, true); addFunction(STRING_LENGTH, UnaryStringInt64TypeComputer.INSTANCE, true); addFunction(STRING_LOWERCASE, StringStringTypeComputer.INSTANCE, true); addFunction(STRING_UPPERCASE, StringStringTypeComputer.INSTANCE, true); @@ -1724,16 +1725,15 @@ public class BuiltinFunctions { addFunction(STRING_REGEXP_POSITION, StringInt32TypeComputer.INSTANCE, true); addFunction(STRING_REGEXP_POSITION_WITH_FLAG, StringInt32TypeComputer.INSTANCE, true); addFunction(STRING_REGEXP_REPLACE, StringStringTypeComputer.INSTANCE, true); - addFunction(STRING_REGEXP_REPLACE_WITH_FLAG, - StringIntToStringTypeComputer.INSTANCE_STRING_REGEXP_REPLACE_WITH_FLAG, true); // TODO + addFunction(STRING_REGEXP_REPLACE_WITH_FLAG, AStringTypeComputer.INSTANCE_NULLABLE, true); addFunction(STRING_REPLACE, StringStringTypeComputer.INSTANCE, true); - addFunction(STRING_REPLACE_WITH_LIMIT, StringIntToStringTypeComputer.INSTANCE_TRIPLE_STRING, true); // TODO + addFunction(STRING_REPLACE_WITH_LIMIT, AStringTypeComputer.INSTANCE_NULLABLE, true); addFunction(STRING_REVERSE, StringStringTypeComputer.INSTANCE, true); addFunction(SUBSTRING_BEFORE, StringStringTypeComputer.INSTANCE, true); addFunction(SUBSTRING_AFTER, StringStringTypeComputer.INSTANCE, true); addPrivateFunction(STRING_EQUAL, StringBooleanTypeComputer.INSTANCE, true); - addFunction(STRING_JOIN, AStringTypeComputer.INSTANCE, true); // TODO - addFunction(STRING_REPEAT, StringIntToStringTypeComputer.INSTANCE, true); // TODO + addFunction(STRING_JOIN, StringJoinTypeComputer.INSTANCE, true); + addFunction(STRING_REPEAT, AStringTypeComputer.INSTANCE_NULLABLE, true); addFunction(STRING_SPLIT, StringToStringListTypeComputer.INSTANCE, true); addPrivateFunction(ORDERED_LIST_CONSTRUCTOR, OrderedListConstructorTypeComputer.INSTANCE, true); @@ -2147,7 +2147,6 @@ public class BuiltinFunctions { addFunction(BINARY_BASE64_CONSTRUCTOR, ABinaryTypeComputer.INSTANCE, true); addPrivateFunction(SUBSET_COLLECTION, SubsetCollectionTypeComputer.INSTANCE, true); - addFunction(SUBSTRING, SubstringTypeComputer.INSTANCE, true); addFunction(SWITCH_CASE, SwitchCaseComputer.INSTANCE, true); addFunction(SLEEP, SleepTypeComputer.INSTANCE, false); addPrivateFunction(INJECT_FAILURE, InjectFailureTypeComputer.INSTANCE, true); diff --git a/asterixdb/asterix-om/src/main/java/org/apache/asterix/om/typecomputer/impl/AStringTypeComputer.java b/asterixdb/asterix-om/src/main/java/org/apache/asterix/om/typecomputer/impl/AStringTypeComputer.java index 3537fb7..4de96b8 100644 --- a/asterixdb/asterix-om/src/main/java/org/apache/asterix/om/typecomputer/impl/AStringTypeComputer.java +++ b/asterixdb/asterix-om/src/main/java/org/apache/asterix/om/typecomputer/impl/AStringTypeComputer.java @@ -19,6 +19,7 @@ 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; @@ -26,14 +27,17 @@ import org.apache.hyracks.algebricks.core.algebra.base.ILogicalExpression; public class AStringTypeComputer extends AbstractResultTypeComputer { - public static final AStringTypeComputer INSTANCE = new AStringTypeComputer(); + public static final AStringTypeComputer INSTANCE = new AStringTypeComputer(false); + public static final AStringTypeComputer INSTANCE_NULLABLE = new AStringTypeComputer(true); - private AStringTypeComputer() { + private final boolean nullable; + + private AStringTypeComputer(boolean nullable) { + this.nullable = nullable; } @Override protected IAType getResultType(ILogicalExpression expr, IAType... strippedInputTypes) throws AlgebricksException { - return BuiltinType.ASTRING; + return nullable ? AUnionType.createNullableType(BuiltinType.ASTRING) : BuiltinType.ASTRING; } - } diff --git a/asterixdb/asterix-om/src/main/java/org/apache/asterix/om/typecomputer/impl/AbstractStringTypeComputer.java b/asterixdb/asterix-om/src/main/java/org/apache/asterix/om/typecomputer/impl/AbstractStringTypeComputer.java index d888958..ba7fc65 100644 --- a/asterixdb/asterix-om/src/main/java/org/apache/asterix/om/typecomputer/impl/AbstractStringTypeComputer.java +++ b/asterixdb/asterix-om/src/main/java/org/apache/asterix/om/typecomputer/impl/AbstractStringTypeComputer.java @@ -23,9 +23,12 @@ import org.apache.asterix.om.types.ATypeTag; import org.apache.asterix.om.types.AUnionType; import org.apache.asterix.om.types.IAType; -abstract public class AbstractStringTypeComputer extends AbstractResultTypeComputer { +/** + * For function signature: nullable_return_type fun(string...) + */ +public abstract class AbstractStringTypeComputer extends AbstractResultTypeComputer { - protected IAType getType(IAType returnType, IAType... argsTypes) { + protected static IAType getType(IAType returnType, IAType... argsTypes) { // all args are expected to be strings. If any arg is not string (ANY or mismatched-type), return nullable for (IAType actualType : argsTypes) { if (actualType.getTypeTag() != ATypeTag.STRING) { @@ -34,11 +37,4 @@ abstract public class AbstractStringTypeComputer extends AbstractResultTypeCompu } return returnType; } - - protected IAType getType(IAType returnType, IAType argType) { - if (argType.getTypeTag() != ATypeTag.STRING) { - return AUnionType.createNullableType(returnType); - } - return returnType; - } } diff --git a/asterixdb/asterix-om/src/main/java/org/apache/asterix/om/typecomputer/impl/StringIntToStringTypeComputer.java b/asterixdb/asterix-om/src/main/java/org/apache/asterix/om/typecomputer/impl/StringIntToStringTypeComputer.java deleted file mode 100644 index 4499e31..0000000 --- a/asterixdb/asterix-om/src/main/java/org/apache/asterix/om/typecomputer/impl/StringIntToStringTypeComputer.java +++ /dev/null @@ -1,116 +0,0 @@ -/* - * 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 java.util.EnumSet; -import java.util.Set; - -import org.apache.asterix.om.exceptions.TypeMismatchException; -import org.apache.asterix.om.typecomputer.base.AbstractResultTypeComputer; -import org.apache.asterix.om.types.ATypeTag; -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; -import org.apache.hyracks.algebricks.core.algebra.functions.FunctionIdentifier; -import org.apache.hyracks.api.exceptions.SourceLocation; - -public class StringIntToStringTypeComputer extends AbstractResultTypeComputer { - public static final StringIntToStringTypeComputer INSTANCE = new StringIntToStringTypeComputer(0, 0, 1, 1, false); - - public static final StringIntToStringTypeComputer INSTANCE_NULLABLE = - new StringIntToStringTypeComputer(0, 0, 1, 1, true); - - public static final StringIntToStringTypeComputer INSTANCE_TRIPLE_STRING = - new StringIntToStringTypeComputer(0, 2, 3, 3, false); - - public static final StringIntToStringTypeComputer INSTANCE_STRING_REGEXP_REPLACE_WITH_FLAG = - new StringIntToStringTypeComputer(0, 3, 3, 3, false); - - private final int stringArgIdxMin; - - private final int stringArgIdxMax; - - private final int intArgIdxMin; - - private final int intArgIdxMax; - - private final boolean nullable; - - public StringIntToStringTypeComputer(int stringArgIdxMin, int stringArgIdxMax, int intArgIdxMin, int intArgIdxMax, - boolean nullable) { - this.stringArgIdxMin = stringArgIdxMin; - this.stringArgIdxMax = stringArgIdxMax; - this.intArgIdxMin = intArgIdxMin; - this.intArgIdxMax = intArgIdxMax; - this.nullable = nullable; - } - - @Override - public void checkArgType(FunctionIdentifier funcId, int argIndex, IAType type, SourceLocation sourceLoc) - throws AlgebricksException { - ATypeTag tag = type.getTypeTag(); - boolean expectedStringType = false; - if (stringArgIdxMin <= argIndex && argIndex <= stringArgIdxMax) { - if (tag == ATypeTag.STRING) { - return; - } - expectedStringType = true; - } - - boolean expectedIntType = false; - if (intArgIdxMin <= argIndex && argIndex <= intArgIdxMax) { - switch (tag) { - case TINYINT: - case SMALLINT: - case INTEGER: - case BIGINT: - return; - } - expectedIntType = true; - } - - throw new TypeMismatchException(sourceLoc, funcId, argIndex, tag, - getExpectedTypes(expectedStringType, expectedIntType)); - } - - @Override - public IAType getResultType(ILogicalExpression expr, IAType... types) throws AlgebricksException { - IAType resultType = BuiltinType.ASTRING; - if (nullable) { - resultType = AUnionType.createNullableType(resultType); - } - return resultType; - } - - private ATypeTag[] getExpectedTypes(boolean expectedStringType, boolean expectedIntType) { - Set<ATypeTag> expectedTypes = EnumSet.noneOf(ATypeTag.class); - if (expectedStringType) { - expectedTypes.add(ATypeTag.STRING); - } - if (expectedIntType) { - expectedTypes.add(ATypeTag.TINYINT); - expectedTypes.add(ATypeTag.SMALLINT); - expectedTypes.add(ATypeTag.INTEGER); - expectedTypes.add(ATypeTag.BIGINT); - } - return expectedTypes.toArray(new ATypeTag[0]); - } -} diff --git a/asterixdb/asterix-om/src/main/java/org/apache/asterix/om/typecomputer/impl/AStringTypeComputer.java b/asterixdb/asterix-om/src/main/java/org/apache/asterix/om/typecomputer/impl/StringJoinTypeComputer.java similarity index 57% copy from asterixdb/asterix-om/src/main/java/org/apache/asterix/om/typecomputer/impl/AStringTypeComputer.java copy to asterixdb/asterix-om/src/main/java/org/apache/asterix/om/typecomputer/impl/StringJoinTypeComputer.java index 3537fb7..407a991 100644 --- a/asterixdb/asterix-om/src/main/java/org/apache/asterix/om/typecomputer/impl/AStringTypeComputer.java +++ b/asterixdb/asterix-om/src/main/java/org/apache/asterix/om/typecomputer/impl/StringJoinTypeComputer.java @@ -18,22 +18,35 @@ */ package org.apache.asterix.om.typecomputer.impl; +import static org.apache.asterix.om.types.BuiltinType.ASTRING; + import org.apache.asterix.om.typecomputer.base.AbstractResultTypeComputer; -import org.apache.asterix.om.types.BuiltinType; +import org.apache.asterix.om.types.ATypeTag; +import org.apache.asterix.om.types.AUnionType; +import org.apache.asterix.om.types.AbstractCollectionType; 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 AStringTypeComputer extends AbstractResultTypeComputer { +/** + * For function signature: nullable_string fun([string], string) + */ +public class StringJoinTypeComputer extends AbstractResultTypeComputer { - public static final AStringTypeComputer INSTANCE = new AStringTypeComputer(); + public static final StringJoinTypeComputer INSTANCE = new StringJoinTypeComputer(); - private AStringTypeComputer() { + private StringJoinTypeComputer() { } @Override protected IAType getResultType(ILogicalExpression expr, IAType... strippedInputTypes) throws AlgebricksException { - return BuiltinType.ASTRING; + return validArgs(strippedInputTypes) ? ASTRING : AUnionType.createNullableType(ASTRING); } + private static boolean validArgs(IAType... strippedInputTypes) { + IAType firstArg = strippedInputTypes[0]; + return firstArg.getTypeTag().isListType() + && ((AbstractCollectionType) firstArg).getItemType().getTypeTag() == ATypeTag.STRING + && strippedInputTypes[1].getTypeTag() == ATypeTag.STRING; + } } diff --git a/asterixdb/asterix-om/src/main/java/org/apache/asterix/om/typecomputer/impl/UnaryStringInt64TypeComputer.java b/asterixdb/asterix-om/src/main/java/org/apache/asterix/om/typecomputer/impl/UnaryStringInt64TypeComputer.java index 37c2230..13079e2 100644 --- a/asterixdb/asterix-om/src/main/java/org/apache/asterix/om/typecomputer/impl/UnaryStringInt64TypeComputer.java +++ b/asterixdb/asterix-om/src/main/java/org/apache/asterix/om/typecomputer/impl/UnaryStringInt64TypeComputer.java @@ -35,6 +35,6 @@ public class UnaryStringInt64TypeComputer extends AbstractStringTypeComputer { @Override public IAType getResultType(ILogicalExpression expr, IAType... types) throws AlgebricksException { - return getType(BuiltinType.AINT64, types[0]); + return getType(BuiltinType.AINT64, types); } } diff --git a/asterixdb/asterix-om/src/main/java/org/apache/asterix/om/types/hierachy/DoubleToInt32TypeConvertComputer.java b/asterixdb/asterix-om/src/main/java/org/apache/asterix/om/types/hierachy/DoubleToInt32TypeConvertComputer.java index 8bd9f3e..5653731 100644 --- a/asterixdb/asterix-om/src/main/java/org/apache/asterix/om/types/hierachy/DoubleToInt32TypeConvertComputer.java +++ b/asterixdb/asterix-om/src/main/java/org/apache/asterix/om/types/hierachy/DoubleToInt32TypeConvertComputer.java @@ -66,7 +66,7 @@ public class DoubleToInt32TypeConvertComputer implements ITypeConvertComputer { return new AInt32(targetValue); } - private int convert(double sourceValue) throws HyracksDataException { + public int convert(double sourceValue) throws HyracksDataException { // Boundary check if (Double.isNaN(sourceValue)) { if (strict) { diff --git a/asterixdb/asterix-runtime/src/main/java/org/apache/asterix/runtime/aggregates/std/AbstractMinMaxAggregateFunction.java b/asterixdb/asterix-runtime/src/main/java/org/apache/asterix/runtime/aggregates/std/AbstractMinMaxAggregateFunction.java index 022e0f5..534d10a 100644 --- a/asterixdb/asterix-runtime/src/main/java/org/apache/asterix/runtime/aggregates/std/AbstractMinMaxAggregateFunction.java +++ b/asterixdb/asterix-runtime/src/main/java/org/apache/asterix/runtime/aggregates/std/AbstractMinMaxAggregateFunction.java @@ -43,7 +43,7 @@ import org.apache.hyracks.data.std.util.ArrayBackedValueStorage; import org.apache.hyracks.dataflow.common.data.accessors.IFrameTupleReference; public abstract class AbstractMinMaxAggregateFunction extends AbstractAggregateFunction { - private final String FUN_NAME = "min/max"; + private static final String FUN_NAME = "min/max"; private final ArrayBackedValueStorage resultStorage = new ArrayBackedValueStorage(); private final IPointable inputVal = new VoidPointable(); private final ArrayBackedValueStorage outputVal = new ArrayBackedValueStorage(); diff --git a/asterixdb/asterix-runtime/src/main/java/org/apache/asterix/runtime/evaluators/common/ArgumentUtils.java b/asterixdb/asterix-runtime/src/main/java/org/apache/asterix/runtime/evaluators/common/ArgumentUtils.java new file mode 100644 index 0000000..7c27e0e --- /dev/null +++ b/asterixdb/asterix-runtime/src/main/java/org/apache/asterix/runtime/evaluators/common/ArgumentUtils.java @@ -0,0 +1,76 @@ +/* + * 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 static org.apache.asterix.om.types.ATypeTag.VALUE_TYPE_MAPPING; + +import org.apache.asterix.om.base.AMutableInt32; +import org.apache.asterix.om.exceptions.ExceptionUtil; +import org.apache.asterix.om.types.ATypeTag; +import org.apache.asterix.om.types.hierachy.ATypeHierarchy; +import org.apache.asterix.om.types.hierachy.DoubleToInt32TypeConvertComputer; +import org.apache.hyracks.algebricks.core.algebra.functions.FunctionIdentifier; +import org.apache.hyracks.algebricks.runtime.base.IEvaluatorContext; +import org.apache.hyracks.api.exceptions.HyracksDataException; +import org.apache.hyracks.api.exceptions.SourceLocation; + +import com.google.common.math.DoubleMath; + +/** + * Utility methods for argument handling + */ +public final class ArgumentUtils { + + public static final byte[] EXPECTED_NUMERIC = { 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 }; + private static final DoubleToInt32TypeConvertComputer LAX_DOUBLE_TO_INT32 = + DoubleToInt32TypeConvertComputer.getInstance(false); + + private ArgumentUtils() { + } + + /** + * Checks that {@code value} is of numeric type and a finite mathematical integer (i.e. no fractions) returning + * true if it is and storing the integer value in {@code outInteger}. Otherwise, returns false and issues a + * warning. If the integer value of {@code value} cannot fit in an int32, then {@link Integer#MAX_VALUE} or + * {@link Integer#MIN_VALUE} is stored. + * + * @param value data to be checked + * @param outInteger where the integer read from {@code value} will be stored + */ + public static boolean checkWarnOrSetInteger(IEvaluatorContext ctx, SourceLocation sourceLoc, + FunctionIdentifier funcID, int argIdx, byte[] value, int offset, AMutableInt32 outInteger) + throws HyracksDataException { + byte type = value[offset]; + if (ATypeHierarchy.getTypeDomain(VALUE_TYPE_MAPPING[type]) != ATypeHierarchy.Domain.NUMERIC) { + ExceptionUtil.warnTypeMismatch(ctx, sourceLoc, funcID, type, argIdx, EXPECTED_NUMERIC); + return false; + } + // deal with NaN, +/-INF + double doubleValue = ATypeHierarchy.getDoubleValue(funcID.getName(), argIdx, value, offset); + if (!DoubleMath.isMathematicalInteger(doubleValue)) { + ExceptionUtil.warnNonInteger(ctx, sourceLoc, funcID, argIdx, doubleValue); + return false; + } + outInteger.setValue(LAX_DOUBLE_TO_INT32.convert(doubleValue)); + return true; + } +} diff --git a/asterixdb/asterix-runtime/src/main/java/org/apache/asterix/runtime/evaluators/functions/AbstractQuadStringStringEval.java b/asterixdb/asterix-runtime/src/main/java/org/apache/asterix/runtime/evaluators/functions/AbstractQuadStringStringEval.java index 17db0fe..ba17616 100644 --- a/asterixdb/asterix-runtime/src/main/java/org/apache/asterix/runtime/evaluators/functions/AbstractQuadStringStringEval.java +++ b/asterixdb/asterix-runtime/src/main/java/org/apache/asterix/runtime/evaluators/functions/AbstractQuadStringStringEval.java @@ -27,9 +27,9 @@ import java.io.IOException; import org.apache.asterix.formats.nontagged.SerializerDeserializerProvider; import org.apache.asterix.om.base.AMutableString; +import org.apache.asterix.om.exceptions.ExceptionUtil; import org.apache.asterix.om.types.ATypeTag; import org.apache.asterix.om.types.BuiltinType; -import org.apache.asterix.runtime.exceptions.TypeMismatchException; import org.apache.hyracks.algebricks.core.algebra.functions.FunctionIdentifier; import org.apache.hyracks.algebricks.runtime.base.IEvaluatorContext; import org.apache.hyracks.algebricks.runtime.base.IScalarEvaluator; @@ -64,6 +64,7 @@ public abstract class AbstractQuadStringStringEval implements IScalarEvaluator { private ISerializerDeserializer strSerde = SerializerDeserializerProvider.INSTANCE.getSerializerDeserializer(BuiltinType.ASTRING); + private final IEvaluatorContext ctx; private final UTF8StringPointable strPtr0 = new UTF8StringPointable(); private final UTF8StringPointable strPtr1 = new UTF8StringPointable(); private final UTF8StringPointable strPtr2 = new UTF8StringPointable(); @@ -72,6 +73,7 @@ public abstract class AbstractQuadStringStringEval implements IScalarEvaluator { public AbstractQuadStringStringEval(IEvaluatorContext context, IScalarEvaluatorFactory eval0, IScalarEvaluatorFactory eval1, IScalarEvaluatorFactory eval2, IScalarEvaluatorFactory eval3, FunctionIdentifier funcID, SourceLocation sourceLoc) throws HyracksDataException { + this.ctx = context; this.eval0 = eval0.createScalarEvaluator(context); this.eval1 = eval1.createScalarEvaluator(context); this.eval2 = eval2.createScalarEvaluator(context); @@ -92,10 +94,11 @@ public abstract class AbstractQuadStringStringEval implements IScalarEvaluator { return; } - processArgument(0, ptr0, strPtr0); - processArgument(1, ptr1, strPtr1); - processArgument(2, ptr2, strPtr2); - processArgument(3, ptr3, strPtr3); + if (!processArgument(0, ptr0, strPtr0) || !processArgument(1, ptr1, strPtr1) + || !processArgument(2, ptr2, strPtr2) || !processArgument(3, ptr3, strPtr3)) { + PointableHelper.setNull(result); + return; + } resultStorage.reset(); try { @@ -108,17 +111,18 @@ public abstract class AbstractQuadStringStringEval implements IScalarEvaluator { result.set(resultStorage); } - protected void processArgument(int argIdx, IPointable argPtr, UTF8StringPointable outStrPtr) + protected boolean processArgument(int argIdx, IPointable argPtr, UTF8StringPointable outStrPtr) throws HyracksDataException { byte[] bytes = argPtr.getByteArray(); int start = argPtr.getStartOffset(); // Type check. if (bytes[start] != ATypeTag.SERIALIZED_STRING_TYPE_TAG) { - throw new TypeMismatchException(sourceLoc, funcID, argIdx, bytes[start], - ATypeTag.SERIALIZED_STRING_TYPE_TAG); + ExceptionUtil.warnTypeMismatch(ctx, sourceLoc, funcID, bytes[start], argIdx, ATypeTag.STRING); + return false; } int len = argPtr.getLength(); outStrPtr.set(bytes, start + 1, len); + return true; } protected abstract String compute(UTF8StringPointable strPtr1st, UTF8StringPointable strPtr2nd, diff --git a/asterixdb/asterix-runtime/src/main/java/org/apache/asterix/runtime/evaluators/functions/AbstractStringStringStringIntEval.java b/asterixdb/asterix-runtime/src/main/java/org/apache/asterix/runtime/evaluators/functions/AbstractStringStringStringIntEval.java index ea282d9..4aae469 100644 --- a/asterixdb/asterix-runtime/src/main/java/org/apache/asterix/runtime/evaluators/functions/AbstractStringStringStringIntEval.java +++ b/asterixdb/asterix-runtime/src/main/java/org/apache/asterix/runtime/evaluators/functions/AbstractStringStringStringIntEval.java @@ -21,9 +21,10 @@ package org.apache.asterix.runtime.evaluators.functions; import java.io.DataOutput; +import org.apache.asterix.om.base.AMutableInt32; +import org.apache.asterix.om.exceptions.ExceptionUtil; import org.apache.asterix.om.types.ATypeTag; -import org.apache.asterix.om.types.hierachy.ATypeHierarchy; -import org.apache.asterix.runtime.exceptions.TypeMismatchException; +import org.apache.asterix.runtime.evaluators.common.ArgumentUtils; import org.apache.hyracks.algebricks.core.algebra.functions.FunctionIdentifier; import org.apache.hyracks.algebricks.runtime.base.IEvaluatorContext; import org.apache.hyracks.algebricks.runtime.base.IScalarEvaluator; @@ -37,11 +38,13 @@ import org.apache.hyracks.data.std.util.ArrayBackedValueStorage; import org.apache.hyracks.dataflow.common.data.accessors.IFrameTupleReference; public abstract class AbstractStringStringStringIntEval implements IScalarEvaluator { + + private final IEvaluatorContext ctx; // Argument evaluators. - private IScalarEvaluator eval0; - private IScalarEvaluator eval1; - private IScalarEvaluator eval2; - private IScalarEvaluator eval3; + private final IScalarEvaluator eval0; + private final IScalarEvaluator eval1; + private final IScalarEvaluator eval2; + private final IScalarEvaluator eval3; // Argument pointables. final IPointable argPtrFirst = new VoidPointable(); @@ -51,6 +54,7 @@ public abstract class AbstractStringStringStringIntEval implements IScalarEvalua private final UTF8StringPointable strPtr1st = new UTF8StringPointable(); private final UTF8StringPointable strPtr2nd = new UTF8StringPointable(); private final UTF8StringPointable strPtr3rd = new UTF8StringPointable(); + private final AMutableInt32 mutableInt = new AMutableInt32(0); // For outputting results. ArrayBackedValueStorage resultStorage = new ArrayBackedValueStorage(); @@ -63,6 +67,7 @@ public abstract class AbstractStringStringStringIntEval implements IScalarEvalua AbstractStringStringStringIntEval(IEvaluatorContext context, IScalarEvaluatorFactory eval0, IScalarEvaluatorFactory eval1, IScalarEvaluatorFactory eval2, IScalarEvaluatorFactory eval3, FunctionIdentifier funcID, SourceLocation sourceLoc) throws HyracksDataException { + this.ctx = context; this.sourceLoc = sourceLoc; this.eval0 = eval0.createScalarEvaluator(context); this.eval1 = eval1.createScalarEvaluator(context); @@ -100,30 +105,21 @@ public abstract class AbstractStringStringStringIntEval implements IScalarEvalua int start3 = argPtrFourth.getStartOffset(); // Type check. - if (bytes0[start0] != ATypeTag.SERIALIZED_STRING_TYPE_TAG) { - throw new TypeMismatchException(sourceLoc, funcID, 0, bytes0[start0], ATypeTag.SERIALIZED_STRING_TYPE_TAG); - } - if (bytes1[start1] != ATypeTag.SERIALIZED_STRING_TYPE_TAG) { - throw new TypeMismatchException(sourceLoc, funcID, 1, bytes1[start1], ATypeTag.SERIALIZED_STRING_TYPE_TAG); - } - if (bytes2[start2] != ATypeTag.SERIALIZED_STRING_TYPE_TAG) { - throw new TypeMismatchException(sourceLoc, funcID, 2, bytes2[start2], ATypeTag.SERIALIZED_STRING_TYPE_TAG); + if (!checkStringsAndWarn(bytes0[start0], bytes1[start1], bytes2[start2])) { + PointableHelper.setNull(result); + return; } - if (bytes3[start3] != ATypeTag.SERIALIZED_INT8_TYPE_TAG && bytes3[start3] != ATypeTag.SERIALIZED_INT16_TYPE_TAG - && bytes3[start3] != ATypeTag.SERIALIZED_INT32_TYPE_TAG - && bytes3[start3] != ATypeTag.SERIALIZED_INT64_TYPE_TAG) { - throw new TypeMismatchException(sourceLoc, funcID, 3, bytes3[start3], ATypeTag.SERIALIZED_INT8_TYPE_TAG, - ATypeTag.SERIALIZED_INT16_TYPE_TAG, ATypeTag.SERIALIZED_INT32_TYPE_TAG, - ATypeTag.SERIALIZED_INT64_TYPE_TAG); + // check that the int argument is numeric without fractions (in case arg is double or float) + if (!ArgumentUtils.checkWarnOrSetInteger(ctx, sourceLoc, funcID, 3, bytes3, start3, mutableInt)) { + PointableHelper.setNull(result); + return; } - + int int4th = mutableInt.getIntegerValue(); // Sets argument UTF8Pointables. strPtr1st.set(bytes0, start0 + 1, len0 - 1); strPtr2nd.set(bytes1, start1 + 1, len1 - 1); strPtr3rd.set(bytes2, start2 + 1, len2 - 1); - long int4th = ATypeHierarchy.getLongValue(funcID.getName(), 3, bytes3, start3); - // Resets the output storage. resultStorage.reset(); // The actual processing. @@ -146,5 +142,18 @@ public abstract class AbstractStringStringStringIntEval implements IScalarEvalua * @throws HyracksDataException */ protected abstract void process(UTF8StringPointable first, UTF8StringPointable second, UTF8StringPointable third, - long fourth, IPointable resultPointable) throws HyracksDataException; + int fourth, IPointable resultPointable) throws HyracksDataException; + + /** Checks the arguments expected to be strings returning {@code false} if they are not and issuing a warning */ + private boolean checkStringsAndWarn(byte actualType0, byte actualType1, byte actualType2) { + return checkAndWarn(0, actualType0) && checkAndWarn(1, actualType1) && checkAndWarn(2, actualType2); + } + + private boolean checkAndWarn(int argIdx, byte actualType) { + if (actualType != ATypeTag.SERIALIZED_STRING_TYPE_TAG) { + ExceptionUtil.warnTypeMismatch(ctx, sourceLoc, funcID, actualType, argIdx, ATypeTag.STRING); + return false; + } + return true; + } } diff --git a/asterixdb/asterix-runtime/src/main/java/org/apache/asterix/runtime/evaluators/functions/StringJoinDescriptor.java b/asterixdb/asterix-runtime/src/main/java/org/apache/asterix/runtime/evaluators/functions/StringJoinDescriptor.java index 6037a79..0c39fae 100644 --- a/asterixdb/asterix-runtime/src/main/java/org/apache/asterix/runtime/evaluators/functions/StringJoinDescriptor.java +++ b/asterixdb/asterix-runtime/src/main/java/org/apache/asterix/runtime/evaluators/functions/StringJoinDescriptor.java @@ -22,23 +22,16 @@ import java.io.DataOutput; import java.io.IOException; import org.apache.asterix.common.annotations.MissingNullInOutFunction; -import org.apache.asterix.formats.nontagged.SerializerDeserializerProvider; -import org.apache.asterix.om.base.AMissing; -import org.apache.asterix.om.base.ANull; +import org.apache.asterix.om.exceptions.ExceptionUtil; 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.ListAccessor; -import org.apache.asterix.runtime.exceptions.TypeMismatchException; -import org.apache.asterix.runtime.exceptions.UnsupportedItemTypeException; import org.apache.hyracks.algebricks.core.algebra.functions.FunctionIdentifier; import org.apache.hyracks.algebricks.runtime.base.IEvaluatorContext; import org.apache.hyracks.algebricks.runtime.base.IScalarEvaluator; import org.apache.hyracks.algebricks.runtime.base.IScalarEvaluatorFactory; -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.VoidPointable; @@ -50,12 +43,7 @@ import org.apache.hyracks.util.string.UTF8StringUtil; public class StringJoinDescriptor extends AbstractScalarFunctionDynamicDescriptor { private static final long serialVersionUID = 1L; - public static final IFunctionDescriptorFactory FACTORY = new IFunctionDescriptorFactory() { - @Override - public IFunctionDescriptor createFunctionDescriptor() { - return new StringJoinDescriptor(); - } - }; + public static final IFunctionDescriptorFactory FACTORY = StringJoinDescriptor::new; @Override public IScalarEvaluatorFactory createEvaluatorFactory(final IScalarEvaluatorFactory[] args) { @@ -74,13 +62,9 @@ public class StringJoinDescriptor extends AbstractScalarFunctionDynamicDescripto private final IPointable inputArgSep = new VoidPointable(); private final IScalarEvaluator evalList = listEvalFactory.createScalarEvaluator(ctx); private final IScalarEvaluator evalSep = sepEvalFactory.createScalarEvaluator(ctx); - @SuppressWarnings("unchecked") - private ISerializerDeserializer<ANull> nullSerde = - SerializerDeserializerProvider.INSTANCE.getSerializerDeserializer(BuiltinType.ANULL); - @SuppressWarnings("unchecked") - private ISerializerDeserializer<AMissing> missingSerde = - SerializerDeserializerProvider.INSTANCE.getSerializerDeserializer(BuiltinType.AMISSING); private final byte[] tempLengthArray = new byte[5]; + private final byte[] expectedTypeList = + { ATypeTag.SERIALIZED_ORDEREDLIST_TYPE_TAG, ATypeTag.SERIALIZED_UNORDEREDLIST_TYPE_TAG }; @Override public void evaluate(IFrameTupleReference tuple, IPointable result) throws HyracksDataException { @@ -96,15 +80,18 @@ public class StringJoinDescriptor extends AbstractScalarFunctionDynamicDescripto int listOffset = inputArgList.getStartOffset(); if (listBytes[listOffset] != ATypeTag.SERIALIZED_ORDEREDLIST_TYPE_TAG && listBytes[listOffset] != ATypeTag.SERIALIZED_UNORDEREDLIST_TYPE_TAG) { - throw new TypeMismatchException(sourceLoc, getIdentifier(), 0, listBytes[listOffset], - ATypeTag.SERIALIZED_ORDEREDLIST_TYPE_TAG, - ATypeTag.SERIALIZED_UNORDEREDLIST_TYPE_TAG); + PointableHelper.setNull(result); + ExceptionUtil.warnTypeMismatch(ctx, sourceLoc, getIdentifier(), listBytes[listOffset], 0, + expectedTypeList); + return; } byte[] sepBytes = inputArgSep.getByteArray(); int sepOffset = inputArgSep.getStartOffset(); if (sepBytes[sepOffset] != ATypeTag.SERIALIZED_STRING_TYPE_TAG) { - throw new TypeMismatchException(sourceLoc, getIdentifier(), 1, sepBytes[sepOffset], - ATypeTag.SERIALIZED_STRING_TYPE_TAG); + PointableHelper.setNull(result); + ExceptionUtil.warnTypeMismatch(ctx, sourceLoc, getIdentifier(), sepBytes[sepOffset], 1, + ATypeTag.STRING); + return; } int sepLen = UTF8StringUtil.getUTFLength(sepBytes, sepOffset + 1); int sepMetaLen = UTF8StringUtil.getNumBytesToStoreLength(sepLen); @@ -123,18 +110,17 @@ public class StringJoinDescriptor extends AbstractScalarFunctionDynamicDescripto itemOffset += 1; } if (itemType != ATypeTag.STRING) { - if (itemType == ATypeTag.NULL) { - nullSerde.serialize(ANull.NULL, out); - result.set(resultStorage); - return; - } if (itemType == ATypeTag.MISSING) { - missingSerde.serialize(AMissing.MISSING, out); - result.set(resultStorage); + PointableHelper.setMissing(result); return; } - throw new UnsupportedItemTypeException(sourceLoc, getIdentifier(), - itemType.serialize()); + PointableHelper.setNull(result); + if (itemType != ATypeTag.NULL) { + // warn only if the call is: string_join([1,3], "/") where elements are non-null + ExceptionUtil.warnUnsupportedType(ctx, sourceLoc, getIdentifier().getName(), + itemType); + } + return; } int currentSize = UTF8StringUtil.getUTFLength(listBytes, itemOffset); if (i != size - 1 && currentSize != 0) { diff --git a/asterixdb/asterix-runtime/src/main/java/org/apache/asterix/runtime/evaluators/functions/StringRegExpReplaceWithFlagDescriptor.java b/asterixdb/asterix-runtime/src/main/java/org/apache/asterix/runtime/evaluators/functions/StringRegExpReplaceWithFlagDescriptor.java index cb18f5a..f43fedc 100644 --- a/asterixdb/asterix-runtime/src/main/java/org/apache/asterix/runtime/evaluators/functions/StringRegExpReplaceWithFlagDescriptor.java +++ b/asterixdb/asterix-runtime/src/main/java/org/apache/asterix/runtime/evaluators/functions/StringRegExpReplaceWithFlagDescriptor.java @@ -19,14 +19,17 @@ package org.apache.asterix.runtime.evaluators.functions; import java.io.IOException; +import java.util.Arrays; import org.apache.asterix.common.annotations.MissingNullInOutFunction; +import org.apache.asterix.om.base.AMutableInt32; +import org.apache.asterix.om.exceptions.ExceptionUtil; 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.hierachy.ATypeHierarchy; import org.apache.asterix.runtime.evaluators.base.AbstractScalarFunctionDynamicDescriptor; +import org.apache.asterix.runtime.evaluators.common.ArgumentUtils; import org.apache.asterix.runtime.evaluators.functions.utils.RegExpMatcher; import org.apache.hyracks.algebricks.core.algebra.functions.FunctionIdentifier; import org.apache.hyracks.algebricks.runtime.base.IEvaluatorContext; @@ -40,12 +43,7 @@ import org.apache.hyracks.data.std.primitive.UTF8StringPointable; public class StringRegExpReplaceWithFlagDescriptor extends AbstractScalarFunctionDynamicDescriptor { private static final long serialVersionUID = 1L; - public static final IFunctionDescriptorFactory FACTORY = new IFunctionDescriptorFactory() { - @Override - public IFunctionDescriptor createFunctionDescriptor() { - return new StringRegExpReplaceWithFlagDescriptor(); - } - }; + public static final IFunctionDescriptorFactory FACTORY = StringRegExpReplaceWithFlagDescriptor::new; @Override public IScalarEvaluatorFactory createEvaluatorFactory(final IScalarEvaluatorFactory[] args) { @@ -58,30 +56,43 @@ public class StringRegExpReplaceWithFlagDescriptor extends AbstractScalarFunctio StringRegExpReplaceWithFlagDescriptor.this.getIdentifier(), sourceLoc) { private final UTF8StringPointable emptyFlags = UTF8StringPointable.generateUTF8Pointable(""); private final RegExpMatcher matcher = new RegExpMatcher(); + private final AMutableInt32 mutableInt = new AMutableInt32(0); + private byte[] expectedTypes; private int limit; @Override - protected void processArgument(int argIdx, IPointable argPtr, UTF8StringPointable outStrPtr) + protected boolean processArgument(int argIdx, IPointable argPtr, UTF8StringPointable outStrPtr) throws HyracksDataException { if (argIdx == 3) { byte[] bytes = argPtr.getByteArray(); int start = argPtr.getStartOffset(); ATypeTag tt = ATypeTag.VALUE_TYPE_MAPPING[bytes[start]]; + if (ATypeHierarchy.getTypeDomain(tt) != ATypeHierarchy.Domain.NUMERIC + && tt != ATypeTag.STRING) { + ExceptionUtil.warnTypeMismatch(ctx, sourceLoc, funcID, bytes[start], argIdx, + getExpectedTypes()); + return false; + } switch (tt) { case TINYINT: case SMALLINT: case INTEGER: case BIGINT: - limit = ATypeHierarchy.getIntegerValue(funcID.getName(), argIdx, bytes, start, - true); + case FLOAT: + case DOUBLE: + if (!ArgumentUtils.checkWarnOrSetInteger(ctx, sourceLoc, funcID, argIdx, bytes, + start, mutableInt)) { + return false; + } + limit = mutableInt.getIntegerValue(); outStrPtr.set(emptyFlags); - return; + return true; default: limit = Integer.MAX_VALUE; break; } } - super.processArgument(argIdx, argPtr, outStrPtr); + return super.processArgument(argIdx, argPtr, outStrPtr); } @Override @@ -90,6 +101,15 @@ public class StringRegExpReplaceWithFlagDescriptor extends AbstractScalarFunctio matcher.build(srcPtr, patternPtr, flagsPtr); return matcher.replace(replacePtr, limit); } + + private byte[] getExpectedTypes() { + if (expectedTypes == null) { + expectedTypes = Arrays.copyOf(ArgumentUtils.EXPECTED_NUMERIC, + ArgumentUtils.EXPECTED_NUMERIC.length + 1); + expectedTypes[expectedTypes.length - 1] = ATypeTag.SERIALIZED_STRING_TYPE_TAG; + } + return expectedTypes; + } }; } }; diff --git a/asterixdb/asterix-runtime/src/main/java/org/apache/asterix/runtime/evaluators/functions/StringRepeatDescriptor.java b/asterixdb/asterix-runtime/src/main/java/org/apache/asterix/runtime/evaluators/functions/StringRepeatDescriptor.java index b30f3ee..83e3f8e 100644 --- a/asterixdb/asterix-runtime/src/main/java/org/apache/asterix/runtime/evaluators/functions/StringRepeatDescriptor.java +++ b/asterixdb/asterix-runtime/src/main/java/org/apache/asterix/runtime/evaluators/functions/StringRepeatDescriptor.java @@ -23,15 +23,13 @@ import java.io.DataOutput; import java.io.IOException; import org.apache.asterix.common.annotations.MissingNullInOutFunction; -import org.apache.asterix.common.exceptions.ErrorCode; -import org.apache.asterix.common.exceptions.RuntimeDataException; +import org.apache.asterix.om.base.AMutableInt32; +import org.apache.asterix.om.exceptions.ExceptionUtil; 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.hierachy.ATypeHierarchy; import org.apache.asterix.runtime.evaluators.base.AbstractScalarFunctionDynamicDescriptor; -import org.apache.asterix.runtime.exceptions.TypeMismatchException; +import org.apache.asterix.runtime.evaluators.common.ArgumentUtils; import org.apache.hyracks.algebricks.core.algebra.functions.FunctionIdentifier; import org.apache.hyracks.algebricks.runtime.base.IEvaluatorContext; import org.apache.hyracks.algebricks.runtime.base.IScalarEvaluator; @@ -46,12 +44,7 @@ import org.apache.hyracks.util.string.UTF8StringUtil; @MissingNullInOutFunction public class StringRepeatDescriptor extends AbstractScalarFunctionDynamicDescriptor { private static final long serialVersionUID = 1L; - public static final IFunctionDescriptorFactory FACTORY = new IFunctionDescriptorFactory() { - @Override - public IFunctionDescriptor createFunctionDescriptor() { - return new StringRepeatDescriptor(); - } - }; + public static final IFunctionDescriptorFactory FACTORY = StringRepeatDescriptor::new; @Override public IScalarEvaluatorFactory createEvaluatorFactory(final IScalarEvaluatorFactory[] args) { @@ -68,6 +61,7 @@ public class StringRepeatDescriptor extends AbstractScalarFunctionDynamicDescrip // Argument pointers. private IPointable argString = new VoidPointable(); private IPointable argNumber = new VoidPointable(); + private final AMutableInt32 mutableInt = new AMutableInt32(0); // For outputting the result. private ArrayBackedValueStorage resultStorage = new ArrayBackedValueStorage(); @@ -89,12 +83,17 @@ public class StringRepeatDescriptor extends AbstractScalarFunctionDynamicDescrip // Gets the repeating times. byte[] bytes = argNumber.getByteArray(); int offset = argNumber.getStartOffset(); - int repeatingTimes = - ATypeHierarchy.getIntegerValue(getIdentifier().getName(), 1, bytes, offset); + if (!ArgumentUtils.checkWarnOrSetInteger(ctx, sourceLoc, getIdentifier(), 1, bytes, offset, + mutableInt)) { + PointableHelper.setNull(result); + return; + } + int repeatingTimes = mutableInt.getIntegerValue(); // Checks repeatingTimes. It should be a non-negative value. if (repeatingTimes < 0) { - throw new RuntimeDataException(ErrorCode.NEGATIVE_VALUE, sourceLoc, - getIdentifier().getName(), 1, repeatingTimes); + PointableHelper.setNull(result); + ExceptionUtil.warnNegativeValue(ctx, sourceLoc, getIdentifier(), 1, repeatingTimes); + return; } // Gets the input string. @@ -102,8 +101,10 @@ public class StringRepeatDescriptor extends AbstractScalarFunctionDynamicDescrip offset = argString.getStartOffset(); // Checks the type of the string argument. if (bytes[offset] != ATypeTag.SERIALIZED_STRING_TYPE_TAG) { - throw new TypeMismatchException(sourceLoc, getIdentifier(), 0, bytes[offset], - ATypeTag.SERIALIZED_STRING_TYPE_TAG); + PointableHelper.setNull(result); + ExceptionUtil.warnTypeMismatch(ctx, sourceLoc, getIdentifier(), bytes[offset], 0, + ATypeTag.STRING); + return; } // Calculates the result string length. @@ -133,5 +134,4 @@ public class StringRepeatDescriptor extends AbstractScalarFunctionDynamicDescrip public FunctionIdentifier getIdentifier() { return BuiltinFunctions.STRING_REPEAT; } - } diff --git a/asterixdb/asterix-runtime/src/main/java/org/apache/asterix/runtime/evaluators/functions/StringReplaceWithLimitDescriptor.java b/asterixdb/asterix-runtime/src/main/java/org/apache/asterix/runtime/evaluators/functions/StringReplaceWithLimitDescriptor.java index c84d791..4bb3bb5 100644 --- a/asterixdb/asterix-runtime/src/main/java/org/apache/asterix/runtime/evaluators/functions/StringReplaceWithLimitDescriptor.java +++ b/asterixdb/asterix-runtime/src/main/java/org/apache/asterix/runtime/evaluators/functions/StringReplaceWithLimitDescriptor.java @@ -21,7 +21,6 @@ package org.apache.asterix.runtime.evaluators.functions; import org.apache.asterix.common.annotations.MissingNullInOutFunction; 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.runtime.evaluators.base.AbstractScalarFunctionDynamicDescriptor; import org.apache.asterix.runtime.evaluators.functions.utils.StringReplacer; @@ -36,12 +35,7 @@ import org.apache.hyracks.data.std.primitive.UTF8StringPointable; @MissingNullInOutFunction public class StringReplaceWithLimitDescriptor extends AbstractScalarFunctionDynamicDescriptor { private static final long serialVersionUID = 1L; - public static final IFunctionDescriptorFactory FACTORY = new IFunctionDescriptorFactory() { - @Override - public IFunctionDescriptor createFunctionDescriptor() { - return new StringReplaceWithLimitDescriptor(); - } - }; + public static final IFunctionDescriptorFactory FACTORY = StringReplaceWithLimitDescriptor::new; @Override public IScalarEvaluatorFactory createEvaluatorFactory(final IScalarEvaluatorFactory[] args) { @@ -57,9 +51,9 @@ public class StringReplaceWithLimitDescriptor extends AbstractScalarFunctionDyna @Override protected void process(UTF8StringPointable first, UTF8StringPointable second, - UTF8StringPointable third, long fourth, IPointable resultPointable) + UTF8StringPointable third, int fourth, IPointable resultPointable) throws HyracksDataException { - if (replacer.findAndReplace(first, second, third, (int) fourth)) { + if (replacer.findAndReplace(first, second, third, fourth)) { replacer.assignResult(resultPointable); } else { resultPointable.set(argPtrFirst); diff --git a/asterixdb/asterix-runtime/src/main/java/org/apache/asterix/runtime/evaluators/functions/Substring2Descriptor.java b/asterixdb/asterix-runtime/src/main/java/org/apache/asterix/runtime/evaluators/functions/Substring2Descriptor.java index 63cc9d3..41ae0c3 100644 --- a/asterixdb/asterix-runtime/src/main/java/org/apache/asterix/runtime/evaluators/functions/Substring2Descriptor.java +++ b/asterixdb/asterix-runtime/src/main/java/org/apache/asterix/runtime/evaluators/functions/Substring2Descriptor.java @@ -22,14 +22,14 @@ import java.io.DataOutput; import java.io.IOException; import org.apache.asterix.common.annotations.MissingNullInOutFunction; +import org.apache.asterix.om.base.AMutableInt32; +import org.apache.asterix.om.exceptions.ExceptionUtil; 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.functions.IFunctionTypeInferer; import org.apache.asterix.om.types.ATypeTag; -import org.apache.asterix.om.types.hierachy.ATypeHierarchy; -import org.apache.asterix.runtime.exceptions.TypeMismatchException; +import org.apache.asterix.runtime.evaluators.common.ArgumentUtils; import org.apache.asterix.runtime.functions.FunctionTypeInferers; +import org.apache.asterix.runtime.utils.DescriptorFactoryUtil; import org.apache.hyracks.algebricks.core.algebra.functions.FunctionIdentifier; import org.apache.hyracks.algebricks.runtime.base.IEvaluatorContext; import org.apache.hyracks.algebricks.runtime.base.IScalarEvaluator; @@ -46,17 +46,8 @@ import org.apache.hyracks.dataflow.common.data.accessors.IFrameTupleReference; @MissingNullInOutFunction public class Substring2Descriptor extends AbstractStringOffsetConfigurableDescriptor { private static final long serialVersionUID = 1L; - public static final IFunctionDescriptorFactory FACTORY = new IFunctionDescriptorFactory() { - @Override - public IFunctionDescriptor createFunctionDescriptor() { - return new Substring2Descriptor(); - } - - @Override - public IFunctionTypeInferer createFunctionTypeInferer() { - return FunctionTypeInferers.SET_STRING_OFFSET; - } - }; + public static final IFunctionDescriptorFactory FACTORY = + DescriptorFactoryUtil.createFactory(Substring2Descriptor::new, FunctionTypeInferers.SET_STRING_OFFSET); @Override public IScalarEvaluatorFactory createEvaluatorFactory(final IScalarEvaluatorFactory[] args) { @@ -68,15 +59,17 @@ public class Substring2Descriptor extends AbstractStringOffsetConfigurableDescri @Override public IScalarEvaluator createScalarEvaluator(final IEvaluatorContext ctx) throws HyracksDataException { return new IScalarEvaluator() { - private ArrayBackedValueStorage resultStorage = new ArrayBackedValueStorage(); - private DataOutput out = resultStorage.getDataOutput(); - private IPointable argString = new VoidPointable(); - private IPointable argStart = new VoidPointable(); - private IScalarEvaluator evalString = args[0].createScalarEvaluator(ctx); - private IScalarEvaluator evalStart = args[1].createScalarEvaluator(ctx); + private final ArrayBackedValueStorage resultStorage = new ArrayBackedValueStorage(); + private final DataOutput out = resultStorage.getDataOutput(); + private final IPointable argString = new VoidPointable(); + private final IPointable argStart = new VoidPointable(); + private final IScalarEvaluator evalString = args[0].createScalarEvaluator(ctx); + private final IScalarEvaluator evalStart = args[1].createScalarEvaluator(ctx); private final GrowableArray array = new GrowableArray(); private final UTF8StringBuilder builder = new UTF8StringBuilder(); private final UTF8StringPointable string = new UTF8StringPointable(); + private final FunctionIdentifier funID = getIdentifier(); + private final AMutableInt32 mutableInt = new AMutableInt32(0); @Override public void evaluate(IFrameTupleReference tuple, IPointable result) throws HyracksDataException { @@ -90,13 +83,19 @@ public class Substring2Descriptor extends AbstractStringOffsetConfigurableDescri byte[] bytes = argStart.getByteArray(); int offset = argStart.getStartOffset(); - int start = ATypeHierarchy.getIntegerValue(getIdentifier().getName(), 1, bytes, offset); + // check that the int argument is numeric without fractions (in case arg is double or float) + if (!ArgumentUtils.checkWarnOrSetInteger(ctx, sourceLoc, funID, 1, bytes, offset, mutableInt)) { + PointableHelper.setNull(result); + return; + } + int start = mutableInt.getIntegerValue(); bytes = argString.getByteArray(); offset = argString.getStartOffset(); int len = argString.getLength(); if (bytes[offset] != ATypeTag.SERIALIZED_STRING_TYPE_TAG) { - throw new TypeMismatchException(sourceLoc, getIdentifier(), 0, bytes[offset], - ATypeTag.SERIALIZED_STRING_TYPE_TAG); + PointableHelper.setNull(result); + ExceptionUtil.warnTypeMismatch(ctx, sourceLoc, funID, bytes[offset], 0, ATypeTag.STRING); + return; } string.set(bytes, offset + 1, len - 1); array.reset();
