@Override
    protected void generatePredicate(NodeProcessorCtx procCtx,
                                     FilterOperator fop, TableScanOperator top) throws SemanticException{
      FixedBucketPruningOptimizerCtxt ctxt = ((FixedBucketPruningOptimizerCtxt) procCtx);
      Table tbl = top.getConf().getTableMetadata();
      int numBuckets = tbl.getNumBuckets();
      if (numBuckets <= 0 || tbl.getBucketCols().size() != 1) {
        // bucketing isn't consistent or there are >1 bucket columns
        // optimizer does not extract multiple column predicates for this
        return;
      }

      if (tbl.isPartitioned()) {
        // Make sure all the partitions have same bucket count.
        PrunedPartitionList prunedPartList =
            PartitionPruner.prune(top, ctxt.pctx, top.getConf().getAlias());
        if (prunedPartList != null) {
          for (Partition p : prunedPartList.getPartitions()) {
            if (numBuckets != p.getBucketCount()) {
              // disable feature
              return;
            }
          }
        }
      }
      
      ExprNodeGenericFuncDesc filter = top.getConf().getFilterExpr();
      if (filter == null) {
        return;
      }
      // the sargs are closely tied to hive.optimize.index.filter
      SearchArgument sarg = ConvertAstToSearchArg.create(ctxt.pctx.getConf(), filter);
      if (sarg == null) {
        return;
      }
      final String bucketCol = tbl.getBucketCols().get(0);
      StructField bucketField = null;
      for (StructField fs : tbl.getFields()) {
        if(fs.getFieldName().equals(bucketCol)) {
          bucketField = fs;
          break;
        }
      }
      Preconditions.checkArgument(bucketField != null);
      List<Object> literals = new ArrayList<Object>();
      List<PredicateLeaf> leaves = sarg.getLeaves();
      Set<PredicateLeaf> bucketLeaves = new HashSet<PredicateLeaf>();
      for (PredicateLeaf l : leaves) {
        if (bucketCol.equals(l.getColumnName())) {
          switch (l.getOperator()) {
          case EQUALS:
          case IN:
            // supported
            break;
          case IS_NULL:
            // TODO: (a = 1) and NOT (a is NULL) can be potentially folded earlier into a NO-OP
            // fall through
          case BETWEEN:
            // TODO: for ordinal types you can produce a range (BETWEEN 1444442100 1444442107)
            // fall through
          default:
            // cannot optimize any others
            return;
          }
          bucketLeaves.add(l);
        }
      }
      if (bucketLeaves.size() == 0) {
        return;
      }
      // TODO: Add support for AND clauses under OR clauses
      // first-cut takes a known minimal tree and no others.
      // $expr = (a=1)
      //         (a=1 or a=2)
      //         (a in (1,2))
      //         ($expr and *)
      //         (* and $expr)
      ExpressionTree expr = sarg.getExpression();
      if (expr.getOperator() == Operator.LEAF) {
        PredicateLeaf l = leaves.get(expr.getLeaf());
        if (!addLiteral(literals, l)) {
          return;
        }
      } else if (expr.getOperator() == Operator.AND) {
        boolean found = false;
        for (ExpressionTree subExpr : expr.getChildren()) {
          if (subExpr.getOperator() == Operator.LEAF) {
            // one of the branches is definitely a bucket-leaf
            PredicateLeaf l = leaves.get(subExpr.getLeaf());
            if (bucketLeaves.contains(l)) {
              if (!addLiteral(literals, l)) {
                return;
              }
              found = true;
            }
          }
        }
        if (!found) {
          return;
        }
      } else if (expr.getOperator() == Operator.OR) {
        for (ExpressionTree subExpr : expr.getChildren()) {
          if (subExpr.getOperator() != Operator.LEAF) {
            return;
          }
          PredicateLeaf l = leaves.get(subExpr.getLeaf());
          if (bucketLeaves.contains(l)) {
            if (!addLiteral(literals, l)) {
              return;
            }
          } else {
            // all of the OR branches need to be bucket-leaves
            return;
          }
        }
      } else if (expr.getOperator() == Operator.NOT) {
        // TODO: think we can handle NOT IS_NULL?
        return;
      }
      // invariant: bucket-col IN literals of type bucketField
      BitSet bs = new BitSet(numBuckets);
      bs.clear();
      PrimitiveObjectInspector bucketOI = (PrimitiveObjectInspector)bucketField.getFieldObjectInspector();
      PrimitiveObjectInspector constOI = PrimitiveObjectInspectorFactory.getPrimitiveWritableObjectInspector(bucketOI.getTypeInfo());
      // Fetch the bucketing version from table scan operator
      int bucketingVersion = top.getConf().getTableMetadata().getBucketingVersion();

      for (Object literal: literals) {
        PrimitiveObjectInspector origOI = PrimitiveObjectInspectorFactory.getPrimitiveObjectInspectorFromClass(literal.getClass());
        Converter conv = ObjectInspectorConverters.getConverter(origOI, constOI);
        // exact type conversion or get out
        if (conv == null) {
          return;
        }
        Object convCols[] = new Object[] {conv.convert(literal)};
        int n = bucketingVersion == 2 ?
            ObjectInspectorUtils.getBucketNumber(convCols, new ObjectInspector[]{constOI}, numBuckets) :
            ObjectInspectorUtils.getBucketNumberOld(convCols, new ObjectInspector[]{constOI}, numBuckets);
        bs.set(n);
        if (bucketingVersion == 1 && ctxt.isCompat()) {
          int h = ObjectInspectorUtils.getBucketHashCodeOld(convCols, new ObjectInspector[]{constOI});
          // -ve hashcodes had conversion to positive done in different ways in the past
          // abs() is now obsolete and all inserts now use & Integer.MAX_VALUE 
          // the compat mode assumes that old data could've been loaded using the other conversion
          n = ObjectInspectorUtils.getBucketNumber(Math.abs(h), numBuckets);
          bs.set(n);
        }
      }
      if (bs.cardinality() < numBuckets) {
        // there is a valid bucket pruning filter
        top.getConf().setIncludedBuckets(bs);
        top.getConf().setNumBuckets(numBuckets);
      }
    }
Fixed bucket pruning optimizer goes through all the table scans and annotates them
with a bucketing inclusion bit-set.
ql/src/java/org/apache/hadoop/hive/ql/optimizer/FixedBucketPruningOptimizer.java