Repository: asterixdb
Updated Branches:
  refs/heads/master 735532e43 -> bacf0c595


ASTERIXDB-1901 Fix IntroduceDynamicTypeCastForExternalFunctionRule

1. Instead of pattern matching, now we will inspect every paramter of
   UDF. If there is a type mismatch, a cast function will be added.
2. Fixed the issue that type casting only applies to the first argument.
3. Added test case for this.

Change-Id: I6f44b2460ae3322fc52451e7939b6b5e711790a7
Reviewed-on: https://asterix-gerrit.ics.uci.edu/1730
Reviewed-by: Yingyi Bu <buyin...@gmail.com>
Sonar-Qube: Jenkins <jenk...@fulliautomatix.ics.uci.edu>
Tested-by: Jenkins <jenk...@fulliautomatix.ics.uci.edu>


Project: http://git-wip-us.apache.org/repos/asf/asterixdb/repo
Commit: http://git-wip-us.apache.org/repos/asf/asterixdb/commit/bacf0c59
Tree: http://git-wip-us.apache.org/repos/asf/asterixdb/tree/bacf0c59
Diff: http://git-wip-us.apache.org/repos/asf/asterixdb/diff/bacf0c59

Branch: refs/heads/master
Commit: bacf0c595eb0172522ce88a2600a6deaaafbc98b
Parents: 735532e
Author: Xikui Wang <xkk...@gmail.com>
Authored: Thu May 11 17:08:23 2017 -0700
Committer: Yingyi Bu <buyin...@gmail.com>
Committed: Thu May 11 18:26:15 2017 -0700

----------------------------------------------------------------------
 ...eDynamicTypeCastForExternalFunctionRule.java | 140 ++++++++-----------
 .../upperCase/upperCase.1.ddl.aql               |  27 ++++
 .../upperCase/upperCase.2.lib.aql               |  19 +++
 .../upperCase/upperCase.3.query.aql             |  25 ++++
 .../upperCase/upperCase.4.lib.aql               |  19 +++
 .../external-library/upperCase/upperCase.1.adm  |   2 +
 .../test/resources/runtimets/testsuite_it.xml   |   5 +
 7 files changed, 154 insertions(+), 83 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/asterixdb/blob/bacf0c59/asterixdb/asterix-algebra/src/main/java/org/apache/asterix/optimizer/rules/IntroduceDynamicTypeCastForExternalFunctionRule.java
----------------------------------------------------------------------
diff --git 
a/asterixdb/asterix-algebra/src/main/java/org/apache/asterix/optimizer/rules/IntroduceDynamicTypeCastForExternalFunctionRule.java
 
b/asterixdb/asterix-algebra/src/main/java/org/apache/asterix/optimizer/rules/IntroduceDynamicTypeCastForExternalFunctionRule.java
index a813285..c9a873b 100644
--- 
a/asterixdb/asterix-algebra/src/main/java/org/apache/asterix/optimizer/rules/IntroduceDynamicTypeCastForExternalFunctionRule.java
+++ 
b/asterixdb/asterix-algebra/src/main/java/org/apache/asterix/optimizer/rules/IntroduceDynamicTypeCastForExternalFunctionRule.java
@@ -19,26 +19,25 @@
 
 package org.apache.asterix.optimizer.rules;
 
-import java.util.ArrayList;
-import java.util.List;
-
+import org.apache.asterix.lang.common.util.FunctionUtil;
 import org.apache.asterix.metadata.functions.ExternalScalarFunctionInfo;
 import org.apache.asterix.om.functions.BuiltinFunctions;
+import org.apache.asterix.om.typecomputer.base.TypeCastUtils;
 import org.apache.asterix.om.types.ARecordType;
 import org.apache.asterix.om.types.AUnionType;
+import org.apache.asterix.om.types.BuiltinType;
 import org.apache.asterix.om.types.IAType;
 import org.apache.asterix.om.utils.NonTaggedFormatUtil;
 import org.apache.commons.lang3.mutable.Mutable;
+import org.apache.commons.lang3.mutable.MutableObject;
 import org.apache.hyracks.algebricks.common.exceptions.AlgebricksException;
 import org.apache.hyracks.algebricks.core.algebra.base.ILogicalExpression;
 import org.apache.hyracks.algebricks.core.algebra.base.ILogicalOperator;
 import org.apache.hyracks.algebricks.core.algebra.base.IOptimizationContext;
 import org.apache.hyracks.algebricks.core.algebra.base.LogicalExpressionTag;
 import org.apache.hyracks.algebricks.core.algebra.base.LogicalOperatorTag;
-import org.apache.hyracks.algebricks.core.algebra.base.LogicalVariable;
-import 
org.apache.hyracks.algebricks.core.algebra.expressions.IVariableTypeEnvironment;
+import 
org.apache.hyracks.algebricks.core.algebra.expressions.AbstractFunctionCallExpression;
 import 
