private static boolean isSemijoinExpr(ExprNodeDesc expr) {
    if (expr instanceof ExprNodeDynamicListDesc) {
      // DYNAMIC PARTITION PRUNING
      return true;
    }
    if (FunctionRegistry.isOpBetween(expr) &&
        expr.getChildren().get(2) instanceof ExprNodeDynamicValueDesc) {
      // BETWEEN in SJ
      return true;
    }
    if (FunctionRegistry.isOpInBloomFilter(expr) &&
        expr.getChildren().get(1) instanceof ExprNodeDynamicValueDesc) {
      // IN_BLOOM_FILTER in SJ
      return true;
    }
    return false;
  }
When we call this method, we have already verified that the SJ expressions targeting
two TS operators are the same.
Since we already had a method to push the filter expressions on top of the discardable
TS (pushFilterToTopOfTableScan), here we remove the old SJ expressions from the
discardable TS (and follow-up Filters if present) and we add the SJ expressions
from the retainable TS. That way the SJ expressions will be pushed on top of the
discardable TS by pushFilterToTopOfTableScan.
ql/src/java/org/apache/hadoop/hive/ql/optimizer/SharedWorkOptimizer.java