summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Changelog1
-rw-r--r--src/bson/bson.h2
-rw-r--r--src/ejdb/ejdb.c71
-rw-r--r--src/ejdb/tests/ejdbtest2.c44
4 files changed, 94 insertions, 24 deletions
diff --git a/Changelog b/Changelog
index 2ab202b..7c2051f 100644
--- a/Changelog
+++ b/Changelog
@@ -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);