diff options
author | Kaloian Manassiev <kaloian.manassiev@mongodb.com> | 2014-05-07 17:10:59 -0400 |
---|---|---|
committer | Kaloian Manassiev <kaloian.manassiev@mongodb.com> | 2014-05-09 15:53:49 -0400 |
commit | dddf68d86a8c12b0e37d8b3ccfdb58623aaba4e3 (patch) | |
tree | b0e0847de6499d318885b122371341698c03d243 /src/mongo/db/matcher | |
parent | 19cceceb780ab13104d5a4e44c373472ac5f430d (diff) | |
download | mongo-dddf68d86a8c12b0e37d8b3ccfdb58623aaba4e3.tar.gz |
SERVER-13797 Abstract $where processing and remove usages of getContext
Diffstat (limited to 'src/mongo/db/matcher')
-rw-r--r-- | src/mongo/db/matcher/expression_geo_test.cpp | 2 | ||||
-rw-r--r-- | src/mongo/db/matcher/expression_parser.cpp | 22 | ||||
-rw-r--r-- | src/mongo/db/matcher/expression_parser.h | 84 | ||||
-rw-r--r-- | src/mongo/db/matcher/expression_where.cpp | 92 | ||||
-rw-r--r-- | src/mongo/db/matcher/expression_where_noop.cpp | 128 | ||||
-rw-r--r-- | src/mongo/db/matcher/matcher.cpp | 9 | ||||
-rw-r--r-- | src/mongo/db/matcher/matcher.h | 10 |
7 files changed, 251 insertions, 96 deletions
diff --git a/src/mongo/db/matcher/expression_geo_test.cpp b/src/mongo/db/matcher/expression_geo_test.cpp index f49b966c25b..6f22f47c20f 100644 --- a/src/mongo/db/matcher/expression_geo_test.cpp +++ b/src/mongo/db/matcher/expression_geo_test.cpp @@ -34,7 +34,7 @@ #include "mongo/db/jsobj.h" #include "mongo/db/json.h" -#include "mongo/db/matcher.h" +#include "mongo/db/matcher/matcher.h" #include "mongo/db/matcher/expression.h" #include "mongo/db/matcher/expression_geo.h" diff --git a/src/mongo/db/matcher/expression_parser.cpp b/src/mongo/db/matcher/expression_parser.cpp index e8b1ad250ce..e2bdd1dc066 100644 --- a/src/mongo/db/matcher/expression_parser.cpp +++ b/src/mongo/db/matcher/expression_parser.cpp @@ -41,6 +41,7 @@ #include "mongo/util/log.h" #include "mongo/util/mongoutils/str.h" + namespace { using namespace mongo; @@ -333,12 +334,7 @@ namespace mongo { root->add( new AtomicMatchExpression() ); } else if ( mongoutils::str::equals( "where", rest ) ) { - /* - if ( !topLevel ) - return StatusWithMatchExpression( ErrorCodes::BadValue, - "$where has to be at the top level" ); - */ - StatusWithMatchExpression s = expressionParserWhereCallback( e ); + StatusWithMatchExpression s = _whereCallback->parseWhere(e); if ( !s.isOK() ) return s; root->add( s.getValue() ); @@ -796,6 +792,12 @@ namespace mongo { return StatusWithMatchExpression( myAnd.release() ); } + StatusWithMatchExpression MatchExpressionParser::WhereCallback::parseWhere( + const BSONElement& where) const { + return StatusWithMatchExpression(ErrorCodes::NoWhereParseContext, + "no context for parsing $where"); + } + // Geo StatusWithMatchExpression expressionParserGeoCallbackDefault( const char* name, int type, @@ -806,14 +808,6 @@ namespace mongo { MatchExpressionParserGeoCallback expressionParserGeoCallback = expressionParserGeoCallbackDefault; - // Where - StatusWithMatchExpression expressionParserWhereCallbackDefault(const BSONElement& where) { - return StatusWithMatchExpression( ErrorCodes::BadValue, "$where not linked in" ); - } - - MatchExpressionParserWhereCallback expressionParserWhereCallback = - expressionParserWhereCallbackDefault; - // Text StatusWithMatchExpression expressionParserTextCallbackDefault( const BSONObj& queryObj ) { return StatusWithMatchExpression( ErrorCodes::BadValue, "$text not linked in" ); diff --git a/src/mongo/db/matcher/expression_parser.h b/src/mongo/db/matcher/expression_parser.h index b4c45e6cb81..ae2c2a991ac 100644 --- a/src/mongo/db/matcher/expression_parser.h +++ b/src/mongo/db/matcher/expression_parser.h @@ -46,16 +46,37 @@ namespace mongo { public: /** + * In general, expression parsing and matching should not require context, but the $where + * clause is an exception in that it needs to read the sys.js collection. + * + * The default behaviour is to return an error status that $where context is not present. + * + * Do not use this class to pass-in generic context as it should only be used for $where. + */ + class WhereCallback { + public: + virtual StatusWithMatchExpression parseWhere(const BSONElement& where) const; + + virtual ~WhereCallback() { } + }; + + /** * caller has to maintain ownership obj * the tree has views (BSONElement) into obj */ - static StatusWithMatchExpression parse( const BSONObj& obj ) { + static StatusWithMatchExpression parse(const BSONObj& obj, + const WhereCallback& whereCallback = WhereCallback()) { // The 0 initializes the match expression tree depth. - return _parse( obj, 0 ); + return MatchExpressionParser(&whereCallback)._parse(obj, 0); } private: + explicit MatchExpressionParser(const WhereCallback* whereCallback) + : _whereCallback(whereCallback) { + + } + /** * 5 = false * { a : 5 } = false @@ -66,7 +87,7 @@ namespace mongo { * { $id : "x" } = false (if incomplete DBRef is allowed) * { $db : "mydb" } = false (if incomplete DBRef is allowed) */ - static bool _isExpressionDocument( const BSONElement& e, bool allowIncompleteDBRef ); + bool _isExpressionDocument( const BSONElement& e, bool allowIncompleteDBRef ); /** * { $ref: "s", $id: "x" } = true @@ -74,7 +95,7 @@ namespace mongo { * { $id : "x" } = true (if incomplete DBRef is allowed) * { $db : "x" } = true (if incomplete DBRef is allowed) */ - static bool _isDBRefDocument( const BSONObj& obj, bool allowIncompleteDBRef ); + bool _isDBRefDocument( const BSONObj& obj, bool allowIncompleteDBRef ); /** * Parse 'obj' and return either a MatchExpression or an error. @@ -83,14 +104,14 @@ namespace mongo { * function. Used in order to apply special logic at the top-level and to return an * error if the tree exceeds the maximum allowed depth. */ - static StatusWithMatchExpression _parse( const BSONObj& obj, int level ); + StatusWithMatchExpression _parse( const BSONObj& obj, int level ); /** * parses a field in a sub expression * if the query is { x : { $gt : 5, $lt : 8 } } * e is { $gt : 5, $lt : 8 } */ - static Status _parseSub( const char* name, + Status _parseSub( const char* name, const BSONObj& obj, AndMatchExpression* root, int level ); @@ -100,57 +121,82 @@ namespace mongo { * if the query is { x : { $gt : 5, $lt : 8 } } * e is $gt : 5 */ - static StatusWithMatchExpression _parseSubField( const BSONObj& context, + StatusWithMatchExpression _parseSubField( const BSONObj& context, const AndMatchExpression* andSoFar, const char* name, const BSONElement& e, int level ); - static StatusWithMatchExpression _parseComparison( const char* name, + StatusWithMatchExpression _parseComparison( const char* name, ComparisonMatchExpression* cmp, const BSONElement& e ); - static StatusWithMatchExpression _parseMOD( const char* name, + StatusWithMatchExpression _parseMOD( const char* name, const BSONElement& e ); - static StatusWithMatchExpression _parseRegexElement( const char* name, + StatusWithMatchExpression _parseRegexElement( const char* name, const BSONElement& e ); - static StatusWithMatchExpression _parseRegexDocument( const char* name, + StatusWithMatchExpression _parseRegexDocument( const char* name, const BSONObj& doc ); - static Status _parseArrayFilterEntries( ArrayFilterEntries* entries, + Status _parseArrayFilterEntries( ArrayFilterEntries* entries, const BSONObj& theArray ); // arrays - static StatusWithMatchExpression _parseElemMatch( const char* name, + StatusWithMatchExpression _parseElemMatch( const char* name, const BSONElement& e, int level ); - static StatusWithMatchExpression _parseAll( const char* name, + StatusWithMatchExpression _parseAll( const char* name, const BSONElement& e, int level ); // tree - static Status _parseTreeList( const BSONObj& arr, ListOfMatchExpression* out, int level ); + Status _parseTreeList( const BSONObj& arr, ListOfMatchExpression* out, int level ); - static StatusWithMatchExpression _parseNot( const char* name, + StatusWithMatchExpression _parseNot( const char* name, const BSONElement& e, int level ); // The maximum allowed depth of a query tree. Just to guard against stack overflow. static const int kMaximumTreeDepth; + + // Performs parsing for the $where clause. We do not own this pointer - it has to live + // as long as the parser is active. + const WhereCallback* _whereCallback; + }; + + /** + * This implementation is used for the server-side code. + */ + class WhereCallbackReal : public MatchExpressionParser::WhereCallback { + public: + WhereCallbackReal(const StringData& dbName); + + virtual StatusWithMatchExpression parseWhere(const BSONElement& where) const; + + private: + const StringData _dbName; }; + /** + * This is just a pass-through implementation, used by sharding only. + */ + class WhereCallbackNoop : public MatchExpressionParser::WhereCallback { + public: + WhereCallbackNoop(); + + virtual StatusWithMatchExpression parseWhere(const BSONElement& where) const; + }; + + typedef boost::function<StatusWithMatchExpression(const char* name, int type, const BSONObj& section)> MatchExpressionParserGeoCallback; extern MatchExpressionParserGeoCallback expressionParserGeoCallback; - typedef boost::function<StatusWithMatchExpression(const BSONElement& where)> MatchExpressionParserWhereCallback; - extern MatchExpressionParserWhereCallback expressionParserWhereCallback; - typedef boost::function<StatusWithMatchExpression(const BSONObj& queryObj)> MatchExpressionParserTextCallback; extern MatchExpressionParserTextCallback expressionParserTextCallback; diff --git a/src/mongo/db/matcher/expression_where.cpp b/src/mongo/db/matcher/expression_where.cpp index 8486155a6e9..2bc99116d22 100644 --- a/src/mongo/db/matcher/expression_where.cpp +++ b/src/mongo/db/matcher/expression_where.cpp @@ -38,6 +38,7 @@ #include "mongo/db/matcher/expression_parser.h" #include "mongo/scripting/engine.h" + namespace mongo { class WhereMatchExpression : public MatchExpression { @@ -45,7 +46,7 @@ namespace mongo { WhereMatchExpression() : MatchExpression( WHERE ){ _func = 0; } virtual ~WhereMatchExpression(){} - Status init( const StringData& ns, const StringData& theCode, const BSONObj& scope ); + Status init(const StringData& dbName, const StringData& theCode, const BSONObj& scope); virtual bool matches( const MatchableDocument* doc, MatchDetails* details = 0 ) const; @@ -55,7 +56,7 @@ namespace mongo { virtual MatchExpression* shallowClone() const { WhereMatchExpression* e = new WhereMatchExpression(); - e->init(_ns, _code, _userScope); + e->init(_dbName, _code, _userScope); if ( getTag() ) { e->setTag(getTag()->clone()); } @@ -69,7 +70,7 @@ namespace mongo { virtual void resetTag() { setTag(NULL); } private: - string _ns; + string _dbName; string _code; BSONObj _userScope; @@ -77,25 +78,25 @@ namespace mongo { ScriptingFunction _func; }; - Status WhereMatchExpression::init( const StringData& ns, + Status WhereMatchExpression::init( const StringData& dbName, const StringData& theCode, const BSONObj& scope ) { - if ( ns.size() == 0 ) - return Status( ErrorCodes::BadValue, "ns for $where cannot be empty" ); + if (dbName.size() == 0) { + return Status(ErrorCodes::BadValue, "ns for $where cannot be empty"); + } - if ( theCode.size() == 0 ) - return Status( ErrorCodes::BadValue, "code for $where cannot be empty" ); + if (theCode.size() == 0) { + return Status(ErrorCodes::BadValue, "code for $where cannot be empty"); + } - _ns = ns.toString(); + _dbName = dbName.toString(); _code = theCode.toString(); _userScope = scope.getOwned(); - NamespaceString nswrapper( _ns ); const string userToken = ClientBasic::getCurrent()->getAuthorizationSession() ->getAuthenticatedUserNamesToken(); - _scope = globalScriptEngine->getPooledScope( nswrapper.db().toString(), - "where" + userToken ); + _scope = globalScriptEngine->getPooledScope(_dbName, "where" + userToken); _func = _scope->createFunction( _code.c_str() ); if ( !_func ) @@ -133,7 +134,7 @@ namespace mongo { debug << "$where\n"; _debugAddSpace( debug, level + 1 ); - debug << "ns: " << _ns << "\n"; + debug << "dbName: " << _dbName << "\n"; _debugAddSpace( debug, level + 1 ); debug << "code: " << _code << "\n"; @@ -147,57 +148,38 @@ namespace mongo { return false; const WhereMatchExpression* realOther = static_cast<const WhereMatchExpression*>(other); return - _ns == realOther->_ns && + _dbName == realOther->_dbName && _code == realOther->_code && _userScope == realOther->_userScope; } + WhereCallbackReal::WhereCallbackReal(const StringData& dbName) + : _dbName(dbName) { - // ----------------- - - StatusWithMatchExpression expressionParserWhereCallbackReal(const BSONElement& where) { - if ( !haveClient() ) - return StatusWithMatchExpression( ErrorCodes::BadValue, "no current client needed for $where" ); - - Client::Context* context = cc().getContext(); - if ( !context ) - return StatusWithMatchExpression( ErrorCodes::NoClientContext, - "no context in $where parsing" ); - - const char* ns = context->ns(); - if ( !ns ) - return StatusWithMatchExpression( ErrorCodes::BadValue, "no ns in $where parsing" ); - - if ( !globalScriptEngine ) - return StatusWithMatchExpression( ErrorCodes::BadValue, "no globalScriptEngine in $where parsing" ); + } - auto_ptr<WhereMatchExpression> exp( new WhereMatchExpression() ); - if ( where.type() == String || where.type() == Code ) { - Status s = exp->init( ns, where.valuestr(), BSONObj() ); - if ( !s.isOK() ) - return StatusWithMatchExpression( s ); - return StatusWithMatchExpression( exp.release() ); + StatusWithMatchExpression WhereCallbackReal::parseWhere(const BSONElement& where) const { + if (!globalScriptEngine) + return StatusWithMatchExpression(ErrorCodes::BadValue, + "no globalScriptEngine in $where parsing"); + + auto_ptr<WhereMatchExpression> exp(new WhereMatchExpression()); + if (where.type() == String || where.type() == Code) { + Status s = exp->init(_dbName, where.valuestr(), BSONObj()); + if (!s.isOK()) + return StatusWithMatchExpression(s); + return StatusWithMatchExpression(exp.release()); } - if ( where.type() == CodeWScope ) { - Status s = exp->init( ns, - where.codeWScopeCode(), - BSONObj( where.codeWScopeScopeDataUnsafe() ) ); - if ( !s.isOK() ) - return StatusWithMatchExpression( s ); - return StatusWithMatchExpression( exp.release() ); + if (where.type() == CodeWScope) { + Status s = exp->init(_dbName, + where.codeWScopeCode(), + BSONObj(where.codeWScopeScopeDataUnsafe())); + if (!s.isOK()) + return StatusWithMatchExpression(s); + return StatusWithMatchExpression(exp.release()); } - return StatusWithMatchExpression( ErrorCodes::BadValue, "$where got bad type" ); + return StatusWithMatchExpression(ErrorCodes::BadValue, "$where got bad type"); } - - MONGO_INITIALIZER( MatchExpressionWhere )( ::mongo::InitializerContext* context ) { - // This could be overrided by MatchExpressionWhereNoOp in mongos - expressionParserWhereCallback = expressionParserWhereCallbackReal; - return Status::OK(); - } - - - - } diff --git a/src/mongo/db/matcher/expression_where_noop.cpp b/src/mongo/db/matcher/expression_where_noop.cpp new file mode 100644 index 00000000000..10aee8ff457 --- /dev/null +++ b/src/mongo/db/matcher/expression_where_noop.cpp @@ -0,0 +1,128 @@ +// expression_where_noop.cpp + +/** + * Copyright (C) 2013 10gen Inc. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License, version 3, + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + * As a special exception, the copyright holders give permission to link the + * code of portions of this program with the OpenSSL library under certain + * conditions as described in each individual source file and distribute + * linked combinations including the program with the OpenSSL library. You + * must comply with the GNU Affero General Public License in all respects for + * all of the code used other than as permitted herein. If you modify file(s) + * with this exception, you may extend this exception to your version of the + * file(s), but you are not obligated to do so. If you do not wish to do so, + * delete this exception statement from your version. If you delete this + * exception statement from all source files in the program, then also delete + * it in the license file. + */ + +#include "mongo/pch.h" +#include "mongo/base/init.h" +#include "mongo/db/matcher/expression.h" +#include "mongo/db/matcher/expression_parser.h" + +namespace mongo { + + /** + * Bogus no-op $where match expression to parse $where in mongos, + * since mongos doesn't have script engine to compile JS functions. + * + * Linked into mongos, instead of the real WhereMatchExpression. + */ + class WhereNoOpMatchExpression : public MatchExpression { + public: + WhereNoOpMatchExpression() : MatchExpression( WHERE ){ } + virtual ~WhereNoOpMatchExpression(){} + + Status init( const StringData& theCode ); + + virtual bool matches( const MatchableDocument* doc, MatchDetails* details = 0 ) const { + return false; + } + + virtual bool matchesSingleElement( const BSONElement& e ) const { + return false; + } + + virtual MatchExpression* shallowClone() const { + WhereNoOpMatchExpression* e = new WhereNoOpMatchExpression(); + e->init(_code); + if ( getTag() ) { + e->setTag(getTag()->clone()); + } + return e; + } + + virtual void debugString( StringBuilder& debug, int level = 0 ) const; + + virtual bool equivalent( const MatchExpression* other ) const ; + + virtual void resetTag() { setTag(NULL); } + + private: + string _code; + }; + + Status WhereNoOpMatchExpression::init(const StringData& theCode ) { + if ( theCode.size() == 0 ) + return Status( ErrorCodes::BadValue, "code for $where cannot be empty" ); + + _code = theCode.toString(); + + return Status::OK(); + } + + void WhereNoOpMatchExpression::debugString( StringBuilder& debug, int level ) const { + _debugAddSpace( debug, level ); + debug << "$where (only in mongos)\n"; + + _debugAddSpace( debug, level + 1 ); + debug << "code: " << _code << "\n"; + } + + bool WhereNoOpMatchExpression::equivalent( const MatchExpression* other ) const { + if ( matchType() != other->matchType() ) + return false; + const WhereNoOpMatchExpression* noopOther = static_cast<const WhereNoOpMatchExpression*>(other); + return _code == noopOther->_code; + } + + + // ----------------- + + WhereCallbackNoop::WhereCallbackNoop() { + + } + + StatusWithMatchExpression WhereCallbackNoop::parseWhere(const BSONElement& where) const { + + auto_ptr<WhereNoOpMatchExpression> exp( new WhereNoOpMatchExpression() ); + if ( where.type() == String || where.type() == Code ) { + Status s = exp->init( where.valuestr() ); + if ( !s.isOK() ) + return StatusWithMatchExpression( s ); + return StatusWithMatchExpression( exp.release() ); + } + + if ( where.type() == CodeWScope ) { + Status s = exp->init( where.codeWScopeCode() ); + if ( !s.isOK() ) + return StatusWithMatchExpression( s ); + return StatusWithMatchExpression( exp.release() ); + } + + return StatusWithMatchExpression( ErrorCodes::BadValue, "$where got bad type" ); + } +} diff --git a/src/mongo/db/matcher/matcher.cpp b/src/mongo/db/matcher/matcher.cpp index a1d76fd943d..78528544250 100644 --- a/src/mongo/db/matcher/matcher.cpp +++ b/src/mongo/db/matcher/matcher.cpp @@ -41,10 +41,11 @@ namespace mongo { - Matcher2::Matcher2( const BSONObj& pattern, bool nested ) - : _pattern( pattern ) { + Matcher::Matcher(const BSONObj& pattern, + const MatchExpressionParser::WhereCallback& whereCallback) + : _pattern(pattern) { - StatusWithMatchExpression result = MatchExpressionParser::parse( pattern ); + StatusWithMatchExpression result = MatchExpressionParser::parse(pattern, whereCallback); uassert( 16810, mongoutils::str::stream() << "bad query: " << result.toString(), result.isOK() ); @@ -52,7 +53,7 @@ namespace mongo { _expression.reset( result.getValue() ); } - bool Matcher2::matches(const BSONObj& doc, MatchDetails* details ) const { + bool Matcher::matches(const BSONObj& doc, MatchDetails* details ) const { if ( !_expression ) return true; diff --git a/src/mongo/db/matcher/matcher.h b/src/mongo/db/matcher/matcher.h index c09f79b279a..278f6668789 100644 --- a/src/mongo/db/matcher/matcher.h +++ b/src/mongo/db/matcher/matcher.h @@ -36,18 +36,22 @@ #include "mongo/base/status.h" #include "mongo/bson/bsonobj.h" #include "mongo/db/matcher/expression.h" +#include "mongo/db/matcher/expression_parser.h" #include "mongo/db/matcher/match_details.h" + namespace mongo { /** * Matcher is a simple wrapper around a BSONObj and the MatchExpression created from it. */ - class Matcher2 { - MONGO_DISALLOW_COPYING( Matcher2 ); + class Matcher { + MONGO_DISALLOW_COPYING(Matcher); public: - explicit Matcher2( const BSONObj& pattern, bool nested=false /* do not use */ ); + explicit Matcher(const BSONObj& pattern, + const MatchExpressionParser::WhereCallback& whereCallback = + MatchExpressionParser::WhereCallback()); bool matches(const BSONObj& doc, MatchDetails* details = NULL ) const; |