diff options
Diffstat (limited to 'src/mongo/db/dbhelpers.cpp')
-rw-r--r-- | src/mongo/db/dbhelpers.cpp | 353 |
1 files changed, 353 insertions, 0 deletions
diff --git a/src/mongo/db/dbhelpers.cpp b/src/mongo/db/dbhelpers.cpp new file mode 100644 index 00000000000..39540c9ce89 --- /dev/null +++ b/src/mongo/db/dbhelpers.cpp @@ -0,0 +1,353 @@ +// dbhelpers.cpp + +/** +* Copyright (C) 2008 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/>. +*/ + +#include "pch.h" +#include "db.h" +#include "dbhelpers.h" +#include "json.h" +#include "queryoptimizer.h" +#include "btree.h" +#include "pdfile.h" +#include "oplog.h" +#include "ops/update.h" +#include "ops/delete.h" + +namespace mongo { + + void Helpers::ensureIndex(const char *ns, BSONObj keyPattern, bool unique, const char *name) { + NamespaceDetails *d = nsdetails(ns); + if( d == 0 ) + return; + + { + NamespaceDetails::IndexIterator i = d->ii(); + while( i.more() ) { + if( i.next().keyPattern().woCompare(keyPattern) == 0 ) + return; + } + } + + if( d->nIndexes >= NamespaceDetails::NIndexesMax ) { + problem() << "Helper::ensureIndex fails, MaxIndexes exceeded " << ns << '\n'; + return; + } + + string system_indexes = cc().database()->name + ".system.indexes"; + + BSONObjBuilder b; + b.append("name", name); + b.append("ns", ns); + b.append("key", keyPattern); + b.appendBool("unique", unique); + BSONObj o = b.done(); + + theDataFileMgr.insert(system_indexes.c_str(), o.objdata(), o.objsize()); + } + + /* fetch a single object from collection ns that matches query + set your db SavedContext first + */ + bool Helpers::findOne(const char *ns, const BSONObj &query, BSONObj& result, bool requireIndex) { + DiskLoc loc = findOne( ns, query, requireIndex ); + if ( loc.isNull() ) + return false; + result = loc.obj(); + return true; + } + + /* fetch a single object from collection ns that matches query + set your db SavedContext first + */ + DiskLoc Helpers::findOne(const char *ns, const BSONObj &query, bool requireIndex) { + shared_ptr<Cursor> c = NamespaceDetailsTransient::getCursor( ns, query, BSONObj(), requireIndex ); + while( c->ok() ) { + if ( c->currentMatches() && !c->getsetdup( c->currLoc() ) ) { + return c->currLoc(); + } + c->advance(); + } + return DiskLoc(); + } + + bool Helpers::findById(Client& c, const char *ns, BSONObj query, BSONObj& result , + bool * nsFound , bool * indexFound ) { + d.dbMutex.assertAtLeastReadLocked(); + Database *database = c.database(); + assert( database ); + NamespaceDetails *d = database->namespaceIndex.details(ns); + if ( ! d ) + return false; + if ( nsFound ) + *nsFound = 1; + + int idxNo = d->findIdIndex(); + if ( idxNo < 0 ) + return false; + if ( indexFound ) + *indexFound = 1; + + IndexDetails& i = d->idx( idxNo ); + + BSONObj key = i.getKeyFromQuery( query ); + + DiskLoc loc = i.idxInterface().findSingle(i , i.head , key); + if ( loc.isNull() ) + return false; + result = loc.obj(); + return true; + } + + DiskLoc Helpers::findById(NamespaceDetails *d, BSONObj idquery) { + assert(d); + int idxNo = d->findIdIndex(); + uassert(13430, "no _id index", idxNo>=0); + IndexDetails& i = d->idx( idxNo ); + BSONObj key = i.getKeyFromQuery( idquery ); + return i.idxInterface().findSingle(i , i.head , key); + } + + bool Helpers::isEmpty(const char *ns, bool doAuth) { + Client::Context context(ns, dbpath, doAuth); + shared_ptr<Cursor> c = DataFileMgr::findAll(ns); + return !c->ok(); + } + + /* Get the first object from a collection. Generally only useful if the collection + only ever has a single object -- which is a "singleton collection. + + Returns: true if object exists. + */ + bool Helpers::getSingleton(const char *ns, BSONObj& result) { + Client::Context context(ns); + + shared_ptr<Cursor> c = DataFileMgr::findAll(ns); + if ( !c->ok() ) { + context.getClient()->curop()->done(); + return false; + } + + result = c->current(); + context.getClient()->curop()->done(); + return true; + } + + bool Helpers::getLast(const char *ns, BSONObj& result) { + Client::Context ctx(ns); + shared_ptr<Cursor> c = findTableScan(ns, reverseNaturalObj); + if( !c->ok() ) + return false; + result = c->current(); + return true; + } + + void Helpers::upsert( const string& ns , const BSONObj& o ) { + BSONElement e = o["_id"]; + assert( e.type() ); + BSONObj id = e.wrap(); + + OpDebug debug; + Client::Context context(ns); + updateObjects(ns.c_str(), o, /*pattern=*/id, /*upsert=*/true, /*multi=*/false , /*logtheop=*/true , debug ); + } + + void Helpers::putSingleton(const char *ns, BSONObj obj) { + OpDebug debug; + Client::Context context(ns); + updateObjects(ns, obj, /*pattern=*/BSONObj(), /*upsert=*/true, /*multi=*/false , /*logtheop=*/true , debug ); + context.getClient()->curop()->done(); + } + + void Helpers::putSingletonGod(const char *ns, BSONObj obj, bool logTheOp) { + OpDebug debug; + Client::Context context(ns); + _updateObjects(/*god=*/true, ns, obj, /*pattern=*/BSONObj(), /*upsert=*/true, /*multi=*/false , logTheOp , debug ); + context.getClient()->curop()->done(); + } + + BSONObj Helpers::toKeyFormat( const BSONObj& o , BSONObj& key ) { + BSONObjBuilder me; + BSONObjBuilder k; + + BSONObjIterator i( o ); + while ( i.more() ) { + BSONElement e = i.next(); + k.append( e.fieldName() , 1 ); + me.appendAs( e , "" ); + } + key = k.obj(); + return me.obj(); + } + + long long Helpers::removeRange( const string& ns , const BSONObj& min , const BSONObj& max , bool yield , bool maxInclusive , RemoveCallback * callback ) { + BSONObj keya , keyb; + BSONObj minClean = toKeyFormat( min , keya ); + BSONObj maxClean = toKeyFormat( max , keyb ); + assert( keya == keyb ); + + Client::Context ctx(ns); + NamespaceDetails* nsd = nsdetails( ns.c_str() ); + if ( ! nsd ) + return 0; + + int ii = nsd->findIndexByKeyPattern( keya ); + assert( ii >= 0 ); + + long long num = 0; + + IndexDetails& i = nsd->idx( ii ); + + shared_ptr<Cursor> c( BtreeCursor::make( nsd , ii , i , minClean , maxClean , maxInclusive, 1 ) ); + auto_ptr<ClientCursor> cc( new ClientCursor( QueryOption_NoCursorTimeout , c , ns ) ); + cc->setDoingDeletes( true ); + + while ( c->ok() ) { + + if ( yield && ! cc->yieldSometimes( ClientCursor::WillNeed) ) { + // cursor got finished by someone else, so we're done + cc.release(); // if the collection/db is dropped, cc may be deleted + break; + } + + if ( ! c->ok() ) + break; + + DiskLoc rloc = c->currLoc(); + + if ( callback ) + callback->goingToDelete( c->current() ); + + c->advance(); + c->noteLocation(); + + logOp( "d" , ns.c_str() , rloc.obj()["_id"].wrap() ); + theDataFileMgr.deleteRecord(ns.c_str() , rloc.rec(), rloc); + num++; + + c->checkLocation(); + + getDur().commitIfNeeded(); + + + } + + return num; + } + + void Helpers::emptyCollection(const char *ns) { + Client::Context context(ns); + deleteObjects(ns, BSONObj(), false); + } + + DbSet::~DbSet() { + if ( name_.empty() ) + return; + try { + Client::Context c( name_.c_str() ); + if ( nsdetails( name_.c_str() ) ) { + string errmsg; + BSONObjBuilder result; + dropCollection( name_, errmsg, result ); + } + } + catch ( ... ) { + problem() << "exception cleaning up DbSet" << endl; + } + } + + void DbSet::reset( const string &name, const BSONObj &key ) { + if ( !name.empty() ) + name_ = name; + if ( !key.isEmpty() ) + key_ = key.getOwned(); + Client::Context c( name_.c_str() ); + if ( nsdetails( name_.c_str() ) ) { + Helpers::emptyCollection( name_.c_str() ); + } + else { + string err; + massert( 10303 , err, userCreateNS( name_.c_str(), fromjson( "{autoIndexId:false}" ), err, false ) ); + } + Helpers::ensureIndex( name_.c_str(), key_, true, "setIdx" ); + } + + bool DbSet::get( const BSONObj &obj ) const { + Client::Context c( name_.c_str() ); + BSONObj temp; + return Helpers::findOne( name_.c_str(), obj, temp, true ); + } + + void DbSet::set( const BSONObj &obj, bool val ) { + Client::Context c( name_.c_str() ); + if ( val ) { + try { + BSONObj k = obj; + theDataFileMgr.insertWithObjMod( name_.c_str(), k, false ); + } + catch ( DBException& ) { + // dup key - already in set + } + } + else { + deleteObjects( name_.c_str(), obj, true, false, false ); + } + } + + RemoveSaver::RemoveSaver( const string& a , const string& b , const string& why) : _out(0) { + static int NUM = 0; + + _root = dbpath; + if ( a.size() ) + _root /= a; + if ( b.size() ) + _root /= b; + assert( a.size() || b.size() ); + + _file = _root; + + stringstream ss; + ss << why << "." << terseCurrentTime(false) << "." << NUM++ << ".bson"; + _file /= ss.str(); + + } + + RemoveSaver::~RemoveSaver() { + if ( _out ) { + _out->close(); + delete _out; + _out = 0; + } + } + + void RemoveSaver::goingToDelete( const BSONObj& o ) { + if ( ! _out ) { + create_directories( _root ); + _out = new ofstream(); + _out->open( _file.string().c_str() , ios_base::out | ios_base::binary ); + if ( ! _out->good() ) { + log( LL_WARNING ) << "couldn't create file: " << _file.string() << " for remove saving" << endl; + delete _out; + _out = 0; + return; + } + + } + _out->write( o.objdata() , o.objsize() ); + } + + +} // namespace mongo |