[NO ISSUE][COMP] Support skip-index for IN operator

Details:
- Support skip-index hint for IN operator:
  WHERE indexed_field /*+ skip-index */ IN [...]

Change-Id: Ib76f43b53c2bc1b0f5c12e03f555a8d2c1d75701
Reviewed-on: https://asterix-gerrit.ics.uci.edu/c/asterixdb/+/7603
Integration-Tests: Jenkins <jenkins@fulliautomatix.ics.uci.edu>
Tested-by: Dmitry Lychagin <dmitry.lychagin@couchbase.com>
Reviewed-by: Dmitry Lychagin <dmitry.lychagin@couchbase.com>
Reviewed-by: Ali Alsuliman <ali.al.solaiman@gmail.com>
diff --git a/asterixdb/asterix-algebra/src/main/java/org/apache/asterix/optimizer/rules/DisjunctivePredicateToJoinRule.java b/asterixdb/asterix-algebra/src/main/java/org/apache/asterix/optimizer/rules/DisjunctivePredicateToJoinRule.java
index 916fd75..fe03d75 100644
--- a/asterixdb/asterix-algebra/src/main/java/org/apache/asterix/optimizer/rules/DisjunctivePredicateToJoinRule.java
+++ b/asterixdb/asterix-algebra/src/main/java/org/apache/asterix/optimizer/rules/DisjunctivePredicateToJoinRule.java
@@ -18,9 +18,13 @@
  */
 package org.apache.asterix.optimizer.rules;
 
+import java.util.Collections;
+import java.util.HashMap;
 import java.util.HashSet;
 import java.util.List;
+import java.util.Map;
 
+import org.apache.asterix.common.annotations.SkipSecondaryIndexSearchExpressionAnnotation;
 import org.apache.asterix.metadata.declared.MetadataProvider;
 import org.apache.asterix.om.base.AOrderedList;
 import org.apache.asterix.om.constants.AsterixConstantValue;
@@ -39,6 +43,7 @@
 import org.apache.hyracks.algebricks.core.algebra.expressions.AbstractFunctionCallExpression;
 import org.apache.hyracks.algebricks.core.algebra.expressions.BroadcastExpressionAnnotation;
 import org.apache.hyracks.algebricks.core.algebra.expressions.ConstantExpression;
+import org.apache.hyracks.algebricks.core.algebra.expressions.IExpressionAnnotation;
 import org.apache.hyracks.algebricks.core.algebra.expressions.IndexedNLJoinExpressionAnnotation;
 import org.apache.hyracks.algebricks.core.algebra.expressions.ScalarFunctionCallExpression;
 import org.apache.hyracks.algebricks.core.algebra.expressions.UnnestingFunctionCallExpression;
@@ -89,6 +94,7 @@
         VariableReferenceExpression varEx = null;
         IAType valType = null;
         HashSet<AsterixConstantValue> values = new HashSet<AsterixConstantValue>();
+        Map<Object, IExpressionAnnotation> allAnnotations = Collections.emptyMap();
 
         for (Mutable<ILogicalExpression> arg : args) {
             AbstractFunctionCallExpression fctCall;
@@ -128,6 +134,12 @@
             if (!(haveVar && haveConst)) {
                 return false;
             }
+            if (!fctCall.getAnnotations().isEmpty()) {
+                if (allAnnotations.isEmpty()) {
+                    allAnnotations = new HashMap<>();
+                }
+                allAnnotations.putAll(fctCall.getAnnotations());
+            }
         }
 
         SourceLocation sourceLoc = select.getSourceLocation();
@@ -142,8 +154,9 @@
 
         ILogicalExpression cExp = new ConstantExpression(new AsterixConstantValue(list));
         Mutable<ILogicalExpression> mutCExp = new MutableObject<>(cExp);
-        IFunctionInfo scanFctInfo = BuiltinFunctions.getAsterixFunctionInfo(BuiltinFunctions.SCAN_COLLECTION);
-        UnnestingFunctionCallExpression scanExp = new UnnestingFunctionCallExpression(scanFctInfo, mutCExp);
+        UnnestingFunctionCallExpression scanExp = new UnnestingFunctionCallExpression(
+                BuiltinFunctions.getAsterixFunctionInfo(BuiltinFunctions.SCAN_COLLECTION));
+        scanExp.getArguments().add(mutCExp);
         scanExp.setSourceLocation(sourceLoc);
         LogicalVariable scanVar = context.newVar();
         UnnestOperator unn = new UnnestOperator(scanVar, new MutableObject<>(scanExp));
@@ -158,11 +171,14 @@
         scanVarRef.setSourceLocation(sourceLoc);
         eqExp.getArguments().add(new MutableObject<>(scanVarRef));
         eqExp.getArguments().add(new MutableObject<>(varEx.cloneExpression()));
