#include <regex.h>
-#include <tcejdb/bson.h>
#include "ejdb_private.h"
#include "ejdbutl.h"
TCMALLOC(jb, sizeof (*jb));
_ejdbclear(jb);
jb->metadb = tctdbnew();
+ tctdbsetcache(jb->metadb, 1024, 0, 0);
#ifdef _DEBUG
tchdbsetdbgfd(jb->metadb->hdb, fileno(stderr));
#endif
bson_iterator it;
TCMAP *qobjmap = ejq->qobjmap;
TCTDB *tdb = jcoll->tdb;
- TCMAP *rowm = tcmapnew2(64);
+ TCMAP *rowm = tcmapnew2(TCMAPTINYBNUM);
bson src;
bson_create_from_buffer2(&src, bsbuf, bsbufsz);
}
}
bson_finish(&bsout);
+ if (bsout.err) {
+ rv = false;
+ _ejdbsetecode(jcoll->jb, JBEINVALIDBSON, __FILE__, __LINE__, __func__);
+ goto finish;
+ }
//fetch oid
bt = bson_find(&it, &src, JDBIDKEYNAME);
if (!(qflags & JBQRYCOUNT)) {
bt = bson_find(&it, ejq->hints, "$fields"); //Collect required fields
if (bt == BSON_OBJECT) {
- TCMAP *fmap = tcmapnew2(64);
+ TCMAP *fmap = tcmapnew2(TCMAPTINYBNUM);
static const bool yes = true;
bson_iterator_subiterator(&it, &sit);
while ((bt = bson_iterator_next(&sit)) != BSON_EOO) {
EJQF *qf = (EJQF*) tcmapget(qmap, mkey, mkeysz, &mvalsz); //discard const
assert(mvalsz == sizeof (EJQF));
assert(qf->fpath);
-
- if ((qf->tcop == TDBQCSTREQ || qf->tcop == TDBQCSTROREQ) &&
- !strcmp(JDBIDKEYNAME, qf->fpath)) { //OID PK matching
+ if (qf->flags & (EJCONDSET | EJCONDINC)) { //skip updating qfields
+ continue;
+ }
+ //OID PK matching
+ if ((qf->tcop == TDBQCSTREQ || qf->tcop == TDBQCSTROREQ) && !strcmp(JDBIDKEYNAME, qf->fpath)) {
qf->flags |= EJFPKMATCHING;
*mqf = qf;
break;
bson_type ftype;
for (int i = 0; (ftype = bson_iterator_next(it)) != BSON_EOO; ++i) {
switch (ftype) {
- case BSON_SYMBOL:
case BSON_STRING:
*type = ftype;
if (tflags & JBICASE) { //ignore case
tclistpush2(pathStack, fkey);
qf.ftype = ftype;
} else {
- if (!pqf) { //Require parent query object
- ret = JBEQERROR;
- break;
- }
- qf = *pqf;
- if (!strcmp("$not", fkey)) {
- qf.negate = !qf.negate;
- } else if (!strcmp("$gt", fkey)) {
- qf.flags |= EJCOMPGT;
- } else if (!strcmp("$gte", fkey)) {
- qf.flags |= EJCOMPGTE;
- } else if (!strcmp("$lt", fkey)) {
- qf.flags |= EJCOMPLT;
- } else if (!strcmp("$lte", fkey)) {
- qf.flags |= EJCOMPLTE;
- } else if (!strcmp("$begin", fkey)) {
- qf.flags |= EJCONDSTARTWITH;
- } else if (!strcmp("$icase", fkey)) {
- qf.flags |= EJCONDICASE;
+ if (!strcmp("$set", fkey) || !strcmp("$inc", fkey)) {
+ if (pqf) { //Top level ops
+ ret = JBEQERROR;
+ break;
+ }
+ } else {
+ if (!pqf) { //Require parent query object
+ ret = JBEQERROR;
+ break;
+ }
+ qf = *pqf;
+ if (!strcmp("$not", fkey)) {
+ qf.negate = !qf.negate;
+ } else if (!strcmp("$gt", fkey)) {
+ qf.flags |= EJCOMPGT;
+ } else if (!strcmp("$gte", fkey)) {
+ qf.flags |= EJCOMPGTE;
+ } else if (!strcmp("$lt", fkey)) {
+ qf.flags |= EJCOMPLT;
+ } else if (!strcmp("$lte", fkey)) {
+ qf.flags |= EJCOMPLTE;
+ } else if (!strcmp("$begin", fkey)) {
+ qf.flags |= EJCONDSTARTWITH;
+ } else if (!strcmp("$icase", fkey)) {
+ qf.flags |= EJCONDICASE;
+ }
}
}
case BSON_OBJECT:
{
if (isckey) {
- if (!strcmp("$set", fkey)) {
+ if (pqf == NULL && !strcmp("$set", fkey)) { //top level set OP
qf.flags |= EJCONDSET;
qf.q->flags |= EJQUPDATING;
} else if (!strcmp("$inc", fkey)) {
bson_append_field_from_iterator(&sit, qf.updateobj);
}
bson_finish(qf.updateobj);
+ if (qf.updateobj->err) {
+ ret = JBEQERROR;
+ }
+ qf.fpath = strdup(fkey);
+ qf.fpathsz = strlen(qf.fpath);
+ qf.tcop = TDBQTRUE;
+ qf.flags |= EJFEXCLUDED;
+ tcmapputkeep(qmap, qf.fpath, qf.fpathsz, &qf, sizeof (qf));
break;
}
}
tcmapputkeep(qmap, qf.fpath, qf.fpathsz, &qf, sizeof (qf));
break;
}
-
- case BSON_SYMBOL:
case BSON_STRING:
{
assert(!qf.fpath && !qf.expr);
continue;
}
if (imap == NULL) {
- imap = tcmapnew2(16);
- rimap = tcmapnew2(16);
+ imap = tcmapnew2(TCMAPTINYBNUM);
+ rimap = tcmapnew2(TCMAPTINYBNUM);
}
for (int i = 4; i <= 7; ++i) { /* JBIDXNUM, JBIDXSTR, JBIDXARR, JBIDXISTR */
bool rm = false;
TCLIST *q1res = ejdbqryexecute(coll, q1, &count, 0, log);
//fprintf(stderr, "%s", TCXSTRPTR(log));
-// for (int i = 0; i < TCLISTNUM(q1res); ++i) {
-// void *bsdata = TCLISTVALPTR(q1res, i);
-// bson_print_raw(stderr, bsdata, 0);
-// }
+ // for (int i = 0; i < TCLISTNUM(q1res); ++i) {
+ // void *bsdata = TCLISTVALPTR(q1res, i);
+ // bson_print_raw(stderr, bsdata, 0);
+ // }
bson_type bt;
bson_iterator it;
bt = bson_find_fieldpath_value("_id", &it);
CU_ASSERT_TRUE(bt == BSON_OID);
bson_iterator_from_buffer(&it, bsdata);
- bt = bson_find_fieldpath_value("address", &it);
+ bt = bson_find_fieldpath_value("address", &it);
CU_ASSERT_TRUE(bt == BSON_OBJECT);
bson_iterator_from_buffer(&it, bsdata);
- bt = bson_find_fieldpath_value("address.city", &it);
+ bt = bson_find_fieldpath_value("address.city", &it);
CU_ASSERT_TRUE(bt == BSON_STRING);
CU_ASSERT_FALSE(strcmp("Novosibirsk", bson_iterator_string(&it)));
bson_iterator_from_buffer(&it, bsdata);
- bt = bson_find_fieldpath_value("address.zip", &it);
+ bt = bson_find_fieldpath_value("address.zip", &it);
CU_ASSERT_TRUE(bt == BSON_EOO);
bson_iterator_from_buffer(&it, bsdata);
- bt = bson_find_fieldpath_value("age", &it);
+ bt = bson_find_fieldpath_value("age", &it);
CU_ASSERT_TRUE(bt == BSON_EOO);
bson_iterator_from_buffer(&it, bsdata);
- bt = bson_find_fieldpath_value("name", &it);
+ bt = bson_find_fieldpath_value("name", &it);
CU_ASSERT_TRUE(bt == BSON_EOO);
bson_iterator_from_buffer(&it, bsdata);
- bt = bson_find_fieldpath_value("labels", &it);
+ bt = bson_find_fieldpath_value("labels", &it);
CU_ASSERT_TRUE(bt == BSON_EOO);
} else if (!bson_compare_string("444-123-333", TCLISTVALPTR(q1res, i), "phone")) {
++ccount;
bt = bson_find_fieldpath_value("_id", &it);
CU_ASSERT_TRUE(bt == BSON_OID);
bson_iterator_from_buffer(&it, bsdata);
- bt = bson_find_fieldpath_value("address", &it);
+ bt = bson_find_fieldpath_value("address", &it);
CU_ASSERT_TRUE(bt == BSON_OBJECT);
bson_iterator_from_buffer(&it, bsdata);
- bt = bson_find_fieldpath_value("address.city", &it);
+ bt = bson_find_fieldpath_value("address.city", &it);
CU_ASSERT_TRUE(bt == BSON_STRING);
CU_ASSERT_FALSE(strcmp("Novosibirsk", bson_iterator_string(&it)));
bson_iterator_from_buffer(&it, bsdata);
- bt = bson_find_fieldpath_value("address.zip", &it);
+ bt = bson_find_fieldpath_value("address.zip", &it);
CU_ASSERT_TRUE(bt == BSON_EOO);
bson_iterator_from_buffer(&it, bsdata);
- bt = bson_find_fieldpath_value("age", &it);
+ bt = bson_find_fieldpath_value("age", &it);
CU_ASSERT_TRUE(bt == BSON_EOO);
bson_iterator_from_buffer(&it, bsdata);
- bt = bson_find_fieldpath_value("name", &it);
+ bt = bson_find_fieldpath_value("name", &it);
CU_ASSERT_TRUE(bt == BSON_EOO);
bson_iterator_from_buffer(&it, bsdata);
- bt = bson_find_fieldpath_value("labels", &it);
+ bt = bson_find_fieldpath_value("labels", &it);
CU_ASSERT_TRUE(bt == BSON_ARRAY);
CU_ASSERT_FALSE(bson_compare_string("red", bsdata, "labels.0"));
CU_ASSERT_FALSE(bson_compare_string("green", bsdata, "labels.1"));
tclistdel(q1res);
tcxstrdel(log);
ejdbquerydel(q1);
+}
+
+void testUpdate1() { //https://github.com/Softmotions/ejdb/issues/9
+
+ EJCOLL *coll = ejdbcreatecoll(jb, "contacts", NULL);
+ CU_ASSERT_PTR_NOT_NULL_FATAL(coll);
+
+ bson_iterator it;
+
+ //q: {name : 'John Travolta', $set : {'labels' : ['black', 'blue'], 'age' : 58}}
+ bson bsq1;
+ bson_init_as_query(&bsq1);
+ bson_append_string(&bsq1, "name", "John Travolta");
+ bson_append_start_object(&bsq1, "$set");
+ bson_append_start_array(&bsq1, "labels");
+ bson_append_string(&bsq1, "0", "black");
+ bson_append_string(&bsq1, "1", "blue");
+ bson_append_finish_array(&bsq1);
+ bson_append_int(&bsq1, "age", 58);
+ 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();
+ TCLIST *q1res = ejdbqryexecute(coll, q1, &count, 0, log);
+ //fprintf(stderr, "%s", TCXSTRPTR(log));
+ CU_ASSERT_EQUAL(1, count);
+ CU_ASSERT_PTR_NOT_NULL(strstr(TCXSTRPTR(log), "UPDATING MODE: YES"));
+
+ for (int i = 0; i < TCLISTNUM(q1res); ++i) {
+ CU_ASSERT_TRUE(!bson_compare_string("John Travolta", TCLISTVALPTR(q1res, i), "name"));
+ bson_iterator_from_buffer(&it, TCLISTVALPTR(q1res, i));
+ CU_ASSERT_TRUE(bson_find_from_buffer(&it, TCLISTVALPTR(q1res, i), "age") == BSON_EOO);
+ }
+
+ bson_destroy(&bsq1);
+ tclistdel(q1res);
+ tcxstrdel(log);
+ ejdbquerydel(q1);
+
+ //q2: {name : 'John Travolta', age: 58}
+ bson_init_as_query(&bsq1);
+ bson_append_string(&bsq1, "name", "John Travolta");
+ bson_append_int(&bsq1, "age", 58);
+ bson_append_finish_object(&bsq1);
+ bson_finish(&bsq1);
+ CU_ASSERT_FALSE_FATAL(bsq1.err);
+
+ q1 = ejdbcreatequery(jb, &bsq1, NULL, 0, NULL);
+ CU_ASSERT_PTR_NOT_NULL_FATAL(q1);
+ count = 0;
+ log = tcxstrnew();
+ q1res = ejdbqryexecute(coll, q1, &count, 0, log);
+ //fprintf(stderr, "%s", TCXSTRPTR(log));
+ CU_ASSERT_PTR_NOT_NULL(strstr(TCXSTRPTR(log), "UPDATING MODE: NO"));
+
+ for (int i = 0; i < TCLISTNUM(q1res); ++i) {
+ CU_ASSERT_FALSE(bson_compare_long(58, TCLISTVALPTR(q1res, i), "age"));
+ CU_ASSERT_FALSE(bson_compare_string("black", TCLISTVALPTR(q1res, i), "labels.0"));
+ CU_ASSERT_FALSE(bson_compare_string("blue", TCLISTVALPTR(q1res, i), "labels.1"));
+ }
+ bson_destroy(&bsq1);
+ tclistdel(q1res);
+ tcxstrdel(log);
+ ejdbquerydel(q1);
+
+
}
(NULL == CU_add_test(pSuite, "testEmptyFieldIndex", testEmptyFieldIndex)) ||
(NULL == CU_add_test(pSuite, "testICaseIndex", testICaseIndex)) ||
(NULL == CU_add_test(pSuite, "testTicket7", testTicket7)) ||
- (NULL == CU_add_test(pSuite, "testTicket8", testTicket8))
+ (NULL == CU_add_test(pSuite, "testTicket8", testTicket8)) ||
+ (NULL == CU_add_test(pSuite, "testUpdate1", testUpdate1))
) {
CU_cleanup_registry();
return CU_get_error();