diff options
author | Anton Adamansky <adamansky@gmail.com> | 2015-04-28 22:25:24 +0600 |
---|---|---|
committer | Anton Adamansky <adamansky@gmail.com> | 2015-04-28 22:25:24 +0600 |
commit | 0478ea61300762aaf66f3b158dfb6ab63e87cb69 (patch) | |
tree | b4cf9f8b4dd1eef2b3e8aef6f939299289637f70 /src | |
parent | 000984a89f7e015c8d02bbb34cd0833231ea99d8 (diff) | |
download | ejdb-0478ea61300762aaf66f3b158dfb6ab63e87cb69.tar.gz ejdb-0478ea61300762aaf66f3b158dfb6ab63e87cb69.tar.bz2 ejdb-0478ea61300762aaf66f3b158dfb6ab63e87cb69.zip |
$push and $pushAll implemented. Fixed #130
Diffstat (limited to 'src')
-rw-r--r-- | src/bson/bson.c | 96 | ||||
-rw-r--r-- | src/bson/bson.h | 24 | ||||
-rw-r--r-- | src/ejdb/ejdb.c | 332 | ||||
-rw-r--r-- | src/ejdb/ejdb.h | 34 | ||||
-rw-r--r-- | src/ejdb/ejdb_private.h | 53 | ||||
-rw-r--r-- | src/ejdb/tests/ejdbtest2.c | 112 |
6 files changed, 430 insertions, 221 deletions
diff --git a/src/bson/bson.c b/src/bson/bson.c index 2f8e02e..28d7cf5 100644 --- a/src/bson/bson.c +++ b/src/bson/bson.c @@ -899,10 +899,8 @@ int bson_ensure_space(bson *b, const int bytesNeeded) { int bson_finish(bson *b) { int i; - if (b->err & BSON_NOT_UTF8) return BSON_ERROR; - if (!b->finished) { if (bson_ensure_space(b, 1) == BSON_ERROR) return BSON_ERROR; bson_append_byte(b, 0); @@ -910,7 +908,6 @@ int bson_finish(bson *b) { bson_little_endian32(b->data, &i); b->finished = 1; } - return BSON_OK; } @@ -1501,11 +1498,11 @@ typedef struct { const void *bsdata2; //bsdata to merge with int nstack; //nested object stack pos int matched; //number of matched merge fields -} _BSON_MERGE3_CTX; +} _BSONMERGE3CTX; static bson_visitor_cmd_t _bson_merge3_visitor(const char *ipath, int ipathlen, const char *key, int keylen, const bson_iterator *it, bool after, void *op) { - _BSON_MERGE3_CTX *ctx = op; + _BSONMERGE3CTX *ctx = op; assert(ctx && ctx->bsout && ctx->mfields && ipath && key && it && op); const void *buf; const char *mpath; @@ -1582,7 +1579,7 @@ int bson_merge3(const void *bsdata1, const void *bsdata2, bson *out) { BSON_ITERATOR_FROM_BUFFER(&it2, bsdata2); const char *it2start = it2.cur; TCMAP *mfields = tcmapnew2(TCMAPTINYBNUM); - _BSON_MERGE3_CTX ctx = { + _BSONMERGE3CTX ctx = { .bsout = out, .mfields = mfields, .bsdata2 = bsdata2, @@ -1679,7 +1676,6 @@ int bson_merge_recursive2(const void *b1data, const void *b2data, bson_bool_t ov bson_append_field_from_iterator(&sit, out); ++c; } - char kbuf[TCNUMBUFSIZ]; BSON_ITERATOR_SUBITERATOR(&it2, &sit); while ((sbt = bson_iterator_next(&sit)) != BSON_EOO) { @@ -1772,8 +1768,11 @@ static bson_visitor_cmd_t _bsonstripvisitor_exclude(const char *ipath, int ipath bson_append_field_from_iterator(it, sctx->bsout); return (BSON_VCMD_SKIP_NESTED | BSON_VCMD_SKIP_AFTER); } - } else if (!after && ictx->astack > 0 && bson_isnumstr(key, keylen)) { - bson_append_undefined(sctx->bsout, key); + } else { + if (!after && ictx->astack > 0 && bson_isnumstr(key, keylen)) { + bson_append_undefined(sctx->bsout, key); + } + ictx->matched++; } return (BSON_VCMD_SKIP_NESTED | BSON_VCMD_SKIP_AFTER); } @@ -1859,15 +1858,18 @@ static bson_visitor_cmd_t _bsonstripvisitor_include(const char *ipath, int ipath return rv; } -int bson_strip(TCMAP *ifields, bool imode, const void *bsbuf, bson *bsout) { +int bson_strip(TCMAP *ifields, bool imode, const void *bsbuf, bson *bsout, int *matched) { BSONSTRIPCTX sctx = { .ifields = ifields, .imode = imode, .bsbuf = bsbuf, .bsout = bsout, - .fkfields = NULL + .fkfields = NULL, + .matched = 0 }; - return bson_strip2(&sctx); + int rc = bson_strip2(&sctx); + *matched = sctx.matched; + return rc; } /* Include or exclude fpaths in the specified BSON and put resulting data into `bsout`. */ @@ -1886,6 +1888,7 @@ int bson_strip2(BSONSTRIPCTX *sctx) { BSON_ITERATOR_FROM_BUFFER(&it, sctx->bsbuf); bson_visit_fields(&it, 0, (sctx->imode) ? _bsonstripvisitor_include : _bsonstripvisitor_exclude, &ictx); assert(ictx.nstack == 0); + sctx->matched = ictx.matched; return bson_finish(sctx->bsout); } @@ -2110,7 +2113,7 @@ void bson_init_with_data(bson *bs, const void *bsdata) { bs->finished = true; } -bool bson_find_merged_array_sets(const void *mbuf, const void *inbuf, bool expandall) { +bool bson_find_merged_arrays(const void *mbuf, const void *inbuf, bool expandall) { assert(mbuf && inbuf); bool found = false; bson_iterator it, it2; @@ -2149,7 +2152,7 @@ bool bson_find_merged_array_sets(const void *mbuf, const void *inbuf, bool expan return found; } -bool bson_find_unmerged_array_sets(const void *mbuf, const void *inbuf) { +bool bson_find_unmerged_arrays(const void *mbuf, const void *inbuf) { assert(mbuf && inbuf); bool allfound = false; bson_iterator it, it2; @@ -2188,10 +2191,16 @@ typedef struct { int ecode; bool duty; bool expandall; -} BSON_MASETS_CTX; + bson_merge_array_mode mode; +} _BSONMARRCTX; + -static bson_visitor_cmd_t bson_merge_array_sets_pull_tf(const char *fpath, int fpathlen, const char *key, int keylen, const bson_iterator *it, bool after, void *op) { - BSON_MASETS_CTX *ctx = op; +static bson_visitor_cmd_t _bson_merge_arrays_pull_visitor( + const char *fpath, int fpathlen, + const char *key, int keylen, + const bson_iterator *it, bool after, void *op) { + + _BSONMARRCTX *ctx = op; assert(ctx && ctx->mfields >= 0); bson_iterator mit; bson_type bt = BSON_ITERATOR_TYPE(it); @@ -2250,8 +2259,13 @@ static bson_visitor_cmd_t bson_merge_array_sets_pull_tf(const char *fpath, int f return (BSON_VCMD_OK); } -static bson_visitor_cmd_t bson_merge_array_sets_tf(const char *fpath, int fpathlen, const char *key, int keylen, const bson_iterator *it, bool after, void *op) { - BSON_MASETS_CTX *ctx = op; +static bson_visitor_cmd_t _bson_merge_arrays_visitor( + const char *fpath, int fpathlen, + const char *key, int keylen, + const bson_iterator *it, + bool after, void *op) { + + _BSONMARRCTX *ctx = op; assert(ctx && ctx->mfields >= 0); bson_iterator mit; bson_type bt = BSON_ITERATOR_TYPE(it); @@ -2297,11 +2311,13 @@ static bson_visitor_cmd_t bson_merge_array_sets_tf(const char *fpath, int fpathl BSON_ITERATOR_SUBITERATOR(&mit, &mitsub); //mit has BSON_ARRAY type while ((bt = bson_iterator_next(&mitsub)) != BSON_EOO) { found = false; - BSON_ITERATOR_SUBITERATOR(it, &ait); //Rewind main array iterator - while ((bt = bson_iterator_next(&ait)) != BSON_EOO) { - if (!bson_compare_it_current(&ait, &mitsub)) { - found = true; - break; + if (ctx->mode == BSON_MERGE_ARRAY_ADDSET) { + BSON_ITERATOR_SUBITERATOR(it, &ait); //Rewind main array iterator + while ((bt = bson_iterator_next(&ait)) != BSON_EOO) { + if (!bson_compare_it_current(&ait, &mitsub)) { + found = true; + break; + } } } if (!found) { //Append missing element @@ -2313,7 +2329,9 @@ static bson_visitor_cmd_t bson_merge_array_sets_tf(const char *fpath, int fpathl } } else { //Single element to add while ((bt = bson_iterator_next(&ait)) != BSON_EOO) { - if (!found && !bson_compare_it_current(&ait, &mit)) { + if ((ctx->mode == BSON_MERGE_ARRAY_ADDSET) && + !found && + !bson_compare_it_current(&ait, &mit)) { found = true; } bson_append_field_from_iterator(&ait, ctx->bsout); @@ -2336,21 +2354,30 @@ static bson_visitor_cmd_t bson_merge_array_sets_tf(const char *fpath, int fpathl bson_append_finish_object(ctx->bsout); } return (BSON_VCMD_OK); - } -int bson_merge_array_sets(const void *mbuf, const void *inbuf, bool pull, bool expandall, bson *bsout) { +int bson_merge_arrays(const void *mbuf, + const void *inbuf, + bson_merge_array_mode mode, + bool expandall, + bson *bsout) { + assert(mbuf && inbuf && bsout); + assert(mode == BSON_MERGE_ARRAY_ADDSET || + mode == BSON_MERGE_ARRAY_PULL || + mode == BSON_MERGE_ARRAY_PUSH); + if (bsout->finished) { return BSON_ERROR; } - BSON_MASETS_CTX ctx = { + _BSONMARRCTX ctx = { .bsout = bsout, .mbuf = mbuf, .mfields = 0, .duty = false, .expandall = expandall, - .ecode = BSON_OK + .ecode = BSON_OK, + .mode = mode }; bson_type bt, bt2; bson_iterator it, it2; @@ -2362,12 +2389,13 @@ int bson_merge_array_sets(const void *mbuf, const void *inbuf, bool pull, bool e ctx.mfields++; } BSON_ITERATOR_FROM_BUFFER(&it, inbuf); - if (pull) { - bson_visit_fields(&it, 0, bson_merge_array_sets_pull_tf, &ctx); + if (mode == BSON_MERGE_ARRAY_PULL) { + bson_visit_fields(&it, 0, _bson_merge_arrays_pull_visitor, &ctx); } else { - bson_visit_fields(&it, 0, bson_merge_array_sets_tf, &ctx); + bson_visit_fields(&it, 0, _bson_merge_arrays_visitor, &ctx); } - if (ctx.mfields == 0 || pull) { + if (ctx.mfields == 0 || //all fields are merged + mode == BSON_MERGE_ARRAY_PULL) { return ctx.ecode; } @@ -2415,12 +2443,10 @@ int bson_merge_array_sets(const void *mbuf, const void *inbuf, bool pull, bool e int res = bson_merge_recursive(&bsc, &bst, false, bsout); bson_destroy(&bsc); bson_destroy(&bst); - if (res != BSON_OK) { return BSON_ERROR; } } - return ctx.ecode; } diff --git a/src/bson/bson.h b/src/bson/bson.h index 3de71e7..26947e2 100644 --- a/src/bson/bson.h +++ b/src/bson/bson.h @@ -1161,6 +1161,7 @@ typedef struct { const void *bsbuf; //Required BSON buffer to process. bson *bsout; //Required Allocated output not finished bson* object. TCMAP *fkfields; //Optional: Map (fpath => bson key) used to force specific bson keys for selected fpaths. + int matched; //Output: number of matched fieldpaths } BSONSTRIPCTX; /** @@ -1171,9 +1172,10 @@ typedef struct { * @param imode If true fpaths will be included. Otherwise fpaths will be excluded from bson. * @param bsbuf BSON buffer to process. * @param bsout Allocated output not finished bson* object + * @param matched[out] Number of matched include/exclude fieldpaths. * @return BSON error code */ -EJDB_EXPORT int bson_strip(TCMAP *ifields, bool imode, const void *bsbuf, bson *bsout); +EJDB_EXPORT int bson_strip(TCMAP *ifields, bool imode, const void *bsbuf, bson *bsout, int *matched); EJDB_EXPORT int bson_strip2(BSONSTRIPCTX *sctx); @@ -1185,7 +1187,9 @@ EJDB_EXPORT int bson_strip2(BSONSTRIPCTX *sctx); * @param fplen Length of fpath value */ EJDB_EXPORT int bson_compare(const void *bsdata1, const void *bsdata2, const char* fpath, int fplen); -EJDB_EXPORT int bson_compare_fpaths(const void *bsdata1, const void *bsdata2, const char *fpath1, int fplen1, const char *fpath2, int fplen2); +EJDB_EXPORT int bson_compare_fpaths(const void *bsdata1, const void *bsdata2, + const char *fpath1, int fplen1, + const char *fpath2, int fplen2); EJDB_EXPORT int bson_compare_it_current(const bson_iterator *it1, const bson_iterator *it2); EJDB_EXPORT int bson_compare_string(const char* cv, const void *bsdata, const char *fpath); EJDB_EXPORT int bson_compare_long(const int64_t cv, const void *bsdata, const char *fpath); @@ -1206,10 +1210,18 @@ EJDB_EXPORT bson* bson_create_from_buffer(const void *buf, int bufsz); EJDB_EXPORT bson* bson_create_from_buffer2(bson *bs, const void *buf, int bufsz); EJDB_EXPORT void bson_init_with_data(bson *bs, const void *bsdata); -EJDB_EXPORT bool bson_find_unmerged_array_sets(const void *mbuf, const void *inbuf); -EJDB_EXPORT bool bson_find_merged_array_sets(const void *mbuf, const void *inbuf, bool expandall); -EJDB_EXPORT int bson_merge_array_sets(const void *mbuf, const void *inbuf, bool pull, bool expandall, bson *bsout); - +typedef enum { + BSON_MERGE_ARRAY_ADDSET = 0, + BSON_MERGE_ARRAY_PULL = 1, + BSON_MERGE_ARRAY_PUSH = 2 +} bson_merge_array_mode; + +EJDB_EXPORT int bson_merge_arrays(const void *mbuf, const void *inbuf, + bson_merge_array_mode mode, + bool expandall, bson *bsout); + +EJDB_EXPORT bool bson_find_unmerged_arrays(const void *mbuf, const void *inbuf); +EJDB_EXPORT bool bson_find_merged_arrays(const void *mbuf, const void *inbuf, bool expandall); /** * Convert BSON into JSON buffer. diff --git a/src/ejdb/ejdb.c b/src/ejdb/ejdb.c index d568a57..d4639a1 100644 --- a/src/ejdb/ejdb.c +++ b/src/ejdb/ejdb.c @@ -2776,7 +2776,8 @@ static bool _pushprocessedbson(_QRYCTX *ctx, const void *bsbuf, int bsbufsz) { .fkfields = _fkfields, .imode = ctx->imode, .bsbuf = inbuf, - .bsout = &bsout + .bsout = &bsout, + .matched = 0 }; if (bson_strip2(&sctx) != BSON_OK) { _ejdbsetecode(jb, JBEINVALIDBSON, __FILE__, __LINE__, __func__); @@ -2892,7 +2893,7 @@ static bool _qryupdate(_QRYCTX *ctx, void *bsbuf, int bsbufsz) { assert(ctx && ctx->q && (ctx->q->flags & EJQUPDATING) && bsbuf && ctx->didxctx); bool rv = true; - bool update = false; + int update = 0; EJCOLL *coll = ctx->coll; EJQ *q = ctx->q; bson_oid_t *oid; @@ -2900,7 +2901,7 @@ static bool _qryupdate(_QRYCTX *ctx, void *bsbuf, int bsbufsz) { bson_iterator it, it2; TCMAP *rowm = NULL; - if (q->flags & EJQDROPALL) { //Record will be dropped + if (q->flags & EJQDROPALL) { //Records will be dropped bt = bson_find_from_buffer(&it, bsbuf, JDBIDKEYNAME); if (bt != BSON_OID) { _ejdbsetecode(coll->jb, JBEQUPDFAILED, __FILE__, __LINE__, __func__); @@ -2926,6 +2927,7 @@ static bool _qryupdate(_QRYCTX *ctx, void *bsbuf, int bsbufsz) { } return rv; } + //Apply update operation bson bsout; bsout.data = NULL; @@ -2937,62 +2939,169 @@ static bool _qryupdate(_QRYCTX *ctx, void *bsbuf, int bsbufsz) { 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*/ - //$set, $inc, $addToSet, $addToSetAll, $pull, $pullAll operations for (int i = 0; i < TCLISTNUM(q->qflist); ++i) { + const EJQF *qf = TCLISTVALPTR(q->qflist, i); + const uint32_t flags = qf->flags; + if (qf->updateobj == NULL) { continue; - } - if (qf->flags & EJCONDSET) { //$set + } + if (flags & EJCONDSET) { //$set setqf = qf; - continue; - } - if (qf->flags & EJCONDUNSET) { //$unset + } else if (flags & EJCONDUNSET) { //$unset unsetqf = qf; - continue; - } - if (qf->flags & EJCONDINC) { //$inc + } else if (flags & EJCONDINC) { //$inc incqf = qf; - continue; - } - if (qf->flags & EJCONDRENAME) { //$rename + } else if (flags & EJCONDRENAME) { //$rename renameqf = qf; - continue; - } - if (qf->flags & EJCONDADDSET) { //$addToSet, $addToSetAll - if (qf->flags & EJCONDALL) { + } else if (flags & EJCONDADDSET) { //$addToSet, $addToSetAll + if (flags & EJCONDALL) { addsetqf[1] = qf; } else { addsetqf[0] = qf; } - } - if (qf->flags & EJCONDPULL) { //$pull, $pullAll - if (qf->flags & EJCONDALL) { + } else if (flags & EJCONDPUSH) { //$push, $pushAll + if (flags & EJCONDALL) { + pushqf[1] = qf; + } else { + pushqf[0] = qf; + } + } else if (flags & EJCONDPULL) { //$pull, $pullAll + if (flags & EJCONDALL) { pullqf[1] = qf; } else { pullqf[0] = qf; } } } + + if (renameqf) { //todo rename nested fields! + char *inbuf = (bsout.finished) ? bsout.data : bsbuf; + if (bsout.finished) { + //reinit `bsout`, `inbuf` already points to `bsout.data` and will be freed later + bson_init_size(&bsout, bson_size(&bsout)); + } else { + assert(bsout.data == NULL); + bson_init_size(&bsout, bsbufsz); + } + TCMAP *efields = NULL; + bson *updobj = _qfgetupdateobj(renameqf); + BSON_ITERATOR_INIT(&it, updobj); + while (rv && (bt = bson_iterator_next(&it)) != BSON_EOO) { + if (bt != BSON_STRING) { + continue; + } + const char *ofpath = BSON_ITERATOR_KEY(&it); + const char *nfpath = bson_iterator_string(&it); + bt2 = bson_find_from_buffer(&it2, inbuf, ofpath); + if (bt2 == BSON_EOO) { + continue; + } + if (bson_append_field_from_iterator2(nfpath, &it2, &bsout) != BSON_OK) { + rv = false; + _ejdbsetecode(coll->jb, JBEQUPDFAILED, __FILE__, __LINE__, __func__); + break; + } + update++; + if (!efields) { + efields = tcmapnew2(TCMAPTINYBNUM); + } + tcmapputkeep(efields, ofpath, strlen(ofpath), "", 0); + tcmapputkeep(efields, nfpath, strlen(nfpath), "", 0); + } + + BSON_ITERATOR_FROM_BUFFER(&it, inbuf); + while (rv && (bt = bson_iterator_next(&it)) != BSON_EOO) { + const char *fpath = BSON_ITERATOR_KEY(&it); + if (efields && tcmapget2(efields, fpath)) { + continue; + } + if (bson_append_field_from_iterator(&it, &bsout) != BSON_OK) { + rv = false; + _ejdbsetecode(coll->jb, JBEQUPDFAILED, __FILE__, __LINE__, __func__); + break; + } + } + if (efields) { + tcmapdel(efields); + } + bson_finish(&bsout); + if (inbuf != bsbuf) { + TCFREE(inbuf); + } + if (updobj != renameqf->updateobj) { + bson_del(updobj); + } + if (!rv) { + goto finish; + } + } + + if (unsetqf) { //$unset + char *inbuf = (bsout.finished) ? bsout.data : bsbuf; + if (bsout.finished) { + bson_init_size(&bsout, bson_size(&bsout)); + } else { + assert(bsout.data == NULL); + bson_init_size(&bsout, bsbufsz); + } + int matched = 0; + bson *updobj = _qfgetupdateobj(unsetqf); + TCMAP *ifields = tcmapnew2(TCMAPTINYBNUM); + BSON_ITERATOR_INIT(&it, updobj); + while ((bt = bson_iterator_next(&it)) != BSON_EOO) { + const char *fpath = BSON_ITERATOR_KEY(&it); + tcmapput(ifields, fpath, strlen(fpath), &yes, sizeof(yes)); + } + if (bson_strip(ifields, false, inbuf, &bsout, &matched) != BSON_OK) { + rv = false; + _ejdbsetecode(coll->jb, JBEQUPDFAILED, __FILE__, __LINE__, __func__); + } + if (matched > 0) { + update++; + } + tcmapdel(ifields); + bson_finish(&bsout); + if (inbuf != bsbuf) { + TCFREE(inbuf); + } + if (updobj != unsetqf->updateobj) { + bson_del(updobj); + } + if (!rv) { + goto finish; + } + } if (setqf) { //$set + update++; bson *updobj = _qfgetupdateobj(setqf); - update = true; - bson_init_size(&bsout, bsbufsz); + char *inbuf = (bsout.finished) ? bsout.data : bsbuf; + if (bsout.finished) { + bson_init_size(&bsout, bson_size(&bsout)); + } else { + assert(bsout.data == NULL); + bson_init_size(&bsout, bsbufsz); + } int err = bson_merge3(bsbuf, bson_data(updobj), &bsout); if (err) { rv = false; _ejdbsetecode(coll->jb, JBEQUPDFAILED, __FILE__, __LINE__, __func__); } bson_finish(&bsout); + if (inbuf != bsbuf) { + TCFREE(inbuf); + } if (updobj != setqf->updateobj) { bson_del(updobj); } - } - if (!rv) { - goto finish; + if (!rv) { + goto finish; + } } if (incqf) { //$inc @@ -3022,7 +3131,7 @@ static bool _qryupdate(_QRYCTX *ctx, void *bsbuf, int bsbufsz) { _ejdbsetecode(coll->jb, JBEQUPDFAILED, __FILE__, __LINE__, __func__); break; } - update = true; + update++; } else { int64_t v = bson_iterator_long(&it2); v += bson_iterator_long(&it); @@ -3031,7 +3140,7 @@ static bool _qryupdate(_QRYCTX *ctx, void *bsbuf, int bsbufsz) { _ejdbsetecode(coll->jb, JBEQUPDFAILED, __FILE__, __LINE__, __func__); break; } - update = true; + update++; } } if (updobj != incqf->updateobj) { @@ -3046,16 +3155,16 @@ static bool _qryupdate(_QRYCTX *ctx, void *bsbuf, int bsbufsz) { const EJQF *qf = pullqf[i]; if (!qf) continue; char *inbuf = (bsout.finished) ? bsout.data : bsbuf; - if (bson_find_merged_array_sets(bson_data(qf->updateobj), inbuf, (qf->flags & EJCONDALL))) { + if (bson_find_merged_arrays(bson_data(qf->updateobj), inbuf, (qf->flags & EJCONDALL))) { if (bsout.finished) { - //reinit `bsout`, `inbuf` already points to `bsout.data` and will be freed later bson_init_size(&bsout, bson_size(&bsout)); } else { assert(bsout.data == NULL); bson_init_size(&bsout, bsbufsz); } //$pull $pullAll merge - if (bson_merge_array_sets(bson_data(qf->updateobj), inbuf, true, (qf->flags & EJCONDALL), &bsout)) { + if (bson_merge_arrays(bson_data(qf->updateobj), inbuf, + BSON_MERGE_ARRAY_PULL, (qf->flags & EJCONDALL), &bsout)) { rv = false; _ejdbsetecode(coll->jb, JBEQUPDFAILED, __FILE__, __LINE__, __func__); } @@ -3063,28 +3172,51 @@ static bool _qryupdate(_QRYCTX *ctx, void *bsbuf, int bsbufsz) { TCFREE(inbuf); } bson_finish(&bsout); - update = true; + update++; } if (!rv) { goto finish; } } + + for (int i = 0; i < 2; ++i) { //$push $pushAll + const EJQF *qf = pushqf[i]; + if (!qf) continue; + char *inbuf = (bsout.finished) ? bsout.data : bsbuf; + if (bsout.finished) { + bson_init_size(&bsout, bson_size(&bsout)); + } else { + assert(bsout.data == NULL); + bson_init_size(&bsout, bsbufsz); + } + //$push $pushAll merge + if (bson_merge_arrays(bson_data(qf->updateobj), inbuf, + BSON_MERGE_ARRAY_PUSH, (qf->flags & EJCONDALL), &bsout)) { + rv = false; + _ejdbsetecode(coll->jb, JBEQUPDFAILED, __FILE__, __LINE__, __func__); + } + if (inbuf != bsbuf) { + TCFREE(inbuf); + } + bson_finish(&bsout); + update++; + } for (int i = 0; i < 2; ++i) { //$addToSet $addToSetAll const EJQF *qf = addsetqf[i]; if (!qf) continue; char *inbuf = (bsout.finished) ? bsout.data : bsbuf; - if ((qf->flags & EJCONDALL) || bson_find_unmerged_array_sets(bson_data(qf->updateobj), inbuf)) { + if ((qf->flags & EJCONDALL) || bson_find_unmerged_arrays(bson_data(qf->updateobj), inbuf)) { //Missing $addToSet element in some array field found if (bsout.finished) { - //reinit `bsout`, `inbuf` already points to `bsout.data` and will be freed later bson_init_size(&bsout, bson_size(&bsout)); } else { assert(bsout.data == NULL); bson_init_size(&bsout, bsbufsz); } //$addToSet $addToSetAll merge - if (bson_merge_array_sets(bson_data(qf->updateobj), inbuf, false, (qf->flags & EJCONDALL), &bsout)) { + if (bson_merge_arrays(bson_data(qf->updateobj), inbuf, + BSON_MERGE_ARRAY_ADDSET, (qf->flags & EJCONDALL), &bsout)) { rv = false; _ejdbsetecode(coll->jb, JBEQUPDFAILED, __FILE__, __LINE__, __func__); } @@ -3092,97 +3224,14 @@ static bool _qryupdate(_QRYCTX *ctx, void *bsbuf, int bsbufsz) { TCFREE(inbuf); } bson_finish(&bsout); - update = true; - } - } - - if (unsetqf) { //$unset - char *inbuf = (bsout.finished) ? bsout.data : bsbuf; - if (bsout.finished) { - //reinit `bsout`, `inbuf` already points to `bsout.data` and will be freed later - bson_init_size(&bsout, bson_size(&bsout)); - } else { - assert(bsout.data == NULL); - bson_init_size(&bsout, bsbufsz); - } - bson *updobj = _qfgetupdateobj(unsetqf); - TCMAP *ifields = tcmapnew2(TCMAPTINYBNUM); - BSON_ITERATOR_INIT(&it, updobj); - while ((bt = bson_iterator_next(&it)) != BSON_EOO) { - const char *fpath = BSON_ITERATOR_KEY(&it); - tcmapput(ifields, fpath, strlen(fpath), &yes, sizeof(yes)); + update++; } - if (bson_strip(ifields, false, inbuf, &bsout) != BSON_OK) { - rv = false; - _ejdbsetecode(coll->jb, JBEQUPDFAILED, __FILE__, __LINE__, __func__); - } - tcmapdel(ifields); - if (inbuf != bsbuf) { - TCFREE(inbuf); + if (!rv) { + goto finish; } - bson_finish(&bsout); - update = true; } - if (!rv) { - goto finish; - } - - if (renameqf) { - char *inbuf = (bsout.finished) ? bsout.data : bsbuf; - if (bsout.finished) { - //reinit `bsout`, `inbuf` already points to `bsout.data` and will be freed later - bson_init_size(&bsout, bson_size(&bsout)); - } else { - assert(bsout.data == NULL); - bson_init_size(&bsout, bsbufsz); - } - - TCMAP *efields = tcmapnew2(TCMAPTINYBNUM); - bson *updobj = _qfgetupdateobj(renameqf); - BSON_ITERATOR_INIT(&it, updobj); - while (rv && (bt = bson_iterator_next(&it)) != BSON_EOO) { - if (bt != BSON_STRING) { - continue; - } - const char *ofpath = BSON_ITERATOR_KEY(&it); - const char *nfpath = bson_iterator_string(&it); - - BSON_ITERATOR_FROM_BUFFER(&it2, inbuf); - bt2 = bson_find_from_buffer(&it2, inbuf, ofpath); - if (bt2 == BSON_EOO) { - continue; - } - - if (bson_append_field_from_iterator2(nfpath, &it2, &bsout) != BSON_OK) { - rv = false; - _ejdbsetecode(coll->jb, JBEQUPDFAILED, __FILE__, __LINE__, __func__); - break; - } - tcmapputkeep(efields, ofpath, strlen(ofpath), "", 0); - tcmapputkeep(efields, nfpath, strlen(nfpath), "", 0); - } - - BSON_ITERATOR_FROM_BUFFER(&it, inbuf); - while (rv && (bt = bson_iterator_next(&it)) != BSON_EOO) { - const char *fpath = BSON_ITERATOR_KEY(&it); - if (tcmapget2(efields, fpath)) { - continue; - } - - if (bson_append_field_from_iterator(&it, &bsout) != BSON_OK) { - rv = false; - _ejdbsetecode(coll->jb, JBEQUPDFAILED, __FILE__, __LINE__, __func__); - break; - } - } - tcmapdel(efields); - if (inbuf != bsbuf) { - TCFREE(inbuf); - } - bson_finish(&bsout); - update = true; - } - + + //Finishing document update if (!update || !rv) { goto finish; } @@ -4726,9 +4775,11 @@ static int _parse_qobj_impl(EJDB *jb, EJQ *q, bson_iterator *it, TCLIST *qlist, !strcmp("$dropall", fkey) || !strcmp("$addToSet", fkey) || !strcmp("$addToSetAll", fkey) || - !strcmp("$upsert", fkey) || + !strcmp("$push", fkey) || + !strcmp("$pushAll", fkey) || !strcmp("$pull", fkey) || !strcmp("$pullAll", fkey) || + !strcmp("$upsert", fkey) || !strcmp("$do", fkey) || !strcmp("$unset", fkey) || !strcmp("$rename", fkey) @@ -4861,23 +4912,28 @@ static int _parse_qobj_impl(EJDB *jb, EJQ *q, bson_iterator *it, TCLIST *qlist, if (!strcmp("$inc", fkey)) { qf.flags |= EJCONDINC; } else if (!pqf) { //top level op - if (!strcmp("$set", fkey)) { //top level set OP + if (!strcmp("$do", fkey)) { + qf.flags |= EJCONDOIT; + } else if (!strcmp("$set", fkey)) { //top level set OP qf.flags |= EJCONDSET; } else if (!strcmp("$addToSet", fkey)) { qf.flags |= EJCONDADDSET; - } else if (!strcmp("$pull", fkey)) { - qf.flags |= EJCONDPULL; - } else if (!strcmp("$upsert", fkey)) { - qf.flags |= EJCONDSET; - qf.flags |= EJCONDUPSERT; } else if (!strcmp("$addToSetAll", fkey)) { qf.flags |= EJCONDADDSET; qf.flags |= EJCONDALL; + } else if (!strcmp("$push", fkey)) { + qf.flags |= EJCONDPUSH; + } else if (!strcmp("$pushAll", fkey)) { + qf.flags |= EJCONDPUSH; + qf.flags |= EJCONDALL; + } else if (!strcmp("$pull", fkey)) { + qf.flags |= EJCONDPULL; } else if (!strcmp("$pullAll", fkey)) { qf.flags |= EJCONDPULL; qf.flags |= EJCONDALL; - } else if (!strcmp("$do", fkey)) { - qf.flags |= EJCONDOIT; + } else if (!strcmp("$upsert", fkey)) { + qf.flags |= EJCONDSET; + qf.flags |= EJCONDUPSERT; } else if (!strcmp("$unset", fkey)) { qf.flags |= EJCONDUNSET; } else if (!strcmp("$rename", fkey)) { @@ -4885,7 +4941,10 @@ static int _parse_qobj_impl(EJDB *jb, EJQ *q, bson_iterator *it, TCLIST *qlist, } } - if ((qf.flags & (EJCONDSET | EJCONDINC | EJCONDADDSET | EJCONDPULL | EJCONDUNSET | EJCONDRENAME))) { + if ((qf.flags & + (EJCONDSET | EJCONDINC | EJCONDADDSET | EJCONDPULL | EJCONDPUSH | + EJCONDUNSET | EJCONDRENAME))) { + assert(qf.updateobj == NULL); qf.q->flags |= EJQUPDATING; qf.updateobj = bson_create(); @@ -4895,10 +4954,11 @@ static int _parse_qobj_impl(EJDB *jb, EJQ *q, bson_iterator *it, TCLIST *qlist, BSON_ITERATOR_SUBITERATOR(it, &sit); while ((sbt = bson_iterator_next(&sit)) != BSON_EOO) { if ((qf.flags & EJCONDALL) && sbt != BSON_ARRAY) { - //$addToSetAll & $pullAll accepts only a"$set"rrays + //addToSet, push, pull accept only an arrays continue; } - if (!(qf.flags & EJCONDALL) && (qf.flags & (EJCONDSET | EJCONDINC | EJCONDUNSET | EJCONDRENAME))) { //Checking the $(query) positional operator. + if (!(qf.flags & EJCONDALL) && + (qf.flags & (EJCONDSET | EJCONDINC | EJCONDUNSET | EJCONDRENAME))) { //Checking the $(query) positional operator. const char* ukey = BSON_ITERATOR_KEY(&sit); char *pptr; if ((pptr = strstr(ukey, ".$")) && pptr && (*(pptr + 2) == '\0' || *(pptr + 2) == '.')) {// '.$' || '.$.' diff --git a/src/ejdb/ejdb.h b/src/ejdb/ejdb.h index 90ae8f2..cc87b7a 100644 --- a/src/ejdb/ejdb.h +++ b/src/ejdb/ejdb.h @@ -71,29 +71,29 @@ enum { /** Error codes */ }; enum { /** Database open modes */ - JBOREADER = 1 << 0, /**< Open as a reader. */ - JBOWRITER = 1 << 1, /**< Open as a writer. */ - JBOCREAT = 1 << 2, /**< Create if db file not exists. */ - JBOTRUNC = 1 << 3, /**< Truncate db on open. */ - JBONOLCK = 1 << 4, /**< Open without locking. */ - JBOLCKNB = 1 << 5, /**< Lock without blocking. */ - JBOTSYNC = 1 << 6 /**< Synchronize every transaction. */ + JBOREADER = 1u << 0, /**< Open as a reader. */ + JBOWRITER = 1u << 1, /**< Open as a writer. */ + JBOCREAT = 1u << 2, /**< Create if db file not exists. */ + JBOTRUNC = 1u << 3, /**< Truncate db on open. */ + JBONOLCK = 1u << 4, /**< Open without locking. */ + JBOLCKNB = 1u << 5, /**< Lock without blocking. */ + JBOTSYNC = 1u << 6 /**< Synchronize every transaction. */ }; enum { /** Index modes, index types. */ - JBIDXDROP = 1 << 0, /**< Drop index. */ - JBIDXDROPALL = 1 << 1, /**< Drop index for all types. */ - JBIDXOP = 1 << 2, /**< Optimize indexes. */ - JBIDXREBLD = 1 << 3, /**< Rebuild index. */ - JBIDXNUM = 1 << 4, /**< Number index. */ - JBIDXSTR = 1 << 5, /**< String index.*/ - JBIDXARR = 1 << 6, /**< Array token index. */ - JBIDXISTR = 1 << 7 /**< Case insensitive string index */ + JBIDXDROP = 1u << 0, /**< Drop index. */ + JBIDXDROPALL = 1u << 1, /**< Drop index for all types. */ + JBIDXOP = 1u << 2, /**< Optimize indexes. */ + JBIDXREBLD = 1u << 3, /**< Rebuild index. */ + JBIDXNUM = 1u << 4, /**< Number index. */ + JBIDXSTR = 1u << 5, /**< String index.*/ + JBIDXARR = 1u << 6, /**< Array token index. */ + JBIDXISTR = 1u << 7 /**< Case insensitive string index */ }; enum { /*< Query search mode flags in ejdbqryexecute() */ - JBQRYCOUNT = 1, /*< Query only count(*) */ - JBQRYFINDONE = 1 << 1 /*< Fetch first record only */ + JBQRYCOUNT = 1u, /*< Query only count(*) */ + JBQRYFINDONE = 1u << 1 /*< Fetch first record only */ }; /** diff --git a/src/ejdb/ejdb_private.h b/src/ejdb/ejdb_private.h index 206ac51..4bb03ff 100644 --- a/src/ejdb/ejdb_private.h +++ b/src/ejdb/ejdb_private.h @@ -50,36 +50,37 @@ struct EJDB { enum { /**> Query field flags */ // Comparison flags - EJCOMPGT = 1, /**> Comparison GT */ - EJCOMPGTE = 1 << 1, /**> Comparison GTE */ - EJCOMPLT = 1 << 2, /**> Comparison LT */ - EJCOMPLTE = 1 << 3, /**> Comparison LTE */ - EJCONDSTARTWITH = 1 << 4, /**> Starts with */ - - EJFEXCLUDED = 1 << 5, /**> If query field excluded from matching */ - EJFNOINDEX = 1 << 6, /**> Do not use index for field */ - EJFORDERUSED = 1 << 7, /**> This ordering field was used */ - EJFPKMATCHING = 1 << 8, /**> _id PK field matching */ - - EJCONDICASE = 1 << 9, /**> Ignore case in matching */ - - EJCONDSET = 1 << 10, /**> $set Set field update operation */ - EJCONDINC = 1 << 11, /**> $inc Inc field update operation */ - EJCONDADDSET = 1 << 12, /**> $addToSet Adds value to the array only if its not in the array already. */ - EJCONDPULL = 1 << 13, /**> $pull Removes all occurrences of value from field, if field is an array */ - EJCONDUPSERT = 1 << 14, /**> $upsert Upsert $set operation */ - EJCONDALL = 1 << 15, /**> 'All' modificator for $pull or $addToSet ($addToSetAll or $pullAll) */ - EJCONDOIT = 1 << 16, /**> $do query field operation */ - EJCONDUNSET = 1 << 17, /**> $unset Field value */ - EJCONDRENAME = 1 << 18 /**> $rename Field value */ + EJCOMPGT = 1u, /**> Comparison GT */ + EJCOMPGTE = 1u << 1, /**> Comparison GTE */ + EJCOMPLT = 1u << 2, /**> Comparison LT */ + EJCOMPLTE = 1u << 3, /**> Comparison LTE */ + EJCONDSTARTWITH = 1u << 4, /**> Starts with */ + + EJFEXCLUDED = 1u << 5, /**> If query field excluded from matching */ + EJFNOINDEX = 1u << 6, /**> Do not use index for field */ + EJFORDERUSED = 1u << 7, /**> This ordering field was used */ + EJFPKMATCHING = 1u << 8, /**> _id PK field matching */ + + EJCONDICASE = 1u << 9, /**> Ignore case in matching */ + + EJCONDSET = 1u << 10, /**> $set Set field update operation */ + EJCONDINC = 1u << 11, /**> $inc Inc field update operation */ + EJCONDADDSET = 1u << 12, /**> $addToSet, $addToSetAll. Adds a value to the array only if its not in the array already. */ + EJCONDPULL = 1u << 13, /**> $pull Removes all occurrences of value from field, if field is an array */ + EJCONDUPSERT = 1u << 14, /**> $upsert Upsert $set operation */ + EJCONDALL = 1u << 15, /**> 'All' modificator for $pull(All), $addToSet(All), $push(All) */ + EJCONDOIT = 1u << 16, /**> $do query field operation */ + EJCONDUNSET = 1u << 17, /**> $unset Field value */ + EJCONDRENAME = 1u << 18, /**> $rename Field value */ + EJCONDPUSH = 1u << 19 /**> $push, $pushAll. Adds a value to the array */ }; enum { /**> Query flags */ EJQINTERNAL = 1, /**> Internal query object used in _ejdbqryexecute */ - EJQUPDATING = 1 << 1, /**> Query in updating mode */ - EJQDROPALL = 1 << 2, /**> Drop bson object if matched */ - EJQONLYCOUNT = 1 << 3, /**> Only count mode */ - EJQHASUQUERY = 1 << 4 /**> It means the query contains update $(query) fields #91 */ + EJQUPDATING = 1u << 1, /**> Query in updating mode */ + EJQDROPALL = 1u << 2, /**> Drop bson object if matched */ + EJQONLYCOUNT = 1u << 3, /**> Only count mode */ + EJQHASUQUERY = 1u << 4 /**> It means the query contains update $(query) fields #91 */ }; typedef struct { /**> $(query) matchin slot used in update $ placeholder processing. #91 */ diff --git a/src/ejdb/tests/ejdbtest2.c b/src/ejdb/tests/ejdbtest2.c index 42c480c..f8f3d98 100644 --- a/src/ejdb/tests/ejdbtest2.c +++ b/src/ejdb/tests/ejdbtest2.c @@ -3983,6 +3983,114 @@ void testTicket123(void) { ejdbquerydel(q1); } +void testPush(void) { + EJCOLL *coll = ejdbcreatecoll(jb, "ticket123", NULL); + CU_ASSERT_PTR_NOT_NULL_FATAL(coll); + + bson bsq1; + bson_init_as_query(&bsq1); + bson_append_start_object(&bsq1, "$push"); + bson_append_string(&bsq1, "abc.g", "f"); + bson_append_finish_object(&bsq1); + bson_finish(&bsq1); + CU_ASSERT_FALSE_FATAL(bsq1.err); + + EJQ *q1 = ejdbcreatequery(jb, &bsq1, NULL, 0, NULL); + CU_ASSERT_PTR_NOT_NULL_FATAL(q1); + uint32_t count = 0; + TCXSTR *log = tcxstrnew(); + ejdbqryexecute(coll, q1, &count, JBQRYCOUNT, log); + CU_ASSERT_PTR_NOT_NULL(strstr(TCXSTRPTR(log), "UPDATING MODE: YES")); + CU_ASSERT_PTR_NOT_NULL(strstr(TCXSTRPTR(log), "RS COUNT: 1")); + CU_ASSERT_PTR_NOT_NULL(strstr(TCXSTRPTR(log), "RS SIZE: 0")); + //fprintf(stderr, "%s", TCXSTRPTR(log)); + bson_destroy(&bsq1); + tcxstrdel(log); + ejdbquerydel(q1); + + //check updated data + bson_init_as_query(&bsq1); + bson_finish(&bsq1); + CU_ASSERT_FALSE_FATAL(bsq1.err); + + q1 = ejdbcreatequery(jb, &bsq1, NULL, 0, NULL); + CU_ASSERT_PTR_NOT_NULL_FATAL(q1); + log = tcxstrnew(); + TCLIST *q1res = ejdbqryexecute(coll, q1, &count, 0, log); + + CU_ASSERT_EQUAL(TCLISTNUM(q1res), 1); + //fprintf(stderr, "\n\n%s", TCXSTRPTR(log)); + + for (int i = 0; i < TCLISTNUM(q1res); ++i) { + CU_ASSERT_FALSE(bson_compare_string("g", TCLISTVALPTR(q1res, i), "abc.de.0")); + CU_ASSERT_FALSE(bson_compare_string("f", TCLISTVALPTR(q1res, i), "abc.g.0")); + CU_ASSERT_FALSE(bson_compare_string("f", TCLISTVALPTR(q1res, i), "abc.g.1")); + } + + bson_destroy(&bsq1); + tclistdel(q1res); + tcxstrdel(log); + ejdbquerydel(q1); +} + + +void testPushAll(void) { + EJCOLL *coll = ejdbcreatecoll(jb, "ticket123", NULL); + CU_ASSERT_PTR_NOT_NULL_FATAL(coll); + + bson bsq1; + bson_init_as_query(&bsq1); + bson_append_start_object(&bsq1, "$pushAll"); + bson_append_start_array(&bsq1, "abc.g"); + bson_append_string(&bsq1, "0", "h"); + bson_append_string(&bsq1, "1", "h"); + bson_append_int(&bsq1, "2", 11); + bson_append_finish_array(&bsq1); + bson_append_finish_object(&bsq1); + bson_finish(&bsq1); + CU_ASSERT_FALSE_FATAL(bsq1.err); + + EJQ *q1 = ejdbcreatequery(jb, &bsq1, NULL, 0, NULL); + CU_ASSERT_PTR_NOT_NULL_FATAL(q1); + uint32_t count = 0; + TCXSTR *log = tcxstrnew(); + ejdbqryexecute(coll, q1, &count, JBQRYCOUNT, log); + CU_ASSERT_PTR_NOT_NULL(strstr(TCXSTRPTR(log), "UPDATING MODE: YES")); + CU_ASSERT_PTR_NOT_NULL(strstr(TCXSTRPTR(log), "RS COUNT: 1")); + CU_ASSERT_PTR_NOT_NULL(strstr(TCXSTRPTR(log), "RS SIZE: 0")); + //fprintf(stderr, "%s", TCXSTRPTR(log)); + bson_destroy(&bsq1); + tcxstrdel(log); + ejdbquerydel(q1); + + //check updated data + bson_init_as_query(&bsq1); + bson_finish(&bsq1); + CU_ASSERT_FALSE_FATAL(bsq1.err); + + q1 = ejdbcreatequery(jb, &bsq1, NULL, 0, NULL); + CU_ASSERT_PTR_NOT_NULL_FATAL(q1); + log = tcxstrnew(); + TCLIST *q1res = ejdbqryexecute(coll, q1, &count, 0, log); + + CU_ASSERT_EQUAL(TCLISTNUM(q1res), 1); + //fprintf(stderr, "\n\n%s", TCXSTRPTR(log)); + + for (int i = 0; i < TCLISTNUM(q1res); ++i) { + CU_ASSERT_FALSE(bson_compare_string("g", TCLISTVALPTR(q1res, i), "abc.de.0")); + CU_ASSERT_FALSE(bson_compare_string("f", TCLISTVALPTR(q1res, i), "abc.g.0")); + CU_ASSERT_FALSE(bson_compare_string("f", TCLISTVALPTR(q1res, i), "abc.g.1")); + CU_ASSERT_FALSE(bson_compare_string("h", TCLISTVALPTR(q1res, i), "abc.g.2")); + CU_ASSERT_FALSE(bson_compare_string("h", TCLISTVALPTR(q1res, i), "abc.g.3")); + CU_ASSERT_FALSE(bson_compare_long(11, TCLISTVALPTR(q1res, i), "abc.g.4")); + } + + bson_destroy(&bsq1); + tclistdel(q1res); + tcxstrdel(log); + ejdbquerydel(q1); +} + void testPull(void) { EJCOLL *coll = ejdbcreatecoll(jb, "contacts", NULL); @@ -4019,7 +4127,7 @@ void testPull(void) { CU_ASSERT_PTR_NOT_NULL_FATAL(q1); log = tcxstrnew(); TCLIST *q1res = ejdbqryexecute(coll, q1, &count, 0, log); - \ + CU_ASSERT_EQUAL(TCLISTNUM(q1res), 1); //fprintf(stderr, "\n\n%s", TCXSTRPTR(log)); @@ -5898,6 +6006,8 @@ int main() { (NULL == CU_add_test(pSuite, "testOneFieldManyConditions", testOneFieldManyConditions)) || (NULL == CU_add_test(pSuite, "testAddToSet", testAddToSet)) || (NULL == CU_add_test(pSuite, "testTicket123", testTicket123)) || + (NULL == CU_add_test(pSuite, "testPush", testPush)) || + (NULL == CU_add_test(pSuite, "testPushAll", testPushAll)) || (NULL == CU_add_test(pSuite, "testPull", testPull)) || (NULL == CU_add_test(pSuite, "testFindInComplexArray", testFindInComplexArray)) || (NULL == CU_add_test(pSuite, "testElemMatch", testElemMatch)) || |