@Override
    public Object process(Node nd, Stack<Node> stack, NodeProcessorCtx procCtx,
        Object... nodeOutputs) throws SemanticException {
      ExprNodeGenericFuncDesc fd = getInExprNode((ExprNodeDesc)nd);

      /***************************************************************************************\
       BEGIN : Early terminations for Partition Column Separator
      /***************************************************************************************/
      // 1. If the input node is not an IN operator, we bail out.
      if (fd == null) {
        LOG.debug("Partition columns not separated for {}, is not IN operator : ", fd);
        return null;
      }

      // 2. Check if the input is an IN operator with struct children
      List<ExprNodeDesc> children = fd.getChildren();
      if (!(children.get(0) instanceof ExprNodeGenericFuncDesc) ||
          (!(((ExprNodeGenericFuncDesc) children.get(0)).getGenericUDF()
           instanceof GenericUDFStruct))) {
        if (LOG.isDebugEnabled()) {
          LOG.debug("Partition columns not separated for " + fd + ", children size " +
           children.size() + ", child expression : " + children.get(0).getExprString());
        }
        return null;
      }

      // 3. See if the IN (STRUCT(EXP1, EXP2,..) has at least one expression with partition
      // column with single table alias. If not bail out.
      // We might have expressions containing only partitioning columns, say, T1.A + T2.B
      // where T1.A and T2.B are both partitioning columns.
      // However, these expressions should not be considered as valid expressions for separation.
      if (!hasAtLeastOneSubExprWithPartColOrVirtualColWithOneTableAlias(children.get(0))) {
        LOG.debug(
            "Partition columns not separated for {}, there are no expression containing partition columns in struct fields",
            fd);
        return null;
      }

      // 4. See if all the field expressions of the left hand side of IN are expressions
      // containing constants or only partition columns coming from same table.
      // If so, we need not perform this optimization and we should bail out.
      if (hasAllSubExprWithConstOrPartColOrVirtualColWithOneTableAlias(children.get(0))) {
          LOG.debug("Partition columns not separated for {}" +
          ", all fields are expressions containing constants or only partition columns"
          + "coming from same table", fd);
        return null;
      }

      /***************************************************************************************\
       END : Early terminations for Partition Column Separator
      /***************************************************************************************/


      /***************************************************************************************\
       BEGIN : Actual processing of the IN (STRUCT(..)) expression.
      /***************************************************************************************/
      Map<String, TableInfo> tableAliasToInfo =
        new HashMap<>();
      ExprNodeGenericFuncDesc originalStructDesc = ((ExprNodeGenericFuncDesc) children.get(0));
      List<ExprNodeDesc> originalDescChildren = originalStructDesc.getChildren();
      /**
       * PASS 1 : Iterate through the original IN(STRUCT(..)) and populate the tableAlias to
       * predicate information inside tableAliasToInfo.
       */
      for (int i = 0; i < originalDescChildren.size(); i++) {
        ExprNodeDesc en =  originalDescChildren.get(i);
        String tabAlias = null;

        // If the current expression node does not have a virtual/partition column or
        // single table alias reference, ignore it and move to the next expression node.
        if (!exprContainsOnlyPartitionColOrVirtualColOrConstants(en) ||
            (tabAlias = getTableAlias(en)) == null) {
          continue;
        }

        TableInfo currTableInfo = null;

        // If the table alias to information map already contains the current table,
        // use the existing TableInfo object. Else, create a new one.
        if (tableAliasToInfo.containsKey(tabAlias)) {
          currTableInfo = tableAliasToInfo.get(tabAlias);
        } else {
          currTableInfo = new TableInfo();
        }
        currTableInfo.exprNodeLHSDescriptor.add(en);

        // Iterate through the children nodes of the IN clauses starting from index 1,
        // which corresponds to the right hand side of the IN list.
        // Insert the value corresponding to the current expression in currExprNodeInfo.exprNodeValues.
        for (int j = 1; j < children.size(); j++) {
          ExprNodeDesc currChildStructExpr = children.get(j);
          ExprNodeDesc newConstStructElement = null;

          // 1. Get the constant value associated with the current element in the struct.
          // If the current child struct expression is a constant struct.
          if (currChildStructExpr instanceof ExprNodeConstantDesc) {
            List<Object> cnCols = (List<Object>)(((ExprNodeConstantDesc) (children.get(j))).getValue());
            newConstStructElement = new ExprNodeConstantDesc(cnCols.get(i));
          } else {
            // This better be a generic struct with constant values as the children.
            List<ExprNodeDesc> cnChildren = ((ExprNodeGenericFuncDesc) children.get(j)).getChildren();
            newConstStructElement = new ExprNodeConstantDesc(
              (((ExprNodeConstantDesc) (cnChildren.get(i))).getValue()));
          }

          // 2. Insert the current constant value into exprNodeStructs list.
          // If there is no struct corresponding to the current element, create a new one, insert
          // the constant value into it and add the struct as part of exprNodeStructs.
          if (currTableInfo.exprNodeRHSStructs.size() < j) {
            List<ExprNodeDesc> newConstStructList = new ArrayList<ExprNodeDesc>();
            newConstStructList.add(newConstStructElement);
            currTableInfo.exprNodeRHSStructs.add(newConstStructList);
          } else {
            // We already have a struct node for the current index. Insert the constant value
            // into the corresponding struct node.
            currTableInfo.exprNodeRHSStructs.get(j-1).add(newConstStructElement);
          }
        }

        // Insert the current table alias entry into the map if not already present in tableAliasToInfo.
        if (!tableAliasToInfo.containsKey(tabAlias)) {
          tableAliasToInfo.put(tabAlias, currTableInfo);
        }
      }

      /**
       * PASS 2 : Iterate through the tableAliasToInfo populated via PASS 1
       * to generate the new expression.
       */
      // subExpr is the list containing generated IN clauses as a result of this optimization.
      final List<ExprNodeDesc> subExpr =
        new ArrayList<ExprNodeDesc>(originalDescChildren.size()+1);

      for (Entry<String, TableInfo> entry :
        tableAliasToInfo.entrySet()) {
        TableInfo currTableInfo = entry.getValue();
        List<List<ExprNodeDesc>> currConstStructList = currTableInfo.exprNodeRHSStructs;

        // IN(STRUCT(..)..) ExprNodeDesc list for the current table alias.
        List<ExprNodeDesc> currInStructExprList = new ArrayList<ExprNodeDesc>();

        // Add the left hand side of the IN clause which contains the struct definition.
        currInStructExprList.add(ExprNodeGenericFuncDesc.newInstance
          (FunctionRegistry.getFunctionInfo(STRUCT_UDF).getGenericUDF(),
          STRUCT_UDF,
          currTableInfo.exprNodeLHSDescriptor));

        // Generate the right hand side of the IN clause
        for (int i = 0; i < currConstStructList.size(); i++) {
          List<ExprNodeDesc> currConstStruct = currConstStructList.get(i);

          // Add the current constant struct to the right hand side of the IN clause.
          currInStructExprList.add(ExprNodeGenericFuncDesc.newInstance
            (FunctionRegistry.getFunctionInfo(STRUCT_UDF).getGenericUDF(),
            STRUCT_UDF,
            currConstStruct));
        }

        // Add the newly generated IN clause to subExpr.
        subExpr.add(new ExprNodeGenericFuncDesc(
          TypeInfoFactory.booleanTypeInfo, FunctionRegistry.
          getFunctionInfo(IN_UDF).getGenericUDF(), currInStructExprList));
      }
      /***************************************************************************************\
       END : Actual processing of the IN (STRUCT(..)) expression.
      /***************************************************************************************/

      // If there is only 1 table ALIAS, return it
      if (subExpr.size() == 1) {
        // Return the new expression containing only partition columns
        return subExpr.get(0);
      }
      // Return the new expression containing only partition columns
      // after concatenating them with AND operator
      return new ExprNodeGenericFuncDesc(
        TypeInfoFactory.booleanTypeInfo,
        FunctionRegistry.getFunctionInfo(AND_UDF).getGenericUDF(), subExpr);
    }
The main process method for StructInExprProcessor to generate additional predicates
containing only partition columns.
ql/src/java/org/apache/hadoop/hive/ql/optimizer/PartitionColumnsSeparator.java