summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorAnton Adamansky <adamansky@gmail.com>2015-04-28 22:25:24 +0600
committerAnton Adamansky <adamansky@gmail.com>2015-04-28 22:25:24 +0600
commit0478ea61300762aaf66f3b158dfb6ab63e87cb69 (patch)
treeb4cf9f8b4dd1eef2b3e8aef6f939299289637f70 /src
parent000984a89f7e015c8d02bbb34cd0833231ea99d8 (diff)
downloadejdb-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.c96
-rw-r--r--src/bson/bson.h24
-rw-r--r--src/ejdb/ejdb.c332
-rw-r--r--src/ejdb/ejdb.h34
-rw-r--r--src/ejdb/ejdb_private.h53
-rw-r--r--src/ejdb/tests/ejdbtest2.c112
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)) ||