diff options
-rw-r--r-- | Changelog | 1 | ||||
-rw-r--r-- | src/bson/bson.h | 2 | ||||
-rw-r--r-- | src/ejdb/ejdb.c | 71 | ||||
-rw-r--r-- | src/ejdb/tests/ejdbtest2.c | 44 |
4 files changed, 94 insertions, 24 deletions
@@ -3,6 +3,7 @@ ejdb (1.2.8) UNRELEASED; urgency=low * Fix: Problem with the bson2json conversion dealing with doubles #135 * $push and $pushAll operations are implemented. #130 * Fix: $rename can operate on nested json objects #107 + * Fix: $inc doesn't create key if it doesn't exist #120 -- Anton Adamansky <adamansky@gmail.com> Mon, 27 Apr 2015 21:30:11 +0600 diff --git a/src/bson/bson.h b/src/bson/bson.h index 37fe385..58bc096 100644 --- a/src/bson/bson.h +++ b/src/bson/bson.h @@ -32,6 +32,7 @@ #include "tcutil.h" #define BSON_IS_NUM_TYPE(atype) (atype == BSON_INT || atype == BSON_LONG || atype == BSON_DOUBLE) +#define BSON_IS_NULL_TYPE(atype) (atype == BSON_UNDEFINED || atype == BSON_NULL) #define BSON_IS_STRING_TYPE(atype) ((atype) == BSON_STRING || (atype) == BSON_SYMBOL) EJDB_EXTERN_C_START @@ -1150,6 +1151,7 @@ EJDB_EXPORT int bson_merge_recursive2(const void *b1data, const void *b2data, bs /** * Merge bsons. * `bsdata2` may contain field path keys (eg: 'foo.bar'). + * * @param bsdata1 BSON data to to be merged in `out` * @param bsdata2 Second BSON data to to be merged in `out` * @param out Resulting `out` bson must be allocated and not finished. diff --git a/src/ejdb/ejdb.c b/src/ejdb/ejdb.c index e168b5e..5ded4c0 100644 --- a/src/ejdb/ejdb.c +++ b/src/ejdb/ejdb.c @@ -2930,17 +2930,15 @@ static bool _qryupdate(_QRYCTX *ctx, void *bsbuf, int bsbufsz) { //Apply update operation bson bsout; - bsout.data = NULL; - bsout.dataSize = 0; - bson_reset(&bsout); - - const EJQF *setqf = NULL; /*$set*/ - const EJQF *unsetqf = NULL; /*$unset*/ - const EJQF *incqf = NULL; /*$inc*/ - const EJQF *renameqf = NULL; /*$rename*/ - const EJQF *addsetqf[2] = {NULL}; /*$addToSet, $addToSetAll*/ - const EJQF *pushqf[2] = {NULL}; /*$push, $pushAll */ - const EJQF *pullqf[2] = {NULL}; /*$pull, $pullAll*/ + bson_init_size(&bsout, 0); + + const EJQF *setqf = NULL; // $set + const EJQF *unsetqf = NULL; // $unset + const EJQF *incqf = NULL; // $inc + const EJQF *renameqf = NULL; // $rename + const EJQF *addsetqf[2] = {NULL}; // $addToSet, $addToSetAll + const EJQF *pushqf[2] = {NULL}; // $push, $pushAll + const EJQF *pullqf[2] = {NULL}; // $pull, $pullAll for (int i = 0; i < TCLISTNUM(q->qflist); ++i) { @@ -2950,27 +2948,27 @@ static bool _qryupdate(_QRYCTX *ctx, void *bsbuf, int bsbufsz) { if (qf->updateobj == NULL) { continue; } - if (flags & EJCONDSET) { //$set + if (flags & EJCONDSET) { // $set setqf = qf; - } else if (flags & EJCONDUNSET) { //$unset + } else if (flags & EJCONDUNSET) { // $unset unsetqf = qf; - } else if (flags & EJCONDINC) { //$inc + } else if (flags & EJCONDINC) { // $inc incqf = qf; - } else if (flags & EJCONDRENAME) { //$rename + } else if (flags & EJCONDRENAME) { // $rename renameqf = qf; - } else if (flags & EJCONDADDSET) { //$addToSet, $addToSetAll + } else if (flags & EJCONDADDSET) { // $addToSet, $addToSetAll if (flags & EJCONDALL) { addsetqf[1] = qf; } else { addsetqf[0] = qf; } - } else if (flags & EJCONDPUSH) { //$push, $pushAll + } else if (flags & EJCONDPUSH) { // $push, $pushAll if (flags & EJCONDALL) { pushqf[1] = qf; } else { pushqf[0] = qf; } - } else if (flags & EJCONDPULL) { //$pull, $pullAll + } else if (flags & EJCONDPULL) { // $pull, $pullAll if (flags & EJCONDALL) { pullqf[1] = qf; } else { @@ -3071,7 +3069,7 @@ static bool _qryupdate(_QRYCTX *ctx, void *bsbuf, int bsbufsz) { assert(bsout.data == NULL); bson_init_size(&bsout, bsbufsz); } - int err = bson_merge_fieldpaths(bsbuf, bson_data(updobj), &bsout); + int err = bson_merge_fieldpaths(inbuf, bson_data(updobj), &bsout); if (err) { rv = false; _ejdbsetecode(coll->jb, JBEQUPDFAILED, __FILE__, __LINE__, __func__); @@ -3088,7 +3086,9 @@ static bool _qryupdate(_QRYCTX *ctx, void *bsbuf, int bsbufsz) { } } - if (incqf) { //$inc + if (incqf) { // $inc + bson bsmerge; // holds missing $inc fieldpaths in object + bson_init_size(&bsmerge, 0); bson *updobj = _qfgetupdateobj(incqf); if (!bsout.data) { bson_create_from_buffer2(&bsout, bsbuf, bsbufsz); @@ -3100,6 +3100,16 @@ static bool _qryupdate(_QRYCTX *ctx, void *bsbuf, int bsbufsz) { } BSON_ITERATOR_INIT(&it2, &bsout); bt2 = bson_find_fieldpath_value(BSON_ITERATOR_KEY(&it), &it2); + if (bt2 == BSON_EOO) { // found a missing field + if (!bsmerge.data) { + bson_init_size(&bsmerge, bson_size(updobj)); + } + if (bt == BSON_DOUBLE) { + bson_append_double(&bsmerge, BSON_ITERATOR_KEY(&it), bson_iterator_double(&it)); + } else { + bson_append_long(&bsmerge, BSON_ITERATOR_KEY(&it), bson_iterator_long(&it)); + } + } if (!BSON_IS_NUM_TYPE(bt2)) { continue; } @@ -3127,6 +3137,27 @@ static bool _qryupdate(_QRYCTX *ctx, void *bsbuf, int bsbufsz) { update++; } } + + if (rv && + bsmerge.data && + bson_finish(&bsmerge) == BSON_OK) { + + bson_finish(&bsout); + char *inbuf = bsout.data; + bson_init_size(&bsout, bson_size(&bsout)); + if (bson_merge_fieldpaths(inbuf, bsmerge.data, &bsout) != BSON_OK) { + rv = false; + _ejdbsetecode(coll->jb, JBEQUPDFAILED, __FILE__, __LINE__, __func__); + } else { + update++; + } + if (inbuf != bsbuf) { + TCFREE(inbuf); + } + } + + bson_finish(&bsout); + bson_destroy(&bsmerge); if (updobj != incqf->updateobj) { bson_del(updobj); } diff --git a/src/ejdb/tests/ejdbtest2.c b/src/ejdb/tests/ejdbtest2.c index 5eb94f6..e46fe48 100644 --- a/src/ejdb/tests/ejdbtest2.c +++ b/src/ejdb/tests/ejdbtest2.c @@ -3308,7 +3308,6 @@ void testUpdate2(void) { //https://github.com/Softmotions/ejdb/issues/9 bson_append_start_object(&bsq1, "$set"); bson_append_bool(&bsq1, "visited", true); bson_append_finish_object(&bsq1); - bson_append_finish_object(&bsq1); bson_finish(&bsq1); CU_ASSERT_FALSE_FATAL(bsq1.err); @@ -3347,7 +3346,47 @@ void testUpdate2(void) { //https://github.com/Softmotions/ejdb/issues/9 tclistdel(q1res); tcxstrdel(log); ejdbquerydel(q1); + + //test nested $inc #120 + bson_init_as_query(&bsq1); + bson_append_string(&bsq1, "name", "John Travolta"); + bson_append_start_object(&bsq1, "$inc"); + bson_append_int(&bsq1, "number.of.coins", 22); + bson_append_finish_object(&bsq1); + bson_finish(&bsq1); + CU_ASSERT_FALSE_FATAL(bsq1.err); + q1 = ejdbcreatequery(jb, &bsq1, NULL, 0, NULL); + CU_ASSERT_PTR_NOT_NULL_FATAL(q1); + count = 0; + log = tcxstrnew(); + ejdbqryexecute(coll, q1, &count, JBQRYCOUNT, log); + bson_destroy(&bsq1); + tcxstrdel(log); + ejdbquerydel(q1); + + //check test nested $inc #120 + bson_init_as_query(&bsq1); + bson_append_string(&bsq1, "name", "John Travolta"); + bson_finish(&bsq1); + CU_ASSERT_FALSE_FATAL(bsq1.err); + + q1 = ejdbcreatequery(jb, &bsq1, NULL, 0, NULL); + CU_ASSERT_PTR_NOT_NULL_FATAL(q1); + count = 0; + log = tcxstrnew(); + q1res = ejdbqryexecute(coll, q1, &count, 0, log); + CU_ASSERT_PTR_NOT_NULL_FATAL(q1res); + CU_ASSERT_EQUAL_FATAL(count, 1); + + for (int i = 0; i < TCLISTNUM(q1res); ++i) { + CU_ASSERT_FALSE(bson_compare_long(22, TCLISTVALPTR(q1res, i), "number.of.coins")); + } + + bson_destroy(&bsq1); + tclistdel(q1res); + tcxstrdel(log); + ejdbquerydel(q1); } void testUpdate3(void) { @@ -3362,7 +3401,6 @@ void testUpdate3(void) { bson_append_string(&bsq1, "name", "John Travolta"); bson_append_start_object(&bsq1, "$rename"); bson_append_string(&bsq1, "name", "fullName"); - //bson_append_finish_object(&bsq1); bson_append_finish_object(&bsq1); bson_finish(&bsq1); CU_ASSERT_FALSE_FATAL(bsq1.err); @@ -3385,7 +3423,6 @@ void testUpdate3(void) { bson_init_as_query(&bsq1); bson_append_string(&bsq1, "fullName", "John Travolta"); - bson_append_finish_object(&bsq1); bson_finish(&bsq1); CU_ASSERT_FALSE_FATAL(bsq1.err); @@ -3411,7 +3448,6 @@ void testUpdate3(void) { bson_append_string(&bsq1, "fullName", "John Travolta"); bson_append_start_object(&bsq1, "$rename"); bson_append_string(&bsq1, "fullName", "name"); - //bson_append_finish_object(&bsq1); bson_append_finish_object(&bsq1); bson_finish(&bsq1); CU_ASSERT_FALSE_FATAL(bsq1.err); |