Skip to content

Commit

Permalink
#101
Browse files Browse the repository at this point in the history
  • Loading branch information
adamansky committed Sep 29, 2013
1 parent 941288f commit e1e9dc4
Show file tree
Hide file tree
Showing 4 changed files with 136 additions and 25 deletions.
6 changes: 6 additions & 0 deletions tcejdb/bson.c
Expand Up @@ -1654,6 +1654,7 @@ int bson_merge(const bson *b1, const bson *b2, bson_bool_t overwrite, bson *out)
typedef struct {
int nstack; //nested object stack pos
int matched; //number of matched include fields
int astack; //nested array stack pos
BSONSTRIPCTX *sctx;
} _BSONSTRIPVISITORCTX;

Expand Down Expand Up @@ -1683,6 +1684,7 @@ static bson_visitor_cmd_t _bsonstripvisitor_exclude(const char *ipath, int ipath
if (bt == BSON_OBJECT) {
bson_append_start_object2(sctx->bsout, key, keylen);
} else if (bt == BSON_ARRAY) {
ictx->astack++;
bson_append_start_array2(sctx->bsout, key, keylen);
}
return (BSON_VCMD_OK);
Expand All @@ -1696,6 +1698,7 @@ static bson_visitor_cmd_t _bsonstripvisitor_exclude(const char *ipath, int ipath
if (bt == BSON_OBJECT) {
bson_append_finish_object(sctx->bsout);
} else if (bt == BSON_ARRAY) {
--ictx->astack;
bson_append_finish_array(sctx->bsout);
}
}
Expand All @@ -1705,6 +1708,8 @@ 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);
}
return (BSON_VCMD_SKIP_NESTED | BSON_VCMD_SKIP_AFTER);
}
Expand Down Expand Up @@ -1809,6 +1814,7 @@ int bson_strip2(BSONSTRIPCTX *sctx) {
}
_BSONSTRIPVISITORCTX ictx = {
.nstack = 0,
.astack = 0,
.matched = 0,
.sctx = sctx
};
Expand Down
59 changes: 48 additions & 11 deletions tcejdb/ejdb.c
Expand Up @@ -2596,7 +2596,7 @@ static bool _pushprocessedbson(_QRYCTX *ctx, const void *bsbuf, int bsbufsz) {
if (q->$ifields) { //we have positional $(projection)
assert(ctx->imode == true); //ensure we are in include mode
if (!_ifields) {
_ifields = tcmapnew2(TCMAPRNUM(q->$ifields));
_ifields = tcmapnew2(q->$ifields->bnum);
} else {
_ifields = tcmapdup(ifields);
}
Expand Down Expand Up @@ -2688,7 +2688,7 @@ static bool _exec$do(_QRYCTX *ctx, const void *bsbuf, bson *bsout) {
return true;
}

//Create update BSON object for $set and $inc operations
//Create update BSON object for $set/$unset/$inc operations
static bson* _qfgetupdateobj(const EJQF *qf) {
assert(qf->updateobj);
if (!qf->$ufields || TCLISTNUM(qf->$ufields) < 1) { //we do not ref $(query) fields.
Expand Down Expand Up @@ -2798,9 +2798,10 @@ static bool _qryupdate(_QRYCTX *ctx, void *bsbuf, int bsbufsz) {
bson_reset(&bsout);

const EJQF *setqf = NULL; /*$set*/
const EJQF *unsetqf = NULL; /*$unset*/
const EJQF *incqf = NULL; /*$inc*/
const EJQF * addsetqf[2] = {NULL}; /*$addToSet, $addToSetAll*/
const EJQF * pullqf[2] = {NULL}; /*$pull, $pullAll*/
const EJQF *addsetqf[2] = {NULL}; /*$addToSet, $addToSetAll*/
const EJQF *pullqf[2] = {NULL}; /*$pull, $pullAll*/

//$set, $inc, $addToSet, $addToSetAll, $pull, $pullAll operations
for (int i = 0; i < TCLISTNUM(q->qflist); ++i) {
Expand All @@ -2812,6 +2813,10 @@ static bool _qryupdate(_QRYCTX *ctx, void *bsbuf, int bsbufsz) {
setqf = qf;
continue;
}
if (qf->flags & EJCONDUNSET) { //$unset
unsetqf = qf;
continue;
}
if (qf->flags & EJCONDINC) { //$inc
incqf = qf;
continue;
Expand Down Expand Up @@ -2849,6 +2854,7 @@ static bool _qryupdate(_QRYCTX *ctx, void *bsbuf, int bsbufsz) {
if (!rv) {
goto finish;
}

if (incqf) { //$inc
bson *updobj = _qfgetupdateobj(incqf);
if (!bsout.data) {
Expand Down Expand Up @@ -2899,7 +2905,7 @@ static bool _qryupdate(_QRYCTX *ctx, void *bsbuf, int bsbufsz) {
for (int i = 0; i < 2; ++i) { //$pull $pullAll
const EJQF *qf = pullqf[i];
if (!qf) continue;
char* inbuf = (bsout.finished) ? bsout.data : bsbuf;
char *inbuf = (bsout.finished) ? bsout.data : bsbuf;
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
Expand Down Expand Up @@ -2927,7 +2933,7 @@ static bool _qryupdate(_QRYCTX *ctx, void *bsbuf, int bsbufsz) {
for (int i = 0; i < 2; ++i) { //$addToSet $addToSetAll
const EJQF *qf = addsetqf[i];
if (!qf) continue;
char* inbuf = (bsout.finished) ? bsout.data : bsbuf;
char *inbuf = (bsout.finished) ? bsout.data : bsbuf;
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) {
Expand All @@ -2948,11 +2954,39 @@ static bool _qryupdate(_QRYCTX *ctx, void *bsbuf, int bsbufsz) {
bson_finish(&bsout);
update = true;
}
if (!update || !rv) {
goto finish;
}

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));
}
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);
}
bson_finish(&bsout);
update = true;
}

if (!update || !rv) {
goto finish;
}
if (bsout.err) { //Resulting BSON is OK?
rv = false;
_ejdbsetecode(coll->jb, JBEQUPDFAILED, __FILE__, __LINE__, __func__);
Expand Down Expand Up @@ -4494,7 +4528,8 @@ static int _parse_qobj_impl(EJDB *jb, EJQ *q, bson_iterator *it, TCLIST *qlist,
!strcmp("$upsert", fkey) ||
!strcmp("$pull", fkey) ||
!strcmp("$pullAll", fkey) ||
!strcmp("$do", fkey)
!strcmp("$do", fkey) ||
!strcmp("$unset", fkey)
) {
if (pqf) { //Top level ops
ret = JBEQERROR;
Expand Down Expand Up @@ -4641,10 +4676,12 @@ static int _parse_qobj_impl(EJDB *jb, EJQ *q, bson_iterator *it, TCLIST *qlist,
qf.flags |= EJCONDALL;
} else if (!strcmp("$do", fkey)) {
qf.flags |= EJCONDOIT;
} else if (!strcmp("$unset", fkey)) {
qf.flags |= EJCONDUNSET;
}
}

if ((qf.flags & (EJCONDSET | EJCONDINC | EJCONDADDSET | EJCONDPULL))) {
if ((qf.flags & (EJCONDSET | EJCONDINC | EJCONDADDSET | EJCONDPULL | EJCONDUNSET))) {
assert(qf.updateobj == NULL);
qf.q->flags |= EJQUPDATING;
qf.updateobj = bson_create();
Expand All @@ -4657,7 +4694,7 @@ static int _parse_qobj_impl(EJDB *jb, EJQ *q, bson_iterator *it, TCLIST *qlist,
//$addToSetAll & $pullAll accepts only a"$set"rrays
continue;
}
if (!(qf.flags & EJCONDALL) && (qf.flags & (EJCONDSET | EJCONDINC))) { //Checking the $(query) positional operator.
if (!(qf.flags & EJCONDALL) && (qf.flags & (EJCONDSET | EJCONDINC | EJCONDUNSET))) { //Checking the $(query) positional operator.
const char* ukey = BSON_ITERATOR_KEY(&sit);
char *pptr;
if ((pptr = strstr(ukey, ".$")) && pptr && (*(pptr + 2) == '\0' || *(pptr + 2) == '.')) {// '.$' || '.$.'
Expand Down
3 changes: 2 additions & 1 deletion tcejdb/ejdb_private.h
Expand Up @@ -69,7 +69,8 @@ enum { /**> Query field flags */
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 */
EJCONDOIT = 1 << 16, /**> $do query field operation */
EJCONDUNSET = 1 << 17 /**> $unset Field value */
};

enum { /**> Query flags */
Expand Down
93 changes: 80 additions & 13 deletions tcejdb/testejdb/t2.c
Expand Up @@ -4670,15 +4670,15 @@ void testTicket96() {
bson_append_finish_object(&bsq1);
bson_append_finish_array(&bsq1);
bson_finish(&bsq1);

CU_ASSERT_EQUAL(bsq1.err, 0);

TCXSTR *log = tcxstrnew();
uint32_t count;
EJQ *q1 = ejdbcreatequery(jb, &bsq1, NULL, 0, NULL);
bson_destroy(&bsq1);
CU_ASSERT_PTR_NOT_NULL_FATAL(q1);
TCLIST *q1res = ejdbqryexecute(coll, q1, &count, 0, log);
TCLIST *q1res = ejdbqryexecute(coll, q1, &count, 0, log);
CU_ASSERT_EQUAL(TCLISTNUM(q1res), 1);

tclistdel(q1res);
Expand Down Expand Up @@ -4811,8 +4811,8 @@ void testDQupdate2() {

void testTicket99() {
EJCOLL *coll = ejdbcreatecoll(jb, "ticket99", NULL);
CU_ASSERT_PTR_NOT_NULL_FATAL(coll);
CU_ASSERT_PTR_NOT_NULL_FATAL(coll);

//create
bson_oid_t oid;
bson data;
Expand All @@ -4826,8 +4826,8 @@ void testTicket99() {
bson_finish(&data);
CU_ASSERT_TRUE(ejdbsavebson(coll, &data, &oid));
bson_destroy(&data);
//set

//set
bson bsquery;
bson_init_as_query(&bsquery);
bson_append_oid(&bsquery, "_id", &oid);
Expand All @@ -4839,19 +4839,19 @@ void testTicket99() {
bson_append_string(&bsquery, "arr.0.test4", "value4");
bson_append_finish_object(&bsquery);
bson_finish(&bsquery);

uint32_t count = ejdbupdate(coll, &bsquery, 0, 0, 0, 0);
CU_ASSERT_EQUAL(count, 1);
bson_destroy(&bsquery);

bson_init_as_query(&bsquery);
bson_finish(&bsquery);
EJQ *q1 = ejdbcreatequery(jb, &bsquery, NULL, 0, NULL);
bson_destroy(&bsquery);
CU_ASSERT_PTR_NOT_NULL_FATAL(q1);
TCLIST *q1res = ejdbqryexecute(coll, q1, &count, 0, NULL);
TCLIST *q1res = ejdbqryexecute(coll, q1, &count, 0, NULL);
CU_ASSERT_EQUAL(TCLISTNUM(q1res), 1);

for (int i = 0; i < TCLISTNUM(q1res); ++i) {
CU_ASSERT_FALSE(bson_compare_string("value0", TCLISTVALPTR(q1res, i), "arr.0.test0"));
CU_ASSERT_FALSE(bson_compare_string("value1", TCLISTVALPTR(q1res, i), "arr.0.test1"));
Expand All @@ -4864,6 +4864,72 @@ void testTicket99() {
ejdbquerydel(q1);
}


void testTicket101() {
EJCOLL *coll = ejdbcreatecoll(jb, "ticket101", NULL);
CU_ASSERT_PTR_NOT_NULL_FATAL(coll);

bson b;
bson_oid_t oid;

bson_init(&b);
bson_append_int(&b, "z", 33);
bson_append_start_object(&b, "obj");
bson_append_string(&b, "name", "abc");
bson_append_int(&b, "score", 12);
bson_append_finish_object(&b); //eof obj
bson_append_start_array(&b, "arr");
bson_append_int(&b, "0", 0);
bson_append_start_object(&b, "1");
bson_append_string(&b, "name", "cde");
bson_append_int(&b, "score", 13);
bson_append_finish_object(&b);
bson_append_int(&b, "2", 2);
bson_append_int(&b, "3", 3);
bson_append_finish_array(&b); //eof arr
bson_finish(&b);
CU_ASSERT_TRUE(ejdbsavebson(coll, &b, &oid));
bson_destroy(&b);

bson bsq;
bson_init_as_query(&bsq);
bson_append_start_object(&bsq, "$unset");
bson_append_string(&bsq, "obj.name", "");
bson_append_bool(&bsq, "arr.1.score", true);
bson_append_bool(&bsq, "arr.2", true);
bson_append_finish_object(&bsq);
bson_finish(&bsq);

uint32_t count = ejdbupdate(coll, &bsq, 0, 0, 0, 0);
bson_destroy(&bsq);
CU_ASSERT_EQUAL(count, 1);


bson_init_as_query(&bsq);
bson_finish(&bsq);
EJQ *q1 = ejdbcreatequery(jb, &bsq, NULL, 0, NULL);
bson_destroy(&bsq);
CU_ASSERT_PTR_NOT_NULL_FATAL(q1);
TCLIST *q1res = ejdbqryexecute(coll, q1, &count, 0, NULL);
CU_ASSERT_EQUAL(TCLISTNUM(q1res), 1);

bson_type bt;
bson_iterator it;
for (int i = 0; i < TCLISTNUM(q1res); ++i) {
CU_ASSERT_FALSE(bson_compare_long(33, TCLISTVALPTR(q1res, i), "z"));
CU_ASSERT_FALSE(bson_compare_long(12, TCLISTVALPTR(q1res, i), "obj.score"));
bson_iterator_from_buffer(&it, TCLISTVALPTR(q1res, i));
bt = bson_find_fieldpath_value("obj.name", &it);
CU_ASSERT_TRUE(bt == BSON_EOO);
bson_iterator_from_buffer(&it, TCLISTVALPTR(q1res, i));
bt = bson_find_fieldpath_value("arr.2", &it);
CU_ASSERT_TRUE(bt == BSON_UNDEFINED);
}

tclistdel(q1res);
ejdbquerydel(q1);
}

int main() {
setlocale(LC_ALL, "en_US.UTF-8");
CU_pSuite pSuite = NULL;
Expand Down Expand Up @@ -4942,9 +5008,10 @@ int main() {
(NULL == CU_add_test(pSuite, "test$update2", testDQupdate2)) ||
(NULL == CU_add_test(pSuite, "testTicket96", testTicket96)) ||
(NULL == CU_add_test(pSuite, "testTicket99", testTicket99)) ||
(NULL == CU_add_test(pSuite, "testMetaInfo", testMetaInfo))

) {
(NULL == CU_add_test(pSuite, "testTicket101", testTicket101)) ||
(NULL == CU_add_test(pSuite, "testMetaInfo", testMetaInfo))

) {
CU_cleanup_registry();
return CU_get_error();
}
Expand Down

0 comments on commit e1e9dc4

Please sign in to comment.