summaryrefslogtreecommitdiff
path: root/tcejdb
diff options
context:
space:
mode:
authoradam <anton@adamansky.com>2012-11-13 14:23:06 +0700
committeradam <anton@adamansky.com>2012-11-13 14:23:06 +0700
commit98f295c4d76f539caa928203668edae06500f9b6 (patch)
treeec4fd25e2ea8b7d37c00747c4073841b24c54039 /tcejdb
parent57e9c6c0a185e8886d8b01f8798940eece3dd4c4 (diff)
parentbfadef87e7b7dc6b4b2c78e3d7a455c283fbfe4c (diff)
downloadejdb-98f295c4d76f539caa928203668edae06500f9b6.tar.gz
ejdb-98f295c4d76f539caa928203668edae06500f9b6.tar.bz2
ejdb-98f295c4d76f539caa928203668edae06500f9b6.zip
Merge branch 'master' into ticket3
Diffstat (limited to 'tcejdb')
-rw-r--r--tcejdb/README8
-rw-r--r--tcejdb/bson.c2
-rwxr-xr-xtcejdb/configure2
-rw-r--r--tcejdb/configure.in2
-rw-r--r--tcejdb/ejdb.c93
-rw-r--r--tcejdb/ejdb_private.h3
-rw-r--r--tcejdb/testejdb/Makefile2
-rw-r--r--tcejdb/testejdb/t2.c64
-rw-r--r--tcejdb/testejdb/t3.c144
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();