-        eqExp.getAnnotations().put(IndexedNLJoinExpressionAnnotation.INSTANCE,
-                IndexedNLJoinExpressionAnnotation.INSTANCE);
+        if (!allAnnotations.containsKey(SkipSecondaryIndexSearchExpressionAnnotation.INSTANCE)) {
+            eqExp.getAnnotations().put(IndexedNLJoinExpressionAnnotation.INSTANCE,
+                    IndexedNLJoinExpressionAnnotation.INSTANCE);
+        }
         BroadcastExpressionAnnotation bcast = new BroadcastExpressionAnnotation();
         bcast.setObject(BroadcastExpressionAnnotation.BroadcastSide.LEFT); // Broadcast the OR predicates branch.
         eqExp.getAnnotations().put(bcast, bcast);
+        eqExp.getAnnotations().putAll(allAnnotations);
 
         InnerJoinOperator jOp = new InnerJoinOperator(new MutableObject<>(eqExp));
         jOp.setSourceLocation(sourceLoc);
diff --git a/asterixdb/asterix-algebra/src/main/java/org/apache/asterix/translator/SqlppExpressionToPlanTranslator.java b/asterixdb/asterix-algebra/src/main/java/org/apache/asterix/translator/SqlppExpressionToPlanTranslator.java
index 5363ae2..2cd402f 100644
--- a/asterixdb/asterix-algebra/src/main/java/org/apache/asterix/translator/SqlppExpressionToPlanTranslator.java
+++ b/asterixdb/asterix-algebra/src/main/java/org/apache/asterix/translator/SqlppExpressionToPlanTranslator.java
@@ -109,6 +109,7 @@
 import org.apache.hyracks.algebricks.core.algebra.expressions.AbstractLogicalExpression;
 import org.apache.hyracks.algebricks.core.algebra.expressions.AggregateFunctionCallExpression;
 import org.apache.hyracks.algebricks.core.algebra.expressions.ConstantExpression;
+import org.apache.hyracks.algebricks.core.algebra.expressions.IExpressionAnnotation;
 import org.apache.hyracks.algebricks.core.algebra.expressions.ScalarFunctionCallExpression;
 import org.apache.hyracks.algebricks.core.algebra.expressions.UnnestingFunctionCallExpression;
 import org.apache.hyracks.algebricks.core.algebra.expressions.VariableReferenceExpression;
@@ -978,7 +979,9 @@
 
         QuantifiedPair qp = qe.getQuantifiedList().get(0);
         VariableExpr varExpr = qp.getVarExpr();
-        List<Expression> operandExprs = ((OperatorExpr) qe.getSatisfiesExpr()).getExprList();
+        OperatorExpr condExpr = (OperatorExpr) qe.getSatisfiesExpr();
+        List<IExpressionAnnotation> condExprHints = condExpr.getHints();
+        List<Expression> operandExprs = condExpr.getExprList();
         int varIdx = operandExprs.indexOf(varExpr);
         Expression operandExpr = operandExprs.get(1 - varIdx);
 
@@ -1011,7 +1014,7 @@
                             throw new CompilationException(ErrorCode.COMPILATION_ILLEGAL_STATE, sourceLoc,
                                     itemExpr.getKind());
                     }
-                    ILogicalExpression eqExpr = createEqExpr(operandVar, inValue, sourceLoc);
+                    ILogicalExpression eqExpr = createEqExpr(operandVar, inValue, condExprHints, sourceLoc);
                     disjuncts.add(new MutableObject<>(eqExpr));
                 }
                 break;
@@ -1022,7 +1025,7 @@
                 inVarCursor.reset();
                 while (inVarCursor.next()) {
                     IAObject inValue = inVarCursor.get();
-                    ILogicalExpression eqExpr = createEqExpr(operandVar, inValue, sourceLoc);
+                    ILogicalExpression eqExpr = createEqExpr(operandVar, inValue, condExprHints, sourceLoc);
                     disjuncts.add(new MutableObject<>(eqExpr));
                 }
                 break;
@@ -1030,25 +1033,25 @@
                 throw new IllegalStateException(String.valueOf(inExpr.getKind()));
         }
 
-        MutableObject<ILogicalExpression> condExpr;
+        MutableObject<ILogicalExpression> disjunctiveExpr;
         if (disjuncts.size() == 1) {
-            condExpr = disjuncts.get(0);
+            disjunctiveExpr = disjuncts.get(0);
         } else {
             AbstractFunctionCallExpression orExpr =
                     createFunctionCallExpressionForBuiltinOperator(OperatorType.OR, sourceLoc);
             orExpr.getArguments().addAll(disjuncts);
-            condExpr = new MutableObject<>(orExpr);
+            disjunctiveExpr = new MutableObject<>(orExpr);
         }
 
         LogicalVariable assignVar = context.newVar();
-        AssignOperator assignOp = new AssignOperator(assignVar, condExpr);
+        AssignOperator assignOp = new AssignOperator(assignVar, disjunctiveExpr);
         assignOp.getInputs().add(topOp);
         assignOp.setSourceLocation(sourceLoc);
         return new Pair<>(assignOp, assignVar);
     }
 
