~ubuntu-branches/ubuntu/trusty/mongodb/trusty-proposed

« back to all changes in this revision

Viewing changes to db/cloner.cpp

  • Committer: Bazaar Package Importer
  • Author(s): Antonin Kral
  • Date: 2010-01-29 19:48:45 UTC
  • Revision ID: james.westby@ubuntu.com-20100129194845-8wbmkf626fwcavc9
Tags: upstream-1.3.1
ImportĀ upstreamĀ versionĀ 1.3.1

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
// cloner.cpp - copy a database (export/import basically)
 
2
 
 
3
/**
 
4
*    Copyright (C) 2008 10gen Inc.
 
5
*
 
6
*    This program is free software: you can redistribute it and/or  modify
 
7
*    it under the terms of the GNU Affero General Public License, version 3,
 
8
*    as published by the Free Software Foundation.
 
9
*
 
10
*    This program is distributed in the hope that it will be useful,
 
11
*    but WITHOUT ANY WARRANTY; without even the implied warranty of
 
12
*    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 
13
*    GNU Affero General Public License for more details.
 
14
*
 
15
*    You should have received a copy of the GNU Affero General Public License
 
16
*    along with this program.  If not, see <http://www.gnu.org/licenses/>.
 
17
*/
 
18
 
 
19
#include "stdafx.h"
 
20
#include "pdfile.h"
 
21
#include "../client/dbclient.h"
 
22
#include "../util/builder.h"
 
23
#include "jsobj.h"
 
24
#include "query.h"
 
25
#include "commands.h"
 
26
#include "db.h"
 
27
#include "instance.h"
 
28
#include "repl.h"
 
