// This is the only iteration method that should ever be called so it does all of the work.
bool DocumentSourceOut::eof() {
    // make sure we only write out once
    if (_done)
        return true;
    _done = true;

    verify(_conn);

    prepTempCollection();
    verify(_tempNs.size() != 0);

    for (bool haveNext = !pSource->eof(); haveNext; haveNext = pSource->advance()) {
        BSONObj toInsert = pSource->getCurrent().toBson();
        _conn->insert(_tempNs.ns(), toInsert);
        BSONObj err = _conn->getLastErrorDetailed();
        uassert(16996, str::stream() << "insert for $out failed: " << err,
                DBClientWithCommands::getLastErrorString(err).empty());
    }

    BSONObj rename = BSON("renameCollection" << _tempNs.ns()
                          << "to" << _outputNs.ns()
                          << "dropTarget" << true
                         );
    BSONObj info;
    bool ok = _conn->runCommand("admin", rename, info);
    uassert(16997,  str::stream() << "renameCollection for $out failed: " << info,
            ok);

    // We don't need to drop the temp collection in our destructor if the rename succeeded.
    _tempNs = NamespaceString("");

    // This "DocumentSource" doesn't produce output documents. This can change in the future
    // if we support using $out in "tee" mode.
    return true;
}
    boost::optional<Document> DocumentSourceOut::getNext() {
        pExpCtx->checkForInterrupt();

        // make sure we only write out once
        if (_done)
            return boost::none;
        _done = true;

        verify(_mongod);
        DBClientBase* conn = _mongod->directClient();

        prepTempCollection();
        verify(_tempNs.size() != 0);

        vector<BSONObj> bufferedObjects;
        int bufferedBytes = 0;
        while (boost::optional<Document> next = pSource->getNext()) {
            BSONObj toInsert = next->toBson();
            bufferedBytes += toInsert.objsize();
            if (!bufferedObjects.empty() && bufferedBytes > BSONObjMaxUserSize) {
                spill(conn, bufferedObjects);
                bufferedObjects.clear();
                bufferedBytes = toInsert.objsize();
            }
            bufferedObjects.push_back(toInsert);
        }

        if (!bufferedObjects.empty())
            spill(conn, bufferedObjects);

        // Checking again to make sure we didn't become sharded while running.
        uassert(17018, str::stream() << "namespace '" << _outputNs.ns()
                                     << "' became sharded so it can't be used for $out'",
                !_mongod->isSharded(_outputNs));

        BSONObj rename = BSON("renameCollection" << _tempNs.ns()
                           << "to" << _outputNs.ns()
                           << "dropTarget" << true
                           );
        BSONObj info;
        bool ok = conn->runCommand("admin", rename, info);
        uassert(16997,  str::stream() << "renameCollection for $out failed: " << info,
                ok);

        // We don't need to drop the temp collection in our destructor if the rename succeeded.
        _tempNs = NamespaceString("");

        // This "DocumentSource" doesn't produce output documents. This can change in the future
        // if we support using $out in "tee" mode.
        return boost::none;
    }