org.apache.hyracks.algebricks.core.algebra.expressions.ScalarFunctionCallExpression;
-import org.apache.hyracks.algebricks.core.algebra.functions.FunctionIdentifier;
 import 
org.apache.hyracks.algebricks.core.algebra.operators.logical.AbstractLogicalOperator;
 import 
org.apache.hyracks.algebricks.core.algebra.operators.logical.AssignOperator;
 import org.apache.hyracks.algebricks.core.rewriter.base.IAlgebraicRewriteRule;
@@ -56,94 +55,69 @@ public class 
IntroduceDynamicTypeCastForExternalFunctionRule implements IAlgebra
         return false;
     }
 
-    @Override
-    public boolean rewritePost(Mutable<ILogicalOperator> opRef, 
IOptimizationContext context)
-            throws AlgebricksException {
-        /**
-         * pattern match: distribute_result - project - assign (external 
function call) - assign (open_record_constructor)
-         * resulting plan: distribute_result - project - assign (external 
function call) - assign (cast-record) - assign(open_record_constructor)
-         */
-        AbstractLogicalOperator op1 = (AbstractLogicalOperator) 
opRef.getValue();
-        if (op1.getOperatorTag() != LogicalOperatorTag.DISTRIBUTE_RESULT) {
+    private boolean rewriteFunctionArgs(ILogicalOperator op, 
Mutable<ILogicalExpression> expRef,
+            IOptimizationContext context) throws AlgebricksException {
+        ILogicalExpression expr = expRef.getValue();
+        if (expr.getExpressionTag() != LogicalExpressionTag.FUNCTION_CALL
+                || !(expr instanceof ScalarFunctionCallExpression)) {
             return false;
         }
-        AbstractLogicalOperator op2 = (AbstractLogicalOperator) 
op1.getInputs().get(0).getValue();
-        if (op2.getOperatorTag() != LogicalOperatorTag.PROJECT) {
-            return false;
+        ScalarFunctionCallExpression funcCallExpr = 
(ScalarFunctionCallExpression) expr;
+        boolean changed = false;
+        IAType inputRecordType;
+        ARecordType requiredRecordType;
+        for (int iter1 = 0; iter1 < funcCallExpr.getArguments().size(); 
iter1++) {
+            inputRecordType = (IAType) op.computeOutputTypeEnvironment(context)
+                    
.getType(funcCallExpr.getArguments().get(iter1).getValue());
+            if (!(((ExternalScalarFunctionInfo) 
funcCallExpr.getFunctionInfo()).getArgumenTypes()
+                    .get(iter1) instanceof ARecordType)) {
+                continue;
+            }
+            requiredRecordType = (ARecordType) ((ExternalScalarFunctionInfo) 
funcCallExpr.getFunctionInfo())
+                    .getArgumenTypes().get(iter1);
+            /**
+             * the input record type can be an union type
+             * for the case when it comes from a subplan or left-outer join
+             */
+            boolean checkUnknown = false;
+            while (NonTaggedFormatUtil.isOptional(inputRecordType)) {
+                /** while-loop for the case there is a nested multi-level 
union */
+                inputRecordType = ((AUnionType) 
inputRecordType).getActualType();
+                checkUnknown = true;
+            }
+            boolean castFlag = 
!IntroduceDynamicTypeCastRule.compatible(requiredRecordType, inputRecordType);
+            if (castFlag || checkUnknown) {
+                AbstractFunctionCallExpression castFunc = new 
ScalarFunctionCallExpression(
+                        
FunctionUtil.getFunctionInfo(BuiltinFunctions.CAST_TYPE));
+                
castFunc.getArguments().add(funcCallExpr.getArguments().get(iter1));
+                TypeCastUtils.setRequiredAndInputTypes(castFunc, 
requiredRecordType, inputRecordType);
+                funcCallExpr.getArguments().set(iter1, new 
MutableObject<>(castFunc));
+                changed = changed || true;
+            }
         }
-        AbstractLogicalOperator op3 = (AbstractLogicalOperator) 
op2.getInputs().get(0).getValue();
-        if (op3.getOperatorTag() != LogicalOperatorTag.ASSIGN) {
+        return changed;
+    }
+
+    @Override
+    public boolean rewritePost(Mutable<ILogicalOperator> opRef, 
IOptimizationContext context)
+            throws AlgebricksException {
+        AbstractLogicalOperator op = (AbstractLogicalOperator) 
opRef.getValue();
+        if (op.getOperatorTag() != LogicalOperatorTag.ASSIGN) {
             return false;
         }
-        AbstractLogicalOperator op4 = (AbstractLogicalOperator) 
op3.getInputs().get(0).getValue();
-        if (op4.getOperatorTag() != LogicalOperatorTag.ASSIGN) {
+        AssignOperator assignOp = (AssignOperator) op;
+        ILogicalExpression assignExpr = 
assignOp.getExpressions().get(0).getValue();
+        if (assignExpr.getExpressionTag() != 
LogicalExpressionTag.FUNCTION_CALL) {
             return false;
         }
-
-        // Op1 : assign (external function call), Op2 : assign 
(open_record_constructor)
-        AssignOperator assignOp1 = (AssignOperator) op3;
-        AssignOperator assignOp2 = (AssignOperator) op4;
-
-        // Checks whether open-record-constructor is called to create a record 
in the first assign operator - assignOp2
-        FunctionIdentifier fid = null;
-        ILogicalExpression assignExpr = 
assignOp2.getExpressions().get(0).getValue();
-        if (assignExpr.getExpressionTag() == 
LogicalExpressionTag.FUNCTION_CALL) {
-            ScalarFunctionCallExpression funcExpr =
-                    (ScalarFunctionCallExpression) 
assignOp2.getExpressions().get(0).getValue();
-            fid = funcExpr.getFunctionIdentifier();
-
-            if (fid != BuiltinFunctions.OPEN_RECORD_CONSTRUCTOR) {
-                return false;
-            }
-        } else {
+        if (BuiltinFunctions.getBuiltinFunctionIdentifier(
+                ((AbstractFunctionCallExpression) 
assignExpr).getFunctionIdentifier()) != null) {
             return false;
         }
-
-        // Checks whether an external function is called in the second assign 
operator - assignOp1
-        assignExpr = assignOp1.getExpressions().get(0).getValue();
-        ScalarFunctionCallExpression funcExpr = null;
-        if (assignExpr.getExpressionTag() == 
LogicalExpressionTag.FUNCTION_CALL) {
-            funcExpr = (ScalarFunctionCallExpression) 
assignOp1.getExpressions().get(0).getValue();
-            fid = funcExpr.getFunctionIdentifier();
-
-            // Checks whether this is an internal function call. Then, we 
return false.
-            if (BuiltinFunctions.getBuiltinFunctionIdentifier(fid) != null) {
-                return false;
-            }
-
+        if (op.acceptExpressionTransform(exprRef -> rewriteFunctionArgs(op, 
exprRef, context))) {
+            return true;
         } else {
             return false;
         }
-
-        ExternalScalarFunctionInfo finfo = (ExternalScalarFunctionInfo) 
funcExpr.getFunctionInfo();
-        ARecordType requiredRecordType = (ARecordType) 
finfo.getArgumenTypes().get(0);
-
-        List<LogicalVariable> recordVar = new ArrayList<LogicalVariable>();
-        recordVar.addAll(assignOp2.getVariables());
-
-        IVariableTypeEnvironment env = 
assignOp2.computeOutputTypeEnvironment(context);
-        IAType inputRecordType = (IAType) env.getVarType(recordVar.get(0));
-
-        /** the input record type can be an union type -- for the case when it 
comes from a subplan or left-outer join */
-        boolean checkUnknown = false;
-        while (NonTaggedFormatUtil.isOptional(inputRecordType)) {
-            /** while-loop for the case there is a nested multi-level union */
-            inputRecordType = ((AUnionType) inputRecordType).getActualType();
-            checkUnknown = true;
-        }
-
-        /** see whether the input record type needs to be casted */
-        boolean cast = 
!IntroduceDynamicTypeCastRule.compatible(requiredRecordType, inputRecordType);
-
-        if (checkUnknown) {
-            recordVar.set(0, 
IntroduceDynamicTypeCastRule.addWrapperFunction(requiredRecordType, 
recordVar.get(0),
-                    assignOp1, context, BuiltinFunctions.CHECK_UNKNOWN));
-        }
-        if (cast) {
-            
IntroduceDynamicTypeCastRule.addWrapperFunction(requiredRecordType, 
recordVar.get(0), assignOp1, context,
-                    BuiltinFunctions.CAST_TYPE);
-        }
-        return cast || checkUnknown;
     }
-
 }

http://git-wip-us.apache.org/repos/asf/asterixdb/blob/bacf0c59/asterixdb/asterix-app/src/test/resources/runtimets/queries/external-library/upperCase/upperCase.1.ddl.aql
----------------------------------------------------------------------
diff --git 
a/asterixdb/asterix-app/src/test/resources/runtimets/queries/external-library/upperCase/upperCase.1.ddl.aql
 
b/asterixdb/asterix-app/src/test/resources/runtimets/queries/external-library/upperCase/upperCase.1.ddl.aql
new file mode 100644
index 0000000..2dcd24d
--- /dev/null
+++ 
b/asterixdb/asterix-app/src/test/resources/runtimets/queries/external-library/upperCase/upperCase.1.ddl.aql
@@ -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.
+ */
+drop dataverse externallibtest if exists;
+create dataverse externallibtest;
+use dataverse externallibtest;
+
+create type TextType if not exists as open {
+    id: int32,
+    text: string
+};
+

http://git-wip-us.apache.org/repos/asf/asterixdb/blob/bacf0c59/asterixdb/asterix-app/src/test/resources/runtimets/queries/external-library/upperCase/upperCase.2.lib.aql
----------------------------------------------------------------------
diff --git 
a/asterixdb/asterix-app/src/test/resources/runtimets/queries/external-library/upperCase/upperCase.2.lib.aql
 
b/asterixdb/asterix-app/src/test/resources/runtimets/queries/external-library/upperCase/upperCase.2.lib.aql
new file mode 100644
index 0000000..d1e0e87
--- /dev/null
+++ 
b/asterixdb/asterix-app/src/test/resources/runtimets/queries/external-library/upperCase/upperCase.2.lib.aql
@@ -0,0 +1,19 @@
+/*
+ * 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.
+ */
+install externallibtest testlib 
target/data/externallib/asterix-external-data-testlib.zip
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/asterixdb/blob/bacf0c59/asterixdb/asterix-app/src/test/resources/runtimets/queries/external-library/upperCase/upperCase.3.query.aql
----------------------------------------------------------------------
diff --git 
a/asterixdb/asterix-app/src/test/resources/runtimets/queries/external-library/upperCase/upperCase.3.query.aql
 
b/asterixdb/asterix-app/src/test/resources/runtimets/queries/external-library/upperCase/upperCase.3.query.aql
new file mode 100644
index 0000000..61ed394
--- /dev/null
+++ 
b/asterixdb/asterix-app/src/test/resources/runtimets/queries/external-library/upperCase/upperCase.3.query.aql
@@ -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.
+ */
+use dataverse externallibtest;
+
+let $i:={"id":1, "text":"lower text"}
+return testlib#toUpper($i);
+
+let $i:=testlib#toUpper({"id":1, "text":"lower text"})
+return $i;

http://git-wip-us.apache.org/repos/asf/asterixdb/blob/bacf0c59/asterixdb/asterix-app/src/test/resources/runtimets/queries/external-library/upperCase/upperCase.4.lib.aql
----------------------------------------------------------------------
diff --git 
a/asterixdb/asterix-app/src/test/resources/runtimets/queries/external-library/upperCase/upperCase.4.lib.aql
 
b/asterixdb/asterix-app/src/test/resources/runtimets/queries/external-library/upperCase/upperCase.4.lib.aql
new file mode 100644
index 0000000..86af80f
--- /dev/null
+++ 
b/asterixdb/asterix-app/src/test/resources/runtimets/queries/external-library/upperCase/upperCase.4.lib.aql
@@ -0,0 +1,19 @@
+/*
+ * 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.
+ */
+uninstall externallibtest testlib
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/asterixdb/blob/bacf0c59/asterixdb/asterix-app/src/test/resources/runtimets/results/external-library/upperCase/upperCase.1.adm
----------------------------------------------------------------------
diff --git 
a/asterixdb/asterix-app/src/test/resources/runtimets/results/external-library/upperCase/upperCase.1.adm
 
b/asterixdb/asterix-app/src/test/resources/runtimets/results/external-library/upperCase/upperCase.1.adm
new file mode 100644
index 0000000..f475435
--- /dev/null
+++ 
b/asterixdb/asterix-app/src/test/resources/runtimets/results/external-library/upperCase/upperCase.1.adm
@@ -0,0 +1,2 @@
+{ "id": -1, "text": "LOWER TEXT" }
+{ "id": -1, "text": "LOWER TEXT" }
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/asterixdb/blob/bacf0c59/asterixdb/asterix-app/src/test/resources/runtimets/testsuite_it.xml
----------------------------------------------------------------------
diff --git 
a/asterixdb/asterix-app/src/test/resources/runtimets/testsuite_it.xml 
b/asterixdb/asterix-app/src/test/resources/runtimets/testsuite_it.xml
index 6609070..ed0ae7a 100644
--- a/asterixdb/asterix-app/src/test/resources/runtimets/testsuite_it.xml
+++ b/asterixdb/asterix-app/src/test/resources/runtimets/testsuite_it.xml
@@ -42,6 +42,11 @@
         <output-dir compare="Text">getCapital</output-dir>
       </compilation-unit>
     </test-case>
+    <test-case FilePath="external-library">
+      <compilation-unit name="upperCase">
+        <output-dir compare="Text">upperCase</output-dir>
+      </compilation-unit>
+    </test-case>
   </test-group>
   <test-group name="feeds">
     <test-case FilePath="feeds">

Reply via email to