@Override
    public Tuple next() throws SQLException {
      if (!initialized) {
        init();
      }

      Tuple next = null;
      while (next == null && !isEnd()) {
        if (queueIterator != null) {
          if (queueIterator.hasNext()) {
            next = join(lhsTuple, queueIterator.next());
          } else {
            boolean eq = nextLhsTuple != null && lhsKey.equals(nextLhsKey);
            advance(true);
            if (eq) {
              queueIterator = queue.iterator();
            } else {
              queue.clear();
              queueIterator = null;
            }
          }
        } else if (lhsTuple != null) {
          if (rhsTuple != null) {
            if (lhsKey.equals(rhsKey)) {
              next = join(lhsTuple, rhsTuple);
              if (nextLhsTuple != null && lhsKey.equals(nextLhsKey)) {
                queue.offer(rhsTuple);
                if (nextRhsTuple == null || !rhsKey.equals(nextRhsKey)) {
                  queueIterator = queue.iterator();
                  advance(true);
                } else if (isSingleValueOnly) {
                  throw new SQLExceptionInfo.Builder(
                          SQLExceptionCode.SINGLE_ROW_SUBQUERY_RETURNS_MULTIPLE_ROWS)
                      .build()
                      .buildException();
                }
              } else if (nextRhsTuple == null || !rhsKey.equals(nextRhsKey)) {
                advance(true);
              } else if (isSingleValueOnly) {
                throw new SQLExceptionInfo.Builder(
                        SQLExceptionCode.SINGLE_ROW_SUBQUERY_RETURNS_MULTIPLE_ROWS)
                    .build()
                    .buildException();
              }
              advance(false);
            } else if (lhsKey.compareTo(rhsKey) < 0) {
              if (type == JoinType.Full || type == JoinType.Left) {
                next = join(lhsTuple, null);
              }
              advance(true);
            } else {
              if (type == JoinType.Full) {
                next = join(null, rhsTuple);
              }
              advance(false);
            }
          } else { // left-join or full-join
            next = join(lhsTuple, null);
            advance(true);
          }
        } else { // full-join
          next = join(null, rhsTuple);
          advance(false);
        }
      }

      return next;
    }
 @Override
 public void close() throws SQLException {
   lhsIterator.close();
   rhsIterator.close();
   queue.close();
 }