-Subproject commit 27ed8b05d1b0aed074d3363d901a206d007b28bc
+Subproject commit c50c33e9397d7a0a8717e8ce7530572907c054ad
#include "bson.h"
#include "encoding.h"
#include "myconf.h"
+#include "tcutil.h"
const int initialBufferSize = 128;
bson_visit_fields_impl(flags, pstack, 0, it, visitor, op);
}
-
-
-
-
static bson_type bson_find_fieldpath_value_impl(char* pstack, int curr, FFPCTX *ffpctx, bson_iterator *it) {
int i;
bson_type t;
}
}
-static bson_bool_t bson_isnumstr(const char *str, int len){
- assert(str);
- bool isnum = false;
- while(len > 0 && *str > '\0' && *str <= ' '){
- str++;
- len--;
- }
- while(len > 0 && *str >= '0' && *str <= '9'){
- isnum = true;
- str++;
- len--;
- }
- while(len > 0 && *str > '\0' && *str <= ' '){
- str++;
- len--;
- }
- return (isnum && (*str == '\0' || len == 0));
+static bson_bool_t bson_isnumstr(const char *str, int len) {
+ assert(str);
+ bool isnum = false;
+ while (len > 0 && *str > '\0' && *str <= ' ') {
+ str++;
+ len--;
+ }
+ while (len > 0 && *str >= '0' && *str <= '9') {
+ isnum = true;
+ str++;
+ len--;
+ }
+ while (len > 0 && *str > '\0' && *str <= ' ') {
+ str++;
+ len--;
+ }
+ return (isnum && (*str == '\0' || len == 0));
}
EJDB_EXPORT void bson_swap_endian64(void *outp, const void *inp) {
return rv;
}
-EJDB_EXPORT bool bson_find_merged_array_sets(const void *mbuf, const void *inbuf) {
+EJDB_EXPORT bool bson_find_merged_array_sets(const void *mbuf, const void *inbuf, bool expandall) {
assert(mbuf && inbuf);
bool found = false;
bson_iterator it, it2;
bson_type bt, bt2;
bson_iterator_from_buffer(&it, mbuf);
- while ((bt = bson_iterator_next(&it)) != BSON_EOO) {
+ while (!found && (bt = bson_iterator_next(&it)) != BSON_EOO) {
bson_iterator_from_buffer(&it2, inbuf);
bt2 = bson_find_fieldpath_value(bson_iterator_key(&it), &it2);
if (bt2 != BSON_ARRAY) {
}
bson_iterator sit;
bson_iterator_subiterator(&it2, &sit);
- while ((bt2 = bson_iterator_next(&sit)) != BSON_EOO) {
- if (!bson_compare_it_current(&sit, &it)) {
- found = true;
- break;
+ while (!found && (bt2 = bson_iterator_next(&sit)) != BSON_EOO) {
+ if (expandall) {
+ if (bt2 != BSON_ARRAY) {
+ continue;
+ }
+ bson_iterator sit2;
+ bson_iterator_subiterator(&sit, &sit2);
+ while ((bt2 = bson_iterator_next(&sit2)) != BSON_EOO) {
+ if (!bson_compare_it_current(&sit, &it)) {
+ found = true;
+ break;
+ }
+ }
+ } else {
+ if (!bson_compare_it_current(&sit, &it)) {
+ found = true;
+ break;
+ }
}
}
- if (found) {
- break;
- }
}
return found;
}
allfound = false;
break;
}
- if (bt2 != BSON_ARRAY) { //some other field
+ if (bt2 != BSON_ARRAY) { //not an array field
continue;
}
allfound = false;
int mfields;
int ecode;
bool duty;
+ bool expandall;
} BSON_MASETS_CTX;
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) {
assert(ctx && ctx->mfields >= 0);
bson_iterator mit;
bson_type bt = bson_iterator_type(it);
- if (bt != BSON_OBJECT && bt != BSON_ARRAY) { //simple primitive case
+ if (bt != BSON_OBJECT && bt != BSON_ARRAY) { //trivial case
if (after) {
- return BSON_VCMD_OK;
+ return (BSON_VCMD_OK);
}
bson_append_field_from_iterator(it, ctx->bsout);
return (BSON_VCMD_SKIP_AFTER);
if (bt == BSON_ARRAY) {
bson_iterator_from_buffer(&mit, ctx->mbuf);
bt = bson_find_fieldpath_value2(fpath, fpathlen, &mit);
- if (bt == BSON_EOO) {
+ if (bt == BSON_EOO || (ctx->expandall && bt != BSON_ARRAY)) {
bson_append_field_from_iterator(it, ctx->bsout);
return (BSON_VCMD_SKIP_NESTED | BSON_VCMD_SKIP_AFTER);
}
bson_iterator_subiterator(it, &ait);
bson_append_start_array(ctx->bsout, key);
int c = 0;
+ bool found = false;
while ((bt = bson_iterator_next(&ait)) != BSON_EOO) {
- if (!bson_compare_it_current(&ait, &mit)) {
- continue;
+ found = false;
+ if (ctx->expandall) {
+ bson_iterator mitsub;
+ bson_iterator_subiterator(&mit, &mitsub);
+ while ((bt = bson_iterator_next(&mitsub)) != BSON_EOO) {
+ if (!bson_compare_it_current(&ait, &mitsub)) {
+ found = true;
+ break;
+ }
+ }
+ } else {
+ found = !bson_compare_it_current(&ait, &mit);
+ }
+ if (!found) {
+ char kbuf[TCNUMBUFSIZ];
+ bson_numstrn(kbuf, TCNUMBUFSIZ, c++);
+ bson_append_field_from_iterator2(kbuf, &ait, ctx->bsout);
}
- char kbuf[TCNUMBUFSIZ];
- bson_numstrn(kbuf, TCNUMBUFSIZ, c++);
- bson_append_field_from_iterator2(kbuf, &ait, ctx->bsout);
}
bson_append_finish_array(ctx->bsout);
return (BSON_VCMD_SKIP_NESTED | BSON_VCMD_SKIP_AFTER);
bson_iterator mit;
bson_type bt = bson_iterator_type(it);
- if (bt != BSON_OBJECT && bt != BSON_ARRAY) { //simple primitive case
+ if (bt != BSON_OBJECT && bt != BSON_ARRAY) { //trivial case
if (after) {
- return BSON_VCMD_OK;
+ return (BSON_VCMD_OK);
}
bson_append_field_from_iterator(it, ctx->bsout);
return (BSON_VCMD_SKIP_AFTER);
if (bt == BSON_ARRAY) {
bson_iterator_from_buffer(&mit, ctx->mbuf);
bt = bson_find_fieldpath_value2(fpath, fpathlen, &mit);
- if (bt == BSON_EOO) {
+ if (bt == BSON_EOO || (ctx->expandall && bt != BSON_ARRAY)) {
bson_append_field_from_iterator(it, ctx->bsout);
return (BSON_VCMD_SKIP_NESTED | BSON_VCMD_SKIP_AFTER);
}
bson_append_start_array(ctx->bsout, key);
bool found = false;
int c = 0;
- while ((bt = bson_iterator_next(&ait)) != BSON_EOO) {
- if (!found && !bson_compare_it_current(&ait, &mit)) {
- found = true;
+ if (ctx->expandall) { //Set of array elements to add
+ while ((bt = bson_iterator_next(&ait)) != BSON_EOO) { //Flush current array
+ bson_append_field_from_iterator(&ait, ctx->bsout);
+ ++c;
+ }
+ //Iterate over set to add
+ bson_iterator mitsub;
+ 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 (!found) { //Append missing element
+ char kbuf[TCNUMBUFSIZ];
+ bson_numstrn(kbuf, TCNUMBUFSIZ, c++);
+ bson_append_field_from_iterator2(kbuf, &mitsub, ctx->bsout);
+ ctx->duty = true;
+ }
+ }
+ } else { //Single element to add
+ while ((bt = bson_iterator_next(&ait)) != BSON_EOO) {
+ if (!found && !bson_compare_it_current(&ait, &mit)) {
+ found = true;
+ }
+ bson_append_field_from_iterator(&ait, ctx->bsout);
+ ++c;
+ }
+ if (!found) { //uppend missing element into array
+ char kbuf[TCNUMBUFSIZ];
+ bson_numstrn(kbuf, TCNUMBUFSIZ, c);
+ bson_append_field_from_iterator2(kbuf, &mit, ctx->bsout);
+ ctx->duty = true;
}
- bson_append_field_from_iterator(&ait, ctx->bsout);
- ++c;
- }
- if (!found) { //uppend missing element into array
- char kbuf[TCNUMBUFSIZ];
- bson_numstrn(kbuf, TCNUMBUFSIZ, c);
- bson_append_field_from_iterator2(kbuf, &mit, ctx->bsout);
- ctx->duty = true;
}
bson_append_finish_array(ctx->bsout);
return (BSON_VCMD_SKIP_NESTED | BSON_VCMD_SKIP_AFTER);
}
-EJDB_EXPORT int bson_merge_array_sets(const void *mbuf, const void *inbuf, bool pull, bson *bsout) {
+EJDB_EXPORT int bson_merge_array_sets(const void *mbuf, const void *inbuf, bool pull, bool expandall, bson *bsout) {
assert(mbuf && inbuf && bsout);
if (bsout->finished) {
return BSON_ERROR;
}
- BSON_MASETS_CTX ctx;
- ctx.bsout = bsout;
- ctx.mbuf = mbuf;
- ctx.mfields = 0;
- ctx.duty = false;
- ctx.ecode = BSON_OK;
-
+ BSON_MASETS_CTX ctx = {
+ .bsout = bsout,
+ .mbuf = mbuf,
+ .mfields = 0,
+ .duty = false,
+ .expandall = expandall,
+ .ecode = BSON_OK
+ };
bson_type bt, bt2;
bson_iterator it, it2;
bson_iterator_from_buffer(&it, mbuf);
EJDB_EXPORT bson_type bson_find_from_buffer(bson_iterator *it, const char *buffer, const char *name);
-
-
typedef struct { /**< Find field path context */
const char* fpath;
int fplen;
EJDB_EXPORT bson* bson_create_from_buffer2(bson *bs, const void *buf, int bufsz);
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);
-EJDB_EXPORT int bson_merge_array_sets(const void *mbuf, const void *inbuf, bool pull, bson *bsout);
+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);
EJDB_EXTERN_C_END
_ejdbsetecode(jcoll->jb, TCEINVALID, __FILE__, __LINE__, __func__);
return NULL;
}
- JBCLOCKMETHOD(jcoll, (q->flags & EJQUPDATING) ? true : false);
+ JBCLOCKMETHOD(jcoll, (q->flags & EJQUPDATING) ? true : false);
_ejdbsetecode(jcoll->jb, TCESUCCESS, __FILE__, __LINE__, __func__);
if (ejdbecode(jcoll->jb) != TCESUCCESS) { //we are not in fatal state
JBCUNLOCKMETHOD(jcoll);
bsout.dataSize = 0;
bson_reset(&bsout);
- const EJQF *setqf = NULL;
- const EJQF *incqf = NULL;
- const EJQF *addsetqf = NULL;
- const EJQF *pullqf = NULL;
+ const EJQF *setqf = NULL; /*$set*/
+ const EJQF *incqf = NULL; /*$inc*/
+ const EJQF * addsetqf[2] = {NULL}; /*$addToSet, $addToSetAll*/
+ const EJQF * pullqf[2] = {NULL}; /*$pull, $pullAll*/
- //$set, $inc, $addToSet, $pull operations
+ //$set, $inc, $addToSet, $addToSetAll, $pull, $pullAll operations
for (int i = 0; i < TCLISTNUM(ejq->qobjlist); ++i) {
const EJQF *qf = TCLISTVALPTR(ejq->qobjlist, i);
if (qf->updateobj == NULL) {
continue;
}
- if (!setqf && qf->flags & EJCONDSET) { //$set
+ if (qf->flags & EJCONDSET) { //$set
setqf = qf;
- } else if (!incqf && qf->flags & EJCONDINC) {
+ continue;
+ }
+ if (qf->flags & EJCONDINC) { //$inc
incqf = qf;
- } else if (!addsetqf && qf->flags & EJCONDADDSET) {
- addsetqf = qf;
- } else if (!pullqf && qf->flags & EJCONDPULL) {
- pullqf = qf;
- } else {
- assert(0);
- break;
+ continue;
+ }
+ if (qf->flags & EJCONDADDSET) { //$addToSet, $addToSetAll
+ if (qf->flags & EJCONDALL) {
+ addsetqf[1] = qf;
+ } else {
+ addsetqf[0] = qf;
+ }
+ }
+ if (qf->flags & EJCONDPULL) { //$pull, $pullAll
+ if (qf->flags & EJCONDALL) {
+ pullqf[1] = qf;
+ } else {
+ pullqf[0] = qf;
+ }
}
}
if (setqf) { //$set
update = true;
}
}
+
+ if (!rv) {
+ goto finish;
+ }
}
- if (!rv) {
- goto finish;
- }
- if (pullqf) { //$pull
+
+ for (int i = 0; i < 2; ++i) { //$pull $pullAll
+ const EJQF *qf = pullqf[i];
+ if (!qf) continue;
char* inbuf = (bsout.finished) ? bsout.data : bsbuf;
- if (bson_find_merged_array_sets(bson_data(pullqf->updateobj), inbuf)) {
+ if (bson_find_merged_array_sets(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));
assert(bsout.data == NULL);
bson_init_size(&bsout, bsbufsz);
}
- //$pull merge
- if (bson_merge_array_sets(bson_data(pullqf->updateobj), inbuf, true, &bsout)) {
+ //$pull $pullAll merge
+ if (bson_merge_array_sets(bson_data(qf->updateobj), inbuf, true, (qf->flags & EJCONDALL), &bsout)) {
rv = false;
_ejdbsetecode(jcoll->jb, JBEQUPDFAILED, __FILE__, __LINE__, __func__);
}
bson_finish(&bsout);
update = true;
}
- }
- if (!rv) {
- goto finish;
+ if (!rv) {
+ goto finish;
+ }
}
- if (addsetqf) { //$addToSet
+ 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 (bson_find_unmerged_array_sets(bson_data(addsetqf->updateobj), inbuf)) {
+ if ((qf->flags & EJCONDALL) || bson_find_unmerged_array_sets(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
assert(bsout.data == NULL);
bson_init_size(&bsout, bsbufsz);
}
- //$addToSet merge
- if (bson_merge_array_sets(bson_data(addsetqf->updateobj), inbuf, false, &bsout)) {
+ //$addToSet $addToSetAll merge
+ if (bson_merge_array_sets(bson_data(qf->updateobj), inbuf, false, (qf->flags & EJCONDALL), &bsout)) {
rv = false;
_ejdbsetecode(jcoll->jb, JBEQUPDFAILED, __FILE__, __LINE__, __func__);
}
bson_finish(&bsout);
update = true;
}
+ if (!update || !rv) {
+ goto finish;
+ }
}
- if (!update || !rv) {
- goto finish;
- }
- if (bsout.err) {
+ if (bsout.err) { //Resulting BSON is OK?
rv = false;
_ejdbsetecode(jcoll->jb, JBEQUPDFAILED, __FILE__, __LINE__, __func__);
goto finish;
!strcmp("$inc", fkey) ||
!strcmp("$dropall", fkey) ||
!strcmp("$addToSet", fkey) ||
+ !strcmp("$addToSetAll", fkey) ||
!strcmp("$upsert", fkey) ||
- !strcmp("$pull", fkey)) {
+ !strcmp("$pull", fkey) ||
+ !strcmp("$pullAll", fkey)) {
if (pqf) { //Top level ops
ret = JBEQERROR;
_ejdbsetecode(jb, ret, __FILE__, __LINE__, __func__);
} 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("$pullAll", fkey)) {
+ qf.flags |= EJCONDPULL;
+ qf.flags |= EJCONDALL;
}
}
if ((qf.flags & (EJCONDSET | EJCONDINC | EJCONDADDSET | EJCONDPULL))) {
bson_iterator sit;
bson_iterator_subiterator(it, &sit);
while ((sbt = bson_iterator_next(&sit)) != BSON_EOO) {
+ if ((qf.flags & EJCONDALL) && sbt != BSON_ARRAY) {
+ //$addToSetAll & $pullAll accepts arrays only as argument
+ continue;
+ }
bson_append_field_from_iterator(&sit, qf.updateobj);
}
bson_finish(qf.updateobj);
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 */
+ EJCONDUPSERT = 1 << 14, /**> $upsert Upsert $set operation */
+ EJCONDALL = 1 << 15 /**> 'All' modificator for $pull or $addToSet ($addToSetAll or $pullAll) */
};
bson_append_finish_object(&bsq1);
bson_append_start_object(&bsq1, "$addToSet");
bson_append_string(&bsq1, "personal.tags", "tag3");
- bson_append_string(&bsq1, "labels", "red");
- bson_append_finish_object(&bsq1);
+ bson_append_finish_object(&bsq1); //EOF $addToSet
+ bson_append_start_object(&bsq1, "$addToSetAll");
+ bson_append_start_array(&bsq1, "labels");
+ bson_append_string(&bsq1, "0", "red");
+ bson_append_string(&bsq1, "1", "black");
+ bson_append_string(&bsq1, "2", "green");
+ bson_append_finish_array(&bsq1);
+ bson_append_finish_object(&bsq1); //EOF $addToSetAll
bson_finish(&bsq1);
CU_ASSERT_FALSE_FATAL(bsq1.err);
CU_ASSERT_FALSE(bson_compare_string("tag3", TCLISTVALPTR(q1res, i), "personal.tags.2"));
CU_ASSERT_FALSE(bson_compare_string("green", TCLISTVALPTR(q1res, i), "labels.0"));
CU_ASSERT_FALSE(bson_compare_string("red", TCLISTVALPTR(q1res, i), "labels.1"));
+ CU_ASSERT_FALSE(bson_compare_string("black", TCLISTVALPTR(q1res, i), "labels.2"));
+ CU_ASSERT_TRUE(bson_compare_string("green", TCLISTVALPTR(q1res, i), "labels.3"));
}
bson_destroy(&bsq1);