Murtadha Hubail has submitted this change and it was merged. Change subject: [NO ISSUE][FUN] Implement object-replace() ......................................................................
[NO ISSUE][FUN] Implement object-replace() - user model changes: no - storage format changes: no - interface changes: no Details: - Implement object-replace function that replaces all occurrences of a value in a given object by a new value. - Add new function to docs. - Add test case. Change-Id: I2907f827a1dc5bb35f340bfd25d51e1fdd6fde20 Reviewed-on: https://asterix-gerrit.ics.uci.edu/2708 Reviewed-by: Murtadha Hubail <[email protected]> Sonar-Qube: Jenkins <[email protected]> Tested-by: Jenkins <[email protected]> Contrib: Jenkins <[email protected]> Integration-Tests: Jenkins <[email protected]> Reviewed-by: Dmitry Lychagin <[email protected]> --- M asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/objects/ObjectsQueries.xml A asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/objects/object_replace/object_replace.1.ddl.sqlpp A asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/objects/object_replace/object_replace.2.update.sqlpp A asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/objects/object_replace/object_replace.3.query.sqlpp A asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/objects/object_replace/object_replace.4.query.sqlpp A asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/objects/object_replace/object_replace.5.query.sqlpp A asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/objects/object_replace/object_replace.6.ddl.sqlpp A asterixdb/asterix-app/src/test/resources/runtimets/results/objects/object_replace/object_replace.3.adm M asterixdb/asterix-doc/src/main/markdown/builtins/8_record.md M asterixdb/asterix-om/src/main/java/org/apache/asterix/om/functions/BuiltinFunctions.java M asterixdb/asterix-runtime/src/main/java/org/apache/asterix/runtime/evaluators/functions/CastTypeEvaluator.java A asterixdb/asterix-runtime/src/main/java/org/apache/asterix/runtime/evaluators/functions/records/RecordReplaceDescriptor.java A asterixdb/asterix-runtime/src/main/java/org/apache/asterix/runtime/evaluators/functions/records/RecordReplaceEvaluator.java M asterixdb/asterix-runtime/src/main/java/org/apache/asterix/runtime/exceptions/TypeMismatchException.java M asterixdb/asterix-runtime/src/main/java/org/apache/asterix/runtime/functions/FunctionCollection.java M asterixdb/asterix-runtime/src/main/java/org/apache/asterix/runtime/functions/FunctionTypeInferers.java 16 files changed, 529 insertions(+), 1 deletion(-) Approvals: Anon. E. Moose #1000171: Jenkins: Verified; No violations found; ; Verified Murtadha Hubail: Looks good to me, but someone else must approve Dmitry Lychagin: Looks good to me, approved diff --git a/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/objects/ObjectsQueries.xml b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/objects/ObjectsQueries.xml index 796913d..184500e 100644 --- a/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/objects/ObjectsQueries.xml +++ b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/objects/ObjectsQueries.xml @@ -133,6 +133,14 @@ </compilation-unit> </test-case> <test-case FilePath="objects"> + <compilation-unit name="object_replace"> + <output-dir compare="Text">object_replace</output-dir> + <expected-error>function object-replace expects its 2nd input parameter to be type primitive</expected-error> + <expected-error>function object-replace expects its 2nd input parameter to be type primitive</expected-error> + <source-location>false</source-location> + </compilation-unit> + </test-case> + <test-case FilePath="objects"> <compilation-unit name="object_pairs"> <output-dir compare="Text">object_pairs</output-dir> </compilation-unit> diff --git a/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/objects/object_replace/object_replace.1.ddl.sqlpp b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/objects/object_replace/object_replace.1.ddl.sqlpp new file mode 100644 index 0000000..0bd154e --- /dev/null +++ b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/objects/object_replace/object_replace.1.ddl.sqlpp @@ -0,0 +1,49 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +/* + * Description : Testing object_replace under different queries. + * Expected Res : Success + */ + +drop dataverse TinySocial if exists; +create dataverse TinySocial; + +use TinySocial; + +create type TinySocial.TwitterUserType as +{ + `screen-name` : string, + lang : string, + friends_count : bigint, + statuses_count : bigint +}; + +create type TinySocial.TweetMessageType as closed { + tweetid : string, + user : TwitterUserType, + `sender-location` : point?, + `send-time` : datetime, + `referred-topics` : {{string}}, + `message-text` : string +}; + +create dataset TwitterUsers(TwitterUserType) primary key `screen-name`; + +create dataset TweetMessages(TweetMessageType) primary key tweetid; diff --git a/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/objects/object_replace/object_replace.2.update.sqlpp b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/objects/object_replace/object_replace.2.update.sqlpp new file mode 100644 index 0000000..a3baf1f --- /dev/null +++ b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/objects/object_replace/object_replace.2.update.sqlpp @@ -0,0 +1,29 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +/* + * Description : Testing object_replace under different queries. + * Expected Res : Success + */ + +use TinySocial; + +load dataset TwitterUsers using localfs ((`path`=`asterix_nc1://data/tinysocial/twu.adm`),(`format`=`adm`)); + +load dataset TweetMessages using localfs ((`path`=`asterix_nc1://data/tinysocial/twm.adm`),(`format`=`adm`)); diff --git a/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/objects/object_replace/object_replace.3.query.sqlpp b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/objects/object_replace/object_replace.3.query.sqlpp new file mode 100644 index 0000000..5718d8b --- /dev/null +++ b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/objects/object_replace/object_replace.3.query.sqlpp @@ -0,0 +1,57 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +/* + * Description : Testing object_replace under different queries. + * Expected Res : Success + */ + +use TinySocial; + +{ + "t1": [ + object_replace(missing, missing, missing) is missing, + object_replace(null, missing, missing) is missing, + object_replace(null, null, missing) is missing, + object_replace(null, null, null) is null, + object_replace({"a":1}, null, "z") is null, + object_replace("non-object", "a", "z") is null + ], + "t2": object_replace({"a":"1", "b":2},"1", 2), + "t3": object_replace({"a":"1", "b":2}, 2, "3"), + "t4": object_replace({"a":1}, 1, null), + "t5": object_replace({"a":1, "b":1}, 1, 2), + "t6": object_replace({"a":1.0, "b":1}, 1, 2), + + /* open type */ + "t7": ( + select value object_replace(u, "en", {"en":"native"}) + from TwitterUsers as u + order by u.screen-name + limit 1 + ), + + /* closed type */ + "t8": ( + select value object_replace(m, "1", "1000") + from TweetMessages as m + order by m.tweetid + limit 1 + ) +}; \ No newline at end of file diff --git a/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/objects/object_replace/object_replace.4.query.sqlpp b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/objects/object_replace/object_replace.4.query.sqlpp new file mode 100644 index 0000000..15c0bce --- /dev/null +++ b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/objects/object_replace/object_replace.4.query.sqlpp @@ -0,0 +1,27 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +/* + * Description : Testing object_replace under different queries. + * Expected Res : Failure + */ + +use TinySocial; + +select value object_replace({"a":1}, {"b":2}, "z"); \ No newline at end of file diff --git a/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/objects/object_replace/object_replace.5.query.sqlpp b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/objects/object_replace/object_replace.5.query.sqlpp new file mode 100644 index 0000000..a46a727 --- /dev/null +++ b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/objects/object_replace/object_replace.5.query.sqlpp @@ -0,0 +1,27 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +/* + * Description : Testing object_replace under different queries. + * Expected Res : Failure + */ + +use TinySocial; + +select value object_replace({"a":1}, ["b","c"], "z"); \ No newline at end of file diff --git a/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/objects/object_replace/object_replace.6.ddl.sqlpp b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/objects/object_replace/object_replace.6.ddl.sqlpp new file mode 100644 index 0000000..1dd03f3 --- /dev/null +++ b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/objects/object_replace/object_replace.6.ddl.sqlpp @@ -0,0 +1,25 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +/* + * Description : Testing object_replace under different queries. + * Expected Res : Success + */ + +drop dataverse TinySocial; \ No newline at end of file diff --git a/asterixdb/asterix-app/src/test/resources/runtimets/results/objects/object_replace/object_replace.3.adm b/asterixdb/asterix-app/src/test/resources/runtimets/results/objects/object_replace/object_replace.3.adm new file mode 100644 index 0000000..cd24aa1 --- /dev/null +++ b/asterixdb/asterix-app/src/test/resources/runtimets/results/objects/object_replace/object_replace.3.adm @@ -0,0 +1 @@ +{ "t1": [ true, true, true, true, true, true ], "t2": { "a": 2, "b": 2 }, "t3": { "a": "1", "b": "3" }, "t4": { "a": null }, "t5": { "a": 2, "b": 2 }, "t6": { "a": 2, "b": 2 }, "t7": [ { "screen-name": "ChangEwing_573", "lang": { "en": "native" }, "friends_count": 182, "statuses_count": 394, "name": "Chang Ewing", "followers_count": 32136 } ], "t8": [ { "tweetid": "1000", "user": { "screen-name": "NathanGiesen@211", "lang": "en", "friends_count": 39339, "statuses_count": 473, "name": "Nathan Giesen", "followers_count": 49416 }, "sender-location": point("47.44,80.65"), "send-time": datetime("2008-04-26T10:10:00.000Z"), "referred-topics": {{ "t-mobile", "customization" }}, "message-text": " love t-mobile its customization is good:)" } ] } \ No newline at end of file diff --git a/asterixdb/asterix-doc/src/main/markdown/builtins/8_record.md b/asterixdb/asterix-doc/src/main/markdown/builtins/8_record.md index f3c256e..df04b33 100644 --- a/asterixdb/asterix-doc/src/main/markdown/builtins/8_record.md +++ b/asterixdb/asterix-doc/src/main/markdown/builtins/8_record.md @@ -384,3 +384,42 @@ 1 } +### object_replace ### + * Syntax: + + object_replace(input_object, old_value, new_value) + + * Returns a new object that has the same fields as `input_object` with all occurrences of value `old_value` replaced by + `new_value` + * Arguments: + * `input_object` : an object value. + * `old_value` : a primitive type value to be replaced by `new_value`. + * `new_value` : a value to replace `old_value`. + * Return Value: + * A new object that has the same fields as `input_object` with all occurrences of value `old_value` replaced by + `new_value`, + * `missing` if any argument is a `missing` value, + * `null` if `input_object` or `old_value` is null, + * a type error will be raised if: + * `old_value` is not a primitive type value. + + * Example: + + object_replace( + { + "id": 1, + "project": "AsterixDB", + "address": {"city": "Irvine", "state": "CA"} + } + , "AsterixDB" + , "Apache AsterixDB" + ); + + * The expected result is: + + { + "id": 1, + "project": "Apache AsterixDB", + "location": {"city": "Irvine", "state": "CA"} + } + 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 a80ba28..3263ae1 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 @@ -226,6 +226,8 @@ new FunctionIdentifier(FunctionConstants.ASTERIX_NS, "object-rename", 3); public static final FunctionIdentifier RECORD_UNWRAP = new FunctionIdentifier(FunctionConstants.ASTERIX_NS, "object-unwrap", 1); + public static final FunctionIdentifier RECORD_REPLACE = + new FunctionIdentifier(FunctionConstants.ASTERIX_NS, "object-replace", 3); // numeric public static final FunctionIdentifier NUMERIC_UNARY_MINUS = @@ -1469,6 +1471,7 @@ addFunction(RECORD_REMOVE, OpenARecordTypeComputer.INSTANCE, true); addFunction(RECORD_RENAME, OpenARecordTypeComputer.INSTANCE, true); addFunction(RECORD_UNWRAP, AnyTypeComputer.INSTANCE, true); + addFunction(RECORD_REPLACE, OpenARecordTypeComputer.INSTANCE, true); // temporal type accessors addFunction(ACCESSOR_TEMPORAL_YEAR, AInt64TypeComputer.INSTANCE, true); diff --git a/asterixdb/asterix-runtime/src/main/java/org/apache/asterix/runtime/evaluators/functions/CastTypeEvaluator.java b/asterixdb/asterix-runtime/src/main/java/org/apache/asterix/runtime/evaluators/functions/CastTypeEvaluator.java index 524b2ed..22efe9a 100644 --- a/asterixdb/asterix-runtime/src/main/java/org/apache/asterix/runtime/evaluators/functions/CastTypeEvaluator.java +++ b/asterixdb/asterix-runtime/src/main/java/org/apache/asterix/runtime/evaluators/functions/CastTypeEvaluator.java @@ -33,7 +33,7 @@ import org.apache.hyracks.data.std.primitive.VoidPointable; import org.apache.hyracks.dataflow.common.data.accessors.IFrameTupleReference; -class CastTypeEvaluator implements IScalarEvaluator { +public class CastTypeEvaluator implements IScalarEvaluator { private final IScalarEvaluator argEvaluator; private final IPointable argPointable = new VoidPointable(); diff --git a/asterixdb/asterix-runtime/src/main/java/org/apache/asterix/runtime/evaluators/functions/records/RecordReplaceDescriptor.java b/asterixdb/asterix-runtime/src/main/java/org/apache/asterix/runtime/evaluators/functions/records/RecordReplaceDescriptor.java new file mode 100644 index 0000000..665f3f6 --- /dev/null +++ b/asterixdb/asterix-runtime/src/main/java/org/apache/asterix/runtime/evaluators/functions/records/RecordReplaceDescriptor.java @@ -0,0 +1,79 @@ +/* + * 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.records; + +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.IAType; +import org.apache.asterix.runtime.evaluators.base.AbstractScalarFunctionDynamicDescriptor; +import org.apache.asterix.runtime.functions.FunctionTypeInferers; +import org.apache.hyracks.algebricks.core.algebra.functions.FunctionIdentifier; +import org.apache.hyracks.algebricks.runtime.base.IScalarEvaluator; +import org.apache.hyracks.algebricks.runtime.base.IScalarEvaluatorFactory; +import org.apache.hyracks.api.context.IHyracksTaskContext; +import org.apache.hyracks.api.exceptions.HyracksDataException; + +public class RecordReplaceDescriptor extends AbstractScalarFunctionDynamicDescriptor { + + public static final IFunctionDescriptorFactory FACTORY = new IFunctionDescriptorFactory() { + @Override + public IFunctionDescriptor createFunctionDescriptor() { + return new RecordReplaceDescriptor(); + } + + @Override + public IFunctionTypeInferer createFunctionTypeInferer() { + return new FunctionTypeInferers.ArgsTypeInferer(); + } + }; + + private static final long serialVersionUID = 1L; + private IAType[] argTypes; + + @Override + public void setImmutableStates(Object... states) { + argTypes = new IAType[states.length]; + for (int i = 0; i < states.length; i++) { + argTypes[i] = (IAType) states[i]; + } + } + + @Override + public IScalarEvaluatorFactory createEvaluatorFactory(final IScalarEvaluatorFactory[] args) { + return new IScalarEvaluatorFactory() { + private static final long serialVersionUID = 1L; + + @Override + public IScalarEvaluator createScalarEvaluator(IHyracksTaskContext ctx) throws HyracksDataException { + final IScalarEvaluator[] argEvals = new IScalarEvaluator[args.length]; + for (int i = 0; i < args.length; i++) { + argEvals[i] = args[i].createScalarEvaluator(ctx); + } + return new RecordReplaceEvaluator(sourceLoc, argEvals[0], argEvals[1], argEvals[2], argTypes); + } + }; + } + + @Override + public FunctionIdentifier getIdentifier() { + return BuiltinFunctions.RECORD_REPLACE; + } +} diff --git a/asterixdb/asterix-runtime/src/main/java/org/apache/asterix/runtime/evaluators/functions/records/RecordReplaceEvaluator.java b/asterixdb/asterix-runtime/src/main/java/org/apache/asterix/runtime/evaluators/functions/records/RecordReplaceEvaluator.java new file mode 100644 index 0000000..5f004ef --- /dev/null +++ b/asterixdb/asterix-runtime/src/main/java/org/apache/asterix/runtime/evaluators/functions/records/RecordReplaceEvaluator.java @@ -0,0 +1,159 @@ +/* + * 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.records; + +import java.io.DataOutput; +import java.io.IOException; +import java.util.List; + +import org.apache.asterix.builders.RecordBuilder; +import org.apache.asterix.om.functions.BuiltinFunctions; +import org.apache.asterix.om.pointables.ARecordVisitablePointable; +import org.apache.asterix.om.pointables.base.DefaultOpenFieldType; +import org.apache.asterix.om.pointables.base.IVisitablePointable; +import org.apache.asterix.om.types.ATypeTag; +import org.apache.asterix.om.types.BuiltinType; +import org.apache.asterix.om.types.IAType; +import org.apache.asterix.om.types.hierachy.ATypeHierarchy; +import org.apache.asterix.runtime.evaluators.comparisons.ComparisonHelper; +import org.apache.asterix.runtime.evaluators.functions.CastTypeEvaluator; +import org.apache.asterix.runtime.evaluators.functions.PointableHelper; +import org.apache.asterix.runtime.exceptions.TypeMismatchException; +import org.apache.hyracks.algebricks.runtime.base.IScalarEvaluator; +import org.apache.hyracks.api.exceptions.HyracksDataException; +import org.apache.hyracks.api.exceptions.SourceLocation; +import org.apache.hyracks.data.std.api.IPointable; +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; + +class RecordReplaceEvaluator implements IScalarEvaluator { + + private final IPointable inputRecordPointable = new VoidPointable(); + private final IPointable oldValuePointable = new VoidPointable(); + private final IPointable newValuePointable = new VoidPointable(); + private final ArrayBackedValueStorage resultStorage = new ArrayBackedValueStorage(); + private final DataOutput resultOutput = resultStorage.getDataOutput(); + private final RecordBuilder outRecordBuilder = new RecordBuilder(); + private final VoidPointable existingValuePtr = new VoidPointable(); + private final VoidPointable oldValuePtr = new VoidPointable(); + private final IScalarEvaluator eval0; + private final IScalarEvaluator eval1; + private final IScalarEvaluator eval2; + private final ARecordVisitablePointable openRecordPointable; + private final CastTypeEvaluator inputRecordCaster; + private final CastTypeEvaluator newValueRecordCaster; + private final SourceLocation sourceLoc; + private final ComparisonHelper comparisonHelper; + + RecordReplaceEvaluator(SourceLocation sourceLoc, IScalarEvaluator eval0, IScalarEvaluator eval1, + IScalarEvaluator eval2, IAType[] argTypes) { + this.sourceLoc = sourceLoc; + this.eval0 = eval0; + this.eval1 = eval1; + this.eval2 = eval2; + openRecordPointable = new ARecordVisitablePointable(DefaultOpenFieldType.NESTED_OPEN_RECORD_TYPE); + inputRecordCaster = new CastTypeEvaluator(BuiltinType.ANY, argTypes[0], eval0); + newValueRecordCaster = new CastTypeEvaluator(BuiltinType.ANY, argTypes[2], eval2); + comparisonHelper = new ComparisonHelper(sourceLoc); + } + + @Override + public void evaluate(IFrameTupleReference tuple, IPointable result) throws HyracksDataException { + resultStorage.reset(); + eval0.evaluate(tuple, inputRecordPointable); + eval1.evaluate(tuple, oldValuePointable); + eval2.evaluate(tuple, newValuePointable); + if (containsMissing(inputRecordPointable, oldValuePointable, newValuePointable)) { + writeTypeTag(ATypeTag.SERIALIZED_MISSING_TYPE_TAG); + result.set(resultStorage); + return; + } + final ATypeTag inputObjectType = PointableHelper.getTypeTag(inputRecordPointable); + final ATypeTag oldValueType = PointableHelper.getTypeTag(oldValuePointable); + if (inputObjectType != ATypeTag.OBJECT || oldValueType == ATypeTag.NULL) { + writeTypeTag(ATypeTag.SERIALIZED_NULL_TYPE_TAG); + result.set(resultStorage); + return; + } + if (oldValueType.isDerivedType()) { + throw new TypeMismatchException(sourceLoc, BuiltinFunctions.RECORD_REPLACE, 1, oldValueType.serialize(), + "primitive"); + } + inputRecordCaster.evaluate(tuple, inputRecordPointable); + final ATypeTag newValueType = PointableHelper.getTypeTag(newValuePointable); + if (newValueType.isDerivedType()) { + newValueRecordCaster.evaluate(tuple, newValuePointable); + } + resultStorage.reset(); + buildOutputRecord(oldValueType); + result.set(resultStorage); + } + + private void buildOutputRecord(ATypeTag oldValueTypeTag) throws HyracksDataException { + openRecordPointable.set(inputRecordPointable); + outRecordBuilder.reset(DefaultOpenFieldType.NESTED_OPEN_RECORD_TYPE); + outRecordBuilder.init(); + final List<IVisitablePointable> fieldNames = openRecordPointable.getFieldNames(); + final List<IVisitablePointable> fieldValues = openRecordPointable.getFieldValues(); + for (int i = 0, fieldCount = fieldNames.size(); i < fieldCount; i++) { + final IVisitablePointable fieldName = fieldNames.get(i); + final IVisitablePointable fieldValue = fieldValues.get(i); + final ATypeTag existingValueTypeTag = PointableHelper.getTypeTag(fieldValue); + if (isEqual(existingValueTypeTag, fieldValue, oldValueTypeTag, oldValuePointable)) { + outRecordBuilder.addField(fieldName, newValuePointable); + } else { + outRecordBuilder.addField(fieldName, fieldValue); + } + } + outRecordBuilder.write(resultOutput, true); + } + + private boolean containsMissing(IPointable... pointables) { + for (int i = 0; i < pointables.length; i++) { + if (PointableHelper.getTypeTag(pointables[i]) == ATypeTag.MISSING) { + return true; + } + } + return false; + } + + private void writeTypeTag(byte typeTag) throws HyracksDataException { + try { + resultOutput.writeByte(typeTag); + } catch (IOException e) { + throw HyracksDataException.create(e); + } + } + + private boolean isEqual(ATypeTag typeTag1, IPointable value1, ATypeTag typeTag2, IPointable value2) + throws HyracksDataException { + if (!ATypeHierarchy.isCompatible(typeTag1, typeTag2)) { + return false; + } + setValuePointer(value1, existingValuePtr); + setValuePointer(value2, oldValuePtr); + return comparisonHelper.compare(typeTag1, typeTag2, existingValuePtr, oldValuePtr) == 0; + } + + private static void setValuePointer(IPointable src, IPointable value) { + value.set(src.getByteArray(), src.getStartOffset() + 1, src.getLength() - 1); + } +} diff --git a/asterixdb/asterix-runtime/src/main/java/org/apache/asterix/runtime/exceptions/TypeMismatchException.java b/asterixdb/asterix-runtime/src/main/java/org/apache/asterix/runtime/exceptions/TypeMismatchException.java index 9fe602b..6c80b67 100644 --- a/asterixdb/asterix-runtime/src/main/java/org/apache/asterix/runtime/exceptions/TypeMismatchException.java +++ b/asterixdb/asterix-runtime/src/main/java/org/apache/asterix/runtime/exceptions/TypeMismatchException.java @@ -57,4 +57,11 @@ toExpectedTypeString(expectedTypeTags), EnumDeserializer.ATYPETAGDESERIALIZER.deserialize(actualTypeTag)); } + + // Parameter type mistmatch. + public TypeMismatchException(SourceLocation sourceLoc, FunctionIdentifier fid, Integer i, byte actualTypeTag, + String expectedType) { + super(ErrorCode.TYPE_MISMATCH, sourceLoc, fid.getName(), indexToPosition(i), expectedType, + EnumDeserializer.ATYPETAGDESERIALIZER.deserialize(actualTypeTag)); + } } 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 271a7f0..58d32e4 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 @@ -285,6 +285,7 @@ import org.apache.asterix.runtime.evaluators.functions.records.RecordRemoveFieldsDescriptor; import org.apache.asterix.runtime.evaluators.functions.records.RecordRenameDescriptor; import org.apache.asterix.runtime.evaluators.functions.records.RecordUnwrapDescriptor; +import org.apache.asterix.runtime.evaluators.functions.records.RecordReplaceDescriptor; import org.apache.asterix.runtime.evaluators.functions.temporal.AdjustDateTimeForTimeZoneDescriptor; import org.apache.asterix.runtime.evaluators.functions.temporal.AdjustTimeForTimeZoneDescriptor; import org.apache.asterix.runtime.evaluators.functions.temporal.CalendarDuartionFromDateDescriptor; @@ -650,6 +651,7 @@ fc.addGenerated(RecordRemoveDescriptor.FACTORY); fc.addGenerated(RecordRenameDescriptor.FACTORY); fc.addGenerated(RecordUnwrapDescriptor.FACTORY); + fc.add(RecordReplaceDescriptor.FACTORY); // Spatial and temporal type accessors fc.addGenerated(TemporalYearAccessor.FACTORY); diff --git a/asterixdb/asterix-runtime/src/main/java/org/apache/asterix/runtime/functions/FunctionTypeInferers.java b/asterixdb/asterix-runtime/src/main/java/org/apache/asterix/runtime/functions/FunctionTypeInferers.java index e5a4301..45749a7 100644 --- a/asterixdb/asterix-runtime/src/main/java/org/apache/asterix/runtime/functions/FunctionTypeInferers.java +++ b/asterixdb/asterix-runtime/src/main/java/org/apache/asterix/runtime/functions/FunctionTypeInferers.java @@ -295,4 +295,20 @@ fd.setImmutableStates((Object[]) argRecordTypes); } } + + public static final class ArgsTypeInferer implements IFunctionTypeInferer { + @Override + public void infer(ILogicalExpression expr, IFunctionDescriptor fd, IVariableTypeEnvironment context, + CompilerProperties compilerProps) throws AlgebricksException { + final AbstractFunctionCallExpression f = (AbstractFunctionCallExpression) expr; + final List<Mutable<ILogicalExpression>> args = f.getArguments(); + final IAType[] types = new IAType[f.getArguments().size()]; + for (int i = 0; i < types.length; i++) { + final IAType argType = (IAType) context.getType(args.get(i).getValue()); + final IAType actualType = TypeComputeUtils.getActualType(argType); + types[i] = actualType; + } + fd.setImmutableStates((Object[]) types); + } + } } -- To view, visit https://asterix-gerrit.ics.uci.edu/2708 To unsubscribe, visit https://asterix-gerrit.ics.uci.edu/settings Gerrit-MessageType: merged Gerrit-Change-Id: I2907f827a1dc5bb35f340bfd25d51e1fdd6fde20 Gerrit-PatchSet: 7 Gerrit-Project: asterixdb Gerrit-Branch: master Gerrit-Owner: Murtadha Hubail <[email protected]> Gerrit-Reviewer: Anon. E. Moose #1000171 Gerrit-Reviewer: Dmitry Lychagin <[email protected]> Gerrit-Reviewer: Jenkins <[email protected]> Gerrit-Reviewer: Murtadha Hubail <[email protected]> Gerrit-Reviewer: Till Westmann <[email protected]>
