diff options
author | adam <anton@adamansky.com> | 2012-11-13 14:23:06 +0700 |
---|---|---|
committer | adam <anton@adamansky.com> | 2012-11-13 14:23:06 +0700 |
commit | 98f295c4d76f539caa928203668edae06500f9b6 (patch) | |
tree | ec4fd25e2ea8b7d37c00747c4073841b24c54039 /tcejdb | |
parent | 57e9c6c0a185e8886d8b01f8798940eece3dd4c4 (diff) | |
parent | bfadef87e7b7dc6b4b2c78e3d7a455c283fbfe4c (diff) | |
download | ejdb-98f295c4d76f539caa928203668edae06500f9b6.tar.gz ejdb-98f295c4d76f539caa928203668edae06500f9b6.tar.bz2 ejdb-98f295c4d76f539caa928203668edae06500f9b6.zip |
Merge branch 'master' into ticket3
Diffstat (limited to 'tcejdb')
-rw-r--r-- | tcejdb/README | 8 | ||||
-rw-r--r-- | tcejdb/bson.c | 2 | ||||
-rwxr-xr-x | tcejdb/configure | 2 | ||||
-rw-r--r-- | tcejdb/configure.in | 2 | ||||
-rw-r--r-- | tcejdb/ejdb.c | 93 | ||||
-rw-r--r-- | tcejdb/ejdb_private.h | 3 | ||||
-rw-r--r-- | tcejdb/testejdb/Makefile | 2 | ||||
-rw-r--r-- | tcejdb/testejdb/t2.c | 64 | ||||
-rw-r--r-- | tcejdb/testejdb/t3.c | 144 |
9 files changed, 302 insertions, 18 deletions
diff --git a/tcejdb/README b/tcejdb/README index f2c7fd0..4933aad 100644 --- a/tcejdb/README +++ b/tcejdb/README @@ -89,6 +89,14 @@ int main() { } ``` +You can save this code in `csnippet.c` And build: + + +```sh +gcc -std=c99 -Wall -pedantic -c -o csnippet.o csnippet.c +gcc -std=c99 -Wall -pedantic -o csnippet csnippet.o -ltcejdb +``` + Building & Installation -------------------------------- diff --git a/tcejdb/bson.c b/tcejdb/bson.c index 091d3ca..86ea16f 100644 --- a/tcejdb/bson.c +++ b/tcejdb/bson.c @@ -1290,6 +1290,8 @@ EJDB_EXPORT int bson_compare_fpaths(const void *bsdata1, const void *bsdata2, co int l1 = bson_iterator_bin_len(&it1); int l2 = bson_iterator_bin_len(&it2); return memcmp(bson_iterator_bin_data(&it1), bson_iterator_bin_data(&it2), MIN(l1, l2)); + } else if (t1 == BSON_OID && t2 == BSON_OID) { + return memcmp(bson_iterator_oid(&it1), bson_iterator_oid(&it2), sizeof(bson_oid_t)); } return 0; } diff --git a/tcejdb/configure b/tcejdb/configure index 0a84ee3..0949dbf 100755 --- a/tcejdb/configure +++ b/tcejdb/configure @@ -2059,7 +2059,7 @@ MYLIBREV=11 MYFORMATVER="1.0" # Targets -MYHEADERFILES="tcutil.h tchdb.h tcbdb.h tcfdb.h tctdb.h tcadb.h ejdb.h bson.h" +MYHEADERFILES="tcutil.h tchdb.h tcbdb.h tcfdb.h tctdb.h tcadb.h ejdb.h bson.h myconf.h" MYLIBRARYFILES="libtcejdb.a" MYLIBOBJFILES="tcutil.o tchdb.o tcbdb.o tcfdb.o tctdb.o tcadb.o myconf.o md5.o ejdb.o \ bson.o timsort.o numbers.o encoding.o utf8proc.o" diff --git a/tcejdb/configure.in b/tcejdb/configure.in index d654061..68015e4 100644 --- a/tcejdb/configure.in +++ b/tcejdb/configure.in @@ -19,7 +19,7 @@ MYLIBREV=11 MYFORMATVER="1.0" # Targets -MYHEADERFILES="tcutil.h tchdb.h tcbdb.h tcfdb.h tctdb.h tcadb.h ejdb.h bson.h" +MYHEADERFILES="tcutil.h tchdb.h tcbdb.h tcfdb.h tctdb.h tcadb.h ejdb.h bson.h myconf.h" MYLIBRARYFILES="libtcejdb.a" MYLIBOBJFILES="tcutil.o tchdb.o tcbdb.o tcfdb.o tctdb.o tcadb.o myconf.o md5.o ejdb.o \ bson.o timsort.o numbers.o encoding.o utf8proc.o" diff --git a/tcejdb/ejdb.c b/tcejdb/ejdb.c index 7f0a6a9..a931728 100644 --- a/tcejdb/ejdb.c +++ b/tcejdb/ejdb.c @@ -1,4 +1,7 @@ +#include "bson.h" + + #include <regex.h> #include "ejdb_private.h" @@ -853,14 +856,24 @@ static bool _qrybsvalmatch(const EJQF *qf, bson_iterator *it, bool expandarrays) switch (qf->tcop) { case TDBQCSTREQ: { - int fvalsz = (bt == BSON_STRING) ? bson_iterator_string_len(it) : 0; - const char *fval = (bt == BSON_STRING) ? bson_iterator_string(it) : ""; - rv = (exprsz == fvalsz - 1) && !memcmp(expr, fval, exprsz); + if (bt == BSON_OID) { + if (exprsz != 24) { + break; + } + bson_oid_t *boid = bson_iterator_oid(it); + bson_oid_t aoid; + bson_oid_from_string(&aoid, expr); + rv = !memcmp(&aoid, boid, sizeof (bson_oid_t)); + } else { + int fvalsz = (bt == BSON_STRING) ? bson_iterator_string_len(it) : 1; + const char *fval = (bt == BSON_STRING) ? bson_iterator_string(it) : ""; + rv = (exprsz == fvalsz - 1) && (exprsz == 0 || !memcmp(expr, fval, exprsz)); + } break; } case TDBQCSTRINC: { - int fvalsz = (bt == BSON_STRING) ? bson_iterator_string_len(it) : 0; + int fvalsz = (bt == BSON_STRING) ? bson_iterator_string_len(it) : 1; const char *fval = (bt == BSON_STRING) ? bson_iterator_string(it) : ""; rv = (exprsz < fvalsz) && strstr(fval, expr); break; @@ -895,7 +908,7 @@ static bool _qrybsvalmatch(const EJQF *qf, bson_iterator *it, bool expandarrays) } case TDBQCSTROREQ: { - int fvalsz = (bt == BSON_STRING) ? bson_iterator_string_len(it) : 0; + int fvalsz = (bt == BSON_STRING) ? bson_iterator_string_len(it) : 1; TCLIST *tokens = qf->exprlist; assert(tokens); const char *fval = (bt == BSON_STRING) ? bson_iterator_string(it) : ""; @@ -1286,6 +1299,9 @@ static TCLIST* _qrysearch(EJCOLL *jcoll, const EJQ *q, uint32_t *outcount, int q uint32_t max = (ejq->max > 0) ? ejq->max : UINT_MAX; uint32_t skip = ejq->skip; const TDBIDX *midx = mqf ? mqf->idx : NULL; + TCHDB *hdb = jcoll->tdb->hdb; + assert(hdb); + if (midx) { //Main index used for ordering if (mqf->orderseq == 1 && !(mqf->tcop == TDBQCSTRAND || mqf->tcop == TDBQCSTROR || mqf->tcop == TDBQCSTRNUMOR)) { @@ -1342,7 +1358,7 @@ static TCLIST* _qrysearch(EJCOLL *jcoll, const EJQ *q, uint32_t *outcount, int q if (max == 0) { goto finish; } - if (!midx) { //Missing main index + if (!midx && (!mqf || !(mqf->flags & EJFPKMATCHING))) { //Missing main index & no PK matching goto fullscan; } if (log) { @@ -1358,12 +1374,46 @@ static TCLIST* _qrysearch(EJCOLL *jcoll, const EJQ *q, uint32_t *outcount, int q TCFREE(_bsbuf); \ } - bool trim = (*midx->name != '\0'); + bool trim = (midx && *midx->name != '\0'); if (anum > 0 && !(mqf->flags & EJFEXCLUDED)) { anum--; mqf->flags |= EJFEXCLUDED; } - if (mqf->tcop == TDBQTRUE) { + + if (mqf->flags & EJFPKMATCHING) { //PK matching + if (log) { + tcxstrprintf(log, "PRIMARY KEY MATCHING: %s\n", mqf->expr); + } + assert(mqf->expr); + do { + bson_oid_t oid; + bson_oid_from_string(&oid, mqf->expr); + void *cdata = tchdbget(jcoll->tdb->hdb, &oid, sizeof (oid), &bsbufsz); + if (!cdata || !bsbufsz) { + break; + } + bsbuf = tcmaploadone(cdata, bsbufsz, JDBCOLBSON, JDBCOLBSONL, &bsbufsz); + if (!bsbuf || bsbufsz <= 4) { + TCFREE(cdata); + break; + } + bool matched = true; + for (int i = 0; i < qfsz; ++i) { + const EJQF *qf = qfs[i]; + if (qf->flags & EJFEXCLUDED) continue; + if (!_qrybsmatch(qf, bsbuf, bsbufsz)) { + matched = false; + break; + } + } + if (matched && (ejq->orqobjsnum == 0 || _qryormatch(jcoll, ejq, &oid, sizeof (oid), bsbuf, bsbufsz))) { + JBQREGREC(bsbuf, bsbufsz); + } else { + TCFREE(bsbuf); + } + TCFREE(cdata); + } while (false); + } else if (mqf->tcop == TDBQTRUE) { BDBCUR *cur = tcbdbcurnew(midx->db); if (mqf->order >= 0) { tcbdbcurfirst(cur); @@ -1686,8 +1736,6 @@ fullscan: /* Full scan */ int lksiz = 0; const char *cbuf; int cbufsz; - TCHDB *hdb = jcoll->tdb->hdb; - assert(hdb); while ((all || count < max) && (pkbuf = tchdbgetnext3(hdb, lkbuf, lksiz, &pkbufsz, &cbuf, &cbufsz)) != NULL) { void *bsbuf = tcmaploadone(cbuf, cbufsz, JDBCOLBSON, JDBCOLBSONL, &bsbufsz); if (!bsbuf) continue; @@ -1923,6 +1971,13 @@ static void _qrypreprocess(EJCOLL *jcoll, EJQ *ejq, EJQF **mqf) { EJQF *qf = (EJQF*) tcmapget(qmap, mkey, mkeysz, &mvalsz); //discard const assert(mvalsz == sizeof (EJQF)); assert(qf->fpath); + + if (qf->ftype == BSON_OID && qf->tcop == TDBQCSTREQ && !strcmp(JDBIDKEYNAME, qf->fpath)) { //OID PK matching + qf->flags |= EJFPKMATCHING; + *mqf = qf; + break; + } + bool firstorderqf = false; qf->idxmeta = _imetaidx(jcoll, qf->fpath); qf->idx = _qryfindidx(jcoll, qf, qf->idxmeta); @@ -1989,7 +2044,6 @@ static void _qrypreprocess(EJCOLL *jcoll, EJQ *ejq, EJQF **mqf) { maxiscore = iscore; } } - if (*mqf == NULL && (oqf && oqf->idx && !oqf->negate)) { *mqf = oqf; } @@ -2338,6 +2392,20 @@ static int _parse_qobj_impl(EJDB *jb, bson_iterator *it, TCMAP *qmap, TCLIST *pa ret = _parse_qobj_impl(jb, &sit, qmap, pathStack, &qf); break; } + case BSON_OID: + { + assert(!qf.fpath && !qf.expr); + qf.ftype = ftype; + TCMALLOC(qf.expr, 25 * sizeof (char)); + bson_oid_to_string(bson_iterator_oid(it), qf.expr); + qf.exprsz = 24; + qf.fpath = tcstrjoin(pathStack, '.'); + qf.fpathsz = strlen(qf.fpath); + qf.tcop = TDBQCSTREQ; + tcmapputkeep(qmap, qf.fpath, qf.fpathsz, &qf, sizeof (qf)); + break; + } + case BSON_SYMBOL: case BSON_STRING: { @@ -2351,6 +2419,9 @@ static int _parse_qobj_impl(EJDB *jb, bson_iterator *it, TCMAP *qmap, TCLIST *pa qf.tcop = TDBQCSTRBW; } else { qf.tcop = TDBQCSTREQ; + if (!strcmp(JDBIDKEYNAME, fkey)) { //_id + qf.ftype = BSON_OID; + } } tcmapputkeep(qmap, qf.fpath, qf.fpathsz, &qf, sizeof (qf)); break; diff --git a/tcejdb/ejdb_private.h b/tcejdb/ejdb_private.h index aa6fe45..c16adf2 100644 --- a/tcejdb/ejdb_private.h +++ b/tcejdb/ejdb_private.h @@ -50,7 +50,8 @@ enum { //Query field flags EJFEXCLUDED = 1 << 5, //If query field excluded from matching EJFNOINDEX = 1 << 6, //Do not use index for field - EJFORDERUSED = 1 << 7 //This ordering field was used + EJFORDERUSED = 1 << 7, //This ordering field was used + EJFPKMATCHING = 1 << 8 //_id PK field matching }; diff --git a/tcejdb/testejdb/Makefile b/tcejdb/testejdb/Makefile index 96769c0..90f2184 100644 --- a/tcejdb/testejdb/Makefile +++ b/tcejdb/testejdb/Makefile @@ -14,8 +14,6 @@ check : all check-t1 check-t2 check-t3; check-valgrind : rm -rf *.vlog make RUNCMD="valgrind --tool=memcheck --leak-check=full --error-exitcode=1" check - grep ERROR *.vlog | grep -v ' 0 errors' ; true - grep 'at exit' *.vlog | grep -v ' 0 bytes' ; true check-t1 : $(RUNENV) $(RUNCMD) ./t1 diff --git a/tcejdb/testejdb/t2.c b/tcejdb/testejdb/t2.c index 11c27d8..a3b697f 100644 --- a/tcejdb/testejdb/t2.c +++ b/tcejdb/testejdb/t2.c @@ -2406,6 +2406,67 @@ void testQuery27() { //$exists ejdbquerydel(q1); } +void testOIDSMatching() { //OID matching + EJCOLL *contacts = ejdbcreatecoll(jb, "contacts", NULL); + CU_ASSERT_PTR_NOT_NULL_FATAL(contacts); + + bson_type bt; + bson bsq1; + bson_init_as_query(&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 = ejdbqrysearch(contacts, q1, &count, 0, log); + CU_ASSERT_TRUE(count > 0); + //fprintf(stderr, "%s", TCXSTRPTR(log)); + + for (int i = 0; i < TCLISTNUM(q1res); ++i) { //first + char soid[25]; + bson_oid_t *oid; + void *bsdata = TCLISTVALPTR(q1res, i); + bson_iterator it2; + bt = bson_find_from_buffer(&it2, bsdata, JDBIDKEYNAME); + CU_ASSERT_EQUAL_FATAL(bt, BSON_OID); + oid = bson_iterator_oid(&it2); + bson_oid_to_string(oid, soid); + //fprintf(stderr, "\nOID: %s", soid); + + //OID in string form maching + bson bsq2; + bson_init_as_query(&bsq2); + + if (i % 2 == 0) { + bson_append_string(&bsq2, JDBIDKEYNAME, soid); + } else { + bson_append_oid(&bsq2, JDBIDKEYNAME, oid); + } + + bson_finish(&bsq2); + CU_ASSERT_FALSE_FATAL(bsq2.err); + + TCXSTR *log2 = tcxstrnew(); + EJQ *q2 = ejdbcreatequery(jb, &bsq2, NULL, 0, NULL); + TCLIST *q2res = ejdbqrysearch(contacts, q2, &count, 0, log2); + CU_ASSERT_PTR_NOT_NULL(strstr(TCXSTRPTR(log2), "PRIMARY KEY MATCHING:")); + CU_ASSERT_EQUAL(count, 1); + + tcxstrdel(log2); + ejdbquerydel(q2); + tclistdel(q2res); + bson_destroy(&bsq2); + } + + bson_destroy(&bsq1); + tclistdel(q1res); + tcxstrdel(log); + ejdbquerydel(q1); +} + int main() { setlocale(LC_ALL, "en_US.UTF-8"); CU_pSuite pSuite = NULL; @@ -2450,7 +2511,8 @@ int main() { (NULL == CU_add_test(pSuite, "testQuery24", testQuery24)) || (NULL == CU_add_test(pSuite, "testQuery25", testQuery25)) || (NULL == CU_add_test(pSuite, "testQuery26", testQuery26)) || - (NULL == CU_add_test(pSuite, "testQuery27", testQuery27)) + (NULL == CU_add_test(pSuite, "testQuery27", testQuery27)) || + (NULL == CU_add_test(pSuite, "testOIDSMatching", testOIDSMatching)) ) { CU_cleanup_registry(); return CU_get_error(); diff --git a/tcejdb/testejdb/t3.c b/tcejdb/testejdb/t3.c index 4a92eb5..192820e 100644 --- a/tcejdb/testejdb/t3.c +++ b/tcejdb/testejdb/t3.c @@ -11,6 +11,7 @@ #include <assert.h> #include "ejdb_private.h" #include <locale.h> +#include <pthread.h> #include "CUnit/Basic.h" @@ -126,6 +127,146 @@ void testPerf1() { ejdbrmcoll(jb, coll->cname, true); } + + +//Race conditions + +typedef struct { + int id; + EJDB *jb; + +} TARGRACE; + +static void eprint(EJDB *jb, int line, const char *func) { + int ecode = ejdbecode(jb); + fprintf(stderr, "%d: %s: error: %d: %s\n", + line, func, ecode, ejdberrmsg(ecode)); +} + +static void *threadrace1(void *_tr) { + const int iterations = 500; + TARGRACE *tr = (TARGRACE*) _tr; + bool err = false; + + bson bq; + bson_init_as_query(&bq); + bson_append_int(&bq, "tid", tr->id); + bson_finish(&bq); + + bson_type bt; + bson_iterator it; + void *bsdata; + bool saved = false; + int lastcnt = 0; + + for (int i = 0; !err && i < iterations; ++i) { + EJCOLL *coll = ejdbcreatecoll(jb, "threadrace1", NULL); + CU_ASSERT_PTR_NOT_NULL_FATAL(coll); + EJQ *q = ejdbcreatequery(jb, &bq, NULL, 0, NULL); + CU_ASSERT_PTR_NOT_NULL_FATAL(q); + + bson_oid_t oid2; + bson_oid_t *oid = NULL; + int cnt = 0; + uint32_t count; + TCLIST *res = NULL; + + if (ejdbecode(jb) != 0) { + eprint(jb, __LINE__, "threadrace1"); + err = true; + goto ffinish; + } + + res = ejdbqrysearch(coll, q, &count, 0, NULL); + + if (ejdbecode(jb) != 0) { + eprint(jb, __LINE__, "threadrace1.ejdbqrysearch"); + err = true; + goto ffinish; + } + if (count != 1 && saved) { + CU_ASSERT_TRUE_FATAL(false); + goto ffinish; + } + if (count > 0) { + bsdata = TCLISTVALPTR(res, 0); + CU_ASSERT_PTR_NOT_NULL_FATAL(bsdata); + bt = bson_find_from_buffer(&it, bsdata, "cnt"); + CU_ASSERT_EQUAL_FATAL(bt, BSON_INT); + cnt = bson_iterator_int(&it); + bt = bson_find_from_buffer(&it, bsdata, "_id"); + CU_ASSERT_EQUAL_FATAL(bt, BSON_OID); + oid = bson_iterator_oid(&it); + CU_ASSERT_PTR_NOT_NULL_FATAL(oid); + } + + bson sbs; + bson_init(&sbs); + if (oid) { + bson_append_oid(&sbs, "_id", oid); + } + bson_append_int(&sbs, "tid", tr->id); + bson_append_int(&sbs, "cnt", ++cnt); + bson_finish(&sbs); + + if (!ejdbsavebson(coll, &sbs, &oid2)) { + eprint(jb, __LINE__, "threadrace1.ejdbsavebson"); + err = true; + } + bson_destroy(&sbs); + lastcnt = cnt; +ffinish: + if (res) tclistdel(res); + if (q) ejdbquerydel(q); + } + bson_destroy(&bq); + CU_ASSERT_EQUAL(lastcnt, iterations); + //fprintf(stderr, "\nThread %d finished", tr->id); + return err ? "error" : NULL; +} + +void testRace1() { + CU_ASSERT_PTR_NOT_NULL_FATAL(jb); + const int tnum = 50; + bool err = false; + TARGRACE targs[tnum]; + pthread_t threads[tnum]; + + EJCOLL *coll = ejdbcreatecoll(jb, "threadrace1", NULL); + CU_ASSERT_PTR_NOT_NULL_FATAL(coll); + if (!ejdbsetindex(coll, "tid", JBIDXNUM)) { + eprint(jb, __LINE__, "testRace1"); + err = true; + } + if (err) { + goto finish; + } + + for (int i = 0; i < tnum; i++) { + targs[i].jb = jb; + targs[i].id = i; + if (pthread_create(threads + i, NULL, threadrace1, targs + i) != 0) { + eprint(jb, __LINE__, "pthread_create"); + targs[i].id = -1; + err = true; + } + } + + for (int i = 0; i < tnum; i++) { + if (targs[i].id == -1) continue; + void *rv; + if (pthread_join(threads[i], &rv) != 0) { + eprint(jb, __LINE__, "pthread_join"); + err = true; + } else if (rv) { + err = true; + } + } + +finish: + CU_ASSERT_FALSE(err); +} + int main() { setlocale(LC_ALL, "en_US.UTF-8"); CU_pSuite pSuite = NULL; @@ -143,7 +284,8 @@ int main() { /* Add the tests to the suite */ if ( - (NULL == CU_add_test(pSuite, "testPerf1", testPerf1)) + //(NULL == CU_add_test(pSuite, "testPerf1", testPerf1)) || + (NULL == CU_add_test(pSuite, "testRace1", testRace1)) ) { CU_cleanup_registry(); |