summaryrefslogtreecommitdiff
path: root/src/mongo/db/matcher
diff options
context:
space:
mode:
authorKaloian Manassiev <kaloian.manassiev@mongodb.com>2014-05-07 17:10:59 -0400
committerKaloian Manassiev <kaloian.manassiev@mongodb.com>2014-05-09 15:53:49 -0400
commitdddf68d86a8c12b0e37d8b3ccfdb58623aaba4e3 (patch)
treeb0e0847de6499d318885b122371341698c03d243 /src/mongo/db/matcher
parent19cceceb780ab13104d5a4e44c373472ac5f430d (diff)
downloadmongo-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.cpp2
-rw-r--r--src/mongo/db/matcher/expression_parser.cpp22
-rw-r--r--src/mongo/db/matcher/expression_parser.h84
-rw-r--r--src/mongo/db/matcher/expression_where.cpp92
-rw-r--r--src/mongo/db/matcher/expression_where_noop.cpp128
-rw-r--r--src/mongo/db/matcher/matcher.cpp9
-rw-r--r--src/mongo/db/matcher/matcher.h10
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;