summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorTyutyunkov Vyacheslav <tve@softmotions.com>2015-04-03 17:43:09 +0600
committerTyutyunkov Vyacheslav <tve@softmotions.com>2015-04-03 17:43:09 +0600
commit430e50e0a941b586376aa2ce3cfe38532c92a0de (patch)
treefccce149f42ffe34e87bf509dd5b5ebdd9161d17
parentfec228b08a3c9c3ca7086745a630d1dc67d33404 (diff)
downloadejdb-430e50e0a941b586376aa2ce3cfe38532c92a0de.tar.gz
ejdb-430e50e0a941b586376aa2ce3cfe38532c92a0de.tar.bz2
ejdb-430e50e0a941b586376aa2ce3cfe38532c92a0de.zip
#123
-rw-r--r--src/bson/bson.c110
-rw-r--r--src/bson/bson.h17
-rw-r--r--src/ejdb/tests/ejdbtest2.c67
3 files changed, 176 insertions, 18 deletions
diff --git a/src/bson/bson.c b/src/bson/bson.c
index c2f92c4..6c5d151 100644
--- a/src/bson/bson.c
+++ b/src/bson/bson.c
@@ -1651,6 +1651,70 @@ int bson_merge(const bson *b1, const bson *b2, bson_bool_t overwrite, bson *out)
return bson_merge2(bson_data(b1), bson_data(b2), overwrite, out);
}
+int bson_merge_recursive2(const void *b1data, const void *b2data, bson_bool_t overwrite, bson *out) {
+ bson_iterator it1, it2;
+ bson_type bt1, bt2;
+
+ BSON_ITERATOR_FROM_BUFFER(&it1, b1data);
+ BSON_ITERATOR_FROM_BUFFER(&it2, b2data);
+ //Append all fields in B1 merging with fields in B2 (for nested objects & arrays)
+ while ((bt1 = bson_iterator_next(&it1)) != BSON_EOO) {
+ const char* k1 = BSON_ITERATOR_KEY(&it1);
+ bt2 = bson_find_from_buffer(&it2, b2data, k1);
+ if (bt1 == BSON_OBJECT && bt2 == BSON_OBJECT) {
+ int res;
+ bson_append_start_object(out, k1);
+ if ((res = bson_merge_recursive2(bson_iterator_value(&it1), bson_iterator_value(&it2), overwrite, out)) != BSON_OK) {
+ return res;
+ }
+ bson_append_finish_object(out);
+ } else if (bt1 == BSON_ARRAY && bt2 == BSON_ARRAY) {
+ int c = 0;
+ bson_iterator sit;
+ bson_type sbt;
+
+ bson_append_start_array(out, k1);
+ BSON_ITERATOR_SUBITERATOR(&it1, &sit);
+ while ((sbt = bson_iterator_next(&sit)) != BSON_EOO) {
+ bson_append_field_from_iterator(&sit, out);
+ ++c;
+ }
+
+ char kbuf[TCNUMBUFSIZ];
+ BSON_ITERATOR_SUBITERATOR(&it2, &sit);
+ while ((sbt = bson_iterator_next(&sit)) != BSON_EOO) {
+ bson_numstrn(kbuf, TCNUMBUFSIZ, c++);
+ bson_append_field_from_iterator2(kbuf, &sit, out);
+ }
+
+ bson_append_finish_array(out);
+ } else if (overwrite && strcmp(JDBIDKEYNAME, k1) && bt2 != BSON_EOO) {
+ bson_append_field_from_iterator(&it2, out);
+ } else {
+ bson_append_field_from_iterator(&it1, out);
+ }
+ }
+
+ BSON_ITERATOR_FROM_BUFFER(&it1, b1data);
+ BSON_ITERATOR_FROM_BUFFER(&it2, b2data);
+ //Append all fields from B2 missing in B1
+ while ((bt2 = bson_iterator_next(&it2)) != BSON_EOO) {
+ const char* k2 = BSON_ITERATOR_KEY(&it2);
+ if ((bt1 = bson_find_from_buffer(&it1, b1data, k2)) == BSON_EOO) {
+ bson_append_field_from_iterator(&it2, out);
+ }
+ }
+ return BSON_OK;
+}
+
+int bson_merge_recursive(const bson *b1, const bson *b2, bson_bool_t overwrite, bson *out) {
+ assert(b1 && b2 && out);
+ if (!b1->finished || !b2->finished || out->finished) {
+ return BSON_ERROR;
+ }
+ return bson_merge_recursive2(bson_data(b1), bson_data(b2), overwrite, out);
+}
+
typedef struct {
int nstack; //nested object stack pos
int matched; //number of matched include fields
@@ -2306,34 +2370,33 @@ int bson_merge_array_sets(const void *mbuf, const void *inbuf, bool pull, bool e
if (ctx.mfields == 0 || pull) {
return ctx.ecode;
}
+
//Append missing arrays fields
BSON_ITERATOR_FROM_BUFFER(&it, mbuf);
while ((bt = bson_iterator_next(&it)) != BSON_EOO) {
const char *fpath = BSON_ITERATOR_KEY(&it);
- BSON_ITERATOR_FROM_BUFFER(&it2, inbuf);
+ // all data from inbuf already in bsout
+ bson_finish(bsout);
+ BSON_ITERATOR_INIT(&it2, bsout);
bt2 = bson_find_fieldpath_value(fpath, &it2);
if (bt2 != BSON_EOO) continue;
int i = 0;
int lvl = 0;
const char *pdp = fpath;
+ bson bst;
+ bson_init(&bst);
while (*(fpath + i) != '\0') {
for (; *(fpath + i) != '\0' && *(fpath + i) != '.'; ++i);
- BSON_ITERATOR_FROM_BUFFER(&it2, inbuf);
- bt2 = bson_find_fieldpath_value2(fpath, i, &it2);
- if (bt2 == BSON_EOO) {
- if (*(fpath + i) == '\0') { //EOF
- assert((fpath + i) - pdp > 0);
- bson_append_start_array2(bsout, pdp, (fpath + i) - pdp);
- bson_append_field_from_iterator2("0", &it, bsout);
- bson_append_finish_array(bsout);
- break;
- } else {
- ++lvl;
- assert((fpath + i) - pdp > 0);
- bson_append_start_object2(bsout, pdp, (fpath + i) - pdp);
- }
- } else if (bt2 != BSON_OBJECT) {
+ if (*(fpath + i) == '\0') { //EOF
+ assert((fpath + i) - pdp > 0);
+ bson_append_start_array2(&bst, pdp, (fpath + i) - pdp);
+ bson_append_field_from_iterator2("0", &it, &bst);
+ bson_append_finish_array(&bst);
break;
+ } else {
+ ++lvl;
+ assert((fpath + i) - pdp > 0);
+ bson_append_start_object2(&bst, pdp, (fpath + i) - pdp);
}
pdp = (fpath + i);
while (*pdp == '.') {
@@ -2342,9 +2405,22 @@ int bson_merge_array_sets(const void *mbuf, const void *inbuf, bool pull, bool e
}
}
for (; lvl > 0; --lvl) {
- bson_append_finish_object(bsout);
+ bson_append_finish_object(&bst);
+ }
+ bson_finish(&bst);
+
+ bson bsc;
+ bson_init_finished_data(&bsc, bson_data(bsout));
+ bson_init_size(bsout, bson_size(bsout));
+ 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 02395d0..f32baaf 100644
--- a/src/bson/bson.h
+++ b/src/bson/bson.h
@@ -1124,6 +1124,23 @@ EJDB_EXPORT int bson_merge(const bson *b1, const bson *b2, bson_bool_t overwrite
EJDB_EXPORT int bson_merge2(const void *b1data, const void *b2data, bson_bool_t overwrite, bson *out);
/**
+ * Recursive merge bson `b2` into `b1` saving result the 'out' object.
+ * `b1` & `b2` bson must be finished BSONS.
+ * Resulting 'out' bson must be allocated and not finished.
+ *
+ * NOTE. Arrays with same paths joined.
+ *
+ * @param b1 BSON to to be merged in `out`
+ * @param b2 Second BSON to to be merged in `out`
+ * @param overwrite if `true` all `b1` fields will be overwriten by corresponding `b2` fields
+ * @param out
+ *
+ * @return BSON_OK or BSON_ERROR.
+ */
+EJDB_EXPORT int bson_merge_recursive(const bson *b1, const bson *b2, bson_bool_t overwrite, bson *out);
+EJDB_EXPORT int bson_merge_recursive2(const void *b1data, const void *b2data, bson_bool_t overwrite, bson *out);
+
+/**
* Merge bsons.
* `bsdata2` may contain field path keys (eg: 'foo.bar').
* @param bsdata1 BSON data to to be merged in `out`
diff --git a/src/ejdb/tests/ejdbtest2.c b/src/ejdb/tests/ejdbtest2.c
index b34ecf3..c710919 100644
--- a/src/ejdb/tests/ejdbtest2.c
+++ b/src/ejdb/tests/ejdbtest2.c
@@ -3771,6 +3771,71 @@ void testAddToSet(void) {
ejdbquerydel(q1);
}
+void testTicket123(void) {
+ EJCOLL *coll = ejdbcreatecoll(jb, "ticket123", NULL);
+ CU_ASSERT_PTR_NOT_NULL_FATAL(coll);
+
+ bson bs1;
+ bson_oid_t oid1;
+ bson_init(&bs1);
+ bson_append_start_object(&bs1, "abc");
+ bson_append_start_array(&bs1, "de");
+ bson_append_string(&bs1, "0", "g");
+ bson_append_finish_array(&bs1);
+ bson_append_start_array(&bs1, "fg");
+ bson_append_finish_array(&bs1);
+ bson_append_finish_object(&bs1);
+ bson_finish(&bs1);
+
+ CU_ASSERT_TRUE_FATAL(ejdbsavebson(coll, &bs1, &oid1));
+
+ bson bsq1;
+ bson_init_as_query(&bsq1);
+ bson_append_start_object(&bsq1, "$addToSet");
+ 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"));
+ }
+
+ bson_destroy(&bs1);
+ bson_destroy(&bsq1);
+ tclistdel(q1res);
+ tcxstrdel(log);
+ ejdbquerydel(q1);
+}
+
+
void testPull(void) {
EJCOLL *coll = ejdbcreatecoll(jb, "contacts", NULL);
CU_ASSERT_PTR_NOT_NULL_FATAL(coll);
@@ -5680,6 +5745,7 @@ int main() {
(NULL == CU_add_test(pSuite, "testTokensBegin", testTokensBegin)) ||
(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, "testPull", testPull)) ||
(NULL == CU_add_test(pSuite, "testFindInComplexArray", testFindInComplexArray)) ||
(NULL == CU_add_test(pSuite, "testElemMatch", testElemMatch)) ||
@@ -5706,7 +5772,6 @@ int main() {
(NULL == CU_add_test(pSuite, "testSlice", testSlice)) ||
(NULL == CU_add_test(pSuite, "testTicket117", testTicket117)) ||
(NULL == CU_add_test(pSuite, "testMetaInfo", testMetaInfo))
-
) {
CU_cleanup_registry();
return CU_get_error();