-    private ILogicalExpression createEqExpr(LogicalVariable lhsVar, IAObject rhsValue, SourceLocation sourceLoc)
-            throws CompilationException {
+    private ILogicalExpression createEqExpr(LogicalVariable lhsVar, IAObject rhsValue,
+            List<IExpressionAnnotation> hints, SourceLocation sourceLoc) throws CompilationException {
         VariableReferenceExpression lhsExpr = new VariableReferenceExpression(lhsVar);
         lhsExpr.setSourceLocation(sourceLoc);
         ILogicalExpression rhsExpr = translateConstantValue(rhsValue, sourceLoc);
@@ -1056,6 +1059,11 @@
                 createFunctionCallExpressionForBuiltinOperator(OperatorType.EQ, sourceLoc);
         opExpr.getArguments().add(new MutableObject<>(lhsExpr));
         opExpr.getArguments().add(new MutableObject<>(rhsExpr));
+        if (hints != null) {
+            for (IExpressionAnnotation hint : hints) {
+                opExpr.getAnnotations().put(hint, hint);
+            }
+        }
         return opExpr;
     }
 
diff --git a/asterixdb/asterix-app/src/test/resources/optimizerts/queries/skip-index/skip-secondary-btree-index-3.sqlpp b/asterixdb/asterix-app/src/test/resources/optimizerts/queries/skip-index/skip-secondary-btree-index-3.sqlpp
new file mode 100644
index 0000000..d634a26
--- /dev/null
+++ b/asterixdb/asterix-app/src/test/resources/optimizerts/queries/skip-index/skip-secondary-btree-index-3.sqlpp
@@ -0,0 +1,43 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+/*
+ * Description  : Skip secondary index for IN operator
+ * Expected Res : Success
+ */
+
+drop  dataverse test if exists;
+create  dataverse test;
+
+use test;
+
+create type test.TestType as
+{
+  id : integer,
+  fname : string,
+  lname : string
+};
+
+create  dataset testdst(TestType) primary key id;
+
+create  index sec_Idx  on testdst (fname) type btree;
+
+select element emp
+from  testdst as emp
+where emp.fname /*+ skip-index */ in ['Max', 'Roger']
+;
diff --git a/asterixdb/asterix-app/src/test/resources/optimizerts/results/skip-index/skip-secondary-btree-index-3.plan b/asterixdb/asterix-app/src/test/resources/optimizerts/results/skip-index/skip-secondary-btree-index-3.plan
new file mode 100644
index 0000000..87bb65f
--- /dev/null
+++ b/asterixdb/asterix-app/src/test/resources/optimizerts/results/skip-index/skip-secondary-btree-index-3.plan
@@ -0,0 +1,15 @@
+-- DISTRIBUTE_RESULT  |PARTITIONED|
+  -- ONE_TO_ONE_EXCHANGE  |PARTITIONED|
+    -- STREAM_PROJECT  |PARTITIONED|
+      -- ONE_TO_ONE_EXCHANGE  |PARTITIONED|
+        -- HYBRID_HASH_JOIN [$$14][$$17]  |PARTITIONED|
+          -- ONE_TO_ONE_EXCHANGE  |PARTITIONED|
+            -- ASSIGN  |PARTITIONED|
+              -- STREAM_PROJECT  |PARTITIONED|
+                -- ONE_TO_ONE_EXCHANGE  |PARTITIONED|
+                  -- DATASOURCE_SCAN  |PARTITIONED|
+                    -- ONE_TO_ONE_EXCHANGE  |PARTITIONED|
+                      -- EMPTY_TUPLE_SOURCE  |PARTITIONED|
+          -- BROADCAST_EXCHANGE  |PARTITIONED|
+            -- UNNEST  |UNPARTITIONED|
+              -- EMPTY_TUPLE_SOURCE  |UNPARTITIONED|
\ No newline at end of file
diff --git a/asterixdb/asterix-lang-sqlpp/src/main/java/org/apache/asterix/lang/sqlpp/rewrites/visitor/OperatorExpressionVisitor.java b/asterixdb/asterix-lang-sqlpp/src/main/java/org/apache/asterix/lang/sqlpp/rewrites/visitor/OperatorExpressionVisitor.java
index 23b4d60..9c49071 100644
--- a/asterixdb/asterix-lang-sqlpp/src/main/java/org/apache/asterix/lang/sqlpp/rewrites/visitor/OperatorExpressionVisitor.java
+++ b/asterixdb/asterix-lang-sqlpp/src/main/java/org/apache/asterix/lang/sqlpp/rewrites/visitor/OperatorExpressionVisitor.java
@@ -103,6 +103,7 @@
         comparison.addOperand(itemExpr);
         comparison.addOperand(bindingVar);
         comparison.setCurrentop(true);
+        comparison.addHints(operatorExpr.getHints());
         comparison.setSourceLocation(operatorExpr.getSourceLocation());
         if (opType == OperatorType.IN) {
             comparison.addOperator(OperatorType.EQ);