29
 
 
30
namespace mongo {
 
31
 
 
32
    void ensureHaveIdIndex(const char *ns);
 
33
 
 
34
    bool replAuthenticate(DBClientConnection *);
 
35
 
 
36
    class Cloner: boost::noncopyable {
 
37
        auto_ptr< DBClientWithCommands > conn;
 
38
        void copy(const char *from_ns, const char *to_ns, bool isindex, bool logForRepl,
 
39
                  bool masterSameProcess, bool slaveOk, Query q = Query());
 
40
        void replayOpLog( DBClientCursor *c, const BSONObj &query );
 
41
    public:
 
42
        Cloner() { }
 
43
 
 
44
        /* slaveOk     - if true it is ok if the source of the data is !ismaster.
 
45
           useReplAuth - use the credentials we normally use as a replication slave for the cloning
 
46
           snapshot    - use $snapshot mode for copying collections.  note this should not be used when it isn't required, as it will be slower.
 
47
                         for example repairDatabase need not use it.
 
48
        */
 
49
        bool go(const char *masterHost, string& errmsg, const string& fromdb, bool logForRepl, bool slaveOk, bool useReplAuth, bool snapshot);
 
50
        bool startCloneCollection( const char *fromhost, const char *ns, const BSONObj &query, string& errmsg, bool logForRepl, bool copyIndexes, int logSizeMb, long long &cursorId );
 
51
        bool finishCloneCollection( const char *fromhost, const char *ns, const BSONObj &query, long long cursorId, string &errmsg );
 
52
    };
 
53
 
 
54
    /* for index info object:
 
55
         { "name" : "name_1" , "ns" : "foo.index3" , "key" :  { "name" : 1.0 } }
 
56
       we need to fix up the value in the "ns" parameter so that the name prefix is correct on a
 
57
       copy to a new name.
 
58
    */
 
59
    BSONObj fixindex(BSONObj o) {
 
60
        BSONObjBuilder b;
 
61
        BSONObjIterator i(o);
 
62
        while ( i.moreWithEOO() ) {
 
63
            BSONElement e = i.next();
 
64
            if ( e.eoo() )
 
65
                break;
 
66
            if ( string("ns") == e.fieldName() ) {
 
67
                uassert( 10024 , "bad ns field for index during dbcopy", e.type() == String);
 
68
                const char *p = strchr(e.valuestr(), '.');
 
69
                uassert( 10025 , "bad ns field for index during dbcopy [2]", p);
 
70
                string newname = cc().database()->name + p;
 
71
                b.append("ns", newname);
 
72
            }
 
73
            else
 
74
                b.append(e);
 
75
        }
 
76
        BSONObj res= b.obj();
 
77
 
 
78
        /*    if( mod ) {
 
79
            out() << "before: " << o.toString() << endl;
 
80
            o.dump();
 
81
            out() << "after:  " << res.toString() << endl;
 
82
            res.dump();
 
83
            }*/
 
84
 
 
85
        return res;
 
86
    }
 
87
 
 
88
    /* copy the specified collection
 
89
       isindex - if true, this is system.indexes collection, in which we do some transformation when copying.
 
90
    */
 
91
    void Cloner::copy(const char *from_collection, const char *to_collection, bool isindex, bool logForRepl, bool masterSameProcess, bool slaveOk, Query query) {
 
92
        auto_ptr<DBClientCursor> c;
 
93
        {
 
94
            dbtemprelease r;
 
95
            c = conn->query( from_collection, query, 0, 0, 0, QueryOption_NoCursorTimeout | ( slaveOk ? QueryOption_SlaveOk : 0 ) );
 
96
        }
 
97
        
 
98
        list<BSONObj> storedForLater;
 
99
        
 
100
        assert( c.get() );
 
101
        long long n = 0;
 
102
        time_t saveLast = time( 0 );
 
103
        while ( 1 ) {
 
104
            {
 
105
                dbtemprelease r;
 
106
                if ( !c->more() )
 
107
                    break;
 
108
            }
 
109
            BSONObj tmp = c->next();
 
110
 
 
111
            /* assure object is valid.  note this will slow us down a little. */
 
112
            if ( !tmp.valid() ) {
 
113
                stringstream ss;
 
114
                ss << "skipping corrupt object from " << from_collection;
 
115
                BSONElement e = tmp.firstElement();
 
116
                try {
 
117
                    e.validate();
 
118
                    ss << " firstElement: " << e;
 
119
                }
 
120
                catch( ... ){
 
121
                    ss << " firstElement corrupt";
 
122
                }
 
123
                out() << ss.str() << endl;
 
124
                continue;
 
125
            }
 
126
 
 
127
            ++n;
 
128
            
 
129
            BSONObj js = tmp;
 
130
            if ( isindex ) {
 
131
                assert( strstr(from_collection, "system.indexes") );
 
132
                js = fixindex(tmp);
 
133
                storedForLater.push_back( js.getOwned() );
 
134
                continue;
 
135
            }
 
136
 
 
137
            try { 
 
138
                theDataFileMgr.insert(to_collection, js);
 
139
                if ( logForRepl )
 
140
                    logOp("i", to_collection, js);
 
141
            }
 
142
            catch( UserException& e ) { 
 
143
                log() << "warning: exception cloning object in " << from_collection << ' ' << e.what() << " obj:" << js.toString() << '\n';
 
144
            }
 
145
            
 
146
            RARELY if ( time( 0 ) - saveLast > 60 ) {
 
147
                log() << n << " objects cloned so far from collection " << from_collection << endl;
 
148
                saveLast = time( 0 );
 
149
            }
 
150
        }
 
151
 
 
152
        if ( storedForLater.size() ){
 
153
            for ( list<BSONObj>::iterator i = storedForLater.begin(); i!=storedForLater.end(); i++ ){
 
154
                BSONObj js = *i;
 
155
                try { 
 
156
                    theDataFileMgr.insert(to_collection, js);
 
157
                    if ( logForRepl )
 
158
                        logOp("i", to_collection, js);
 
159
                }
 
160
                catch( UserException& e ) { 
 
161
                    log() << "warning: exception cloning object in " << from_collection << ' ' << e.what() << " obj:" << js.toString() << '\n';
 
162
                }
 
163
            }
 
164
        }
 
165
    }
 
166
    
 
167
    bool Cloner::go(const char *masterHost, string& errmsg, const string& fromdb, bool logForRepl, bool slaveOk, bool useReplAuth, bool snapshot) {
 
168
 
 
169
                massert( 10289 ,  "useReplAuth is not written to replication log", !useReplAuth || !logForRepl );
 
170
 
 
171
        string todb = cc().database()->name;
 
172
        stringstream a,b;
 
173
        a << "localhost:" << cmdLine.port;
 
174
        b << "127.0.0.1:" << cmdLine.port;
 
175
        bool masterSameProcess = ( a.str() == masterHost || b.str() == masterHost );
 
176
        if ( masterSameProcess ) {
 
177
            if ( fromdb == todb && cc().database()->path == dbpath ) {
 
178
                // guard against an "infinite" loop
 
179
                /* if you are replicating, the local.sources config may be wrong if you get this */
 
180
                errmsg = "can't clone from self (localhost).";
 
181
                return false;
 
182
            }
 
183
        }
 
184
        /* todo: we can put these releases inside dbclient or a dbclient specialization.
 
185
           or just wait until we get rid of global lock anyway.
 
186
           */
 
187
        string ns = fromdb + ".system.namespaces";
 
188
        list<BSONObj> toClone;
 
189
        {  
 
190
            dbtemprelease r;
 
191
                
 
192
            auto_ptr<DBClientCursor> c;
 
193
            {
 
194
                if ( !masterSameProcess ) {
 
195
                    auto_ptr< DBClientConnection > c( new DBClientConnection() );
 
196
                    if ( !c->connect( masterHost, errmsg ) )
 
197
                        return false;
 
198
                    if( !replAuthenticate(c.get()) )
 
199
                        return false;
 
200
                    
 
201
                    conn = c;
 
202
                } else {
 
203
                    conn.reset( new DBDirectClient() );
 
204
                }
 
205
                c = conn->query( ns.c_str(), BSONObj(), 0, 0, 0, slaveOk ? QueryOption_SlaveOk : 0 );
 
206
            }
 
207
 
 
208
            if ( c.get() == 0 ) {
 
209
                errmsg = "query failed " + ns;
 
210
                return false;
 
211
            }
 
212
            
 
213
            while ( c->more() ){
 
214
                BSONObj collection = c->next();
 
215
 
 
216
                log(2) << "\t cloner got " << collection << endl;
 
217
 
 
218
                BSONElement e = collection.findElement("name");
 
219
                if ( e.eoo() ) {
 
220
                    string s = "bad system.namespaces object " + collection.toString();
 
221
                    massert( 10290 , s.c_str(), false);
 
222
                }
 
223
                assert( !e.eoo() );
 
224
                assert( e.type() == String );
 
225
                const char *from_name = e.valuestr();
 
226
 
 
227
                if( strstr(from_name, ".system.") ) { 
 
228
                    /* system.users is cloned -- but nothing else from system. */
 
229
                    if( legalClientSystemNS( from_name , true ) == 0 ){
 
230
                        log(2) << "\t\t not cloning because system collection" << endl;
 
231
                        continue;
 
232
                    }
 
233
                }
 
234
                else if( strchr(from_name, '$') ) {
 
235
                    // don't clone index namespaces -- we take care of those separately below.
 
236
                    log(2) << "\t\t not cloning because has $ " << endl;
 
237
                    continue;
 
238
                }            
 
239
                
 
240
                toClone.push_back( collection.getOwned() );
 
241
            }
 
242
        }
 
243
 
 
244
        for ( list<BSONObj>::iterator i=toClone.begin(); i != toClone.end(); i++ ){
 
245
            {
 
246
                dbtemprelease r;
 
247
            }
 
248
            BSONObj collection = *i;
 
249
            log(2) << "  really will clone: " << collection << endl;
 
250
            const char * from_name = collection["name"].valuestr();
 
251
            BSONObj options = collection.getObjectField("options");
 
252
            
 
253
            /* change name "<fromdb>.collection" -> <todb>.collection */
 
254
            const char *p = strchr(from_name, '.');
 
255
            assert(p);
 
256
            string to_name = todb + p;
 
257
 
 
258
            {
 
259
                string err;
 
260
                const char *toname = to_name.c_str();
 
261
                userCreateNS(toname, options, err, logForRepl);
 
262
            }
 
263
            log(1) << "\t\t cloning " << from_name << " -> " << to_name << endl;
 
264
            Query q;
 
265
            if( snapshot ) 
 
266
                q.snapshot();
 
267
            copy(from_name, to_name.c_str(), false, logForRepl, masterSameProcess, slaveOk, q);
 
268
        }
 
269
 
 
270
        // now build the indexes
 
271
        string system_indexes_from = fromdb + ".system.indexes";
 
272
        string system_indexes_to = todb + ".system.indexes";
 
273
        /* [dm]: is the ID index sometimes not called "_id_"?  There is other code in the system that looks for a "_id" prefix 
 
274
                 rather than this exact value.  we should standardize.  OR, remove names - which is in the bugdb.  Anyway, this 
 
275
                 is dubious here at the moment.
 
276
        */
 
277
        copy(system_indexes_from.c_str(), system_indexes_to.c_str(), true, logForRepl, masterSameProcess, slaveOk, BSON( "name" << NE << "_id_" ) );
 
278
 
 
279
        return true;
 
280
    }
 
281
 
 
282
    bool Cloner::startCloneCollection( const char *fromhost, const char *ns, const BSONObj &query, string &errmsg, bool logForRepl, bool copyIndexes, int logSizeMb, long long &cursorId ) {
 
283
        char db[256];
 
284
        nsToDatabase( ns, db );
 
285
 
 
286
        NamespaceDetails *nsd = nsdetails( ns );
 
287
        if ( nsd ){
 
288
            /** note: its ok to clone into a collection, but only if the range you're copying 
 
289
                doesn't exist on this server */
 
290
            string err;
 
291
            if ( runCount( ns , BSON( "query" << query ) , err ) > 0 ){
 
292
                log() << "WARNING: data already exists for: " << ns << " in range : " << query << " deleting..." << endl;
 
293
                deleteObjects( ns , query , false , logForRepl , false );
 
294
            }
 
295
        }
 
296
 
 
297
        {
 
298
            dbtemprelease r;
 
299
            auto_ptr< DBClientConnection > c( new DBClientConnection() );
 
300
            if ( !c->connect( fromhost, errmsg ) )
 
301
                return false;
 
302
            if( !replAuthenticate(c.get()) )
 
303
                return false;
 
304
            conn = c;
 
305
 
 
306
            // Start temporary op log
 
307
            BSONObjBuilder cmdSpec;
 
308
            cmdSpec << "logCollection" << ns << "start" << 1;
 
309
            if ( logSizeMb != INT_MIN )
 
310
                cmdSpec << "logSizeMb" << logSizeMb;
 
311
            BSONObj info;
 
312
            if ( !conn->runCommand( db, cmdSpec.done(), info ) ) {
 
313
                errmsg = "logCollection failed: " + (string)info;
 
314
                return false;
 
315
            }
 
316
        }
 
317
        
 
318
        if ( ! nsd ) {
 
319
            BSONObj spec = conn->findOne( string( db ) + ".system.namespaces", BSON( "name" << ns ) );
 
320
            if ( !userCreateNS( ns, spec.getObjectField( "options" ), errmsg, true ) )
 
321
                return false;
 
322
        }
 
323
 
 
324
        copy( ns, ns, false, logForRepl, false, false, query );
 
325
 
 
326
        if ( copyIndexes ) {
 
327
            string indexNs = string( db ) + ".system.indexes";
 
328
            copy( indexNs.c_str(), indexNs.c_str(), true, logForRepl, false, false, BSON( "ns" << ns << "name" << NE << "_id_" ) );
 
329
        }
 
330
        
 
331
        auto_ptr< DBClientCursor > c;
 
332
        {
 
333
            dbtemprelease r;
 
334
            string logNS = "local.temp.oplog." + string( ns );
 
335
            c = conn->query( logNS.c_str(), Query(), 0, 0, 0, QueryOption_CursorTailable );
 
336
        }
 
337
        if ( c->more() ) {
 
338
            replayOpLog( c.get(), query );
 
339
            cursorId = c->getCursorId();
 
340
            massert( 10291 ,  "Expected valid tailing cursor", cursorId != 0 );
 
341
        } else {
 
342
            massert( 10292 ,  "Did not expect valid cursor for empty query result", c->getCursorId() == 0 );
 
343
            cursorId = 0;
 
344
        }
 
345
        c->decouple();
 
346
        return true;
 
347
    }
 
348
    
 
349
    void Cloner::replayOpLog( DBClientCursor *c, const BSONObj &query ) {
 
350
        Matcher matcher( query );
 
351
        while( 1 ) {
 
352
            BSONObj op;
 
353
            {
 
354
                dbtemprelease t;
 
355
                if ( !c->more() )
 
356
                    break;
 
357
                op = c->next();
 
358
            }
 
359
            // For sharding v1.0, we don't allow shard key updates -- so just
 
360
            // filter each insert by value.
 
361
            if ( op.getStringField( "op" )[ 0 ] != 'i' || matcher.matches( op.getObjectField( "o" ) ) )
 
362
                ReplSource::applyOperation( op );
 
363
        }        
 
364
    }
 
365
    
 
366
    bool Cloner::finishCloneCollection( const char *fromhost, const char *ns, const BSONObj &query, long long cursorId, string &errmsg ) {
 
367
        char db[256];
 
368
        nsToDatabase( ns, db );
 
369
 
 
370
        auto_ptr< DBClientCursor > cur;
 
371
        {
 
372
            dbtemprelease r;
 
373
            auto_ptr< DBClientConnection > c( new DBClientConnection() );
 
374
            if ( !c->connect( fromhost, errmsg ) )
 
375
                return false;
 
376
            if( !replAuthenticate(c.get()) )
 
377
                return false;
 
378
            conn = c;            
 
379
            string logNS = "local.temp.oplog." + string( ns );
 
380
            if ( cursorId != 0 )
 
381
                cur = conn->getMore( logNS.c_str(), cursorId );
 
382
            else
 
383
                cur = conn->query( logNS.c_str(), Query() );
 
384
        }
 
385
        replayOpLog( cur.get(), query );
 
386
        {
 
387
            dbtemprelease t;
 
388
            BSONObj info;
 
389
            if ( !conn->runCommand( db, BSON( "logCollection" << ns << "validateComplete" << 1 ), info ) ) {
 
390
                errmsg = "logCollection failed: " + (string)info;
 
391
                return false;
 
392
            }
 
393
        }
 
394
        return true;
 
395
    }
 
396
    
 
397
    /* slaveOk     - if true it is ok if the source of the data is !ismaster.
 
398
       useReplAuth - use the credentials we normally use as a replication slave for the cloning
 
399
       snapshot    - use $snapshot mode for copying collections.  note this should not be used when it isn't required, as it will be slower.
 
400
                     for example repairDatabase need not use it.
 
401
    */
 
402
    bool cloneFrom(const char *masterHost, string& errmsg, const string& fromdb, bool logForReplication, 
 
403
                                   bool slaveOk, bool useReplAuth, bool snapshot)
 
404
    {
 
405
        Cloner c;
 
406
        return c.go(masterHost, errmsg, fromdb, logForReplication, slaveOk, useReplAuth, snapshot);
 
407
    }
 
408
    
 
409
    /* Usage:
 
410
       mydb.$cmd.findOne( { clone: "fromhost" } );
 
411
    */
 
412
    class CmdClone : public Command {
 
413
    public:
 
414
        virtual bool slaveOk() {
 
415
            return false;
 
416
        }
 
417
        virtual void help( stringstream &help ) const {
 
418
            help << "clone this database from an instance of the db on another host\n";
 
419
            help << "example: { clone : \"host13\" }";
 
420
        }
 
421
        CmdClone() : Command("clone") { }
 
422
        virtual bool run(const char *ns, BSONObj& cmdObj, string& errmsg, BSONObjBuilder& result, bool fromRepl) {
 
423
            string from = cmdObj.getStringField("clone");
 
424
            if ( from.empty() )
 
425
                return false;
 
426
            /* replication note: we must logOp() not the command, but the cloned data -- if the slave
 
427
               were to clone it would get a different point-in-time and not match.
 
428
               */
 
429
            return cloneFrom(from.c_str(), errmsg, cc().database()->name, 
 
430
                             /*logForReplication=*/!fromRepl, /*slaveok*/false, /*usereplauth*/false, /*snapshot*/true);
 
431
        }
 
432
    } cmdclone;
 
433
    
 
434
    class CmdCloneCollection : public Command {
 
435
    public:
 
436
        virtual bool slaveOk() {
 
437
            return false;
 
438
        }
 
439
        CmdCloneCollection() : Command("cloneCollection") { }
 
440
        virtual void help( stringstream &help ) const {
 
441
            help << " example: { cloneCollection: <collection ns>, from: <hostname>, query: <query> }";
 
442
        }
 
443
        virtual bool run(const char *ns, BSONObj& cmdObj, string& errmsg, BSONObjBuilder& result, bool fromRepl) {
 
444
            string fromhost = cmdObj.getStringField("from");
 
445
            if ( fromhost.empty() ) {
 
446
                errmsg = "missing from spec";
 
447
                return false;
 
448
            }
 
449
            string collection = cmdObj.getStringField("cloneCollection");
 
450
            if ( collection.empty() ) {
 
451
                errmsg = "missing cloneCollection spec";
 
452
                return false;
 
453
            }
 
454
            BSONObj query = cmdObj.getObjectField("query");
 
455
            if ( query.isEmpty() )
 
456
                query = BSONObj();
 
457
            BSONElement copyIndexesSpec = cmdObj.getField("copyindexes");
 
458
            bool copyIndexes = copyIndexesSpec.isBoolean() ? copyIndexesSpec.boolean() : true;
 
459
            // Will not be used if doesn't exist.
 
460
            int logSizeMb = cmdObj.getIntField( "logSizeMb" );
 
461
            
 
462
            /* replication note: we must logOp() not the command, but the cloned data -- if the slave
 
463
             were to clone it would get a different point-in-time and not match.
 
464
             */
 
465
            setClient( collection.c_str() );
 
466
            
 
467
            log() << "cloneCollection.  db:" << ns << " collection:" << collection << " from: " << fromhost << " query: " << query << " logSizeMb: " << logSizeMb << ( copyIndexes ? "" : ", not copying indexes" ) << endl;
 
468
            
 
469
            Cloner c;
 
470
            long long cursorId;
 
471
            if ( !c.startCloneCollection( fromhost.c_str(), collection.c_str(), query, errmsg, !fromRepl, copyIndexes, logSizeMb, cursorId ) )
 
472
                return false;
 
473
            return c.finishCloneCollection( fromhost.c_str(), collection.c_str(), query, cursorId, errmsg);
 
474
        }
 
475
    } cmdclonecollection;
 
476
 
 
477
    class CmdStartCloneCollection : public Command {
 
478
    public:
 
479
        virtual bool slaveOk() {
 
480
            return false;
 
481
        }
 
482
        CmdStartCloneCollection() : Command("startCloneCollection") { }
 
483
        virtual void help( stringstream &help ) const {
 
484
            help << " example: { startCloneCollection: <collection ns>, from: <hostname>, query: <query> }";
 
485
            help << ", returned object includes a finishToken field, the value of which may be passed to the finishCloneCollection command";
 
486
        }
 
487
        virtual bool run(const char *ns, BSONObj& cmdObj, string& errmsg, BSONObjBuilder& result, bool fromRepl) {
 
488
            string fromhost = cmdObj.getStringField("from");
 
489
            if ( fromhost.empty() ) {
 
490
                errmsg = "missing from spec";
 
491
                return false;
 
492
            }
 
493
            string collection = cmdObj.getStringField("startCloneCollection");
 
494
            if ( collection.empty() ) {
 
495
                errmsg = "missing startCloneCollection spec";
 
496
                return false;
 
497
            }
 
498
            BSONObj query = cmdObj.getObjectField("query");
 
499
            if ( query.isEmpty() )
 
500
                query = BSONObj();
 
501
            BSONElement copyIndexesSpec = cmdObj.getField("copyindexes");
 
502
            bool copyIndexes = copyIndexesSpec.isBoolean() ? copyIndexesSpec.boolean() : true;
 
503
            // Will not be used if doesn't exist.
 
504
            int logSizeMb = cmdObj.getIntField( "logSizeMb" );
 
505
            
 
506
            /* replication note: we must logOp() not the command, but the cloned data -- if the slave
 
507
             were to clone it would get a different point-in-time and not match.
 
508
             */
 
509
            setClient( collection.c_str() );
 
510
            
 
511
            log() << "startCloneCollection.  db:" << ns << " collection:" << collection << " from: " << fromhost << " query: " << query << endl;
 
512
            
 
513
            Cloner c;
 
514
            long long cursorId;
 
515
            bool res = c.startCloneCollection( fromhost.c_str(), collection.c_str(), query, errmsg, !fromRepl, copyIndexes, logSizeMb, cursorId );
 
516
            
 
517
            if ( res ) {
 
518
                BSONObjBuilder b;
 
519
                b << "fromhost" << fromhost;
 
520
                b << "collection" << collection;
 
521
                b << "query" << query;
 
522
                b.appendDate( "cursorId", cursorId );
 
523
                BSONObj token = b.done();
 
524
                result << "finishToken" << token;
 
525
            }
 
526
            return res;
 
527
        }
 
528
    } cmdstartclonecollection;
 
529
    
 
530
    class CmdFinishCloneCollection : public Command {
 
531
    public:
 
532
        virtual bool slaveOk() {
 
533
            return false;
 
534
        }
 
535
        CmdFinishCloneCollection() : Command("finishCloneCollection") { }
 
536
        virtual void help( stringstream &help ) const {
 
537
            help << " example: { finishCloneCollection: <finishToken> }";
 
538
        }
 
539
        virtual bool run(const char *ns, BSONObj& cmdObj, string& errmsg, BSONObjBuilder& result, bool fromRepl) {
 
540
            BSONObj fromToken = cmdObj.getObjectField("finishCloneCollection");
 
541
            if ( fromToken.isEmpty() ) {
 
542
                errmsg = "missing finishCloneCollection finishToken spec";
 
543
                return false;
 
544
            }
 
545
            string fromhost = fromToken.getStringField( "fromhost" );
 
546
            if ( fromhost.empty() ) {
 
547
                errmsg = "missing fromhost spec";
 
548
                return false;
 
549
            }
 
550
            string collection = fromToken.getStringField("collection");
 
551
            if ( collection.empty() ) {
 
552
                errmsg = "missing collection spec";
 
553
                return false;
 
554
            }
 
555
            BSONObj query = fromToken.getObjectField("query");
 
556
            if ( query.isEmpty() ) {
 
557
                query = BSONObj();
 
558
            }
 
559
            long long cursorId = 0;
 
560
            BSONElement cursorIdToken = fromToken.getField( "cursorId" );
 
561
            if ( cursorIdToken.type() == Date ) {
 
562
                cursorId = cursorIdToken._numberLong();
 
563
            }
 
564
            
 
565
            setClient( collection.c_str() );
 
566
            
 
567
            log() << "finishCloneCollection.  db:" << ns << " collection:" << collection << " from: " << fromhost << " query: " << query << endl;
 
568
            
 
569
            Cloner c;
 
570
            return c.finishCloneCollection( fromhost.c_str(), collection.c_str(), query, cursorId, errmsg );
 
571
        }
 
572
    } cmdfinishclonecollection;
 
573
 
 
574
    /* Usage:
 
575
       admindb.$cmd.findOne( { copydb: 1, fromhost: <hostname>, fromdb: <db>, todb: <db> } );
 
576
    */
 
577
    class CmdCopyDb : public Command {
 
578
    public:
 
579
        CmdCopyDb() : Command("copydb") { }
 
580
        virtual bool adminOnly() {
 
581
            return true;
 
582
        }
 
583
        virtual bool slaveOk() {
 
584
            return false;
 
585
        }
 
586
        virtual void help( stringstream &help ) const {
 
587
            help << "copy a database from antoher host to this host\n";
 
588
            help << "usage: {copydb: 1, fromhost: <hostname>, fromdb: <db>, todb: <db>}";
 
589
        }
 
590
        virtual bool run(const char *ns, BSONObj& cmdObj, string& errmsg, BSONObjBuilder& result, bool fromRepl) {
 
591
            string fromhost = cmdObj.getStringField("fromhost");
 
592
            if ( fromhost.empty() ) {
 
593
                /* copy from self */
 
594
                stringstream ss;
 
595
                ss << "localhost:" << cmdLine.port;
 
596
                fromhost = ss.str();
 
597
            }
 
598
            string fromdb = cmdObj.getStringField("fromdb");
 
599
            string todb = cmdObj.getStringField("todb");
 
600
            if ( fromhost.empty() || todb.empty() || fromdb.empty() ) {
 
601
                errmsg = "parms missing - {copydb: 1, fromhost: <hostname>, fromdb: <db>, todb: <db>}";
 
602
                return false;
 
603
            }
 
604
            setClient(todb.c_str());
 
605
            bool res = cloneFrom(fromhost.c_str(), errmsg, fromdb, /*logForReplication=*/!fromRepl, /*slaveok*/false, /*replauth*/false, /*snapshot*/true);
 
606
            cc().clearns();
 
607
            return res;
 
608
        }
 
609
    } cmdcopydb;
 
610
    
 
611
    class CmdRenameCollection : public Command {
 
612
    public:
 
613
        CmdRenameCollection() : Command( "renameCollection" ) {}
 
614
        virtual bool adminOnly() {
 
615
            return true;
 
616
        }
 
617
        virtual bool slaveOk() {
 
618
            return false;
 
619
        }
 
620
        virtual bool logTheOp() {
 
621
            return true; // can't log steps when doing fast rename within a db, so always log the op rather than individual steps comprising it.
 
622
        }
 
623
        virtual void help( stringstream &help ) const {
 
624
            help << " example: { renameCollection: foo.a, to: bar.b }";
 
625
        }
 
626
        virtual bool run(const char *ns, BSONObj& cmdObj, string& errmsg, BSONObjBuilder& result, bool fromRepl) {
 
627
            string source = cmdObj.getStringField( name.c_str() );
 
628
            string target = cmdObj.getStringField( "to" );
 
629
            if ( source.empty() || target.empty() ) {
 
630
                errmsg = "invalid command syntax";
 
631
                return false;
 
632
            }
 
633
            
 
634
            setClient( source.c_str() );
 
635
            NamespaceDetails *nsd = nsdetails( source.c_str() );
 
636
            uassert( 10026 ,  "source namespace does not exist", nsd );
 
637
            bool capped = nsd->capped;
 
638
            long long size = 0;
 
639
            if ( capped )
 
640
                for( DiskLoc i = nsd->firstExtent; !i.isNull(); i = i.ext()->xnext )
 
641
                    size += i.ext()->length;
 
642
            
 
643
            setClient( target.c_str() );
 
644
            
 
645
            if ( nsdetails( target.c_str() ) ){
 
646
                uassert( 10027 ,  "target namespace exists", cmdObj["dropTarget"].trueValue() );
 
647
                BSONObjBuilder bb( result.subobjStart( "dropTarget" ) );
 
648
                dropCollection( target , errmsg , bb );
 
649
                bb.done();
 
650
                if ( errmsg.size() > 0 )
 
651
                    return false;
 
652
            }
 
653
 
 
654
            {
 
655
                char from[256];
 
656
                nsToDatabase( source.c_str(), from );
 
657
                char to[256];
 
658
                nsToDatabase( target.c_str(), to );
 
659
                if ( strcmp( from, to ) == 0 ) {
 
660
                    renameNamespace( source.c_str(), target.c_str() );
 
661
                    return true;
 
662
                }
 
663
            }
 
664
 
 
665
            BSONObjBuilder spec;
 
666
            if ( capped ) {
 
667
                spec.appendBool( "capped", true );
 
668
                spec.append( "size", double( size ) );
 
669
            }
 
670
            if ( !userCreateNS( target.c_str(), spec.done(), errmsg, false ) )
 
671
                return false;
 
672
            
 
673
            auto_ptr< DBClientCursor > c;
 
674
            DBDirectClient bridge;
 
675
 
 
676
            {
 
677
                c = bridge.query( source, BSONObj() );
 
678
            }
 
679
            while( 1 ) {
 
680
                {
 
681
                    if ( !c->more() )
 
682
                        break;
 
683
                }
 
684
                BSONObj o = c->next();
 
685
                theDataFileMgr.insert( target.c_str(), o );
 
686
            }
 
687
            
 
688
            char cl[256];
 
689
            nsToDatabase( source.c_str(), cl );
 
690
            string sourceIndexes = string( cl ) + ".system.indexes";
 
691
            nsToDatabase( target.c_str(), cl );
 
692
            string targetIndexes = string( cl ) + ".system.indexes";
 
693
            {
 
694
                c = bridge.query( sourceIndexes, QUERY( "ns" << source ) );
 
695
            }
 
696
            while( 1 ) {
 
697
                {
 
698
                    if ( !c->more() )
 
699
                        break;
 
700
                }
 
701
                BSONObj o = c->next();
 
702
                BSONObjBuilder b;
 
703
                BSONObjIterator i( o );
 
704
                while( i.moreWithEOO() ) {
 
705
                    BSONElement e = i.next();
 
706
                    if ( e.eoo() )
 
707
                        break;
 
708
                    if ( strcmp( e.fieldName(), "ns" ) == 0 ) {
 
709
                        b.append( "ns", target );
 
710
                    } else {
 
711
                        b.append( e );
 
712
                    }
 
713
                }
 
714
                BSONObj n = b.done();
 
715
                theDataFileMgr.insert( targetIndexes.c_str(), n );
 
716
            }
 
717
 
 
718
            setClient( source.c_str() );
 
719
            dropCollection( source, errmsg, result );
 
720
            return true;
 
721
        }
 
722
    } cmdrenamecollection;
 
723
 
 
724
} // namespace mongo