bool isPathPrefixOf(StringData first, StringData second) {
    if (first.size() >= second.size()) {
        return false;
    }

    return second.startsWith(first) && second[first.size()] == '.';
}
Exemple #2
0
bool FieldRef::equalsDottedField( const StringData& other ) const {
    StringData rest = other;

    for ( size_t i = 0; i < _size; i++ ) {

        StringData part = getPart( i );

        if ( !rest.startsWith( part ) )
            return false;

        if ( i == _size - 1 )
            return rest.size() == part.size();

        // make sure next thing is a dot
        if ( rest.size() == part.size() )
            return false;

        if ( rest[part.size()] != '.' )
            return false;

        rest = rest.substr( part.size() + 1 );
    }

    return false;
}
Exemple #3
0
    Status userAllowedWriteNS( const StringData& db, const StringData& coll ) {
        // validity checking

        if ( db.size() == 0 )
            return Status( ErrorCodes::BadValue, "db cannot be blank" );

        if ( !NamespaceString::validDBName( db ) )
            return Status( ErrorCodes::BadValue, "invalid db name" );

        if ( coll.size() == 0 )
            return Status( ErrorCodes::BadValue, "collection cannot be blank" );

        if ( !NamespaceString::validCollectionName( coll ) )
            return Status( ErrorCodes::BadValue, "invalid collection name" );

        if ( db.size() + 1 /* dot */ + coll.size() > Namespace::MaxNsColletionLen )
            return Status( ErrorCodes::BadValue,
                           str::stream()
                             << "fully qualified namespace " << db << '.' << coll << " is too long "
                             << "(max is " << Namespace::MaxNsColletionLen << " bytes)" );

        // check spceial areas

        if ( db == "system" )
            return Status( ErrorCodes::BadValue, "cannot use 'system' database" );


        if ( coll.startsWith( "system." ) ) {
            if ( coll == "system.indexes" ) return Status::OK();
            if ( coll == "system.js" ) return Status::OK();
            if ( coll == "system.profile" ) return Status::OK();
            if ( coll == "system.users" ) return Status::OK();
            if ( db == "admin" ) {
                if ( coll == "system.version" ) return Status::OK();
                if ( coll == "system.roles" ) return Status::OK();
                if ( coll == "system.new_users" ) return Status::OK();
                if ( coll == "system.backup_users" ) return Status::OK();
            }
            if ( db == "local" ) {
                if ( coll == "system.replset" ) return Status::OK();
            }
            return Status( ErrorCodes::BadValue,
                           str::stream() << "cannot write to '" << db << "." << coll << "'" );
        }

        // some special rules

        if ( coll.find( ".system." ) != string::npos ) {
            // this matches old (2.4 and older) behavior, but I'm not sure its a good idea
            return Status( ErrorCodes::BadValue,
                           str::stream() << "cannot write to '" << db << "." << coll << "'" );
        }

        return Status::OK();
    }
bool UpdateIndexData::_startsWith(StringData a, StringData b) const {
    if (!a.startsWith(b))
        return false;

    // make sure there is a dot or EOL right after

    if (a.size() == b.size())
        return true;

    return a[b.size()] == '.';
}
Exemple #5
0
    // ns is either a full namespace or "dbname." when invalidating for a whole db
    void ClientCursor::invalidate(const StringData &ns) {
        Lock::assertWriteLocked(ns);
        size_t dotpos = ns.find('.');
        verify(dotpos != string::npos);
        bool isDB = (dotpos + 1) == ns.size(); // first (and only) dot is the last char

        {
            //cout << "\nTEMP invalidate " << ns << endl;
            Database *db = cc().database();
            verify(db);
            verify( ns.startsWith(db->name()) );

            for( LockedIterator i; i.ok(); ) {
                ClientCursor *cc = i.current();

                bool shouldDelete = false;
                if (cc->c()->shouldDestroyOnNSDeletion() && cc->_db == db) {
                    if (isDB) {
                        // already checked that db matched above
                        dassert( StringData(cc->_ns).startsWith(ns) );
                        shouldDelete = true;
                    }
                    else {
                        if ( ns == cc->_ns )
                            shouldDelete = true;
                    }
                }

                if ( shouldDelete ) {
                    i.deleteAndAdvance();
                }
                else {
                    i.advance();
                }
            }
        }
    }
