diff options
-rw-r--r-- | Changelog | 6 | ||||
-rw-r--r-- | package.json | 2 | ||||
-rw-r--r-- | tcejdb/ejdb.c | 47 | ||||
-rw-r--r-- | tcejdb/ejdb_private.h | 6 | ||||
-rw-r--r-- | tcejdb/testejdb/t2.c | 101 |
5 files changed, 146 insertions, 16 deletions
@@ -1,3 +1,9 @@ +2012-11-27 Anton Adamansky. <adamansky@gmail.com> + * Added $dropall query operation in order to remove matched records + * Better boolean type support, boolean values treated as numbers. + * Various bugfixes + - Release 1.0.17 + 2012-11-26 Anton Adamansky. <adamansky@gmail.com> * NodeJS: Added merge json object option ($merge) in save() method * Added ejdbsavebson2() with merge option. diff --git a/package.json b/package.json index a1393f9..698d304 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name" : "ejdb", - "version" : "1.0.16", + "version" : "1.0.17", "main" : "node/ejdb.js", "description" : "EJDB - Embedded JSON Database engine", "homepage" : "http://ejdb.org", diff --git a/tcejdb/ejdb.c b/tcejdb/ejdb.c index 05a285f..619a434 100644 --- a/tcejdb/ejdb.c +++ b/tcejdb/ejdb.c @@ -391,10 +391,8 @@ EJDB_EXPORT bool ejdbrmbson(EJCOLL *jcoll, bson_oid_t *oid) { goto finish; } olddata = tcmapget3(rmap, JDBCOLBSON, JDBCOLBSONL, &olddatasz); - if (!_updatebsonidx(jcoll, oid, NULL, olddata, olddatasz, NULL)) { - rv = false; - } - if (!tctdbout(jcoll->tdb, oid, sizeof (*oid))) { + if (!_updatebsonidx(jcoll, oid, NULL, olddata, olddatasz, NULL) || + !tctdbout(jcoll->tdb, oid, sizeof (*oid))) { rv = false; } finish: @@ -1570,7 +1568,7 @@ static void _pushstripbson(TCLIST *rs, TCMAP *ifields, void *bsbuf, int bsbufsz) TCFREE(bsbuf); } -static bool _qryupdate(EJCOLL *jcoll, const EJQ *ejq, void *bsbuf, int bsbufsz, TCLIST *didxctx) { +static bool _qryupdate(EJCOLL *jcoll, const EJQ *ejq, void *bsbuf, int bsbufsz, TCLIST *didxctx, TCXSTR *log) { assert(ejq->flags & EJQUPDATING); assert(didxctx); @@ -1586,16 +1584,45 @@ static bool _qryupdate(EJCOLL *jcoll, const EJQ *ejq, void *bsbuf, int bsbufsz, TCMAP *qobjmap = ejq->qobjmap; TCMAP *rowm = NULL; + if (ejq->flags & EJQDROPALL) { //Record will be dropped + bt = bson_find_from_buffer(&it, bsbuf, JDBIDKEYNAME); + if (bt != BSON_OID) { + _ejdbsetecode(jcoll->jb, JBEQUPDFAILED, __FILE__, __LINE__, __func__); + return false; + } + oid = bson_iterator_oid(&it); + assert(oid); + if (log) { + char xoid[25]; + bson_oid_to_string(oid, xoid); + tcxstrprintf(log, "$DROPALL ON: %s\n", xoid); + } + const void *olddata; + int olddatasz = 0; + TCMAP *rmap = tctdbget(jcoll->tdb, oid, sizeof (*oid)); + if (rmap) { + olddata = tcmapget3(rmap, JDBCOLBSON, JDBCOLBSONL, &olddatasz); + if (!_updatebsonidx(jcoll, oid, NULL, olddata, olddatasz, didxctx) || + !tctdbout(jcoll->tdb, oid, sizeof (*oid))) { + rv = false; + } + tcmapdel(rmap); + } + return rv; + } + + //Apply update operation bson src; bson_create_from_buffer2(&src, bsbuf, bsbufsz); - bson bsout; //Resulting updated bson + bson bsout; bsout.data = NULL; bsout.dataSize = 0; const EJQF *setqf = NULL; const EJQF *incqf = NULL; + //$set, $inc operations tcmapiterinit(qobjmap); while ((kbuf = tcmapiternext(qobjmap, &kbufsz)) != NULL) { const EJQF *qf = tcmapiterval(kbuf, &qfsz); @@ -1680,8 +1707,6 @@ static bool _qryupdate(EJCOLL *jcoll, const EJQ *ejq, void *bsbuf, int bsbufsz, oid = bson_iterator_oid(&it); rowm = tcmapnew2(TCMAPTINYBNUM); tcmapput(rowm, JDBCOLBSON, JDBCOLBSONL, bson_data(&bsout), bson_size(&bsout)); - //fprintf(stderr, "\nAFTER\n"); - //bson_print_raw(stderr, bson_data(&bsout), 0); rv = tctdbput(jcoll->tdb, oid, sizeof (*oid), rowm); if (rv) { rv = _updatebsonidx(jcoll, oid, &bsout, bsbuf, bsbufsz, didxctx); @@ -1816,7 +1841,7 @@ static TCLIST* _qryexecute(EJCOLL *jcoll, const EJQ *q, uint32_t *outcount, int #define JBQREGREC(_bsbuf, _bsbufsz) \ ++count; \ if (ejq->flags & EJQUPDATING) { \ - _qryupdate(jcoll, ejq, (_bsbuf), (_bsbufsz), didxctx); \ + _qryupdate(jcoll, ejq, (_bsbuf), (_bsbufsz), didxctx, log); \ } \ if (!onlycount && (all || count > skip)) { \ if (ifields) {\ @@ -3127,12 +3152,12 @@ static int _parse_qobj_impl(EJDB *jb, EJQ *q, bson_iterator *it, TCMAP *qmap, TC bool bv = bson_iterator_bool_raw(it); if (isckey) { if (!strcmp("$dropall", fkey) && bv) { - qf.flags |= EJCONDROPALL; qf.flags |= EJFEXCLUDED; qf.fpath = tcstrjoin(pathStack, '.'); qf.fpathsz = strlen(qf.fpath); qf.tcop = TDBQTRUE; qf.q->flags |= EJQUPDATING; + qf.q->flags |= EJQDROPALL; qf.expr = tcstrdup(""); //Empty string as expr qf.exprsz = 0; tcmapputkeep(qmap, qf.fpath, qf.fpathsz, &qf, sizeof (qf)); @@ -3156,7 +3181,7 @@ static int _parse_qobj_impl(EJDB *jb, EJQ *q, bson_iterator *it, TCMAP *qmap, TC qf.fpathsz = strlen(qf.fpath); qf.exprlongval = (bv ? 1 : 0); qf.exprdblval = qf.exprlongval; - qf.expr = (bv ? "1" : "0"); + qf.expr = strdup(bv ? "1" : "0"); qf.exprsz = 1; tcmapputkeep(qmap, qf.fpath, qf.fpathsz, &qf, sizeof (qf)); break; diff --git a/tcejdb/ejdb_private.h b/tcejdb/ejdb_private.h index b710b06..b607c53 100644 --- a/tcejdb/ejdb_private.h +++ b/tcejdb/ejdb_private.h @@ -55,14 +55,14 @@ enum { /**> Query field flags */ EJCONDICASE = 1 << 9, /**> Ignore case in matching */ EJCONDSET = 1 << 10, /**> Set field update operation */ - EJCONDINC = 1 << 11, /**> Inc field update operation */ - EJCONDROPALL = 1 << 12 /**> Drop bson object if matched */ + EJCONDINC = 1 << 11 /**> Inc field update operation */ }; enum { /**> Query flags */ EJQINTERNAL = 1, /**> Internal query object used in _ejdbqryexecute */ - EJQUPDATING = 1 << 1 /**> Query in updating mode */ + EJQUPDATING = 1 << 1, /**> Query in updating mode */ + EJQDROPALL = 1 << 2 /**> Drop bson object if matched */ }; struct EJQF { /**> Matching field and status */ diff --git a/tcejdb/testejdb/t2.c b/tcejdb/testejdb/t2.c index d1f1d4c..75c6ecf 100644 --- a/tcejdb/testejdb/t2.c +++ b/tcejdb/testejdb/t2.c @@ -2963,6 +2963,103 @@ void testUpdate2() { //https://github.com/Softmotions/ejdb/issues/9 } +void testQueryBool() { + EJCOLL *coll = ejdbcreatecoll(jb, "contacts", NULL); + CU_ASSERT_PTR_NOT_NULL_FATAL(coll); + + bson bsq1; + bson_init_as_query(&bsq1); + bson_append_bool(&bsq1, "visited", true); + 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(); + TCLIST *q1res = ejdbqryexecute(coll, q1, &count, 0, log); + //fprintf(stderr, "%s", TCXSTRPTR(log)); + CU_ASSERT_PTR_NOT_NULL(strstr(TCXSTRPTR(log), "MAIN IDX: 'NONE'")); + CU_ASSERT_PTR_NOT_NULL(strstr(TCXSTRPTR(log), "RS COUNT: 1")); + CU_ASSERT_EQUAL(count, 1); + + bson_destroy(&bsq1); + tclistdel(q1res); + tcxstrdel(log); + ejdbquerydel(q1); + + CU_ASSERT_TRUE(ejdbsetindex(coll, "visited", JBIDXNUM)); + + bson_init_as_query(&bsq1); + bson_append_bool(&bsq1, "visited", true); + 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(); + q1res = ejdbqryexecute(coll, q1, &count, 0, log); + //fprintf(stderr, "%s", TCXSTRPTR(log)); + CU_ASSERT_PTR_NOT_NULL(strstr(TCXSTRPTR(log), "MAIN IDX: 'nvisited'")); + CU_ASSERT_PTR_NOT_NULL(strstr(TCXSTRPTR(log), "RS COUNT: 1")); + CU_ASSERT_EQUAL(count, 1); + + bson_destroy(&bsq1); + tclistdel(q1res); + tcxstrdel(log); + ejdbquerydel(q1); +} + +void testDropAll() { + EJCOLL *coll = ejdbcreatecoll(jb, "contacts", NULL); + CU_ASSERT_PTR_NOT_NULL_FATAL(coll); + + CU_ASSERT_TRUE(ejdbsetindex(coll, "name", JBIDXSTR)); + + bson bsq1; + bson_init_as_query(&bsq1); + bson_append_string(&bsq1, "name", "HeLlo WorlD"); + bson_append_bool(&bsq1, "$dropall", true); + 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(); + TCLIST *q1res = ejdbqryexecute(coll, q1, &count, 0, log); + //fprintf(stderr, "%s", TCXSTRPTR(log)); + CU_ASSERT_PTR_NOT_NULL(strstr(TCXSTRPTR(log), "$DROPALL ON:")); + CU_ASSERT_PTR_NOT_NULL(strstr(TCXSTRPTR(log), "MAIN IDX: 'sname'")); + CU_ASSERT_PTR_NOT_NULL(strstr(TCXSTRPTR(log), "RS COUNT: 1")); + + bson_destroy(&bsq1); + tclistdel(q1res); + tcxstrdel(log); + ejdbquerydel(q1); + + //Select again + bson_init_as_query(&bsq1); + bson_append_string(&bsq1, "name", "HeLlo WorlD"); + 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(); + q1res = ejdbqryexecute(coll, q1, &count, 0, log); + CU_ASSERT_PTR_NOT_NULL(strstr(TCXSTRPTR(log), "MAIN IDX: 'sname'")); + CU_ASSERT_PTR_NOT_NULL(strstr(TCXSTRPTR(log), "RS COUNT: 0")); + //fprintf(stderr, "\n\n%s", TCXSTRPTR(log)); + + bson_destroy(&bsq1); + tclistdel(q1res); + tcxstrdel(log); + ejdbquerydel(q1); +} + int main() { setlocale(LC_ALL, "en_US.UTF-8"); @@ -3015,7 +3112,9 @@ int main() { (NULL == CU_add_test(pSuite, "testTicket7", testTicket7)) || (NULL == CU_add_test(pSuite, "testTicket8", testTicket8)) || (NULL == CU_add_test(pSuite, "testUpdate1", testUpdate1)) || - (NULL == CU_add_test(pSuite, "testUpdate2", testUpdate2)) + (NULL == CU_add_test(pSuite, "testUpdate2", testUpdate2)) || + (NULL == CU_add_test(pSuite, "testQueryBool", testQueryBool)) || + (NULL == CU_add_test(pSuite, "testDropAll", testDropAll)) ) { CU_cleanup_registry(); return CU_get_error(); |