// expression_where.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 .
*
* 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/auth/authorization_session.h"
#include "mongo/db/namespace_string.h"
#include "mongo/db/client.h"
#include "mongo/db/jsobj.h"
#include "mongo/db/matcher/expression.h"
#include "mongo/db/matcher/expression_parser.h"
#include "mongo/scripting/engine.h"
namespace mongo {
class WhereMatchExpression : public MatchExpression {
public:
WhereMatchExpression() : MatchExpression( WHERE ){ _func = 0; }
virtual ~WhereMatchExpression(){}
Status init(const StringData& dbName, const StringData& theCode, const BSONObj& scope);
virtual bool matches( const MatchableDocument* doc, MatchDetails* details = 0 ) const;
virtual bool matchesSingleElement( const BSONElement& e ) const {
return false;
}
virtual MatchExpression* shallowClone() const {
WhereMatchExpression* e = new WhereMatchExpression();
e->init(_dbName, _code, _userScope);
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 _dbName;
string _code;
BSONObj _userScope;
auto_ptr _scope;
ScriptingFunction _func;
};
Status WhereMatchExpression::init( const StringData& dbName,
const StringData& theCode,
const BSONObj& scope ) {
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");
}
_dbName = dbName.toString();
_code = theCode.toString();
_userScope = scope.getOwned();
const string userToken = ClientBasic::getCurrent()->getAuthorizationSession()
->getAuthenticatedUserNamesToken();
_scope = globalScriptEngine->getPooledScope(_dbName, "where" + userToken);
_func = _scope->createFunction( _code.c_str() );
if ( !_func )
return Status( ErrorCodes::BadValue, "$where compile error" );
return Status::OK();
}
bool WhereMatchExpression::matches( const MatchableDocument* doc, MatchDetails* details ) const {
verify( _func );
BSONObj obj = doc->toBSON();
if ( ! _userScope.isEmpty() ) {
_scope->init( &_userScope );
}
_scope->setObject( "obj", const_cast< BSONObj & >( obj ) );
_scope->setBoolean( "fullObject" , true ); // this is a hack b/c fullObject used to be relevant
int err = _scope->invoke( _func, 0, &obj, 1000 * 60, false );
if ( err == -3 ) { // INVOKE_ERROR
stringstream ss;
ss << "error on invocation of $where function:\n"
<< _scope->getError();
uassert( 16812, ss.str(), false);
}
else if ( err != 0 ) { // ! INVOKE_SUCCESS
uassert( 16813, "unknown error in invocation of $where function", false);
}
return _scope->getBoolean( "__returnValue" ) != 0;
}
void WhereMatchExpression::debugString( StringBuilder& debug, int level ) const {
_debugAddSpace( debug, level );
debug << "$where\n";
_debugAddSpace( debug, level + 1 );
debug << "dbName: " << _dbName << "\n";
_debugAddSpace( debug, level + 1 );
debug << "code: " << _code << "\n";
_debugAddSpace( debug, level + 1 );
debug << "scope: " << _userScope << "\n";
}
bool WhereMatchExpression::equivalent( const MatchExpression* other ) const {
if ( matchType() != other->matchType() )
return false;
const WhereMatchExpression* realOther = static_cast(other);
return
_dbName == realOther->_dbName &&
_code == realOther->_code &&
_userScope == realOther->_userScope;
}
WhereCallbackReal::WhereCallbackReal(const StringData& dbName)
: _dbName(dbName) {
}
StatusWithMatchExpression WhereCallbackReal::parseWhere(const BSONElement& where) const {
if (!globalScriptEngine)
return StatusWithMatchExpression(ErrorCodes::BadValue,
"no globalScriptEngine in $where parsing");
auto_ptr 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(_dbName,
where.codeWScopeCode(),
BSONObj(where.codeWScopeScopeDataUnsafe()));
if (!s.isOK())
return StatusWithMatchExpression(s);
return StatusWithMatchExpression(exp.release());
}
return StatusWithMatchExpression(ErrorCodes::BadValue, "$where got bad type");
}
}