protected void generateFullOuterSmallTableNoMatches(byte smallTablePos,
      MapJoinTableContainer substituteSmallTable) throws HiveException {

    // FUTURE: Currently, in the MapJoinOperaotr, we only support FULL OUTER MapJoin for
    // FUTURE  MapJoinBytesTableContainer.  NOTE: Vectorization code will override this method.

    if (matchTracker == null) {

      // When the process method isn't called (i.e. no rows), then we need to create the
      // MatchTracker here.
      //
      // It will indicate no matches, of course.
      //
      createMatchTracker(substituteSmallTable);
    }

    boolean isSmallTableValuesOnly = false;
    int[] smallTableValuesIndex = conf.getValueIndex(smallTablePos);
    if (smallTableValuesIndex == null) {
      List<Integer> valuesList = conf.getRetainList().get(smallTablePos);
      smallTableValuesIndex =
          ArrayUtils.toPrimitive(valuesList.toArray(new Integer[0]));
      isSmallTableValuesOnly = true;
    }
    final int smallTableValuesIndexSize = smallTableValuesIndex.length;

    // Our first output column for Small Table results is based on order.  (The Big Table columns
    // will all be NULL).
    final int firstOutputColumnNum = (posBigTable == (byte) 0 ? fullOuterBigTableRetainSize : 0);

    /*
     * Create iterator that produces each  non-matched Small Table key and a ReusableRowContainer
     * the Small Table values.
     */
    NonMatchedSmallTableIterator nonMatchedIterator =
        substituteSmallTable.createNonMatchedSmallTableIterator(matchTracker);
    while (nonMatchedIterator.isNext()) {
      List<Object> keyObjList = nonMatchedIterator.getCurrentKey();

      MapJoinRowContainer values = nonMatchedIterator.getCurrentRows();
      AbstractRowContainer.RowIterator<List<Object>> iter = values.rowIter();
      for (List<Object> valueObjList = iter.first();
          valueObjList != null;
          valueObjList = iter.next()) {

        // Form non-matched Small Table join result. We only fill in the Small Table columns,
        // so the Big Table retained columns are NULLs from the new allocation.

        Object[] row = new Object[fullOuterBigTableRetainSize + smallTableValuesIndexSize];
        int outputColumnNum = firstOutputColumnNum;

        if (isSmallTableValuesOnly) {
          for (int i = 0; i < smallTableValuesIndexSize; i++) {
            row[outputColumnNum++] = valueObjList.get(smallTableValuesIndex[i]);
          }
        } else {
          for (int i = 0; i < smallTableValuesIndexSize; i++) {
            final int index = smallTableValuesIndex[i];

            if (index >= 0) {

              // Zero and above numbers indicate a big table key is needed for
              // small table result "area".

              row[outputColumnNum++] = keyObjList.get(index);
            } else {

              // Negative numbers indicate a column to be (deserialize) read from the small table's
              // LazyBinary value row.

              int smallTableValueIndex = -index - 1;

              row[outputColumnNum++] = valueObjList.get(smallTableValueIndex);
            }
          }
        }

        Object standardCopyRow =
            ObjectInspectorUtils.copyToStandardObject(
                row, outputObjInspector, ObjectInspectorCopyOption.WRITABLE);

        // FUTURE: Support residual filters for non-equi joins.
        internalForward(standardCopyRow, outputObjInspector);
      }
    }
  }
Postpone processing the big table row temporarily by spilling it to a row container
@param hybridHtContainer Hybrid hashtable container
@param row big table row
ql/src/java/org/apache/hadoop/hive/ql/exec/MapJoinOperator.java