Skip to content

Commit

Permalink
replicate $inc right; implement $set
Browse files Browse the repository at this point in the history
  • Loading branch information
dwight committed Aug 18, 2008
1 parent 03229b8 commit c0867d2
Show file tree
Hide file tree
Showing 4 changed files with 66 additions and 30 deletions.
66 changes: 48 additions & 18 deletions db/query.cpp
Expand Up @@ -245,50 +245,72 @@ int deleteObjects(const char *ns, JSObj pattern, bool justOne, bool god) {
}

struct Mod {
enum Op { INC, SET } op;
const char *fieldName;
double n;
double *n;
static void getMods(vector<Mod>& mods, JSObj from);
static void applyMods(vector<Mod>& mods, JSObj obj);
};

void applyMods(vector<Mod>& mods, JSObj obj) {
void Mod::applyMods(vector<Mod>& mods, JSObj obj) {
for( vector<Mod>::iterator i = mods.begin(); i != mods.end(); i++ ) {
Mod& m = *i;
Element e = obj.findElement(m.fieldName);
if( e.type() == Number ) {
e.number() += m.n;
if( m.op == INC )
*m.n = e.number() += *m.n;
else
e.number() = *m.n; // $set or $SET
}
}
}

/* get special operations like $inc
{ $inc: { a:1, b:1 } }
{ $set: { a:77 } }
NOTE: MODIFIES source from object!
*/
void getMods(vector<Mod>& mods, JSObj from) {
void Mod::getMods(vector<Mod>& mods, JSObj from) {
JSElemIter it(from);
while( it.more() ) {
Element e = it.next();
if( strcmp(e.fieldName(), "$inc") == 0 && e.type() == Object ) {
const char *fn = e.fieldName();
if( *fn == '$' && e.type() == Object &&
fn[4] == 0 ) {
JSObj j = e.embeddedObject();
JSElemIter jt(j);
Op op = Mod::SET;
if( strcmp("$inc",fn) == 0 ) {
op = Mod::INC;
// we rename to $SET instead of $set so that on an op like
// { $set: {x:1}, $inc: {y:1} }
// we don't get two "$set" fields which isn't allowed
strcpy((char *) fn, "$SET");
}
while( jt.more() ) {
Element f = jt.next();
if( f.eoo() )
break;
Mod m;
m.op = op;
m.fieldName = f.fieldName();
if( f.type() == Number ) {
m.n = f.number();
mods.push_back(m);
m.n = &f.number();
mods.push_back( m );
}
}
}
}
}

/*
todo:
smart requery find record immediately
/* todo:
_ smart requery find record immediately
returns:
2: we did applyMods() but didn't logOp()
5: we did applyMods() and did logOp() (so don't do it again)
(clean these up later...)
*/
int _updateObjects(const char *ns, JSObj updateobj, JSObj pattern, bool upsert, stringstream& ss) {
int _updateObjects(const char *ns, JSObj updateobj, JSObj pattern, bool upsert, stringstream& ss, bool logop=false) {
//cout << "TEMP BAD";
//lrutest.find(updateobj);

Expand Down Expand Up @@ -329,12 +351,19 @@ int _updateObjects(const char *ns, JSObj updateobj, JSObj pattern, bool upsert,

/* look for $inc etc. note as listed here, all fields to inc must be this type, you can't set some
regular ones at the moment. */
if( updateobj.firstElement().fieldName()[0] == '$' ) {
const char *firstField = updateobj.firstElement().fieldName();
if( firstField[0] == '$' ) {
vector<Mod> mods;
getMods(mods, updateobj);
applyMods(mods, c->currLoc().obj());
Mod::getMods(mods, updateobj);
Mod::applyMods(mods, c->currLoc().obj());
if( profile )
ss << " fastmod ";
if( logop ) {
if( mods.size() ) {
logOp("u", ns, updateobj, &pattern, &upsert);
return 5;
}
}
return 2;
}

Expand All @@ -352,11 +381,11 @@ int _updateObjects(const char *ns, JSObj updateobj, JSObj pattern, bool upsert,
if( updateobj.firstElement().fieldName()[0] == '$' ) {
/* upsert of an $inc. build a default */
vector<Mod> mods;
getMods(mods, updateobj);
Mod::getMods(mods, updateobj);
JSObjBuilder b;
b.appendElements(pattern);
for( vector<Mod>::iterator i = mods.begin(); i != mods.end(); i++ )
b.append(i->fieldName, i->n);
b.append(i->fieldName, *i->n);
JSObj obj = b.done();
theDataFileMgr.insert(ns, (void*) obj.objdata(), obj.objsize());
if( profile )
Expand All @@ -373,8 +402,9 @@ int _updateObjects(const char *ns, JSObj updateobj, JSObj pattern, bool upsert,
/* todo: we can optimize replication by just doing insert when an upsert triggers.
*/
void updateObjects(const char *ns, JSObj updateobj, JSObj pattern, bool upsert, stringstream& ss) {
int rc = _updateObjects(ns, updateobj, pattern, upsert, ss);
logOp("u", ns, updateobj, &pattern, &upsert);
int rc = _updateObjects(ns, updateobj, pattern, upsert, ss, true);
if( rc != 5 )
logOp("u", ns, updateobj, &pattern, &upsert);
}

int queryTraceLevel = 0;
Expand Down
2 changes: 1 addition & 1 deletion db/query.h
Expand Up @@ -43,7 +43,7 @@
int flags; // 1=upsert
JSObject query;
JSObject objectToUpdate;
objectToUpdate may include { $inc: <field> }.
objectToUpdate may include { $inc: <field> } or { $set: ... }, see struct Mod.
dbQuery:
string collection;
int nToSkip;
Expand Down
12 changes: 7 additions & 5 deletions db/repl.cpp
Expand Up @@ -31,7 +31,7 @@ extern JSObj emptyObj;
extern boost::mutex dbMutex;
auto_ptr<Cursor> findTableScan(const char *ns, JSObj& order);
bool userCreateNS(const char *ns, JSObj& j, string& err);
int _updateObjects(const char *ns, JSObj updateobj, JSObj pattern, bool upsert, stringstream& ss);
int _updateObjects(const char *ns, JSObj updateobj, JSObj pattern, bool upsert, stringstream& ss, bool logOp=false);
bool _runCommands(const char *ns, JSObj& jsobj, stringstream& ss, BufBuilder &b, JSObjBuilder& anObjBuilder);

OpTime last(0, 0);
Expand Down Expand Up @@ -144,7 +144,9 @@ Source::Source(JSObj o) {
Element e = o.getField("syncedTo");
if( !e.eoo() ) {
uassert( e.type() == Date );
syncedTo.asDate() = e.date();
OpTime tmp( e.date() );
syncedTo = tmp;
//syncedTo.asDate() = e.date();
}

JSObj dbsObj = o.getObjectField("dbs");
Expand Down Expand Up @@ -336,8 +338,7 @@ void Source::pullOpLog() {
JSObj op = c->next();
Element ts = op.findElement("ts");
assert( ts.type() == Date );
OpTime t;
t.asDate() = ts.date();
OpTime t( ts.date() );
bool initial = syncedTo.isNull();
if( initial ) {
log() << "pull: initial run\n";
Expand Down Expand Up @@ -372,7 +373,8 @@ void Source::pullOpLog() {
ts = op.findElement("ts");
assert( ts.type() == Date );
OpTime last = t;
t.asDate() = ts.date();
OpTime tmp( ts.date() );
t = tmp;
if( !( last < t ) ) {
problem() << "sync error: last " << last.toString() << " >= t " << t.toString() << endl;
uassert(false);
Expand Down
16 changes: 10 additions & 6 deletions db/repl.h
Expand Up @@ -36,20 +36,24 @@ bool cloneFrom(const char *masterHost, string& errmsg);

#pragma pack(push,4)
class OpTime {
unsigned i;
unsigned secs;
unsigned i;
unsigned secs;
public:
OpTime(unsigned a, unsigned b) { secs = a; i = b; }
OpTime() { secs = 0; i = 0; }
static OpTime now();
OpTime(unsigned long long date) {
reinterpret_cast<unsigned long long&>(*this) = date;
}
OpTime(unsigned a, unsigned b) { secs = a; i = b; }
OpTime() { secs = 0; i = 0; }
static OpTime now();

/* We store OpTime's in the database as Javascript Date datatype -- we needed some sort of
64 bit "container" for these values. While these are not really "Dates", that seems a
better choice for now than say, Number, which is floating point. Note the BinData type
is perhaps the cleanest choice, lacking a true unsigned64 datatype, but BinData has a
couple bytes of overhead.
*/
unsigned long long& asDate() { return *((unsigned long long *) this); }
unsigned long long asDate() const { return *((unsigned long long *) &i); }
// unsigned long long& asDate() { return *((unsigned long long *) &i); }

bool isNull() { return secs == 0; }
string toString() const {
Expand Down

0 comments on commit c0867d2

Please sign in to comment.