private boolean convertJoinBucketMapJoin(JoinOperator joinOp, OptimizeTezProcContext context,
      MapJoinConversion mapJoinConversion, TezBucketJoinProcCtx tezBucketJoinProcCtx) throws SemanticException {

    if (mapJoinConversion.getIsFullOuterJoin() &&
        !mapJoinConversion.getIsFullOuterEnabledForMapJoin()) {
      return false;
    }
    final int bigTablePosition = mapJoinConversion.getBigTablePos();
    if (!checkConvertJoinBucketMapJoin(joinOp, bigTablePosition, tezBucketJoinProcCtx)) {
      LOG.info("Check conversion to bucket map join failed.");
      return false;
    }

    // Incase the join has extra keys other than bucketed columns, partition keys need to be updated
    // on small table(s).
    ReduceSinkOperator bigTableRS = (ReduceSinkOperator)joinOp.getParentOperators().get(bigTablePosition);
    OpTraits opTraits = bigTableRS.getOpTraits();
    List<List<String>> listBucketCols = opTraits.getBucketColNames();
    List<ExprNodeDesc> bigTablePartitionCols = bigTableRS.getConf().getPartitionCols();
    boolean updatePartitionCols = false;
    List<Integer> positions = new ArrayList<>();

    CustomBucketFunction bucketFunction = opTraits.getCustomBucketFunctions().get(0);
    if (listBucketCols.get(0).size() != bigTablePartitionCols.size()) {
      updatePartitionCols = true;
      // Prepare updated partition columns for small table(s).
      // Get the positions of bucketed columns

      int bigTableExprPos = 0;
      Map<String, ExprNodeDesc> colExprMap = bigTableRS.getColumnExprMap();
      final boolean[] retainedColumns = new boolean[listBucketCols.get(0).size()];
      for (ExprNodeDesc bigTableExpr : bigTablePartitionCols) {
        // It is guaranteed there is only 1 list within listBucketCols.
        for (int i = 0; i < listBucketCols.get(0).size(); i++) {
          final String colName = listBucketCols.get(0).get(i);
          if (colExprMap.get(colName).isSame(bigTableExpr)) {
            positions.add(bigTableExprPos);
            retainedColumns[i] = true;
          }
        }
        bigTableExprPos = bigTableExprPos + 1;
      }

      Preconditions.checkState(opTraits.getCustomBucketFunctions().size() == 1);
      if (opTraits.getCustomBucketFunctions().get(0) != null) {
        final Optional<CustomBucketFunction> selected =
            opTraits.getCustomBucketFunctions().get(0).select(retainedColumns);
        if (!selected.isPresent()) {
          LOG.info("{} can't keep itself only with {}", opTraits.getCustomBucketFunctions().get(0), retainedColumns);
          return false;
        }
        bucketFunction = selected.get();
      }
    }

    MapJoinOperator mapJoinOp = convertJoinMapJoin(joinOp, context, mapJoinConversion, true);
    if (mapJoinOp == null) {
      LOG.debug("Conversion to bucket map join failed.");
      return false;
    }
    MapJoinDesc joinDesc = mapJoinOp.getConf();
    joinDesc.setBucketMapJoin(true);

    // we can set the traits for this join operator
    opTraits = new OpTraits(joinOp.getOpTraits().getBucketColNames(), joinOp.getOpTraits().getCustomBucketFunctions(),
        tezBucketJoinProcCtx.getNumBuckets(), null, joinOp.getOpTraits().getNumReduceSinks());
    mapJoinOp.setOpTraits(opTraits);
    preserveOperatorInfos(mapJoinOp, joinOp, context);
    setNumberOfBucketsOnChildren(mapJoinOp);

    // Once the conversion is done, we can set the partitioner to bucket cols on the small table
    Map<String, Integer> bigTableBucketNumMapping = new HashMap<String, Integer>();
    bigTableBucketNumMapping.put(joinDesc.getBigTableAlias(), tezBucketJoinProcCtx.getNumBuckets());
    joinDesc.setBigTableBucketNumMapping(bigTableBucketNumMapping);

    // Update the partition columns in small table to ensure correct routing of hash tables.
    if (updatePartitionCols) {
      // use the positions to only pick the partitionCols which are required
      // on the small table side.
      mapJoinOp.getParentOperators().stream().filter(ReduceSinkOperator.class::isInstance).forEach(op -> {
        ReduceSinkOperator rsOp = (ReduceSinkOperator) op;
        List<ExprNodeDesc> newPartitionCols = new ArrayList<>();
        List<ExprNodeDesc> partitionCols = rsOp.getConf().getPartitionCols();
        for (Integer position : positions) {
          newPartitionCols.add(partitionCols.get(position));
        }
        rsOp.getConf().setPartitionCols(newPartitionCols);
      });
    }

    if (bucketFunction != null) {
      final Operator<?> bigTableOp = mapJoinOp.getParentOperators().get(bigTablePosition);
      for (TableScanOperator tso : OperatorUtils.findOperatorsUpstream(bigTableOp, TableScanOperator.class)) {
        tso.getConf().setGroupingPartitionColumns(bucketFunction.getSourceColumnNames());
        tso.getConf().setGroupingNumBuckets(bucketFunction.getNumBuckets());
      }

      final CustomBucketFunction finalBucketFunction = bucketFunction;
      mapJoinOp.getParentOperators().stream().filter(ReduceSinkOperator.class::isInstance).forEach(op -> {
        ReduceSinkOperator rsOp = (ReduceSinkOperator) op;
        rsOp.getConf().setCustomPartitionFunction(finalBucketFunction);
      });
    }

    // Update the memory monitor info for LLAP.
    MemoryMonitorInfo memoryMonitorInfo = joinDesc.getMemoryMonitorInfo();
    if (memoryMonitorInfo.isLlap()) {
      memoryMonitorInfo.setHashTableInflationFactor(1);
      memoryMonitorInfo.setMemoryOverSubscriptionFactor(0);
    }
    return true;
  }
In data calculation logic, we include some overhead due to java object refs, etc.
However, this overhead may be different when storing values in hashtable for mapjoin.
Hence, we calculate a size adjustment to the original data size for a given input.
ql/src/java/org/apache/hadoop/hive/ql/optimizer/ConvertJoinMapJoin.java