Exemple #6
0
StatusWith<BSONObj> FTSSpec::fixSpec(const BSONObj& spec) {
    if (spec["textIndexVersion"].numberInt() == TEXT_INDEX_VERSION_1) {
        return _fixSpecV1(spec);
    }

    map<string, int> m;

    BSONObj keyPattern;
    {
        BSONObjBuilder b;

        // Populate m and keyPattern.
        {
            bool addedFtsStuff = false;
            BSONObjIterator i(spec["key"].Obj());
            while (i.more()) {
                BSONElement e = i.next();
                if (e.fieldNameStringData() == "_fts") {
                    if (INDEX_NAME != e.valuestrsafe()) {
                        return {ErrorCodes::CannotCreateIndex, "expecting _fts:\"text\""};
                    }
                    addedFtsStuff = true;
                    b.append(e);
                } else if (e.fieldNameStringData() == "_ftsx") {
                    if (e.numberInt() != 1) {
                        return {ErrorCodes::CannotCreateIndex, "expecting _ftsx:1"};
                    }
                    b.append(e);
                } else if (e.type() == String && INDEX_NAME == e.valuestr()) {
                    if (!addedFtsStuff) {
                        _addFTSStuff(&b);
                        addedFtsStuff = true;
                    }

                    m[e.fieldName()] = 1;
                } else {
                    if (e.numberInt() != 1 && e.numberInt() != -1) {
                        return {ErrorCodes::CannotCreateIndex,
                                "expected value 1 or -1 for non-text key in compound index"};
                    }
                    b.append(e);
                }
            }
            verify(addedFtsStuff);
        }
        keyPattern = b.obj();

        // Verify that index key is in the correct format: extraBefore fields, then text
        // fields, then extraAfter fields.
        {
            BSONObjIterator i(spec["key"].Obj());
            verify(i.more());
            BSONElement e = i.next();

            // extraBefore fields
            while (String != e.type()) {
                Status notReservedStatus = verifyFieldNameNotReserved(e.fieldNameStringData());
                if (!notReservedStatus.isOK()) {
                    return notReservedStatus;
                }

                if (!i.more()) {
                    return {ErrorCodes::CannotCreateIndex,
                            "expected additional fields in text index key pattern"};
                }

                e = i.next();
            }

            // text fields
            bool alreadyFixed = (e.fieldNameStringData() == "_fts");
            if (alreadyFixed) {
                if (!i.more()) {
                    return {ErrorCodes::CannotCreateIndex, "expected _ftsx after _fts"};
                }
                e = i.next();
                if (e.fieldNameStringData() != "_ftsx") {
                    return {ErrorCodes::CannotCreateIndex, "expected _ftsx after _fts"};
                }
                e = i.next();
            } else {
                do {
                    Status notReservedStatus = verifyFieldNameNotReserved(e.fieldNameStringData());
                    if (!notReservedStatus.isOK()) {
                        return notReservedStatus;
                    }
                    e = i.next();
                } while (!e.eoo() && e.type() == String);
            }

            // extraAfterFields
            while (!e.eoo()) {
                if (e.type() == BSONType::String) {
                    return {ErrorCodes::CannotCreateIndex,
                            "'text' fields in index must all be adjacent"};
                }
                Status notReservedStatus = verifyFieldNameNotReserved(e.fieldNameStringData());
                if (!notReservedStatus.isOK()) {
                    return notReservedStatus;
                }
                e = i.next();
            }
        }
    }

    if (spec["weights"].type() == Object) {
        BSONObjIterator i(spec["weights"].Obj());
        while (i.more()) {
            BSONElement e = i.next();
            if (!e.isNumber()) {
                return {ErrorCodes::CannotCreateIndex, "weight for text index needs numeric type"};
            }
            m[e.fieldName()] = e.numberInt();
        }
    } else if (spec["weights"].str() == WILDCARD) {
        m[WILDCARD] = 1;
    } else if (!spec["weights"].eoo()) {
        return {ErrorCodes::CannotCreateIndex, "text index option 'weights' must be an object"};
    }

    if (m.empty()) {
        return {ErrorCodes::CannotCreateIndex,
                "text index option 'weights' must specify fields or the wildcard"};
    }

    BSONObj weights;
    {
        BSONObjBuilder b;
        for (map<string, int>::iterator i = m.begin(); i != m.end(); ++i) {
            if (i->second <= 0 || i->second >= MAX_WORD_WEIGHT) {
                return {ErrorCodes::CannotCreateIndex,
                        str::stream() << "text index weight must be in the exclusive interval (0,"
                                      << MAX_WORD_WEIGHT
                                      << ") but found: "
                                      << i->second};
            }

            // Verify weight refers to a valid field.
            if (i->first != "$**") {
                FieldRef keyField(i->first);
                if (keyField.numParts() == 0) {
                    return {ErrorCodes::CannotCreateIndex, "weight cannot be on an empty field"};
                }

                for (size_t partNum = 0; partNum < keyField.numParts(); partNum++) {
                    StringData part = keyField.getPart(partNum);
                    if (part.empty()) {
                        return {ErrorCodes::CannotCreateIndex,
                                "weight cannot have empty path component"};
                    }

                    if (part.startsWith("$")) {
                        return {ErrorCodes::CannotCreateIndex,
                                "weight cannot have path component with $ prefix"};
                    }
                }
            }

            b.append(i->first, i->second);
        }
        weights = b.obj();
    }

    BSONElement default_language_elt = spec["default_language"];
    string default_language(default_language_elt.str());
    if (default_language_elt.eoo()) {
        default_language = moduleDefaultLanguage;
    } else if (default_language_elt.type() != BSONType::String) {
        return {ErrorCodes::CannotCreateIndex, "default_language needs a string type"};
    }

    if (!FTSLanguage::make(default_language, TEXT_INDEX_VERSION_3).getStatus().isOK()) {
        return {ErrorCodes::CannotCreateIndex, "default_language is not valid"};
    }

    BSONElement language_override_elt = spec["language_override"];
    string language_override(language_override_elt.str());
    if (language_override_elt.eoo()) {
        language_override = "language";
    } else if (language_override_elt.type() != BSONType::String) {
        return {ErrorCodes::CannotCreateIndex, "language_override must be a string"};
    } else if (!validateOverride(language_override)) {
        return {ErrorCodes::CannotCreateIndex, "language_override is not valid"};
    }

    int version = -1;
    int textIndexVersion = TEXT_INDEX_VERSION_3;  // default text index version

    BSONObjBuilder b;
    BSONObjIterator i(spec);
    while (i.more()) {
        BSONElement e = i.next();
        StringData fieldName = e.fieldNameStringData();
        if (fieldName == "key") {
            b.append("key", keyPattern);
        } else if (fieldName == "weights") {
            b.append("weights", weights);
            weights = BSONObj();
        } else if (fieldName == "default_language") {
            b.append("default_language", default_language);
            default_language = "";
        } else if (fieldName == "language_override") {
            b.append("language_override", language_override);
            language_override = "";
        } else if (fieldName == "v") {
            version = e.numberInt();
        } else if (fieldName == "textIndexVersion") {
            if (!e.isNumber()) {
                return {ErrorCodes::CannotCreateIndex,
                        "text index option 'textIndexVersion' must be a number"};
            }

            textIndexVersion = e.numberInt();
            if (textIndexVersion != TEXT_INDEX_VERSION_2 &&
                textIndexVersion != TEXT_INDEX_VERSION_3) {
                return {ErrorCodes::CannotCreateIndex,
                        str::stream() << "bad textIndexVersion: " << textIndexVersion};
            }
        } else {
            b.append(e);
        }
    }

    if (!weights.isEmpty()) {
        b.append("weights", weights);
    }
    if (!default_language.empty()) {
        b.append("default_language", default_language);
    }
    if (!language_override.empty()) {
        b.append("language_override", language_override);
    }
    if (version >= 0) {
        b.append("v", version);
    }
    b.append("textIndexVersion", textIndexVersion);

    return b.obj();
}
Exemple #7
0
BSONObj FTSSpec::fixSpec( const BSONObj& spec ) {
    if ( spec["textIndexVersion"].numberInt() == TEXT_INDEX_VERSION_1 ) {
        return _fixSpecV1( spec );
    }

    map<string,int> m;

    BSONObj keyPattern;
    {
        BSONObjBuilder b;

        // Populate m and keyPattern.
        {
            bool addedFtsStuff = false;
            BSONObjIterator i( spec["key"].Obj() );
            while ( i.more() ) {
                BSONElement e = i.next();
                if ( str::equals( e.fieldName(), "_fts" ) ) {
                    uassert( 17271,
                             "expecting _fts:\"text\"",
                             INDEX_NAME == e.valuestrsafe() );
                    addedFtsStuff = true;
                    b.append( e );
                }
                else if ( str::equals( e.fieldName(), "_ftsx" ) ) {
                    uassert( 17272, "expecting _ftsx:1", e.numberInt() == 1 );
                    b.append( e );
                }
                else if ( e.type() == String && INDEX_NAME == e.valuestr() ) {

                    if ( !addedFtsStuff ) {
                        _addFTSStuff( &b );
                        addedFtsStuff = true;
                    }

                    m[e.fieldName()] = 1;
                }
                else {
                    uassert( 17273,
                             "expected value 1 or -1 for non-text key in compound index",
                             e.numberInt() == 1 || e.numberInt() == -1 );
                    b.append( e );
                }
            }
            verify( addedFtsStuff );
        }
        keyPattern = b.obj();

        // Verify that index key is in the correct format: extraBefore fields, then text
        // fields, then extraAfter fields.
        {
            BSONObjIterator i( spec["key"].Obj() );
            BSONElement e;

            // extraBefore fields
            do {
                verify( i.more() );
                e = i.next();
            } while ( INDEX_NAME != e.valuestrsafe() );

            // text fields
            bool alreadyFixed = str::equals( e.fieldName(), "_fts" );
            if ( alreadyFixed ) {
                uassert( 17288, "expected _ftsx after _fts", i.more() );
                e = i.next();
                uassert( 17274,
                         "expected _ftsx after _fts",
                         str::equals( e.fieldName(), "_ftsx" ) );
                e = i.next();
            }
            else {
                do {
                    uassert( 17289,
                             "text index with reserved fields _fts/ftsx not allowed",
                             !str::equals( e.fieldName(), "_fts" ) &&
                             !str::equals( e.fieldName(), "_ftsx" ) );
                    e = i.next();
                } while ( !e.eoo() && INDEX_NAME == e.valuestrsafe() );
            }

            // extraAfterFields
            while ( !e.eoo() ) {
                uassert( 17290,
                         "compound text index key suffix fields must have value 1",
                         e.numberInt() == 1 && !str::equals( "_ftsx", e.fieldName() ) );
                e = i.next();
            }
        }

    }

    if ( spec["weights"].type() == Object ) {
        BSONObjIterator i( spec["weights"].Obj() );
        while ( i.more() ) {
            BSONElement e = i.next();
            uassert( 17283,
                     "weight for text index needs numeric type",
                     e.isNumber() );
            m[e.fieldName()] = e.numberInt();

            // Verify weight refers to a valid field.
            if ( str::equals( e.fieldName(), "$**" ) ) {
                continue;
            }
            FieldRef keyField( e.fieldName() );
            uassert( 17294,
                     "weight cannot be on an empty field",
                     keyField.numParts() != 0 );
            for ( size_t i = 0; i < keyField.numParts(); i++ ) {
                StringData part = keyField.getPart(i);
                uassert( 17291, "weight cannot have empty path component", !part.empty() );
                uassert( 17292,
                         "weight cannot have path component with $ prefix",
                         !part.startsWith( "$" ) );
            }
        }
    }
    else if ( spec["weights"].str() == WILDCARD ) {
        m[WILDCARD] = 1;
    }
    else if ( !spec["weights"].eoo() ) {
        uasserted( 17284, "text index option 'weights' must be an object" );
    }

    BSONObj weights;
    {
        BSONObjBuilder b;
        for ( map<string,int>::iterator i = m.begin(); i != m.end(); ++i ) {
            uassert( 16674, "score for word too high",
                     i->second > 0 && i->second < MAX_WORD_WEIGHT );
            b.append( i->first, i->second );
        }
        weights = b.obj();
    }

    BSONElement default_language_elt = spec["default_language"];
    string default_language( default_language_elt.str() );
    if ( default_language_elt.eoo() ) {
        default_language = moduleDefaultLanguage;
    }
    else {
        uassert( 17263,
                 "default_language needs a string type",
                 default_language_elt.type() == String );
    }
    uassert( 17264,
             "default_language is not valid",
             FTSLanguage::make( default_language,
                                TEXT_INDEX_VERSION_2 ).getStatus().isOK() );

    BSONElement language_override_elt = spec["language_override"];
    string language_override( language_override_elt.str() );
    if ( language_override_elt.eoo() ) {
        language_override = "language";
    }
    else {
        uassert( 17136,
                 "language_override is not valid",
                 language_override_elt.type() == String
                 && validateOverride( language_override ) );
    }

    int version = -1;
    int textIndexVersion = TEXT_INDEX_VERSION_2;

    BSONObjBuilder b;
    BSONObjIterator i( spec );
    while ( i.more() ) {
        BSONElement e = i.next();
        if ( str::equals( e.fieldName(), "key" ) ) {
            b.append( "key", keyPattern );
        }
        else if ( str::equals( e.fieldName(), "weights" ) ) {
            b.append( "weights", weights );
            weights = BSONObj();
        }
        else if ( str::equals( e.fieldName(), "default_language" ) ) {
            b.append( "default_language", default_language);
            default_language = "";
        }
        else if ( str::equals( e.fieldName(), "language_override" ) ) {
            b.append( "language_override", language_override);
            language_override = "";
        }
        else if ( str::equals( e.fieldName(), "v" ) ) {
            version = e.numberInt();
        }
        else if ( str::equals( e.fieldName(), "textIndexVersion" ) ) {
            uassert( 17293,
                     "text index option 'textIndexVersion' must be a number",
                     e.isNumber() );
            textIndexVersion = e.numberInt();
            uassert( 16730,
                     str::stream() << "bad textIndexVersion: " << textIndexVersion,
                     textIndexVersion == TEXT_INDEX_VERSION_2 );
        }
        else {
            b.append( e );
        }
    }

    if ( !weights.isEmpty() )
        b.append( "weights", weights );
    if ( !default_language.empty() )
        b.append( "default_language", default_language);
    if ( !language_override.empty() )
        b.append( "language_override", language_override);

    if ( version >= 0 )
        b.append( "v", version );

    b.append( "textIndexVersion", textIndexVersion );

    return b.obj();

}
Exemple #8
0
bool DBHashCmd::isCachable(StringData ns) const {
    return ns.startsWith("config.");
}
Exemple #9
0
    void ClientCursor::invalidate(const StringData& ns) {
        Lock::assertWriteLocked(ns);

        size_t dot = ns.find( '.' );
        verify( dot != string::npos );

        // first (and only) dot is the last char
        bool isDB = dot == ns.size() - 1;

        Database *db = cc().database();
        verify(db);
        verify(ns.startsWith(db->name()));

        recursive_scoped_lock cclock(ccmutex);
        // Look at all active non-cached Runners.  These are the runners that are in auto-yield mode
        // that are not attached to the the client cursor. For example, all internal runners don't
        // need to be cached -- there will be no getMore.
        for (set<Runner*>::iterator it = nonCachedRunners.begin(); it != nonCachedRunners.end();
             ++it) {

            Runner* runner = *it;
            const string& runnerNS = runner->ns();
            if ( ( isDB && StringData(runnerNS).startsWith(ns) ) || ns == runnerNS ) {
                runner->kill();
            }
        }

        // Look at all cached ClientCursor(s).  The CC may have a Runner, a Cursor, or nothing (see
        // sharding_block.h).
        CCById::const_iterator it = clientCursorsById.begin();
        while (it != clientCursorsById.end()) {
            ClientCursor* cc = it->second;

            // We're only interested in cursors over one db.
            if (cc->_db != db) {
                ++it;
                continue;
            }

            // Note that a valid ClientCursor state is "no cursor no runner."  This is because
            // the set of active cursor IDs in ClientCursor is used as representation of query
            // state.  See sharding_block.h.  TODO(greg,hk): Move this out.
            if (NULL == cc->c() && NULL == cc->_runner.get()) {
                ++it;
                continue;
            }

            bool shouldDelete = false;

            // We will only delete CCs with runners that are not actively in use.  The runners that
            // are actively in use are instead kill()-ed.
            if (NULL != cc->_runner.get()) {
                verify(NULL == cc->c());

                if (isDB || cc->_runner->ns() == ns) {
                    // If there is a pinValue >= 100, somebody is actively using the CC and we do
                    // not delete it.  Instead we notify the holder that we killed it.  The holder
                    // will then delete the CC.
                    if (cc->_pinValue >= 100) {
                        cc->_runner->kill();
                    }
                    else {
                        // pinvalue is <100, so there is nobody actively holding the CC.  We can
                        // safely delete it as nobody is holding the CC.
                        shouldDelete = true;
                    }
                }
            }
            // Begin cursor-only DEPRECATED
            else if (cc->c()->shouldDestroyOnNSDeletion()) {
                verify(NULL == cc->_runner.get());

                if (isDB) {
                    // already checked that db matched above
                    dassert( StringData(cc->_ns).startsWith( ns ) );
                    shouldDelete = true;
                }
                else {
                    if ( ns == cc->_ns ) {
                        shouldDelete = true;
                    }
                }
            }
            // End cursor-only DEPRECATED

            if (shouldDelete) {
                ClientCursor* toDelete = it->second;
                CursorId id = toDelete->cursorid();
                delete toDelete;
                // We're not following the usual paradigm of saving it, ++it, and deleting the saved
                // 'it' because deleting 'it' might invalidate the next thing in clientCursorsById.
                // TODO: Why?
                it = clientCursorsById.upper_bound(id);
            }
            else {
                ++it;
            }
        }
    }