bindings-sophia 0.2.0 → 0.2.0.2
raw patch · 28 files changed
+4424/−544 lines, 28 files
Files
- bindings-sophia.cabal +3/−1
- sophia/README +2/−2
- sophia/db/cat.c +1/−1
- sophia/db/core.h +54/−29
- sophia/db/cursor.c +169/−153
- sophia/db/cursor.h +7/−13
- sophia/db/e.c +23/−14
- sophia/db/e.h +64/−12
- sophia/db/file.c +70/−16
- sophia/db/file.h +42/−17
- sophia/db/gc.c +5/−5
- sophia/db/i.c +30/−8
- sophia/db/i.h +1/−0
- sophia/db/lock.h +18/−1
- sophia/db/macro.h +7/−1
- sophia/db/merge.c +34/−32
- sophia/db/recover.c +103/−20
- sophia/db/recover.h +1/−0
- sophia/db/sophia.h +36/−19
- sophia/db/sp.c +257/−78
- sophia/makefile +4/−5
- sophia/test/common.c +446/−35
- sophia/test/concurrent.c +173/−0
- sophia/test/crash.c +55/−40
- sophia/test/issues.c +129/−0
- sophia/test/limit.c +24/−2
- sophia/test/recover.c +1651/−40
- sophia/test/transaction.c +1015/−0
bindings-sophia.cabal view
@@ -1,5 +1,5 @@ name: bindings-sophia-version: 0.2.0+version: 0.2.0.2 category: Database author: Eyal Lotem <eyal.lotem+sophia@gmail.com>@@ -66,6 +66,8 @@ cbits/wrappers.c cc-options: -g -Wall -O2++ extra-libraries: pthread --------------------------------------------------------------------------------
sophia/README view
@@ -1,5 +1,5 @@ -sophia - is an modern embeddable key-value database-designed for a highload.+sophia - is a modern embeddable key-value database+designed for a high load. http://sphia.org
sophia/db/cat.c view
@@ -125,7 +125,7 @@ return 1; } -inline sppage*+sppage* sp_catfind(spcat *c, char *rkey, int size, uint32_t *index) { register int min = 0;
sophia/db/core.h view
@@ -24,6 +24,12 @@ typedef enum spmagic spmagic; +enum sptxn {+ SPTSS, SPTMS+};++typedef enum sptxn sptxn;+ struct spenv { spmagic m; spe e;@@ -45,47 +51,53 @@ struct sp { spmagic m;- spenv *e;+ spenv *env; spa a; sprep rep;+ sptxn txn; /* transaction mode: single or multi-stmt */ spi *i, i0, i1;+ spi itxn; int iskip; /* skip second index during read */ uint64_t psn; /* page sequence number */ spcat s;+ spbatch lb; /* log batch related */ volatile int stop; sptask merger; sprefset refs; /* pre allocated key buffer (page merge) */+ spfile lockdb; /* per-process database lock */ int lockc; /* incremental cursor lock */ spspinlock lockr; /* repository lock */ spspinlock locks; /* space lock */ spspinlock locki; /* index lock */+ spe e, em; /* separate error contexts: fe and merger */ }; -int sp_rotate(sp*);+int sp_rotate(sp*, spe*); static inline int sp_active(sp *s) { return !s->stop; } -static inline int-sp_e(sp *s, int type, ...) {- va_list args;- va_start(args, type);- sp_ve(&s->e->e, type, args);- va_end(args);- return -1;+static inline spi*+sp_ipair(sp *s) {+ return (s->i == &s->i0 ? &s->i1 : &s->i0); } -static inline int-sp_ee(spenv *e, int type, ...) {- va_list args;- va_start(args, type);- sp_ve(&e->e, type, args);- va_end(args);- return -1;+static inline spi*+sp_iswap(sp *s) {+ spi *old = s->i;+ s->i = sp_ipair(s);+ return old; } static inline void+sp_iskipset(sp *s, int v) {+ sp_lock(&s->locki);+ s->iskip = v;+ sp_unlock(&s->locki);+}++static inline void sp_glock(sp *s) { if (s->lockc > 0) return;@@ -107,23 +119,36 @@ sp_unlock(&s->lockr); } -static inline void-sp_iskipset(sp *s, int v) {- sp_lock(&s->locki);- s->iskip = v;- sp_unlock(&s->locki);+static inline int+sp_evalidate(sp *s) {+ return sp_echeck(&s->e) + sp_echeck(&s->em); } -static inline spi*-sp_ipair(sp *s) {- return (s->i == &s->i0 ? &s->i1 : &s->i0);+static inline int+sp_e(sp *s, int type, ...) {+ va_list args;+ va_start(args, type);+ sp_vef(&s->e, type, args);+ va_end(args);+ return -1; } -static inline spi*-sp_iswap(sp *s) {- spi *old = s->i;- s->i = sp_ipair(s);- return old;+static inline int+sp_em(sp *s, int type, ...) {+ va_list args;+ va_start(args, type);+ sp_vef(&s->em, type, args);+ va_end(args);+ return -1;+}++static inline int+sp_ee(spenv *e, int type, ...) {+ va_list args;+ va_start(args, type);+ sp_vef(&e->e, type, args);+ va_end(args);+ return -1; } #endif
sophia/db/cursor.c view
@@ -22,7 +22,6 @@ assert(c->ph->id > 0); c->pvi = 0 ; c->pv = (spvh*)((char*)c->ph + sizeof(sppageh));- c->dup = 0; } static inline void sp_pageclose(spc *c) {@@ -49,7 +48,7 @@ mid = min + ((max - min) >> 1); spvh *a = (spvh*)((char*)c->ph + sizeof(sppageh) + c->ph->bsize * mid); register int rc =- c->s->e->cmp(a->key, a->size, rkey, size, c->s->e->cmparg);+ c->s->env->cmp(a->key, a->size, rkey, size, c->s->env->cmparg); switch (rc) { case -1: min = mid + 1; continue;@@ -80,14 +79,10 @@ } else { match = min; }- /* not found, set minimal */- if (match >= c->ph->count) {- c->pv = (spvh*)((char*)c->ph + sizeof(sppageh));- c->pvi = 0;- } else {- c->pv = (spvh*)((char*)c->ph + sizeof(sppageh) + c->ph->bsize * match);- c->pvi = match;- }+ if (match >= c->ph->count)+ match = c->ph->count; /* skip */+ c->pv = (spvh*)((char*)c->ph + sizeof(sppageh) + c->ph->bsize * match);+ c->pvi = match; return eq; } @@ -103,7 +98,9 @@ } else { match = max; }- /* not found, set max */+ if (match < 0)+ match = c->ph->count; /* skip */+ else if (match >= c->ph->count) match = c->ph->count - 1; c->pv = (spvh*)((char*)c->ph + sizeof(sppageh) + c->ph->bsize * match);@@ -144,18 +141,22 @@ sp_iopen(&c->i0, c->s->i); if (! c->s->iskip) sp_iopen(&c->i1, sp_ipair(c->s));+ if (c->s->txn == SPTMS)+ sp_iopen(&c->itxn, &c->s->itxn); if (spunlikely(c->s->s.count == 0)) return; sp_pageopen(c, 0); } static inline int-sp_firstkey(spc *c, char *rkey, int size)+sp_lte(spc *c, char *rkey, int size) { /* do lte search on all indexes for the key */ int eq = sp_ilte(c->s->i, &c->i0, rkey, size); if (! c->s->iskip) eq = eq + sp_ilte(sp_ipair(c->s), &c->i1, rkey, size);+ if (c->s->txn == SPTMS)+ eq = eq + sp_ilte(&c->s->itxn, &c->itxn, rkey, size); /* match page for the key */ if (spunlikely(c->s->s.count == 0)) return eq;@@ -166,6 +167,8 @@ assert(p != NULL); sp_pageopen(c, idx); eq = eq + sp_pagesetlte(c, rkey, size);+ if (spunlikely(c->pvi == c->ph->count))+ sp_pageclose(c); return eq; } @@ -177,18 +180,25 @@ sp_iopen(&c->i1, sp_ipair(c->s)); sp_ilast(&c->i1); }+ if (c->s->txn == SPTMS) {+ sp_iopen(&c->itxn, &c->s->itxn);+ sp_ilast(&c->itxn);+ } if (spunlikely(c->s->s.count == 0)) return; sp_pageopen(c, c->s->s.count-1); sp_pagesetlast(c); } -static inline int sp_lastkey(spc *c, char *rkey, int size)+static inline int+sp_gte(spc *c, char *rkey, int size) { /* do gte search the index for the key */ int eq = sp_igte(c->s->i, &c->i0, rkey, size); if (! c->s->iskip) eq = eq + sp_igte(sp_ipair(c->s), &c->i1, rkey, size);+ if (c->s->txn == SPTMS)+ eq = eq + sp_igte(&c->s->itxn, &c->itxn, rkey, size); /* match page for the key */ if (spunlikely(c->s->s.count == 0)) return eq;@@ -199,6 +209,8 @@ assert(p != NULL); sp_pageopen(c, idx); eq = eq + sp_pagesetgte(c, rkey, size);+ if (spunlikely(c->pvi == c->ph->count))+ sp_pageclose(c); return eq; } @@ -210,14 +222,14 @@ c->m = SPMCUR; c->o = o; c->s = s;- c->dup = 0;+ c->mask = SPCNONE; c->p = NULL; c->ph = NULL; c->pi = 0; c->pvi = 0; c->pv = NULL;- c->vsrc = SPCNONE; + sp_iinv(&c->s->itxn, &c->itxn); sp_iinv(c->s->i, &c->i0); sp_iinv(sp_ipair(c->s), &c->i1); @@ -227,7 +239,7 @@ case SPGT: if (rkey) { /* init first key and skip on gt */- if (sp_firstkey(c, rkey, size) && o == SPGT)+ if (sp_lte(c, rkey, size) && o == SPGT) sp_iterate(c); return; }@@ -237,7 +249,7 @@ case SPLT: if (rkey) { /* init last key and skip on lt */- if (sp_lastkey(c, rkey, size) && o == SPLT)+ if (sp_gte(c, rkey, size) && o == SPLT) sp_iterate(c); return; }@@ -253,187 +265,184 @@ } static inline void sp_reset(spc *c) {- c->vsrc = SPCNONE;+ c->mask = SPCNONE; c->r.type = SPREFNONE;- c->dup = 0; } static inline int sp_next(spc *c) {- switch (c->vsrc) {- case SPCI0:+ /* apply last iteration skip mask */+ if (c->mask & SPCITXN)+ sp_inext(&c->itxn);+ if (c->mask & SPCI0) sp_inext(&c->i0);- if (c->dup & SPCPDUP)- sp_pagenext(c);- if (c->dup & SPCVDUP)- sp_inext(&c->i1);- break;- case SPCI1:+ if (c->mask & SPCI1) sp_inext(&c->i1);- if (c->dup & SPCPDUP)- sp_pagenext(c);- if (c->dup & SPCVDUP)- sp_inext(&c->i0);- break;- case SPCP:+ if (c->mask & SPCP) { assert(c->p != NULL); assert(c->r.v.vh == c->pv); sp_pagenext(c);- break;- case SPCNONE:- break; } sp_reset(c);- ++ /* do priority multi-index LTE search */ int rc;- spcsrc src = SPCNONE;- spv *v0 = sp_ival(&c->i0);- spv *v1 = sp_ival(&c->i1);- spv *v = NULL;+ int dup = SPCNONE;+ int src = SPCNONE;+ spv *v = NULL;+ struct {+ spv *v;+ int mask;+ } vv[3] = {+ { sp_ival(&c->itxn), SPCITXN },+ { sp_ival(&c->i0), SPCI0 },+ { sp_ival(&c->i1), SPCI1 }+ }; - /* END */- if (v0 == NULL && v1 == NULL && c->pv == NULL) {- c->vsrc = SPCNONE;+ /* end of iteration */+ if (spunlikely(vv[0].v == NULL &&+ vv[1].v == NULL &&+ vv[2].v == NULL && c->pv == NULL)) { return 0; } - if (v0 && v1) {- rc = c->s->e->cmp(v0->key, v0->size, v1->key, v1->size,- c->s->e->cmparg);+ int i;+ for (i = 0; i < 3; i++) {+ if (vv[i].v == NULL)+ continue;+ if (v == NULL) {+ src = vv[i].mask;+ v = vv[i].v;+ continue;+ }+ rc = c->s->env->cmp(vv[i].v->key, vv[i].v->size, v->key, v->size,+ c->s->env->cmparg); switch (rc) {- case 0: c->dup |= SPCVDUP;- case -1: v = v0, src = SPCI0;+ case 0: dup |= vv[i].mask; break;- case 1: v = v1, src = SPCI1;+ case -1:+ src = vv[i].mask;+ v = vv[i].v; break; }- } else if (v0) {- v = v0, src = SPCI0;- } else if (v1) {- v = v1, src = SPCI1; }- /* no page key */- if (c->pv == NULL) {- c->vsrc = src;- c->r.type = SPREFM;- c->r.v.v = v; - return 1;- }+ if (c->pv == NULL)+ goto index; /* no index key */- if (v == NULL) {- c->vsrc = SPCP;- c->r.type = SPREFD;- c->r.v.vh = c->pv;- return 1;- }+ if (v == NULL)+ goto page; - /* compare in-memory key and page one's */- rc = c->s->e->cmp(v->key, v->size, c->pv->key, c->pv->size,- c->s->e->cmparg);+ /* compare in-memory and on-disk key */+ rc = c->s->env->cmp(v->key, v->size, c->pv->key, c->pv->size,+ c->s->env->cmparg); switch (rc) {- case 0: c->dup |= SPCPDUP;- case -1:- c->vsrc = src;- c->r.type = SPREFM;- c->r.v.v = v; - break;- case 1:- c->vsrc = SPCP;- c->r.type = SPREFD;- c->r.v.vh = c->pv;- break;+ case 0: dup |= SPCP;+ case -1: goto index;+ case 1: goto page; }++ /* unreach */+ assert(0); return 1;+index:+ c->mask = dup|src;+ c->r.type = SPREFM;+ c->r.v.v = v;+ return 1;+page:+ c->mask = dup|SPCP;+ c->r.type = SPREFD;+ c->r.v.vh = c->pv;+ return 1; } static inline int sp_prev(spc *c) {- switch (c->vsrc) {- case SPCI0:+ /* apply last iteration skip mask */+ if (c->mask & SPCITXN)+ sp_iprev(&c->itxn);+ if (c->mask & SPCI0) sp_iprev(&c->i0);- if (c->dup & SPCPDUP)- sp_pageprev(c);- if (c->dup & SPCVDUP)- sp_iprev(&c->i1);- break;- case SPCI1:+ if (c->mask & SPCI1) sp_iprev(&c->i1);- if (c->dup & SPCPDUP)- sp_pageprev(c);- if (c->dup & SPCVDUP)- sp_iprev(&c->i0);- break;- case SPCP:+ if (c->mask & SPCP) { assert(c->p != NULL); assert(c->r.v.vh == c->pv); sp_pageprev(c);- break;- case SPCNONE:- break; } sp_reset(c);- ++ /* do priority multi-index LTE search */ int rc;- spcsrc src = SPCNONE;- spv *v0 = sp_ival(&c->i0);- spv *v1 = sp_ival(&c->i1);- spv *v = NULL;+ int dup = SPCNONE;+ int src = SPCNONE;+ spv *v = NULL;+ struct {+ spv *v;+ int mask;+ } vv[3] = {+ { sp_ival(&c->itxn), SPCITXN },+ { sp_ival(&c->i0), SPCI0 },+ { sp_ival(&c->i1), SPCI1 }+ }; - /* END */- if (v0 == NULL && v1 == NULL && c->pv == NULL) {- c->vsrc = SPCNONE;+ /* end of iteration */+ if (spunlikely(vv[0].v == NULL &&+ vv[1].v == NULL &&+ vv[2].v == NULL && c->pv == NULL)) { return 0; } - if (v0 && v1) {- rc = c->s->e->cmp(v0->key, v0->size, v1->key, v1->size,- c->s->e->cmparg);+ int i;+ for (i = 0; i < 3; i++) {+ if (vv[i].v == NULL)+ continue;+ if (v == NULL) {+ src = vv[i].mask;+ v = vv[i].v;+ continue;+ }+ rc = c->s->env->cmp(vv[i].v->key, vv[i].v->size, v->key, v->size,+ c->s->env->cmparg); switch (rc) {- case 0: c->dup |= SPCVDUP;- case -1: v = v1, src = SPCI1;+ case 0: dup |= vv[i].mask; break;- case 1: v = v0, src = SPCI0;+ case 1:+ src = vv[i].mask;+ v = vv[i].v; break; }- } else if (v0) {- v = v0, src = SPCI0;- } else if (v1) {- v = v1, src = SPCI1; }- /* no page key */- if (c->pv == NULL) {- c->vsrc = src;- c->r.type = SPREFM;- c->r.v.v = v; - return 1;- }+ if (c->pv == NULL)+ goto index; /* no index key */- if (v == NULL) {- c->vsrc = SPCP;- c->r.type = SPREFD;- c->r.v.vh = c->pv;- return 1;- }+ if (v == NULL)+ goto page; - /* compare in-memory key and page one's */- rc = c->s->e->cmp(v->key, v->size, c->pv->key, c->pv->size,- c->s->e->cmparg);+ /* compare in-memory and on-disk key */+ rc = c->s->env->cmp(v->key, v->size, c->pv->key, c->pv->size,+ c->s->env->cmparg); switch (rc) {- case 0: c->dup |= SPCPDUP;- case 1:- c->vsrc = src;- c->r.type = SPREFM;- c->r.v.v = v; - break;- case -1:- c->vsrc = SPCP;- c->r.type = SPREFD;- c->r.v.vh = c->pv;- break;+ case 0: dup |= SPCP;+ case 1: goto index;+ case -1: goto page; }++ /* unreach */+ assert(0); return 1;+index:+ c->mask = dup|src;+ c->r.type = SPREFM;+ c->r.v.v = v;+ return 1;+page:+ c->mask = dup|SPCP;+ c->r.type = SPREFD;+ c->r.v.vh = c->pv;+ return 1; } int sp_iterate(spc *c) {@@ -475,19 +484,26 @@ int sp_match(sp *s, void *k, size_t ksize, void **v, size_t *vsize) {- /* I. match both in-memory index'es for the key */+ /*+ * if multi-stmt transaction is active, try to search the key+ * in the transaction findex first */+ int rc;+ if (s->txn == SPTMS) {+ rc = sp_matchi(s, &s->itxn, k, ksize, v, vsize);+ if (rc == -1 || rc == 1)+ return rc;+ }+ /* match both in-memory index'es for the key */ sp_lock(&s->locki);- int rc = sp_matchi(s, s->i, k, ksize, v, vsize);+ rc = sp_matchi(s, s->i, k, ksize, v, vsize); if (rc == -1 || rc == 1) { sp_unlock(&s->locki); return rc; }- /*- * skip the second index if it is been truncating at the- * somement, all updates are on disk pages.- */+ /* skip the second index if it is been truncating at the+ * somement, all updates are on disk pages */ if (! s->iskip) {- int rc = sp_matchi(s, sp_ipair(s), k, ksize, v, vsize);+ rc = sp_matchi(s, sp_ipair(s), k, ksize, v, vsize); if (rc == -1 || rc == 1) { sp_unlock(&s->locki); return rc;@@ -495,7 +511,7 @@ } sp_unlock(&s->locki); - /* II. match the page */+ /* match the page */ sp_lock(&s->locks); uint32_t page = 0; sppage *p = sp_catfind(&s->s, k, ksize, &page);@@ -504,7 +520,7 @@ return 0; } - /* III. match the key in the page */+ /* match the key in the page */ spepoch *e = (spepoch*)p->epoch; sp_lock(&e->lock); @@ -518,7 +534,7 @@ register spvh *v = (spvh*)(e->db.map + p->offset + sizeof(sppageh) + ph->bsize * mid);- switch (s->e->cmp(v->key, v->size, k, ksize, s->e->cmparg)) {+ switch (s->env->cmp(v->key, v->size, k, ksize, s->env->cmparg)) { case -1: min = mid + 1; continue; case 1: max = mid - 1;
sophia/db/cursor.h view
@@ -11,30 +11,24 @@ typedef struct spc spc; -enum spcsrc {- SPCNONE,- SPCI0,- SPCI1,- SPCP-};--#define SPCVDUP 1-#define SPCPDUP 2--typedef enum spcsrc spcsrc;+#define SPCNONE 0+#define SPCITXN 1+#define SPCI0 2+#define SPCI1 4+#define SPCP 16 struct spc { spmagic m; sporder o; sp *s; spii i0, i1;- int dup; /* last iteration duplicate flags */+ spii itxn; sppageh *ph; sppage *p; int pi; /* page space index */ spvh *pv; int pvi; /* version page index */- spcsrc vsrc; /* last iteration source */+ int mask; /* last iteration advance mask */ spref r; /* last iteration result */ };
sophia/db/e.c view
@@ -9,39 +9,48 @@ #include <sp.h> -void sp_ve(spe *e, int type, va_list args)+void sp_vef(spe *e, int type, va_list args) { sp_lock(&e->lock);- if (e->type != SPENONE) {+ if (e->type & SPEF) { sp_unlock(&e->lock); return; }+ const char *fmt;+ int len;+ uint32_t epoch;+ e->type = type;- switch (type) {+ switch (type & ~SPEF) { case SPE: {- char *fmt = va_arg(args, char*);- int len = snprintf(e->e, sizeof(e->e), "error: ");+ fmt = va_arg(args, const char*);+ len = snprintf(e->e, sizeof(e->e), "error: "); vsnprintf(e->e + len, sizeof(e->e) - len, fmt, args); break; } case SPEOOM: {- char *msg = va_arg(args, char*);- snprintf(e->e, sizeof(e->e), "out-of-memory error: %s", msg);+ fmt = va_arg(args, const char*);+ len = snprintf(e->e, sizeof(e->e), "out-of-memory error: ");+ vsnprintf(e->e + len, sizeof(e->e) - len, fmt, args); break; } case SPESYS: { e->errno_ = errno;- char *msg = va_arg(args, char*);- snprintf(e->e, sizeof(e->e), "system error: %s (errno: %d, %s)",- msg, e->errno_, strerror(e->errno_));+ fmt = va_arg(args, const char*);+ len = snprintf(e->e, sizeof(e->e), "system error: ");+ len += vsnprintf(e->e + len, sizeof(e->e) - len, fmt, args);+ snprintf(e->e + len, sizeof(e->e) - len, " (errno: %d, %s)",+ e->errno_, strerror(e->errno_)); break; } case SPEIO: { e->errno_ = errno;- char *msg = va_arg(args, char*);- uint32_t epoch = va_arg(args, uint32_t);- snprintf(e->e, sizeof(e->e), "io error: [epoch %"PRIu32"] %s (errno: %d, %s)",- epoch, msg, e->errno_, strerror(e->errno_));+ epoch = va_arg(args, uint32_t);+ fmt = va_arg(args, const char*);+ len = snprintf(e->e, sizeof(e->e), "io error: [epoch %"PRIu32"] ", epoch);+ len += vsnprintf(e->e + len, sizeof(e->e) - len, fmt, args);+ snprintf(e->e + len, sizeof(e->e) - len, " (errno: %d, %s)",+ e->errno_, strerror(e->errno_)); break; } }
sophia/db/e.h view
@@ -11,13 +11,12 @@ typedef struct spe spe; -enum {- SPENONE,- SPE, /* general error (with format) */- SPEOOM, /* out of memory */- SPESYS, /* system + errno */- SPEIO /* system-io + errno */-};+#define SPENONE 0+#define SPE 1+#define SPEOOM 2+#define SPESYS 4+#define SPEIO 8+#define SPEF 16 struct spe { spspinlock lock;@@ -26,24 +25,77 @@ char e[256]; }; -static inline void sp_einit(spe *e) {- e->lock = 0;+static inline void+sp_einit(spe *e) { e->type = SPENONE; e->e[0] = 0; sp_lockinit(&e->lock); } -static inline void sp_efree(spe *e) {+static inline void+sp_efree(spe *e) { sp_lockfree(&e->lock); } -static inline int sp_eis(spe *e) {+static inline int+sp_eis(spe *e) { sp_lock(&e->lock); register int is = e->type != SPENONE; sp_unlock(&e->lock); return is; } -void sp_ve(spe *e, int type, va_list args);+static inline void+sp_esetfatal(spe *e) {+ sp_lock(&e->lock);+ assert(e->type != SPENONE);+ e->type |= SPEF;+ sp_unlock(&e->lock);+}++static inline int+sp_eisfatal(spe *e) {+ sp_lock(&e->lock);+ register int is = e->type != SPENONE;+ sp_unlock(&e->lock);+ return is;+}++static inline int+sp_echeck(spe *e) {+ sp_lock(&e->lock);+ register int panic = 0;+ if (spunlikely(e->type != SPENONE)) {+ panic = (e->type & SPEF) > 0;+ if (splikely(! panic)) {+ e->type = SPENONE;+ e->e[0] = 0;+ }+ }+ sp_unlock(&e->lock);+ return panic;+}++static inline void+sp_edup(spe *dst, spe *src) {+ sp_lock(&dst->lock);+ sp_lock(&src->lock);+ dst->type = src->type;+ dst->errno_ = src->errno_;+ memcpy(dst->e, src->e, sizeof(dst->e));+ sp_unlock(&src->lock);+ sp_unlock(&dst->lock);+}++void sp_vef(spe *e, int type, va_list args);++static inline int+sp_ef(spe *e, int type, ...) {+ va_list args;+ va_start(args, type);+ sp_vef(e, type, args);+ va_end(args);+ return -1;+} #endif
sophia/db/file.c view
@@ -158,14 +158,12 @@ static inline int sp_filecomplete(spfile *f) {- if (f->creat == 0)- return 0;- /* remove .incomplete part */ f->creat = 0; char path[1024]; snprintf(path, sizeof(path), "%s", f->file);- char *ext = strrchr(path, '.');- assert(ext != NULL);+ char *ext = strstr(path, ".incomplete");+ if (spunlikely(ext == NULL))+ return 0; *ext = 0; int rc = rename(f->file, path); if (spunlikely(rc == -1))@@ -212,14 +210,15 @@ int sp_mapensure(spfile *f, uint64_t size, float grow) {- if (splikely((f->used + size) < f->size))+ if (splikely((f->used + size) <= f->size)) return 0; int rc = sp_unmap(f); if (spunlikely(rc == -1)) return -1;- long double nsz = f->size * grow;+ uint64_t nsz = f->used + f->size * grow; if (spunlikely(nsz < size))- nsz = size;+ nsz = f->used + size;+ assert(nsz >= (f->used + size)); rc = sp_mapresize(f, nsz); if (spunlikely(rc == -1)) return -1;@@ -298,18 +297,34 @@ return sp_filerm(f->file); } -int sp_logflush(spfile *f)+int sp_logwrite(spfile *f, void *buf, size_t size) {- register struct iovec *v = f->iov;+ size_t n = 0;+ do {+ ssize_t r;+ do {+ r = write(f->fd, (char*)buf + n, size - n);+ } while (r == -1 && errno == EINTR);+ if (r <= 0)+ return -1;+ n += r;+ } while (n != size);+ f->used += size;+ return 0;+}++int sp_logput(spfile *f, spbatch *b)+{+ register struct iovec *v = b->iov; register uint64_t size = 0;- register int n = f->iovc;+ register int n = b->iovc; do { int r; do { r = writev(f->fd, v, n); } while (r == -1 && errno == EINTR); if (r < 0) {- f->iovc = 0;+ b->iovc = 0; return -1; } size += r;@@ -326,13 +341,12 @@ } } while (n > 0); f->used += size;- f->iovc = 0;+ b->iovc = 0; return 0; } int sp_logrlb(spfile *f) {- assert(f->iovc == 0); int rc = ftruncate(f->fd, f->svp); if (spunlikely(rc == -1)) return -1;@@ -345,11 +359,51 @@ { sp_filesvp(f); speofh eof = { SPEOF };- sp_logadd(f, (char*)&eof, sizeof(eof));- int rc = sp_logflush(f);+ int rc = sp_logwrite(f, &eof, sizeof(eof)); if (spunlikely(rc == -1)) { sp_logrlb(f); return -1; } return 0;+}++int sp_lockfile(spfile *f, char *path)+{+ f->creat = 0;+ f->fd = open(path, O_CREAT|O_WRONLY, 0600);+ if (spunlikely(f->fd == -1))+ return -1;+ f->file = sp_strdup(f->a, path);+ if (spunlikely(f->file == NULL)) {+ close(f->fd);+ f->fd = -1;+ return -1;+ }+ struct flock l;+ memset(&l, 0, sizeof(l));+ l.l_whence = SEEK_SET;+ l.l_start = 0;+ l.l_len = 0;+ l.l_type = F_WRLCK;+ int rc = fcntl(f->fd, F_SETLK, &l);+ if (spunlikely(rc == -1)) {+ sp_fileclose(f);+ return 1;+ }+ return 0;+}++int sp_unlockfile(spfile *f)+{+ if (spunlikely(f->fd == -1))+ return 0;+ struct flock l;+ memset(&l, 0, sizeof(l));+ l.l_whence = SEEK_SET;+ l.l_start = 0;+ l.l_len = 0;+ l.l_type = F_UNLCK;+ fcntl(f->fd, F_SETLK, &l);+ unlink(f->file);+ return sp_fileclose(f); }
sophia/db/file.h view
@@ -9,8 +9,16 @@ * BSD License */ +typedef struct spbatch spbatch; typedef struct spfile spfile; +#define SPIOVMAX 1024++struct spbatch {+ struct iovec iov[SPIOVMAX];+ int iovc;+};+ struct spfile { spa *a; int creat;@@ -20,13 +28,18 @@ char *file; int fd; char *map;- struct iovec iov[8];- int iovc; }; int sp_fileexists(char*); int sp_filerm(char*); +static inline int+sp_epochrm(char *dir, uint32_t epoch, char *ext) {+ char path[1024];+ snprintf(path, sizeof(path), "%s/%"PRIu32".%s", dir, epoch, ext);+ return sp_filerm(path);+}+ static inline void sp_fileinit(spfile *f, spa *a) { memset(f, 0, sizeof(*f));@@ -78,29 +91,41 @@ return off <= f->size; } +static inline void+sp_batchinit(spbatch *b) {+ b->iovc = 0;+}++static inline int+sp_batchhas(spbatch *b) {+ return b->iovc > 0;+}++static inline int+sp_batchensure(spbatch *b, int count) {+ return (b->iovc + count) < SPIOVMAX;+}++static inline void+sp_batchadd(spbatch *b, void *ptr, size_t size) {+ assert(b->iovc < SPIOVMAX);+ b->iov[b->iovc].iov_base = ptr;+ b->iov[b->iovc].iov_len = size;+ b->iovc++;+}+ int sp_lognew(spfile*, char*, uint32_t); int sp_logcontinue(spfile*, char*, uint32_t); int sp_logclose(spfile*); int sp_logcomplete(spfile*); int sp_logcompleteforce(spfile*); int sp_logunlink(spfile*);-int sp_logflush(spfile*);+int sp_logwrite(spfile*, void*, size_t);+int sp_logput(spfile*, spbatch*); int sp_logrlb(spfile*); int sp_logeof(spfile*); -static inline void-sp_logadd(spfile *f, const void *buf, int size) {- assert(f->iovc < 8);- f->iov[f->iovc].iov_base = (void*)buf;- f->iov[f->iovc].iov_len = size;- f->iovc++;-}--static inline int-sp_epochrm(char *dir, uint32_t epoch, char *ext) {- char path[1024];- snprintf(path, sizeof(path), "%s/%"PRIu32".%s", dir, epoch, ext);- return sp_filerm(path);-}+int sp_lockfile(spfile*, char*);+int sp_unlockfile(spfile*); #endif
sophia/db/gc.c view
@@ -19,7 +19,7 @@ for (;;) { sp_lock(&s->lockr);- spepoch *g = sp_repgc(&s->rep, s->e->gcfactor);+ spepoch *g = sp_repgc(&s->rep, s->env->gcfactor); sp_unlock(&s->lockr); if (g == NULL) break;@@ -32,10 +32,10 @@ /* map origin page and copy to db file */ sppageh *h = (sppageh*)(g->db.map + p->offset); sp_lock(&x->lock);- rc = sp_mapensure(&x->db, sizeof(sppageh) + h->size, s->e->dbgrow);+ rc = sp_mapensure(&x->db, sizeof(sppageh) + h->size, s->env->dbgrow); if (spunlikely(rc == -1)) { sp_unlock(&x->lock);- return sp_e(s, SPEIO, "failed to remap db file", x->epoch);+ return sp_em(s, SPEIO|SPEF, x->epoch, "failed to remap db file"); } sp_unlock(&x->lock); memcpy(x->db.map + x->db.used, h, sizeof(sppageh) + h->size);@@ -58,10 +58,10 @@ */ rc = sp_mapunlink(&g->db); if (spunlikely(rc == -1))- return sp_e(s, SPEIO, "failed to unlink db file", g->epoch);+ return sp_em(s, SPEIO|SPEF, g->epoch, "failed to unlink db file"); rc = sp_mapclose(&g->db); if (spunlikely(rc == -1))- return sp_e(s, SPEIO, "failed to close db file", g->epoch);+ return sp_em(s, SPEIO|SPEF, g->epoch, "failed to close db file"); sp_lock(&s->lockr); sp_repdetach(&s->rep, g); sp_free(&s->a, g);
sophia/db/i.c view
@@ -79,6 +79,18 @@ return sp_iinit(i, i->a, i->pagesize, i->cmp, i->cmparg); } +int sp_ireset(spi *i)+{+ uint32_t p = 0;+ while (p < i->icount) {+ sp_free(i->a, i->i[p]);+ p++;+ }+ sp_free(i->a, i->i);+ i->i = NULL;+ return sp_iinit(i, i->a, i->pagesize, i->cmp, i->cmparg);+}+ static inline void* sp_iminof(spi *i, spipage *p, char *rkey, int size, uint32_t *idx) {@@ -293,15 +305,15 @@ /* inside index range */ if (l <= 0 && r >= 0) return 0;- /* index min < key */+ /* key > index min */ if (l == -1) return -1;- /* index max > key */+ /* key < index max */ assert(r == 1); return 1; } -inline int sp_ilte(spi *i, spii *ii, char *k, int size)+int sp_ilte(spi *i, spii *ii, char *k, int size) { if (spunlikely(i->count == 0)) { sp_iinv(i, ii);@@ -318,11 +330,16 @@ break; case 1: ii->i = i;- ii->p = i->icount - 1;- ii->n = i->i[i->icount - 1]->count - 1;+ ii->p = 0;+ ii->n = 0; break; case 0:- assert(0);+ if (spunlikely(a >= i->icount))+ a = i->icount - 1;+ ii->i = i;+ ii->p = a;+ ii->n = 0;+ break; } return 0; }@@ -334,7 +351,7 @@ return eq; } -inline int sp_igte(spi *i, spii *ii, char *k, int size)+int sp_igte(spi *i, spii *ii, char *k, int size) { if (spunlikely(i->count == 0)) { sp_iinv(i, ii);@@ -355,7 +372,12 @@ sp_iinv(i, ii); break; case 0:- assert(0);+ if (spunlikely(a >= i->icount))+ a = i->icount - 1;+ ii->i = i;+ ii->p = a;+ ii->n = i->i[a]->count - 1;+ break; } return 0; }
sophia/db/i.h view
@@ -37,6 +37,7 @@ int sp_iinit(spi*, spa*, int, spcmpf, void*); void sp_ifree(spi*); int sp_itruncate(spi*);+int sp_ireset(spi*); int sp_isetorget(spi *i, spv*, spii*); int sp_idelraw(spi*, char*, int, spv**); spv *sp_igetraw(spi*, char*, int);
sophia/db/lock.h view
@@ -9,8 +9,16 @@ * BSD License */ +#include <unistd.h>+ typedef uint8_t spspinlock; +#if defined(__x86_64__) || defined(__i386) || defined(_X86_)+# define CPU_PAUSE __asm__ ("pause")+#else+# define CPU_PAUSE do { } while(0)+#endif+ static inline void sp_lockinit(volatile spspinlock *l) { *l = 0;@@ -23,7 +31,16 @@ static inline void sp_lock(volatile spspinlock *l) {- while (__sync_lock_test_and_set(l, 1)) {}+ if (__sync_lock_test_and_set(l, 1) != 0) {+ unsigned int spin_count = 0U;+ for (;;) {+ CPU_PAUSE;+ if (*l == 0U && __sync_lock_test_and_set(l, 1) == 0)+ break;+ if (++spin_count > 100U)+ usleep(0);+ }+ } } static inline void
sophia/db/macro.h view
@@ -11,7 +11,13 @@ #define sppacked __attribute__((packed)) #define spunused __attribute__((unused))-#define sphot __attribute__((hot))++#if __GNUC__ >= 4 && __GNUC_MINOR__ >= 3+# define sphot __attribute__((hot))+#else+# define sphot+#endif+ #define splikely(EXPR) __builtin_expect(!! (EXPR), 1) #define spunlikely(EXPR) __builtin_expect(!! (EXPR), 0) #define spdiv(a, b) ((a) + (b) - 1) / (b)
sophia/db/merge.c view
@@ -53,16 +53,16 @@ { /* get the new page properties and a data */ spupdate0 u;- sp_mergeget0(&i, s->e->page, &u);+ sp_mergeget0(&i, s->env->page, &u); if (spunlikely(u.count == 0)) break; /* ensure enough space for the page in the file */ sp_lock(&x->lock);- rc = sp_mapensure(&x->db, u.psize, s->e->dbgrow);+ rc = sp_mapensure(&x->db, u.psize, s->env->dbgrow); if (spunlikely(rc == -1)) { sp_unlock(&x->lock);- sp_e(s, SPEIO, "failed to remap db file", x->epoch);+ sp_em(s, SPEIO|SPEF, x->epoch, "failed to remap db file"); goto err; } sp_unlock(&x->lock);@@ -95,7 +95,7 @@ if (spunlikely(min == NULL)) { min = sp_vdup(s, v); if (spunlikely(min == NULL)) {- sp_e(s, SPEOOM, "failed to allocate key");+ sp_em(s, SPEOOM|SPEF, "failed to allocate key"); goto err; } }@@ -123,12 +123,12 @@ /* create in-memory page */ sppage *page = sp_pagenew(s, x); if (spunlikely(page == NULL)) {- sp_e(s, SPEOOM, "failed to allocate page");+ sp_em(s, SPEOOM|SPEF, "failed to allocate page"); goto err; } max = sp_vdup(s, last); if (spunlikely(max == NULL)) {- sp_e(s, SPEOOM, "failed to allocate key");+ sp_em(s, SPEOOM|SPEF, "failed to allocate key"); goto err; } assert(min != NULL);@@ -145,7 +145,7 @@ if (spunlikely(rc == -1)) { sp_unlock(&s->locks); sp_pagefree(s, page);- sp_e(s, SPEOOM, "failed to allocate page index page");+ sp_em(s, SPEOOM|SPEF, "failed to allocate page index page"); goto err; } sp_unlock(&s->locks);@@ -254,9 +254,9 @@ if (m->A < m->a_count && m->B < m->b_count) { register int cmp =- s->e->cmp(m->a->key, m->a->size,- m->b->key,- m->b->size, s->e->cmparg);+ s->env->cmp(m->a->key, m->a->size,+ m->b->key,+ m->b->size, s->env->cmparg); switch (cmp) { case 0: /* use updated key B */@@ -326,13 +326,14 @@ int bsize = m->b_bsize; uint32_t pagesize = sizeof(sppageh); uint32_t count = 0;+ /* * merge in-memory keys with the origin page keys, * skip any deletes and calculate result * page size. */ sp_refsetreset(&s->refs);- while (count < s->e->page && sp_mergenext(s, m)) {+ while (count < s->env->page && sp_mergenext(s, m)) { if (sp_refisdel(&m->last)) continue; sp_refsetadd(&s->refs, &m->last);@@ -349,11 +350,10 @@ /* ensure enough space for the page in the file */ sp_lock(&m->x->lock);- rc = sp_mapensure(&m->x->db, pagesize, s->e->dbgrow);+ rc = sp_mapensure(&m->x->db, pagesize, s->env->dbgrow); if (spunlikely(rc == -1)) { sp_unlock(&m->x->lock);- return sp_e(s, SPEIO, "failed to remap db file",- m->x->epoch);+ return sp_em(s, SPEIO|SPEF, m->x->epoch, "failed to remap db file"); } sp_unlock(&m->x->lock); @@ -429,20 +429,20 @@ /* create in-memory page */ sppage *p = sp_pagenew(s, m->x); if (spunlikely(p == NULL))- return sp_e(s, SPEOOM, "failed to allocate page");+ return sp_em(s, SPEOOM|SPEF, "failed to allocate page"); p->id = psn; p->offset = m->x->db.used; p->size = pagesize; p->min = sp_vdupref(s, min, m->x->epoch); if (spunlikely(p->min == NULL)) { sp_free(&s->a, p);- return sp_e(s, SPEOOM, "failed to allocate key");+ return sp_em(s, SPEOOM|SPEF, "failed to allocate key"); } p->max = sp_vdupref(s, max, m->x->epoch); if (spunlikely(p->max == NULL)) { sp_free(&s->a, p->min); sp_free(&s->a, p);- return sp_e(s, SPEOOM, "failed to allocate key");+ return sp_em(s, SPEOOM|SPEF, "failed to allocate key"); } /* add page to split list */@@ -498,7 +498,7 @@ int rc = sp_catset(&s->s, p, &o); if (spunlikely(rc == -1)) { sp_unlock(&s->locks);- return sp_e(s, SPEOOM, "failed to allocate page index page");+ return sp_em(s, SPEOOM|SPEF, "failed to allocate page index page"); } assert(o == NULL); sp_pageattach(p);@@ -550,8 +550,9 @@ spepoch *x = sp_replive(&s->rep); /* rotate current live epoch */ sp_repset(&s->rep, x, SPXFER);- int rc = sp_rotate(s);+ int rc = sp_rotate(s, &s->em); if (spunlikely(rc == -1)) {+ sp_esetfatal(&s->em); sp_lock(&s->lockr); sp_lock(&s->locki); return -1;@@ -565,15 +566,16 @@ /* complete old live epoch log */ rc = sp_logeof(&x->log); if (spunlikely(rc == -1))- return sp_e(s, SPEIO, "failed to write eof marker", x->epoch);+ return sp_em(s, SPEIO|SPEF, x->epoch, "failed to write eof marker"); rc = sp_logcomplete(&x->log); if (spunlikely(rc == -1))- return sp_e(s, SPEIO, "failed to complete log file", x->epoch);+ return sp_em(s, SPEIO|SPEF, x->epoch, "failed to complete log file"); /* create db file */- rc = sp_mapepochnew(&x->db, s->e->dbnewsize, s->e->dir, x->epoch, "db");+ rc = sp_mapepochnew(&x->db, s->env->dbnewsize, s->env->dir,+ x->epoch, "db"); if (spunlikely(rc == -1))- return sp_e(s, SPEIO, "failed to create db file", x->epoch);+ return sp_em(s, SPEIO|SPEF, x->epoch, "failed to create db file"); /* merge index */ if (splikely(s->s.count > 0))@@ -591,7 +593,7 @@ return -1; /* gc */- if (s->e->gc) {+ if (s->env->gc) { rc = sp_gc(s, x); if (spunlikely(rc == -1)) return -1;@@ -604,7 +606,7 @@ rc = sp_mapcomplete(&x->db); if (spunlikely(rc == -1)) { sp_unlock(&x->lock);- return sp_e(s, SPEIO, "failed to complete db file", x->epoch);+ return sp_em(s, SPEIO|SPEF, x->epoch, "failed to complete db file"); } sp_unlock(&x->lock); /* set epoch as db */@@ -614,19 +616,19 @@ /* remove log file */ rc = sp_logunlink(&x->log); if (spunlikely(rc == -1))- return sp_e(s, SPEIO, "failed to unlink log file", x->epoch);+ return sp_em(s, SPEIO|SPEF, x->epoch, "failed to unlink log file"); rc = sp_logclose(&x->log); if (spunlikely(rc == -1))- return sp_e(s, SPEIO, "failed to close log file", x->epoch);+ return sp_em(s, SPEIO|SPEF, x->epoch, "failed to close log file"); } else { /* there are possible situation when all keys has * been deleted. */ rc = sp_mapunlink(&x->db); if (spunlikely(rc == -1))- return sp_e(s, SPEIO, "failed to unlink db file", x->epoch);+ return sp_em(s, SPEIO|SPEF, x->epoch, "failed to unlink db file"); rc = sp_mapclose(&x->db); if (spunlikely(rc == -1))- return sp_e(s, SPEIO, "failed to close db file", x->epoch);+ return sp_em(s, SPEIO|SPEF, x->epoch, "failed to close db file"); } /* remove all xfer epochs that took part in the merge@@ -640,10 +642,10 @@ break; rc = sp_logunlink(&e->log); if (spunlikely(rc == -1))- return sp_e(s, SPEIO, "failed to unlink log file", e->epoch);+ return sp_em(s, SPEIO|SPEF, e->epoch, "failed to unlink log file"); rc = sp_logclose(&e->log); if (spunlikely(rc == -1))- return sp_e(s, SPEIO, "failed to close log file", e->epoch);+ return sp_em(s, SPEIO|SPEF, e->epoch, "failed to close log file"); sp_lock(&s->lockr); sp_repdetach(&s->rep, e); sp_free(&s->a, e);@@ -655,7 +657,7 @@ rc = sp_itruncate(index); if (spunlikely(rc == -1)) { sp_iskipset(s, 0);- return sp_e(s, SPE, "failed create index");+ return sp_em(s, SPE|SPEF, "failed create index"); } sp_iskipset(s, 0); return 0;
sophia/db/recover.c view
@@ -15,16 +15,89 @@ #include <dirent.h> #include <unistd.h> -static inline int sp_dircreate(sp *s) {- int rc = mkdir(s->e->dir, 0700);+static inline int sp_dircreate(sp *s)+{+ int rc = mkdir(s->env->dir, 0700); if (spunlikely(rc == -1)) { sp_e(s, SPE, "failed to create directory %s (errno: %d, %s)",- s->e->dir, errno, strerror(errno));+ s->env->dir, errno, strerror(errno)); return -1; } return 0; } +typedef struct {+ splist link;+ char dir[];+} spdirlock;++static spspinlock dirlock = 0;+static int dirlockn = 0;+static splist dirlocks;++static int sp_dirlock(sp *s)+{+ /* implement multi-process database exclusive access by creating+ * lock file. */+ char path[1024];+ snprintf(path, sizeof(path), "%s/lock", s->env->dir);+ int rc = sp_lockfile(&s->lockdb, path);+ if (spunlikely(rc == -1))+ sp_e(s, SPE, "failed to create lock file (errno: %d, %s)",+ errno, strerror(errno));+ if (rc == 1)+ return sp_e(s, SPE, "database is locked");+ /* within a single process, add database path to the list and+ * check the presence on a open. */+ sp_lock(&dirlock);+ if (dirlockn == 0) {+ sp_listinit(&dirlocks);+ } else {+ splist *i;+ sp_listforeach(&dirlocks, i) {+ spdirlock *l = spcast(i, spdirlock, link);+ if (! strcmp(s->env->dir, l->dir)) {+ sp_unlock(&dirlock);+ return sp_e(s, SPE, "database is locked");+ }+ }+ }+ int lendir = strlen(s->env->dir) + 1;+ int len = sizeof(spdirlock) + lendir;+ spdirlock *l = malloc(len);+ if (spunlikely(l == NULL)) {+ sp_unlock(&dirlock);+ return sp_e(s, SPEOOM, "failed to allocate memory");+ }+ sp_listinit(&l->link);+ memcpy(l->dir, s->env->dir, lendir);+ sp_listappend(&dirlocks, &l->link);+ dirlockn++;+ sp_unlock(&dirlock);+ return 0;+}++static int sp_dirunlock(sp *s)+{+ sp_lock(&dirlock);+ if (spunlikely(dirlockn == 0)) {+ sp_unlock(&dirlock);+ return 0;+ }+ splist *i, *n;+ sp_listforeach_safe(&dirlocks, i, n) {+ spdirlock *l = spcast(i, spdirlock, link);+ if (! strcmp(s->env->dir, l->dir)) {+ sp_listunlink(&l->link);+ free(l);+ break;+ }+ }+ dirlockn--;+ sp_unlock(&dirlock);+ return sp_unlockfile(&s->lockdb);+}+ static inline ssize_t sp_epochof(char *s) { size_t v = 0;@@ -40,10 +113,10 @@ static int sp_diropen(sp *s) { /* read repository and determine states */- DIR *d = opendir(s->e->dir);+ DIR *d = opendir(s->env->dir); if (spunlikely(d == NULL)) { sp_e(s, SPE, "failed to open directory %s (errno: %d, %s)",- s->e->dir, errno, strerror(errno));+ s->env->dir, errno, strerror(errno)); return -1; } struct dirent *de;@@ -88,9 +161,9 @@ static int sp_recoverdb(sp *s, spepoch *x, sptrack *t) {- int rc = sp_mapepoch(&x->db, s->e->dir, x->epoch, "db");+ int rc = sp_mapepoch(&x->db, s->env->dir, x->epoch, "db"); if (spunlikely(rc == -1))- return sp_e(s, SPEIO, "failed to open db file", x->epoch);+ return sp_e(s, SPEIO, x->epoch, "failed to open db file"); sppageh *h = (sppageh*)(x->db.map); @@ -209,9 +282,9 @@ /* open and map log file */ char *ext = (incomplete ? "log.incomplete" : "log"); int rc;- rc = sp_mapepoch(&x->log, s->e->dir, x->epoch, ext);+ rc = sp_mapepoch(&x->log, s->env->dir, x->epoch, ext); if (spunlikely(rc == -1))- return sp_e(s, SPEIO, "failed to open log file", x->epoch);+ return sp_e(s, SPEIO, x->epoch, "failed to open log file"); /* validate log header */ if (spunlikely(! sp_mapinbound(&x->log, sizeof(splogh)) ))@@ -315,7 +388,7 @@ * during shutdown */ rc = sp_mapunmap(&x->log); if (spunlikely(rc == -1))- return sp_e(s, SPEIO, "failed to unmap log file", x->epoch);+ return sp_e(s, SPEIO, x->epoch, "failed to unmap log file"); /* * if there is eof marker missing, try to add one@@ -328,22 +401,22 @@ if (! eof) { rc = sp_logclose(&x->log); if (spunlikely(rc == -1))- return sp_e(s, SPEIO, "failed to close log file", x->epoch);- rc = sp_logcontinue(&x->log, s->e->dir, x->epoch);+ return sp_e(s, SPEIO, x->epoch, "failed to close log file");+ rc = sp_logcontinue(&x->log, s->env->dir, x->epoch); if (spunlikely(rc == -1)) { sp_logclose(&x->log);- return sp_e(s, SPEIO, "failed to reopen log file", x->epoch);+ return sp_e(s, SPEIO, x->epoch, "failed to reopen log file"); } rc = sp_logeof(&x->log); if (spunlikely(rc == -1)) { sp_logclose(&x->log);- return sp_e(s, SPEIO, "failed to add eof marker", x->epoch);+ return sp_e(s, SPEIO, x->epoch, "failed to add eof marker"); } } rc = sp_logcompleteforce(&x->log); if (spunlikely(rc == -1)) { sp_logclose(&x->log);- return sp_e(s, SPEIO, "failed to complete log file", x->epoch);+ return sp_e(s, SPEIO, x->epoch, "failed to complete log file"); } } return 0;@@ -368,13 +441,13 @@ if (spunlikely(rc == -1)) goto err; if (e->recover == (SPRDB|SPRLOG)) {- rc = sp_epochrm(s->e->dir, e->epoch, "log");+ rc = sp_epochrm(s->env->dir, e->epoch, "log"); if (spunlikely(rc == -1)) goto err; } break; case SPRLOG|SPRDBI:- rc = sp_epochrm(s->e->dir, e->epoch, "db.incomplete");+ rc = sp_epochrm(s->env->dir, e->epoch, "db.incomplete"); if (spunlikely(rc == -1)) goto err; case SPRLOG:@@ -411,17 +484,27 @@ return -1; } +int sp_recoverunlock(sp *s) {+ return sp_dirunlock(s);+}+ int sp_recover(sp *s) {- int exists = sp_fileexists(s->e->dir);+ int exists = sp_fileexists(s->env->dir); int rc; if (!exists) {- if (! (s->e->flags & SPO_CREAT))+ if (! (s->env->flags & SPO_CREAT)) return sp_e(s, SPE, "directory doesn't exists and no SPO_CREAT specified");- if (s->e->flags & SPO_RDONLY)+ if (s->env->flags & SPO_RDONLY) return sp_e(s, SPE, "directory doesn't exists"); rc = sp_dircreate(s);+ if (spunlikely(rc == -1))+ return -1;+ rc = sp_dirlock(s); } else {+ rc = sp_dirlock(s);+ if (spunlikely(rc == -1))+ return -1; rc = sp_diropen(s); if (spunlikely(rc == -1)) return -1;
sophia/db/recover.h view
@@ -9,6 +9,7 @@ * BSD License */ +int sp_recoverunlock(sp*); int sp_recover(sp*); #endif
sophia/db/sophia.h view
@@ -9,12 +9,22 @@ * BSD License */ +#ifdef __cplusplus+extern "C" {+#endif+ #include <stdlib.h> #include <stdint.h> -typedef void *(*spallocf)(void*, size_t, void*);-typedef int (*spcmpf)(char*, size_t, char*, size_t, void*);+#if __GNUC__ >= 4+# define SP_API __attribute__((visibility("default")))+#else+# define SP_API+#endif +typedef void *(*spallocf)(void *ptr, size_t size, void *arg);+typedef int (*spcmpf)(char *a, size_t asz, char *b, size_t bsz, void *arg);+ typedef enum { /* env related */ SPDIR, /* uint32_t, char* */@@ -32,12 +42,12 @@ SPVERSION /* uint32_t*, uint32_t* */ } spopt; -enum {+typedef enum { SPO_RDONLY = 1, SPO_RDWR = 2, SPO_CREAT = 4, SPO_SYNC = 8-};+} spflags; typedef enum { SPGT,@@ -57,20 +67,27 @@ uint32_t indexpages; } spstat; -void *sp_env(void);-void *sp_open(void*);-int sp_ctl(void*, spopt, ...);-int sp_destroy(void*);-int sp_set(void*, const void*, size_t, const void*, size_t);-int sp_delete(void*, const void*, size_t);-int sp_get(void*, const void*, size_t, void**, size_t*);-void *sp_cursor(void*, sporder, const void*, size_t);-int sp_fetch(void*);-const char *sp_key(void*);-size_t sp_keysize(void*);-const char *sp_value(void*);-size_t sp_valuesize(void*);-char *sp_error(void*);-void sp_stat(void*, spstat*);+SP_API void *sp_env(void);+SP_API void *sp_open(void *env);+SP_API int sp_ctl(void*, spopt, ...);+SP_API int sp_destroy(void *ptr);+SP_API int sp_begin(void *db);+SP_API int sp_commit(void *db);+SP_API int sp_rollback(void *db);+SP_API int sp_set(void *db, const void *k, size_t ksize, const void *v, size_t vsize);+SP_API int sp_delete(void *db, const void *k, size_t ksize);+SP_API int sp_get(void *db, const void *k, size_t ksize, void **v, size_t *vsize);+SP_API void *sp_cursor(void *db, sporder, const void *k, size_t ksize);+SP_API int sp_fetch(void *cur);+SP_API const char *sp_key(void *cur);+SP_API size_t sp_keysize(void *cur);+SP_API const char *sp_value(void *cur);+SP_API size_t sp_valuesize(void *cur);+SP_API char *sp_error(void *ptr);+SP_API void sp_stat(void *ptr, spstat*);++#ifdef __cplusplus+}+#endif #endif
sophia/db/sp.c view
@@ -13,7 +13,9 @@ cmpstd(char *a, size_t asz, char *b, size_t bsz, void *arg spunused) { register size_t sz = (asz < bsz ? asz : bsz); register int rc = memcmp(a, b, sz);- return (rc == 0 ? rc : (rc > 0 ? 1 : -1));+ if (rc == 0)+ return (asz == bsz) ? 0 : (asz > bsz ? 1 : -1);+ return rc > 0 ? 1 : -1; } static inline void sp_envinit(spenv *e) {@@ -127,7 +129,7 @@ { switch (opt) { case SPMERGEFORCE:- if (s->e->merge)+ if (s->env->merge) return sp_e(s, SPE, "force merge doesn't work with merger thread active"); return sp_merge(s); default:@@ -161,30 +163,29 @@ return rc; } -int sp_rotate(sp *s)+int sp_rotate(sp *s, spe *err) { int rc; sp_repepochincrement(&s->rep); /* allocate new epoch */ spepoch *e = sp_repalloc(&s->rep, sp_repepoch(&s->rep)); if (spunlikely(s == NULL))- return sp_e(s, SPEOOM, "failed to allocate repository");+ return sp_ef(err, SPEOOM, "failed to allocate repository"); /* create log file */- rc = sp_lognew(&e->log, s->e->dir, sp_repepoch(&s->rep));+ rc = sp_lognew(&e->log, s->env->dir, sp_repepoch(&s->rep)); if (spunlikely(rc == -1)) { sp_free(&s->a, e);- return sp_e(s, SPEIO, "failed to create log file", e->epoch);+ return sp_ef(err, SPEIO, e->epoch, "failed to create log file"); } splogh h; h.magic = SPMAGIC; h.version[0] = SP_VERSION_MAJOR; h.version[1] = SP_VERSION_MINOR;- sp_logadd(&e->log, (char*)&h, sizeof(h));- rc = sp_logflush(&e->log);+ rc = sp_logwrite(&e->log, &h, sizeof(h)); if (spunlikely(rc == -1)) { sp_logclose(&e->log); sp_free(&s->a, e);- return sp_e(s, SPEIO, "failed to write log file", e->epoch);+ return sp_ef(err, SPEIO, e->epoch, "failed to write log file"); } /* attach epoch and mark it is as live */ sp_repattach(&s->rep, e);@@ -209,28 +210,28 @@ if (e->nupdate == 0) { rc = sp_logunlink(&e->log); if (spunlikely(rc == -1))- rcret = sp_e(s, SPEIO, "failed to unlink log file", e->epoch);+ rcret = sp_e(s, SPEIO, e->epoch, "failed to unlink log file"); rc = sp_logclose(&e->log); if (spunlikely(rc == -1))- rcret = sp_e(s, SPEIO, "failed to close log file", e->epoch);+ rcret = sp_e(s, SPEIO, e->epoch, "failed to close log file"); break; } else { rc = sp_logeof(&e->log); if (spunlikely(rc == -1))- rcret = sp_e(s, SPEIO, "failed to write eof marker", e->epoch);+ rcret = sp_e(s, SPEIO, e->epoch, "failed to write eof marker"); } case SPXFER: rc = sp_logcomplete(&e->log); if (spunlikely(rc == -1))- rcret = sp_e(s, SPEIO, "failed to complete log file", e->epoch);+ rcret = sp_e(s, SPEIO, e->epoch, "failed to complete log file"); rc = sp_logclose(&e->log); if (spunlikely(rc == -1))- rcret = sp_e(s, SPEIO, "failed to close log file", e->epoch);+ rcret = sp_e(s, SPEIO, e->epoch, "failed to close log file"); break; case SPDB: rc = sp_mapclose(&e->db); if (spunlikely(rc == -1))- rcret = sp_e(s, SPEIO, "failed to close db file", e->epoch);+ rcret = sp_e(s, SPEIO, e->epoch, "failed to close db file"); break; } sp_free(&s->a, e);@@ -243,7 +244,7 @@ int rcret = 0; int rc = 0; s->stop = 1;- if (s->e->merge) {+ if (s->env->merge) { rc = sp_taskstop(&s->merger); if (spunlikely(rc == -1)) rcret = sp_e(s, SPESYS, "failed to stop merger thread");@@ -252,13 +253,19 @@ rc = sp_closerep(s); if (spunlikely(rc == -1)) rcret = -1;+ rc = sp_recoverunlock(s);+ if (spunlikely(rc == -1))+ rcret = -1; sp_ifree(&s->i0); sp_ifree(&s->i1);+ sp_ifree(&s->itxn); /* equal to rollback */ sp_catfree(&s->s);- s->e->inuse = 0;+ s->env->inuse = 0; sp_lockfree(&s->lockr); sp_lockfree(&s->locks); sp_lockfree(&s->locki);+ sp_efree(&s->e);+ sp_efree(&s->em); return rcret; } @@ -268,7 +275,7 @@ sp *s = self->arg; do { sp_lock(&s->locki);- int merge = s->i->count > s->e->mergewm;+ int merge = s->i->count > s->env->mergewm; sp_unlock(&s->locki); if (! merge) continue;@@ -293,34 +300,45 @@ sp_allocinit(&a, env->alloc, env->allocarg); sp *s = sp_malloc(&a, sizeof(sp)); if (spunlikely(s == NULL)) {- sp_e(s, SPEOOM, "failed to allocate db handle");+ sp_ee(env, SPEOOM, "failed to allocate db handle"); return NULL; } memset(s, 0, sizeof(sp));+ sp_einit(&s->e);+ sp_einit(&s->em); s->m = SPMDB;- s->e = env;- s->e->inuse = 1;+ s->env = env;+ s->env->inuse = 1; memcpy(&s->a, &a, sizeof(s->a)); /* init locks */+ sp_fileinit(&s->lockdb, &s->a); sp_lockinit(&s->lockr); sp_lockinit(&s->locks); sp_lockinit(&s->locki); s->lockc = 0; /* init key index */- rc = sp_iinit(&s->i0, &s->a, 1024, s->e->cmp, s->e->cmparg);+ rc = sp_iinit(&s->i0, &s->a, 1024, s->env->cmp, s->env->cmparg); if (spunlikely(rc == -1)) { sp_e(s, SPEOOM, "failed to allocate key index"); goto e0; }- rc = sp_iinit(&s->i1, &s->a, 1024, s->e->cmp, s->e->cmparg);+ rc = sp_iinit(&s->i1, &s->a, 1024, s->env->cmp, s->env->cmparg); if (spunlikely(rc == -1)) { sp_e(s, SPEOOM, "failed to allocate key index"); goto e1; } s->i = &s->i0;+ /* init transaction index */+ rc = sp_iinit(&s->itxn, &s->a, 1024, s->env->cmp, s->env->cmparg);+ if (spunlikely(rc == -1)) {+ sp_e(s, SPEOOM, "failed to allocate transaction index");+ goto e2;+ }+ /* set current transaction state as single-stmt */+ s->txn = SPTSS; /* init page index */ s->psn = 0;- rc = sp_catinit(&s->s, &s->a, 512, s->e->cmp, s->e->cmparg);+ rc = sp_catinit(&s->s, &s->a, 512, s->env->cmp, s->env->cmparg); if (spunlikely(rc == -1)) { sp_e(s, SPEOOM, "failed to allocate page index"); goto e2;@@ -330,18 +348,18 @@ if (spunlikely(rc == -1)) goto e3; /* do not create new live epoch in read-only mode */- if (! (s->e->flags & SPO_RDONLY)) {- rc = sp_rotate(s);+ if (! (s->env->flags & SPO_RDONLY)) {+ rc = sp_rotate(s, &s->e); if (spunlikely(rc == -1)) goto e3; } s->stop = 0;- rc = sp_refsetinit(&s->refs, &s->a, s->e->page);+ rc = sp_refsetinit(&s->refs, &s->a, s->env->page); if (spunlikely(rc == -1)) { sp_e(s, SPEOOM, "failed to allocate key buffer"); goto e3; }- if (s->e->merge) {+ if (s->env->merge) { rc = sp_taskstart(&s->merger, merger, s); if (spunlikely(rc == -1)) { sp_e(s, SPESYS, "failed to start merger thread");@@ -350,15 +368,25 @@ sp_taskwakeup(&s->merger); } return s;-e4: sp_refsetfree(&s->refs, &s->a);-e3: sp_closerep(s);+e4:+ sp_refsetfree(&s->refs, &s->a);+e3:+ sp_closerep(s);+ sp_recoverunlock(s); sp_catfree(&s->s);-e2: sp_ifree(&s->i1);-e1: sp_ifree(&s->i0);-e0: s->e->inuse = 0;+e2:+ sp_ifree(&s->itxn);+ sp_ifree(&s->i1);+e1:+ sp_ifree(&s->i0);+e0:+ s->env->inuse = 0; sp_lockfree(&s->lockr); sp_lockfree(&s->locks); sp_lockfree(&s->locki);+ sp_edup(&env->e, &s->e);+ sp_efree(&s->e);+ sp_efree(&s->em); sp_free(&a, s); return NULL; }@@ -404,27 +432,158 @@ char *sp_error(void *o) { spmagic *magic = (spmagic*)o;- spe *e = NULL;+ spenv *env; switch (*magic) {- case SPMDB: {- sp *s = o;- e = &s->e->e;- break;- }- case SPMENV: {- spenv *env = o;- e = &env->e;- break;- }+ case SPMENV:+ env = o;+ if (! sp_eis(&env->e))+ return NULL;+ return env->e.e;+ case SPMDB: break; default: assert(0); return NULL; }- if (! sp_eis(e))- return NULL;- return e->e;+ sp *s = o;+ if (sp_eis(&s->em))+ return s->em.e;+ if (sp_eis(&s->e))+ return s->e.e;+ return NULL; } +int sp_begin(void *o)+{+ sp *s = o;+ assert(s->m == SPMDB);+ if (spunlikely(sp_evalidate(s)))+ return -1;+ if (spunlikely(s->txn == SPTMS))+ return -1;+ if (spunlikely(s->lockc))+ return sp_e(s, SPE, "begin with open cursor");+ s->txn = SPTMS;+ return 0;+}++int sp_commit(void *o)+{+ sp *s = o;+ assert(s->m == SPMDB);+ if (spunlikely(sp_evalidate(s)))+ return -1;+ if (spunlikely(s->txn == SPTSS))+ return sp_e(s, SPE, "no active transaction to commit");+ if (spunlikely(s->lockc))+ return sp_e(s, SPE, "commit with open cursor");+ if (spunlikely(s->itxn.count == 0)) {+ s->txn = SPTSS;+ return 0;+ }++ /* prepare to write the transaction+ * to the log */+ int n = s->itxn.count;++ sp_lock(&s->lockr);+ sp_lock(&s->locki);++ spepoch *live = sp_replive(&s->rep);+ sp_filesvp(&live->log);++ char hbuf[sizeof(spvh) * 512];+ unsigned int hpos = 0;++ spii it;+ sp_iopen(&it, &s->itxn);+ int rc;+ do {+ spv *v = sp_ival(&it);++ if (spunlikely(! sp_batchensure(&s->lb, 3))) {+ rc = sp_logput(&live->log, &s->lb);+ if (spunlikely(rc == -1)) {+ sp_e(s, SPEIO|SPEF, live->epoch, "failed to write log file");+ goto abort;+ }+ hpos = 0;+ }++ v->epoch = live->epoch;+ assert(hpos < sizeof(hbuf));+ spvh *hp = (spvh*)(hbuf + hpos);+ hp->crc = 0;+ hp->size = v->size;+ hp->voffset = 0;+ hp->vsize = sp_vvsize(v);+ hp->flags = v->flags;+ hp->crc = sp_crc32c(v->crc, &hp->size, sizeof(spvh) - sizeof(uint32_t));+ sp_batchadd(&s->lb, hp, sizeof(spvh));+ sp_batchadd(&s->lb, v->key, v->size);+ sp_batchadd(&s->lb, sp_vv(v), hp->vsize);+ hpos += sizeof(spvh);++ spv *old = NULL;+ rc = sp_iset(s->i, v, &old);+ if (spunlikely(rc == -1)) {+ sp_e(s, SPEOOM|SPEF, "failed to allocate key index page");+ goto abort;+ }+ if (old)+ sp_free(&s->a, old);++ } while (sp_inext(&it));++ if (sp_batchhas(&s->lb)) {+ rc = sp_logput(&live->log, &s->lb);+ if (spunlikely(rc == -1)) {+ sp_e(s, SPEIO|SPEF, live->epoch, "failed to write log file");+ goto abort;+ }+ }++ /* clean up transaction index (pages only) */+ sp_ireset(&s->itxn);++ sp_unlock(&s->locki);+ sp_unlock(&s->lockr);++ /* set transaction as single-stmt */+ s->txn = SPTSS;++ /* wake up merger if necessary */+ live->nupdate += n;+ if (live->nupdate >= s->env->mergewm) {+ if (splikely(s->env->merge))+ sp_taskwakeup(&s->merger);+ }+ return 0;++abort:+ sp_rollback(o);+ sp_logrlb(&live->log);+ sp_unlock(&s->locki);+ sp_unlock(&s->lockr);+ return -1;+}++int sp_rollback(void *o)+{+ sp *s = o;+ assert(s->m == SPMDB);+ if (spunlikely(sp_evalidate(s)))+ return -1;+ if (spunlikely(s->txn == SPTSS))+ return sp_e(s, SPE, "no active transaction to rollback");+ if (spunlikely(s->lockc))+ return sp_e(s, SPE, "rollback with open cursor");+ int rc = sp_itruncate(&s->itxn);+ if (spunlikely(rc == -1))+ return sp_e(s, SPEOOM, "failed to allocate key index page");+ s->txn = SPTSS;+ return 0;+}+ static inline int sp_do(sp *s, int op, void *k, size_t ksize, void *v, size_t vsize) {@@ -450,35 +609,57 @@ crc = sp_crc32c(crc, v, vsize); h.crc = sp_crc32c(crc, &h.size, sizeof(spvh) - sizeof(uint32_t)); + n->flags = op;+ n->crc = crc;++ /* in case of multi-stmt transaction, simply add version to the+ * transaction index only. */+ int rc;+ if (s->txn == SPTMS) {+ spv *old = NULL;+ rc = sp_iset(&s->itxn, n, &old);+ if (spunlikely(rc == -1)) {+ sp_free(&s->a, n);+ return sp_e(s, SPEOOM, "failed to allocate transacton key index page");+ }+ if (old)+ sp_free(&s->a, old);+ return 0;+ }+ sp_lock(&s->lockr); sp_lock(&s->locki); /* write to current live epoch log */ spepoch *live = sp_replive(&s->rep); sp_filesvp(&live->log);- sp_logadd(&live->log, &h, sizeof(spvh));- sp_logadd(&live->log, k, ksize);- sp_logadd(&live->log, v, vsize);- int rc = sp_logflush(&live->log);+ sp_batchadd(&s->lb, &h, sizeof(spvh));+ sp_batchadd(&s->lb, k, ksize);+ sp_batchadd(&s->lb, v, vsize);+ rc = sp_logput(&live->log, &s->lb); if (spunlikely(rc == -1)) { sp_free(&s->a, n);- sp_logrlb(&live->log);+ rc = sp_logrlb(&live->log);+ if (spunlikely(rc == -1))+ sp_esetfatal(&s->e); sp_unlock(&s->locki); sp_unlock(&s->lockr);- return sp_e(s, SPEIO, "failed to write log file", live->epoch);+ return sp_e(s, SPEIO, live->epoch, "failed to write log file"); } /* add new version to the index */- spv *old = NULL; n->epoch = live->epoch;- n->flags = op;- n->crc = crc;+ spv *old = NULL; rc = sp_iset(s->i, n, &old); if (spunlikely(rc == -1)) { sp_free(&s->a, n);+ rc = sp_logrlb(&live->log);+ if (spunlikely(rc == -1))+ sp_esetfatal(&s->e); sp_unlock(&s->locki); sp_unlock(&s->lockr);- return sp_e(s, SPEOOM, "failed to allocate key index page");+ return (spunlikely(rc == -1)) ? -1 :+ sp_e(s, SPEOOM, "failed to allocate key index page"); } sp_unlock(&s->locki);@@ -489,8 +670,8 @@ /* wake up merger on merge watermark reached */ live->nupdate++;- if ((live->nupdate % s->e->mergewm) == 0) {- if (splikely(s->e->merge))+ if ((live->nupdate % s->env->mergewm) == 0) {+ if (splikely(s->env->merge)) sp_taskwakeup(&s->merger); } return 0;@@ -500,14 +681,16 @@ { sp *s = o; assert(s->m == SPMDB);- if (spunlikely(sp_eis(&s->e->e)))+ if (spunlikely(sp_evalidate(s))) return -1;- if (spunlikely(s->e->flags & SPO_RDONLY))+ if (spunlikely(s->env->flags & SPO_RDONLY)) return sp_e(s, SPE, "db handle is read-only"); if (spunlikely(ksize > UINT16_MAX)) return sp_e(s, SPE, "key size limit reached"); if (spunlikely(vsize > UINT32_MAX)) return sp_e(s, SPE, "value size limit reached");+ if (spunlikely(s->lockc))+ return sp_e(s, SPE, "modify with open cursor"); return sp_do(s, SPSET, (char*)k, ksize, (char*)v, vsize); } @@ -515,31 +698,25 @@ { sp *s = o; assert(s->m == SPMDB);- if (spunlikely(sp_eis(&s->e->e)))+ if (spunlikely(sp_evalidate(s))) return -1;- if (spunlikely(s->e->flags & SPO_RDONLY))+ if (spunlikely(s->env->flags & SPO_RDONLY)) return sp_e(s, SPE, "db handle is read-only"); if (spunlikely(ksize > UINT16_MAX)) return sp_e(s, SPE, "key size limit reached");+ if (spunlikely(s->lockc))+ return sp_e(s, SPE, "modify with open cursor"); return sp_do(s, SPDEL, (char*)k, ksize, NULL, 0); } -static inline int-sp_checkro(sp *s, size_t ksize)-{- if (spunlikely(sp_eis(&s->e->e)))- return -1;- if (spunlikely(ksize > UINT16_MAX))- return sp_e(s, SPE, "key size limit reached");- return 0;-}- int sp_get(void *o, const void *k, size_t ksize, void **v, size_t *vsize) { sp *s = o; assert(s->m == SPMDB);- if (spunlikely(sp_checkro(s, ksize) == -1))+ if (spunlikely(sp_evalidate(s))) return -1;+ if (spunlikely(ksize > UINT16_MAX))+ return sp_e(s, SPE, "key size limit reached"); return sp_match(s, (char*)k, ksize, v, vsize); } @@ -547,8 +724,12 @@ { sp *s = o; assert(s->m == SPMDB);- if (spunlikely(sp_checkro(s, ksize) == -1))+ if (spunlikely(sp_evalidate(s))) return NULL;+ if (spunlikely(ksize > UINT16_MAX)) {+ sp_e(s, SPE, "key size limit reached");+ return NULL;+ } spc *c = sp_malloc(&s->a, sizeof(spc)); if (spunlikely(c == NULL)) { sp_e(s, SPEOOM, "failed to allocate cursor handle");@@ -562,7 +743,7 @@ int sp_fetch(void *o) { spc *c = o; assert(c->m == SPMCUR);- if (spunlikely(sp_eis(&c->s->e->e)))+ if (spunlikely(sp_evalidate(c->s))) return -1; return sp_iterate(c); }@@ -606,7 +787,6 @@ sp_lock(&s->lockr); sp_lock(&s->locki); sp_lock(&s->locks);- stat->epoch = s->rep.epoch; stat->psn = s->psn; stat->repn = s->rep.n;@@ -615,7 +795,6 @@ stat->catn = s->s.count; stat->indexn = s->i->count; stat->indexpages = s->i->icount;- sp_unlock(&s->locks); sp_unlock(&s->locki); sp_unlock(&s->lockr);
sophia/makefile view
@@ -1,7 +1,6 @@- all:- @(cd db; make --no-print-directory)- @(cd test; make --no-print-directory)+ @(cd db; $(MAKE))+ @(cd test; $(MAKE)) clean:- @(cd db; make clean --no-print-directory)- @(cd test; make clean --no-print-directory)+ @(cd db; $(MAKE) clean)+ @(cd test; $(MAKE) clean)
sophia/test/common.c view
@@ -331,6 +331,24 @@ } static void+cursor_set(void) {+ void *env = sp_env();+ t( env != NULL );+ t( sp_ctl(env, SPDIR, SPO_CREAT|SPO_RDWR, dbrep) == 0 );+ t( sp_ctl(env, SPCMP, cmp, NULL) == 0 );+ void *db = sp_open(env);+ t( db != NULL );+ void *cur = sp_cursor(db, SPGTE, NULL, 0);+ t( cur != NULL );+ uint32_t k = 1;+ t( sp_set(db, &k, sizeof(k), &k, sizeof(k)) == -1 );+ t( sp_destroy(cur) == 0 );+ t( sp_destroy(db) == 0 );+ t( sp_destroy(env) == 0 );+ t( rmrf(dbrep) == 0 );+}++static void fetch_gte_empty(void) { void *env = sp_env(); t( env != NULL );@@ -807,62 +825,447 @@ t( rmrf(dbrep) == 0 ); } -#if 0 static void-rotate_empty(void) {+fetch_kgte_random(void) { void *env = sp_env(); t( env != NULL ); t( sp_ctl(env, SPDIR, SPO_CREAT|SPO_RDWR, dbrep) == 0 ); t( sp_ctl(env, SPCMP, cmp, NULL) == 0 );+ t( sp_ctl(env, SPPAGE, 64) == 0 ); t( sp_ctl(env, SPMERGE, 0) == 0 );- t( sp_ctl(env, SPTTL, 2) == 0 );+ t( sp_ctl(env, SPMERGEWM, 256) == 0 ); void *db = sp_open(env);- t( exists(dbrep, 1, "log.incomplete") == 1 );+ t( db != NULL );++ uint32_t k = 0;+ while (k < 1024) {+ t( sp_set(db, &k, sizeof(k), &k, sizeof(k)) == 0 );+ k++;+ }++ k = 0;+ while (k < 1024) {+ void *cur = sp_cursor(db, SPGTE, &k, sizeof(k));+ t( cur != NULL );+ uint32_t n = 0;+ while (sp_fetch(cur)) {+ t( *(uint32_t*)sp_key(cur) == k + n );+ t( *(uint32_t*)sp_value(cur) == k + n );+ n++;+ }+ t( sp_destroy(cur) == 0 );+ t( n == (1024 - k) );+ k++;+ }+ t( sp_destroy(db) == 0 );- t( exists(dbrep, 1, "log.incomplete") == 0 );- t( exists(dbrep, 1, "log") == 0 ); t( sp_destroy(env) == 0 ); t( rmrf(dbrep) == 0 ); } static void-rotate(void) {+fetch_kgte_random_merge(void) { void *env = sp_env(); t( env != NULL ); t( sp_ctl(env, SPDIR, SPO_CREAT|SPO_RDWR, dbrep) == 0 ); t( sp_ctl(env, SPCMP, cmp, NULL) == 0 );+ t( sp_ctl(env, SPPAGE, 64) == 0 ); t( sp_ctl(env, SPMERGE, 0) == 0 );- t( sp_ctl(env, SPTTL, 2) == 0 );+ t( sp_ctl(env, SPMERGEWM, 256) == 0 ); void *db = sp_open(env); t( db != NULL );- uint32_t k = 1, v = 1;- t( sp_set(db, &k, sizeof(k), &v, sizeof(v)) == 0);- t( exists(dbrep, 1, "log.incomplete") == 1 );- t( exists(dbrep, 1, "db.incomplete") == 0 );- t( exists(dbrep, 1, "db") == 0 );- t( sp_set(db, &k, sizeof(k), &v, sizeof(v)) == 0);- t( exists(dbrep, 1, "log.incomplete") == 1 );- t( exists(dbrep, 2, "log.incomplete") == 1 );- t( sp_set(db, &k, sizeof(k), &v, sizeof(v)) == 0);- t( exists(dbrep, 3, "log.incomplete") == 0 );- t( sp_set(db, &k, sizeof(k), &v, sizeof(v)) == 0);- t( exists(dbrep, 1, "log.incomplete") == 1 );- t( exists(dbrep, 2, "log.incomplete") == 1 );- t( exists(dbrep, 3, "log.incomplete") == 1 );++ uint32_t k = 0;+ while (k < 512) {+ t( sp_set(db, &k, sizeof(k), &k, sizeof(k)) == 0 );+ k++;+ }++ t( sp_ctl(db, SPMERGEFORCE) == 0 );++ while (k < 1024) {+ t( sp_set(db, &k, sizeof(k), &k, sizeof(k)) == 0 );+ k++;+ }++ k = 0;+ while (k < 1024) {+ void *cur = sp_cursor(db, SPGTE, &k, sizeof(k));+ t( cur != NULL );+ uint32_t n = 0;+ while (sp_fetch(cur)) {+ t( *(uint32_t*)sp_key(cur) == k + n );+ t( *(uint32_t*)sp_value(cur) == k + n );+ n++;+ }+ t( sp_destroy(cur) == 0 );+ t( n == (1024 - k) );+ k++;+ }+ t( sp_destroy(db) == 0 );- t( exists(dbrep, 1, "db.incomplete") == 0 );- t( exists(dbrep, 1, "db") == 0 );- t( exists(dbrep, 1, "log.incomplete") == 0 );- t( exists(dbrep, 2, "log.incomplete") == 0 );- t( exists(dbrep, 3, "log.incomplete") == 0 );- t( exists(dbrep, 1, "log") == 1 );- t( exists(dbrep, 2, "log") == 1 );- t( exists(dbrep, 3, "log") == 0 ); t( sp_destroy(env) == 0 ); t( rmrf(dbrep) == 0 ); }-#endif +static void+fetch_kgte_random_merge_rev(void) {+ void *env = sp_env();+ t( env != NULL );+ t( sp_ctl(env, SPDIR, SPO_CREAT|SPO_RDWR, dbrep) == 0 );+ t( sp_ctl(env, SPCMP, cmp, NULL) == 0 );+ t( sp_ctl(env, SPPAGE, 64) == 0 );+ t( sp_ctl(env, SPMERGE, 0) == 0 );+ t( sp_ctl(env, SPMERGEWM, 256) == 0 );+ void *db = sp_open(env);+ t( db != NULL );++ uint32_t k = 512;+ while (k < 1024) {+ t( sp_set(db, &k, sizeof(k), &k, sizeof(k)) == 0 );+ k++;+ }++ t( sp_ctl(db, SPMERGEFORCE) == 0 );++ k = 0;+ while (k < 512) {+ t( sp_set(db, &k, sizeof(k), &k, sizeof(k)) == 0 );+ k++;+ }++ k = 0;+ while (k < 1024) {+ void *cur = sp_cursor(db, SPGTE, &k, sizeof(k));+ t( cur != NULL );+ uint32_t n = 0;+ while (sp_fetch(cur)) {+ t( *(uint32_t*)sp_key(cur) == k + n );+ t( *(uint32_t*)sp_value(cur) == k + n );+ n++;+ }+ t( sp_destroy(cur) == 0 );+ t( n == (1024 - k) );+ k++;+ }++ t( sp_destroy(db) == 0 );+ t( sp_destroy(env) == 0 );+ t( rmrf(dbrep) == 0 );+}++static void+fetch_klte_random(void) {+ void *env = sp_env();+ t( env != NULL );+ t( sp_ctl(env, SPDIR, SPO_CREAT|SPO_RDWR, dbrep) == 0 );+ t( sp_ctl(env, SPCMP, cmp, NULL) == 0 );+ t( sp_ctl(env, SPPAGE, 64) == 0 );+ t( sp_ctl(env, SPMERGE, 0) == 0 );+ t( sp_ctl(env, SPMERGEWM, 256) == 0 );+ void *db = sp_open(env);+ t( db != NULL );++ int32_t k = 0;+ while (k < 1024) {+ t( sp_set(db, &k, sizeof(k), &k, sizeof(k)) == 0 );+ k++;+ }++ k = 1023;+ while (k >= 0) {+ void *cur = sp_cursor(db, SPLTE, &k, sizeof(k));+ t( cur != NULL );+ uint32_t n = 0;+ while (sp_fetch(cur)) {+ t( *(uint32_t*)sp_key(cur) == k - n );+ t( *(uint32_t*)sp_value(cur) == k - n );+ n++;+ }+ t( sp_destroy(cur) == 0 );+ t( n == (k + 1) );+ k--;+ }++ t( sp_destroy(db) == 0 );+ t( sp_destroy(env) == 0 );+ t( rmrf(dbrep) == 0 );+}++static void+fetch_klte_random_merge(void) {+ void *env = sp_env();+ t( env != NULL );+ t( sp_ctl(env, SPDIR, SPO_CREAT|SPO_RDWR, dbrep) == 0 );+ t( sp_ctl(env, SPCMP, cmp, NULL) == 0 );+ t( sp_ctl(env, SPPAGE, 64) == 0 );+ t( sp_ctl(env, SPMERGE, 0) == 0 );+ t( sp_ctl(env, SPMERGEWM, 256) == 0 );+ void *db = sp_open(env);+ t( db != NULL );++ int32_t k = 0;+ while (k < 512) {+ t( sp_set(db, &k, sizeof(k), &k, sizeof(k)) == 0 );+ k++;+ }++ t( sp_ctl(db, SPMERGEFORCE) == 0 );++ while (k < 1024) {+ t( sp_set(db, &k, sizeof(k), &k, sizeof(k)) == 0 );+ k++;+ }++ k = 1023;+ while (k >= 0) {+ void *cur = sp_cursor(db, SPLTE, &k, sizeof(k));+ t( cur != NULL );+ uint32_t n = 0;+ while (sp_fetch(cur)) {+ t( *(uint32_t*)sp_key(cur) == k - n );+ t( *(uint32_t*)sp_value(cur) == k - n );+ n++;+ }+ t( sp_destroy(cur) == 0 );+ t( n == (k + 1) );+ k--;+ }++ t( sp_destroy(db) == 0 );+ t( sp_destroy(env) == 0 );+ t( rmrf(dbrep) == 0 );+}++static void+fetch_klte_random_merge_rev(void) {+ void *env = sp_env();+ t( env != NULL );+ t( sp_ctl(env, SPDIR, SPO_CREAT|SPO_RDWR, dbrep) == 0 );+ t( sp_ctl(env, SPCMP, cmp, NULL) == 0 );+ t( sp_ctl(env, SPPAGE, 64) == 0 );+ t( sp_ctl(env, SPMERGE, 0) == 0 );+ t( sp_ctl(env, SPMERGEWM, 256) == 0 );+ void *db = sp_open(env);+ t( db != NULL );++ int32_t k = 512;+ while (k < 1024) {+ t( sp_set(db, &k, sizeof(k), &k, sizeof(k)) == 0 );+ k++;+ }++ t( sp_ctl(db, SPMERGEFORCE) == 0 );++ k = 0;+ while (k < 512) {+ t( sp_set(db, &k, sizeof(k), &k, sizeof(k)) == 0 );+ k++;+ }++ k = 1023;+ while (k >= 0) {+ void *cur = sp_cursor(db, SPLTE, &k, sizeof(k));+ t( cur != NULL );+ uint32_t n = 0;+ while (sp_fetch(cur)) {+ t( *(uint32_t*)sp_key(cur) == k - n );+ t( *(uint32_t*)sp_value(cur) == k - n );+ n++;+ }+ t( sp_destroy(cur) == 0 );+ t( n == (k + 1) );+ k--;+ }++ t( sp_destroy(db) == 0 );+ t( sp_destroy(env) == 0 );+ t( rmrf(dbrep) == 0 );+}+++static void+fetch_kgt_random_merge(void) {+ void *env = sp_env();+ t( env != NULL );+ t( sp_ctl(env, SPDIR, SPO_CREAT|SPO_RDWR, dbrep) == 0 );+ t( sp_ctl(env, SPCMP, cmp, NULL) == 0 );+ t( sp_ctl(env, SPPAGE, 64) == 0 );+ t( sp_ctl(env, SPMERGE, 0) == 0 );+ t( sp_ctl(env, SPMERGEWM, 256) == 0 );+ void *db = sp_open(env);+ t( db != NULL );++ uint32_t k = 0;+ while (k < 512) {+ t( sp_set(db, &k, sizeof(k), &k, sizeof(k)) == 0 );+ k++;+ }++ t( sp_ctl(db, SPMERGEFORCE) == 0 );++ while (k < 1024) {+ t( sp_set(db, &k, sizeof(k), &k, sizeof(k)) == 0 );+ k++;+ }++ k = 0;+ while (k < 1024) {+ void *cur = sp_cursor(db, SPGT, &k, sizeof(k));+ t( cur != NULL );+ uint32_t n = 0;+ while (sp_fetch(cur)) {+ t( *(uint32_t*)sp_key(cur) == k + (n + 1));+ t( *(uint32_t*)sp_key(cur) == k + (n + 1));+ n++;+ }+ t( sp_destroy(cur) == 0 );+ t( n == (1023 - k) );+ k++;+ }++ t( sp_destroy(db) == 0 );+ t( sp_destroy(env) == 0 );+ t( rmrf(dbrep) == 0 );+}++static void+fetch_kgt_random_merge_rev(void) {+ void *env = sp_env();+ t( env != NULL );+ t( sp_ctl(env, SPDIR, SPO_CREAT|SPO_RDWR, dbrep) == 0 );+ t( sp_ctl(env, SPCMP, cmp, NULL) == 0 );+ t( sp_ctl(env, SPPAGE, 64) == 0 );+ t( sp_ctl(env, SPMERGE, 0) == 0 );+ t( sp_ctl(env, SPMERGEWM, 256) == 0 );+ void *db = sp_open(env);+ t( db != NULL );++ uint32_t k = 512;+ while (k < 1024) {+ t( sp_set(db, &k, sizeof(k), &k, sizeof(k)) == 0 );+ k++;+ }++ t( sp_ctl(db, SPMERGEFORCE) == 0 );++ k = 0;+ while (k < 512) {+ t( sp_set(db, &k, sizeof(k), &k, sizeof(k)) == 0 );+ k++;+ }++ k = 0;+ while (k < 1024) {+ void *cur = sp_cursor(db, SPGT, &k, sizeof(k));+ t( cur != NULL );+ uint32_t n = 0;+ while (sp_fetch(cur)) {+ t( *(uint32_t*)sp_key(cur) == k + (n + 1));+ t( *(uint32_t*)sp_key(cur) == k + (n + 1));+ n++;+ }+ t( sp_destroy(cur) == 0 );+ t( n == (1023 - k) );+ k++;+ }++ t( sp_destroy(db) == 0 );+ t( sp_destroy(env) == 0 );+ t( rmrf(dbrep) == 0 );+}++static void+fetch_klt_random_merge(void) {+ void *env = sp_env();+ t( env != NULL );+ t( sp_ctl(env, SPDIR, SPO_CREAT|SPO_RDWR, dbrep) == 0 );+ t( sp_ctl(env, SPCMP, cmp, NULL) == 0 );+ t( sp_ctl(env, SPPAGE, 64) == 0 );+ t( sp_ctl(env, SPMERGE, 0) == 0 );+ t( sp_ctl(env, SPMERGEWM, 256) == 0 );+ void *db = sp_open(env);+ t( db != NULL );++ int32_t k = 0;+ while (k < 512) {+ t( sp_set(db, &k, sizeof(k), &k, sizeof(k)) == 0 );+ k++;+ }++ t( sp_ctl(db, SPMERGEFORCE) == 0 );++ while (k < 1024) {+ t( sp_set(db, &k, sizeof(k), &k, sizeof(k)) == 0 );+ k++;+ }++ k = 1023;+ while (k >= 0) {+ void *cur = sp_cursor(db, SPLT, &k, sizeof(k));+ t( cur != NULL );+ uint32_t n = 0;+ while (sp_fetch(cur)) {+ t( *(uint32_t*)sp_key(cur) == k - (n + 1));+ t( *(uint32_t*)sp_value(cur) == k - (n + 1));+ n++;+ }+ t( sp_destroy(cur) == 0 );+ t( n == k );+ k--;+ }++ t( sp_destroy(db) == 0 );+ t( sp_destroy(env) == 0 );+ t( rmrf(dbrep) == 0 );+}++static void+fetch_klt_random_merge_rev(void) {+ void *env = sp_env();+ t( env != NULL );+ t( sp_ctl(env, SPDIR, SPO_CREAT|SPO_RDWR, dbrep) == 0 );+ t( sp_ctl(env, SPCMP, cmp, NULL) == 0 );+ t( sp_ctl(env, SPPAGE, 64) == 0 );+ t( sp_ctl(env, SPMERGE, 0) == 0 );+ t( sp_ctl(env, SPMERGEWM, 256) == 0 );+ void *db = sp_open(env);+ t( db != NULL );++ int32_t k = 512;+ while (k < 1024) {+ t( sp_set(db, &k, sizeof(k), &k, sizeof(k)) == 0 );+ k++;+ }++ t( sp_ctl(db, SPMERGEFORCE) == 0 );++ k = 0;+ while (k < 512) {+ t( sp_set(db, &k, sizeof(k), &k, sizeof(k)) == 0 );+ k++;+ }++ k = 1023;+ while (k >= 0) {+ void *cur = sp_cursor(db, SPLT, &k, sizeof(k));+ t( cur != NULL );+ uint32_t n = 0;+ while (sp_fetch(cur)) {+ t( *(uint32_t*)sp_key(cur) == k - (n + 1));+ t( *(uint32_t*)sp_value(cur) == k - (n + 1));+ n++;+ }+ t( sp_destroy(cur) == 0 );+ t( n == k );+ k--;+ }++ t( sp_destroy(db) == 0 );+ t( sp_destroy(env) == 0 );+ t( rmrf(dbrep) == 0 );+}+ int main(int argc, char *argv[]) {@@ -886,6 +1289,7 @@ test(delete); test(delete_set_get); test(cursor);+ test(cursor_set); test(fetch_gte_empty); test(fetch_gt_empty); test(fetch_lte_empty);@@ -903,9 +1307,16 @@ test(fetch_klte); test(fetch_klt); test(fetch_after_end);- /*- test(rotate_empty);- test(rotate);- */++ test(fetch_kgte_random);+ test(fetch_kgte_random_merge);+ test(fetch_kgte_random_merge_rev);+ test(fetch_klte_random);+ test(fetch_klte_random_merge);+ test(fetch_klte_random_merge_rev);+ test(fetch_kgt_random_merge);+ test(fetch_kgt_random_merge_rev);+ test(fetch_klt_random_merge);+ test(fetch_klt_random_merge_rev); return 0; }
+ sophia/test/concurrent.c view
@@ -0,0 +1,173 @@++/*+ * sophia database+ * sphia.org+ *+ * Copyright (c) Dmitry Simonenko+ * BSD License+*/++#include <sophia.h>+#include <sys/types.h>+#include <sys/wait.h>+#include <unistd.h>+#include "test.h"++static char *dbrep1 = "./rep";+static char *dbrep2 = "./rep2";++static inline int+cmp(char *a, size_t asz, char *b, size_t bsz, void *arg) {+ register uint32_t av = *(uint32_t*)a;+ register uint32_t bv = *(uint32_t*)b;+ if (av == bv)+ return 0;+ return (av > bv) ? 1 : -1;+}++static void+single_process(void) {+ void *env = sp_env();+ t( env != NULL );+ t( sp_ctl(env, SPDIR, SPO_CREAT|SPO_RDWR, dbrep1) == 0 );+ void *db = sp_open(env);+ t( db != NULL );+ void *env2 = sp_env();+ t( env2 != NULL );+ t( sp_ctl(env2, SPDIR, SPO_CREAT|SPO_RDWR, dbrep1) == 0 );+ void *db2 = sp_open(env2);+ t( db2 == NULL );+ t( sp_destroy(env2) == 0 );+ t( sp_destroy(db) == 0 );+ t( sp_destroy(env) == 0 );+ rmrf(dbrep1);+}++static void+single_process_2(void) {+ void *env = sp_env();+ t( env != NULL );+ t( sp_ctl(env, SPDIR, SPO_CREAT|SPO_RDWR, dbrep1) == 0 );+ t( sp_ctl(env, SPCMP, cmp, NULL) == 0 );+ void *db = sp_open(env);+ t( db != NULL );+ void *env2 = sp_env();+ t( env != NULL );+ t( sp_ctl(env2, SPDIR, SPO_CREAT|SPO_RDWR, dbrep2) == 0 );+ t( sp_ctl(env2, SPCMP, cmp, NULL) == 0 );+ void *db2 = sp_open(env2);+ t( db2 != NULL );+ uint32_t k = 1;+ t( sp_set(db2, &k, sizeof(k), &k, sizeof(k)) == 0 );+ size_t vsize = 0;+ void *vp = NULL;+ t( sp_get(db2, &k, sizeof(k), &vp, &vsize) == 1 );+ t( vsize == sizeof(k) );+ t( *(uint32_t*)vp == k );+ free(vp);+ k = 3;+ t( sp_set(db, &k, sizeof(k), &k, sizeof(k)) == 0 );+ t( sp_get(db, &k, sizeof(k), &vp, &vsize) == 1 );+ t( vsize == sizeof(k) );+ t( *(uint32_t*)vp == k );+ free(vp);+ t( sp_destroy(db) == 0 );+ t( sp_destroy(env) == 0 );+ t( sp_destroy(db2) == 0 );+ t( sp_destroy(env2) == 0 );+ t( rmrf(dbrep1) == 0 );+ t( rmrf(dbrep2) == 0 );+}++static void+multi_process(void) {+ void *env = sp_env();+ t( env != NULL );+ t( sp_ctl(env, SPDIR, SPO_CREAT|SPO_RDWR, dbrep1) == 0 );+ void *db = sp_open(env);+ t( db != NULL );+ int pid = fork();+ t( pid != -1 );+ if (pid == 0) {+ /* new process */+ void *env2 = sp_env();+ t( env2 != NULL );+ t( sp_ctl(env2, SPDIR, SPO_CREAT|SPO_RDWR, dbrep1) == 0 );+ void *db2 = sp_open(env2);+ t( db2 == NULL );+ t( sp_destroy(env2) == 0 );+ /* valgrind: parent db and env are unfreed here, and that+ * is correct otherwise destroy would+ * corrupt the database.+ */+ exit(0);+ } else {+ int status = 0;+ t( waitpid(pid, &status, 0) == pid );+ t( status == 0 );+ }+ t( sp_destroy(db) == 0 );+ t( sp_destroy(env) == 0 );+ rmrf(dbrep1);+}++static void+multi_process_2(void) {+ void *env = sp_env();+ t( env != NULL );+ t( sp_ctl(env, SPDIR, SPO_CREAT|SPO_RDWR, dbrep1) == 0 );+ t( sp_ctl(env, SPCMP, cmp, NULL) == 0 );+ void *db = sp_open(env);+ t( db != NULL );+ void *env2 = sp_env();+ t( env != NULL );+ t( sp_ctl(env2, SPDIR, SPO_CREAT|SPO_RDWR, dbrep2) == 0 );+ t( sp_ctl(env2, SPCMP, cmp, NULL) == 0 );+ uint32_t k = 1;+ size_t vsize = 0;+ void *vp = NULL;+ int pid = fork();+ t( pid != -1 );+ if (pid == 0) {+ void *db2 = sp_open(env2);+ t( db2 != NULL );+ t( sp_set(db2, &k, sizeof(k), &k, sizeof(k)) == 0 );+ t( sp_get(db2, &k, sizeof(k), &vp, &vsize) == 1 );+ t( vsize == sizeof(k) );+ t( *(uint32_t*)vp == k );+ free(vp);+ t( sp_destroy(db2) == 0 );+ t( sp_destroy(env2) == 0 );+ t( rmrf(dbrep2) == 0 );+ /* valgrind: parent db and env are unfreed here, and that+ * is correct otherwise destroy would+ * corrupt the database.+ */+ exit(0);+ }+ k = 3;+ t( sp_set(db, &k, sizeof(k), &k, sizeof(k)) == 0 );+ t( sp_get(db, &k, sizeof(k), &vp, &vsize) == 1 );+ t( vsize == sizeof(k) );+ t( *(uint32_t*)vp == k );+ free(vp);+ t( sp_destroy(db) == 0 );+ t( sp_destroy(env) == 0 );+ t( rmrf(dbrep1) == 0 );+ int status = 0;+ t( waitpid(pid, &status, 0) == pid );+ t( status == 0 );+}++int+main(int argc, char *argv[])+{+ rmrf(dbrep1);+ rmrf(dbrep2);++ test(single_process);+ test(single_process_2);+ test(multi_process);+ test(multi_process_2);+ return 0;+}
sophia/test/crash.c view
@@ -83,15 +83,17 @@ }; speofh eof = { SPEOF }; spfile f;+ spbatch b; sp_fileinit(&f, &a);+ sp_batchinit(&b); t( mkdir(dbrep, 0755) == 0 ); t( sp_lognew(&f, dbrep, 1) == 0 );- sp_logadd(&f, &h, sizeof(h));- sp_logadd(&f, &vh, sizeof(vh));- sp_logadd(&f, &k, sizeof(k));- sp_logadd(&f, &k, sizeof(k));- sp_logadd(&f, &eof, sizeof(eof));- t( sp_logflush(&f) == 0 );+ sp_batchadd(&b, &h, sizeof(h));+ sp_batchadd(&b, &vh, sizeof(vh));+ sp_batchadd(&b, &k, sizeof(k));+ sp_batchadd(&b, &k, sizeof(k));+ sp_batchadd(&b, &eof, sizeof(eof));+ t( sp_logput(&f, &b) == 0 ); t( sp_logcomplete(&f) == 0 ); t( sp_logclose(&f) == 0 ); t( exists(dbrep, 1, "log") == 1 );@@ -124,15 +126,17 @@ }; speofh eof = { SPEOF }; spfile f;+ spbatch b; sp_fileinit(&f, &a);+ sp_batchinit(&b); t( mkdir(dbrep, 0755) == 0 ); t( sp_lognew(&f, dbrep, 1) == 0 );- sp_logadd(&f, &h, sizeof(h));- sp_logadd(&f, &vh, sizeof(vh));- sp_logadd(&f, &k, sizeof(k));- sp_logadd(&f, &k, sizeof(k));- sp_logadd(&f, &eof, sizeof(eof));- t( sp_logflush(&f) == 0 );+ sp_batchadd(&b, &h, sizeof(h));+ sp_batchadd(&b, &vh, sizeof(vh));+ sp_batchadd(&b, &k, sizeof(k));+ sp_batchadd(&b, &k, sizeof(k));+ sp_batchadd(&b, &eof, sizeof(eof));+ t( sp_logput(&f, &b) == 0 ); t( sp_logclose(&f) == 0 ); t( exists(dbrep, 1, "log.incomplete") == 1 ); void *env = sp_env();@@ -175,14 +179,16 @@ }; speofh eof = { SPEOF }; spfile f;+ spbatch b;+ sp_batchinit(&b); sp_fileinit(&f, &a); t( sp_lognew(&f, dbrep, 2) == 0 );- sp_logadd(&f, &h, sizeof(h));- sp_logadd(&f, &vh, sizeof(vh));- sp_logadd(&f, &k, sizeof(k));- sp_logadd(&f, &k, sizeof(k));- sp_logadd(&f, &eof, sizeof(eof));- t( sp_logflush(&f) == 0 );+ sp_batchadd(&b, &h, sizeof(h));+ sp_batchadd(&b, &vh, sizeof(vh));+ sp_batchadd(&b, &k, sizeof(k));+ sp_batchadd(&b, &k, sizeof(k));+ sp_batchadd(&b, &eof, sizeof(eof));+ t( sp_logput(&f, &b) == 0 ); t( sp_logcomplete(&f) == 0 ); t( sp_logclose(&f) == 0 ); t( exists(dbrep, 2, "log") == 1 );@@ -214,14 +220,16 @@ crc = sp_crc32c(crc, &k, sizeof(k)); vh.crc = sp_crc32c(crc, &vh.size, sizeof(spvh) - sizeof(uint32_t)); spfile f;+ spbatch b;+ sp_batchinit(&b); sp_fileinit(&f, &a); t( mkdir(dbrep, 0755) == 0 ); t( sp_lognew(&f, dbrep, 1) == 0 );- sp_logadd(&f, &h, sizeof(h));- sp_logadd(&f, &vh, sizeof(vh));- sp_logadd(&f, &k, sizeof(k));- sp_logadd(&f, &k, sizeof(k));- t( sp_logflush(&f) == 0 );+ sp_batchadd(&b, &h, sizeof(h));+ sp_batchadd(&b, &vh, sizeof(vh));+ sp_batchadd(&b, &k, sizeof(k));+ sp_batchadd(&b, &k, sizeof(k));+ t( sp_logput(&f, &b) == 0 ); t( sp_logclose(&f) == 0 ); t( exists(dbrep, 1, "log.incomplete") == 1 ); void *env = sp_env();@@ -262,14 +270,17 @@ crc = sp_crc32c(crc, &k, sizeof(k)); vh.crc = sp_crc32c(crc, &vh.size, sizeof(spvh) - sizeof(uint32_t)); spfile f;+ spbatch b;+ sp_batchinit(&b); sp_fileinit(&f, &a);+ sp_batchinit(&b); t( mkdir(dbrep, 0755) == 0 ); t( sp_lognew(&f, dbrep, 1) == 0 );- sp_logadd(&f, &h, sizeof(h));- sp_logadd(&f, &vh, sizeof(vh));- sp_logadd(&f, &k, sizeof(k));- sp_logadd(&f, &k, sizeof(k));- t( sp_logflush(&f) == 0 );+ sp_batchadd(&b, &h, sizeof(h));+ sp_batchadd(&b, &vh, sizeof(vh));+ sp_batchadd(&b, &k, sizeof(k));+ sp_batchadd(&b, &k, sizeof(k));+ t( sp_logput(&f, &b) == 0 ); t( sp_logcomplete(&f) == 0 ); t( sp_logclose(&f) == 0 ); t( exists(dbrep, 1, "log") == 1 );@@ -382,15 +393,17 @@ crc = sp_crc32c(crc, &k, sizeof(k)); vh.crc = sp_crc32c(crc, &vh.size, sizeof(spvh) - sizeof(uint32_t)); spfile f;+ spbatch b;+ sp_batchinit(&b); sp_fileinit(&f, &a); t( mkdir(dbrep, 0755) == 0 ); t( sp_lognew(&f, dbrep, 1) == 0 );- sp_logadd(&f, &h, sizeof(h));- sp_logadd(&f, &vh, sizeof(vh));- sp_logadd(&f, &k, sizeof(k));- sp_logadd(&f, &k, sizeof(k));- sp_logadd(&f, &eof, sizeof(eof));- t( sp_logflush(&f) == 0 );+ sp_batchadd(&b, &h, sizeof(h));+ sp_batchadd(&b, &vh, sizeof(vh));+ sp_batchadd(&b, &k, sizeof(k));+ sp_batchadd(&b, &k, sizeof(k));+ sp_batchadd(&b, &eof, sizeof(eof));+ t( sp_logput(&f, &b) == 0 ); t( sp_logclose(&f) == 0 ); char path[1024]; snprintf(path, sizeof(path), "%s/%"PRIu32".db.incomplete",@@ -431,15 +444,17 @@ crc = sp_crc32c(crc, &k, sizeof(k)); vh.crc = sp_crc32c(crc, &vh.size, sizeof(spvh) - sizeof(uint32_t)); spfile f;+ spbatch b;+ sp_batchinit(&b); sp_fileinit(&f, &a); t( mkdir(dbrep, 0755) == 0 ); t( sp_lognew(&f, dbrep, 1) == 0 );- sp_logadd(&f, &h, sizeof(h));- sp_logadd(&f, &vh, sizeof(vh));- sp_logadd(&f, &k, sizeof(k));- sp_logadd(&f, &k, sizeof(k));- sp_logadd(&f, &eof, sizeof(eof));- t( sp_logflush(&f) == 0 );+ sp_batchadd(&b, &h, sizeof(h));+ sp_batchadd(&b, &vh, sizeof(vh));+ sp_batchadd(&b, &k, sizeof(k));+ sp_batchadd(&b, &k, sizeof(k));+ sp_batchadd(&b, &eof, sizeof(eof));+ t( sp_logput(&f, &b) == 0 ); t( sp_logcomplete(&f) == 0 ); t( sp_logclose(&f) == 0 ); t( exists(dbrep, 1, "log") == 1 );
+ sophia/test/issues.c view
@@ -0,0 +1,129 @@++/*+ * sophia database+ * sphia.org+ *+ * Copyright (c) Dmitry Simonenko+ * BSD License+*/++#include <string.h>+#include <sophia.h>+#include "test.h"++static char *dbrep = "./rep";++static void+gh_5(void) {+ void *env = sp_env();+ t( env != NULL );+ t( sp_ctl(env, SPDIR, SPO_CREAT|SPO_RDWR, dbrep) == 0 );+ void *db = sp_open(env);+ t( db != NULL );++ char k1[] = "key-1";+ char v1[] = "val-1";+ t( sp_set(db, k1, sizeof(k1), v1, sizeof(v1)) == 0);++ char k2[] = "key-10";+ char v2[] = "val-10";+ t( sp_set(db, k2, sizeof(k2), v2, sizeof(v2)) == 0);++ size_t ressize = 0;+ void *res = NULL;+ t( sp_get(db, k1, sizeof(k1), &res, &ressize) == 1 );++ t( ressize == sizeof(v1) );+ t( strcmp(v1, res) == 0 );++ free(res);++ t( sp_destroy(db) == 0 );+ t( sp_destroy(env) == 0 );+ t( rmrf(dbrep) == 0 );+}++static void+gh_29(void) {+ void *env = sp_env();+ t( env != NULL );+ t( sp_ctl(env, SPDIR, SPO_CREAT|SPO_RDWR, dbrep) == 0 );+ void *db = sp_open(env);+ t( db != NULL );+ int i;+ for (i = 1; i <= 10; i++) {+ char key[4];+ int len = snprintf(key, sizeof(key), "%d", i);+ t( sp_set(db, key, len, key, len) == 0 );+ }+ for (i = 1; i <= 10; i++) {+ char key[4];+ int len = snprintf(key, sizeof(key), "%d", i);+ size_t vsize = 0;+ void *vp = NULL;+ t( sp_get(db, key, len, &vp, &vsize) == 1 );+ t( vsize == len );+ t( memcmp(key, vp, len) == 0 );+ free(vp);+ }+ t( sp_destroy(db) == 0 );+ t( sp_destroy(env) == 0 );+ t( rmrf(dbrep) == 0 );+}++static void+gh_37(void)+{+ const int size = 999;+ const int watermark = 3996;+ const int pagesize = 1022;++ char data[size];+ memset(data, 0, sizeof(data));++ void *env = sp_env();+ int rc = sp_ctl(env, SPDIR, SPO_CREAT | SPO_RDWR, dbrep);+ t(rc == 0);+ if (rc == -1)++ rc = sp_ctl(env, SPMERGE, 0);+ t(rc == 0);++ rc = sp_ctl(env, SPMERGEWM, watermark);+ if (rc == -1)+ t(rc == 0);++ sp_ctl(env, SPPAGE, pagesize);+ t(rc == 0);++ void *db = sp_open(env);+ t(db != NULL);++ int byteCount = 0;+ int i;+ for (i = 0; i < 100000; ++i) {+ rc = sp_set(db, &i, sizeof(int), data, size);+ t(rc == 0);+ }++ byteCount += size;+ if (i != 0 && i % watermark == 0) {+ rc = sp_ctl(db, SPMERGEFORCE);+ t(rc == 0);+ }++ t( sp_destroy(db) == 0 );+ t( sp_destroy(env) == 0 );+ t( rmrf(dbrep) == 0 );+}++int+main(int argc, char *argv[])+{+ rmrf(dbrep);++ test(gh_5);+ test(gh_29);+ test(gh_37);+ return 0;+}
sophia/test/limit.c view
@@ -31,7 +31,7 @@ t( db != NULL ); char buf[1]; t( sp_set(db, buf, UINT16_MAX + 1, buf, sizeof(buf)) == -1 );- t( sp_error(env) != NULL );+ t( sp_error(db) != NULL ); t( sp_destroy(db) == 0 ); t( sp_destroy(env) == 0 ); rmrf(dbrep);@@ -47,12 +47,32 @@ t( db != NULL ); char buf[1]; t( sp_set(db, buf, sizeof(buf), buf, UINT32_MAX + 1ULL) == -1 );- t( sp_error(env) != NULL );+ t( sp_error(db) != NULL ); t( sp_destroy(db) == 0 ); t( sp_destroy(env) == 0 ); rmrf(dbrep); } +static void+error_nonfatal(void) {+ void *env = sp_env();+ t( env != NULL );+ t( sp_ctl(env, SPDIR, SPO_CREAT|SPO_RDWR, dbrep) == 0 );+ t( sp_ctl(env, SPCMP, cmp, NULL) == 0 );+ void *db = sp_open(env);+ t( db != NULL );+ uint32_t k = 1, v = 1;+ t( sp_set(db, &k, sizeof(k), &v, sizeof(v)) == 0);+ t( sp_set(db, &k, UINT16_MAX + 1 , &v, sizeof(v)) == -1);+ t( sp_error(db) != NULL );+ t( sp_set(db, &k, sizeof(k), &v, sizeof(v)) == 0);+ t( sp_error(db) == NULL );+ t( sp_destroy(db) == 0 );+ t( sp_destroy(env) == 0 );+ t( rmrf(dbrep) == 0 );+}++ int main(int argc, char *argv[]) {@@ -61,5 +81,7 @@ test(limit_key); if (sizeof(size_t) > 4) test(limit_value);++ test(error_nonfatal); return 0; }
sophia/test/recover.c view
@@ -1506,45 +1506,1656 @@ t( rmrf(dbrep) == 0 ); } -int-main(int argc, char *argv[])-{- rmrf(dbrep);-- test(recover_log_set_get);- test(recover_log_replace_get);- test(recover_log_set_get_replace_get);- test(recover_log_delete_get);- test(recover_log_delete_set_get);- test(recover_log_fetch_gte);- test(recover_log_fetch_lte);- test(recover_log_fetch_kgte);- test(recover_log_fetch_kgt);- test(recover_log_fetch_klte);- test(recover_log_fetch_klt);- test(recover_log_n_get);- test(recover_log_n_replace);-- test(recover_page_set_get);- test(recover_page_replace_get);- test(recover_page_set_get_replace_get);- test(recover_page_delete_get);- test(recover_page_delete_set_get);- test(recover_page_fetch_gte);- test(recover_page_fetch_lte);- test(recover_page_fetch_kgte);- test(recover_page_fetch_kgt);- test(recover_page_fetch_klte);- test(recover_page_fetch_klt);- test(recover_page_n_get);- test(recover_page_n_replace);-- test(recover_page_log_fetch_gte);- test(recover_page_log_fetch_lte);- test(recover_page_log_fetch_kgte);- test(recover_page_log_fetch_kgt);- test(recover_page_log_fetch_klte);- test(recover_page_log_fetch_klt);- test(recover_page_log_n_replace);+static void+txn_recover_log_set_get(void) {+ void *env = sp_env();+ t( env != NULL );+ t( sp_ctl(env, SPDIR, SPO_CREAT|SPO_RDWR, dbrep) == 0 );+ t( sp_ctl(env, SPCMP, cmp, NULL) == 0 );+ t( sp_ctl(env, SPMERGE, 0) == 0 );+ void *db = sp_open(env);+ t( db != NULL );+ t( sp_begin(db) == 0 );+ uint32_t k = 1, v = 1;+ t( sp_set(db, &k, sizeof(k), &v, sizeof(v)) == 0);+ k = 2;+ t( sp_set(db, &k, sizeof(k), &v, sizeof(v)) == 0);+ k = 3;+ t( sp_set(db, &k, sizeof(k), &v, sizeof(v)) == 0);+ t( sp_commit(db) == 0 );+ t( sp_destroy(db) == 0 );+ db = sp_open(env);+ t( db != NULL );+ size_t vsize = 0;+ void *vp = NULL;+ k = 1;+ t( sp_get(db, &k, sizeof(k), &vp, &vsize) == 1 );+ t( vsize == sizeof(v) );+ t( *(uint32_t*)vp == v );+ free(vp);+ k = 2;+ t( sp_get(db, &k, sizeof(k), &vp, &vsize) == 1 );+ t( vsize == sizeof(v) );+ t( *(uint32_t*)vp == v );+ free(vp);+ k = 3;+ t( sp_get(db, &k, sizeof(k), &vp, &vsize) == 1 );+ t( vsize == sizeof(v) );+ t( *(uint32_t*)vp == v );+ free(vp);+ t( sp_destroy(db) == 0 );+ t( sp_destroy(env) == 0 );+ t( rmrf(dbrep) == 0 );+}++static void+txn_recover_log_replace_get(void) {+ void *env = sp_env();+ t( env != NULL );+ t( sp_ctl(env, SPDIR, SPO_CREAT|SPO_RDWR, dbrep) == 0 );+ t( sp_ctl(env, SPCMP, cmp, NULL) == 0 );+ t( sp_ctl(env, SPMERGE, 0) == 0 );+ void *db = sp_open(env);+ t( db != NULL );+ t( sp_begin(db) == 0 );+ uint32_t k = 1, v = 1;+ t( sp_set(db, &k, sizeof(k), &v, sizeof(v)) == 0);+ v = 2;+ t( sp_set(db, &k, sizeof(k), &v, sizeof(v)) == 0);+ v = 3;+ t( sp_set(db, &k, sizeof(k), &v, sizeof(v)) == 0);+ t( sp_commit(db) == 0 );+ t( sp_destroy(db) == 0 );+ db = sp_open(env);+ t( db != NULL );+ size_t vsize = 0;+ void *vp = NULL;+ k = 1;+ t( sp_get(db, &k, sizeof(k), &vp, &vsize) == 1 );+ t( vsize == sizeof(v) );+ t( *(uint32_t*)vp == 3 );+ free(vp);+ t( sp_destroy(db) == 0 );+ t( sp_destroy(env) == 0 );+ t( rmrf(dbrep) == 0 );+}++static void+txn_recover_log_set_get_replace_get(void) {+ void *env = sp_env();+ t( env != NULL );+ t( sp_ctl(env, SPDIR, SPO_CREAT|SPO_RDWR, dbrep) == 0 );+ t( sp_ctl(env, SPCMP, cmp, NULL) == 0 );+ t( sp_ctl(env, SPMERGE, 0) == 0 );+ void *db = sp_open(env);+ t( db != NULL );+ t( sp_begin(db) == 0 );+ uint32_t k = 1, v = 1;+ t( sp_set(db, &k, sizeof(k), &v, sizeof(v)) == 0);+ t( sp_commit(db) == 0 );+ t( sp_destroy(db) == 0 );+ db = sp_open(env);+ t( db != NULL );+ t( sp_begin(db) == 0 );+ size_t vsize = 0;+ void *vp = NULL;+ k = 1;+ t( sp_get(db, &k, sizeof(k), &vp, &vsize) == 1 );+ t( vsize == sizeof(v) );+ t( *(uint32_t*)vp == 1 );+ free(vp);+ v = 2;+ t( sp_set(db, &k, sizeof(k), &v, sizeof(v)) == 0);+ vsize = 0;+ vp = NULL;+ t( sp_get(db, &k, sizeof(k), &vp, &vsize) == 1 );+ t( vsize == sizeof(v) );+ t( *(uint32_t*)vp == 2 );+ free(vp);+ t( sp_commit(db) == 0 );+ t( sp_destroy(db) == 0 );+ db = sp_open(env);+ t( db != NULL );+ vsize = 0;+ vp = NULL;+ k = 1;+ t( sp_get(db, &k, sizeof(k), &vp, &vsize) == 1 );+ t( vsize == sizeof(v) );+ t( *(uint32_t*)vp == 2 );+ free(vp);+ t( sp_destroy(db) == 0 );+ t( sp_destroy(env) == 0 );+ t( rmrf(dbrep) == 0 );+}++static void+txn_recover_log_delete_get(void) {+ void *env = sp_env();+ t( env != NULL );+ t( sp_ctl(env, SPDIR, SPO_CREAT|SPO_RDWR, dbrep) == 0 );+ t( sp_ctl(env, SPCMP, cmp, NULL) == 0 );+ t( sp_ctl(env, SPMERGE, 0) == 0 );+ void *db = sp_open(env);+ t( db != NULL );+ t( sp_begin(db) == 0 );+ uint32_t k = 1, v = 1;+ t( sp_set(db, &k, sizeof(k), &v, sizeof(v)) == 0 );+ t( sp_delete(db, &k, sizeof(k)) == 0 );+ t( sp_commit(db) == 0 );+ t( sp_destroy(db) == 0 );+ db = sp_open(env);+ t( db != NULL );+ size_t vsize = 0;+ void *vp = NULL;+ t( sp_get(db, &k, sizeof(k), &vp, &vsize) == 0 );+ t( sp_destroy(db) == 0 );+ t( sp_destroy(env) == 0 );+ t( rmrf(dbrep) == 0 );+}++static void+txn_recover_log_delete_set_get(void) {+ void *env = sp_env();+ t( env != NULL );+ t( sp_ctl(env, SPDIR, SPO_CREAT|SPO_RDWR, dbrep) == 0 );+ t( sp_ctl(env, SPCMP, cmp, NULL) == 0 );+ t( sp_ctl(env, SPMERGE, 0) == 0 );+ void *db = sp_open(env);+ t( db != NULL );+ t( sp_begin(db) == 0 );+ uint32_t k = 1, v = 1;+ t( sp_set(db, &k, sizeof(k), &v, sizeof(v)) == 0 );+ t( sp_delete(db, &k, sizeof(k)) == 0 );+ t( sp_commit(db) == 0 );+ t( sp_destroy(db) == 0 );+ db = sp_open(env);+ t( db != NULL );+ t( sp_begin(db) == 0 );+ size_t vsize = 0;+ void *vp = NULL;+ t( sp_get(db, &k, sizeof(k), &vp, &vsize) == 0 );+ v = 2;+ t( sp_set(db, &k, sizeof(k), &v, sizeof(v)) == 0 );+ t( sp_commit(db) == 0 );+ t( sp_destroy(db) == 0 );+ db = sp_open(env);+ t( db != NULL );+ vsize = 0;+ vp = NULL;+ t( sp_get(db, &k, sizeof(k), &vp, &vsize) == 1 );+ t( vsize == sizeof(v) );+ t( *(uint32_t*)vp == 2 );+ free(vp);+ t( sp_destroy(db) == 0 );+ t( sp_destroy(env) == 0 );+ t( rmrf(dbrep) == 0 );+}++static void+txn_recover_log_fetch_gte(void) {+ void *env = sp_env();+ t( env != NULL );+ t( sp_ctl(env, SPDIR, SPO_CREAT|SPO_RDWR, dbrep) == 0 );+ t( sp_ctl(env, SPCMP, cmp, NULL) == 0 );+ t( sp_ctl(env, SPMERGE, 0) == 0 );+ void *db = sp_open(env);+ t( db != NULL );+ t( sp_begin(db) == 0 );+ uint32_t k = 1, v = 2;+ t( sp_set(db, &k, sizeof(k), &v, sizeof(v)) == 0 );+ k = 2;+ t( sp_set(db, &k, sizeof(k), &v, sizeof(v)) == 0 );+ k = 3;+ t( sp_set(db, &k, sizeof(k), &v, sizeof(v)) == 0 );+ t( sp_commit(db) == 0 );+ t( sp_destroy(db) == 0 );+ db = sp_open(env);+ t( db != NULL );+ void *cur = sp_cursor(db, SPGTE, NULL, 0);+ t( cur != NULL );+ t( sp_fetch(cur) == 1 );+ t( *(uint32_t*)sp_key(cur) == 1 );+ t( sp_keysize(cur) == sizeof(k) );+ t( *(uint32_t*)sp_value(cur) == 2 );+ t( sp_valuesize(cur) == sizeof(v) );+ t( sp_fetch(cur) == 1 );+ t( *(uint32_t*)sp_key(cur) == 2 );+ t( sp_keysize(cur) == sizeof(k) );+ t( *(uint32_t*)sp_value(cur) == 2 );+ t( sp_valuesize(cur) == sizeof(v) );+ t( sp_fetch(cur) == 1 );+ t( *(uint32_t*)sp_key(cur) == 3 );+ t( sp_keysize(cur) == sizeof(k) );+ t( *(uint32_t*)sp_value(cur) == 2 );+ t( sp_valuesize(cur) == sizeof(v) );+ t( sp_fetch(cur) == 0 );+ t( sp_fetch(cur) == 0 );+ t( sp_destroy(cur) == 0 );+ t( sp_destroy(db) == 0 );+ t( sp_destroy(env) == 0 );+ t( rmrf(dbrep) == 0 );+}++static void+txn_recover_log_fetch_lte(void) {+ void *env = sp_env();+ t( env != NULL );+ t( sp_ctl(env, SPDIR, SPO_CREAT|SPO_RDWR, dbrep) == 0 );+ t( sp_ctl(env, SPCMP, cmp, NULL) == 0 );+ t( sp_ctl(env, SPMERGE, 0) == 0 );+ void *db = sp_open(env);+ t( db != NULL );+ t( sp_begin(db) == 0 );+ uint32_t k = 1, v = 2;+ t( sp_set(db, &k, sizeof(k), &v, sizeof(v)) == 0 );+ k = 2;+ t( sp_set(db, &k, sizeof(k), &v, sizeof(v)) == 0 );+ k = 3;+ t( sp_set(db, &k, sizeof(k), &v, sizeof(v)) == 0 );+ t( sp_commit(db) == 0 );+ t( sp_destroy(db) == 0 );+ db = sp_open(env);+ t( db != NULL );+ void *cur = sp_cursor(db, SPLTE, NULL, 0);+ t( cur != NULL );+ t( sp_fetch(cur) == 1 );+ t( *(uint32_t*)sp_key(cur) == 3 );+ t( sp_keysize(cur) == sizeof(k) );+ t( *(uint32_t*)sp_value(cur) == 2 );+ t( sp_valuesize(cur) == sizeof(v) );+ t( sp_fetch(cur) == 1 );+ t( *(uint32_t*)sp_key(cur) == 2 );+ t( sp_keysize(cur) == sizeof(k) );+ t( *(uint32_t*)sp_value(cur) == 2 );+ t( sp_valuesize(cur) == sizeof(v) );+ t( sp_fetch(cur) == 1 );+ t( *(uint32_t*)sp_key(cur) == 1 );+ t( sp_keysize(cur) == sizeof(k) );+ t( *(uint32_t*)sp_value(cur) == 2 );+ t( sp_valuesize(cur) == sizeof(v) );+ t( sp_fetch(cur) == 0 );+ t( sp_fetch(cur) == 0 );+ t( sp_destroy(cur) == 0 );+ t( sp_destroy(db) == 0 );+ t( sp_destroy(env) == 0 );+ t( rmrf(dbrep) == 0 );+}++static void+txn_recover_log_fetch_kgte(void) {+ void *env = sp_env();+ t( env != NULL );+ t( sp_ctl(env, SPDIR, SPO_CREAT|SPO_RDWR, dbrep) == 0 );+ t( sp_ctl(env, SPCMP, cmp, NULL) == 0 );+ t( sp_ctl(env, SPMERGE, 0) == 0 );+ void *db = sp_open(env);+ t( db != NULL );+ t( sp_begin(db) == 0 );+ uint32_t k = 1, v = 2;+ t( sp_set(db, &k, sizeof(k), &v, sizeof(v)) == 0 );+ k = 2;+ t( sp_set(db, &k, sizeof(k), &v, sizeof(v)) == 0 );+ k = 3;+ t( sp_set(db, &k, sizeof(k), &v, sizeof(v)) == 0 );+ t( sp_commit(db) == 0 );+ t( sp_destroy(db) == 0 );+ db = sp_open(env);+ t( db != NULL );+ k = 2;+ void *cur = sp_cursor(db, SPGTE, &k, sizeof(k));+ t( cur != NULL );+ t( sp_fetch(cur) == 1 );+ t( *(uint32_t*)sp_key(cur) == 2 );+ t( sp_keysize(cur) == sizeof(k) );+ t( *(uint32_t*)sp_value(cur) == 2 );+ t( sp_valuesize(cur) == sizeof(v) );+ t( sp_fetch(cur) == 1 );+ t( *(uint32_t*)sp_key(cur) == 3 );+ t( sp_keysize(cur) == sizeof(k) );+ t( *(uint32_t*)sp_value(cur) == 2 );+ t( sp_valuesize(cur) == sizeof(v) );+ t( sp_fetch(cur) == 0 );+ t( sp_fetch(cur) == 0 );+ t( sp_destroy(cur) == 0 );+ t( sp_destroy(db) == 0 );+ t( sp_destroy(env) == 0 );+ t( rmrf(dbrep) == 0 );+}++static void+txn_recover_log_fetch_kgt(void) {+ void *env = sp_env();+ t( env != NULL );+ t( sp_ctl(env, SPDIR, SPO_CREAT|SPO_RDWR, dbrep) == 0 );+ t( sp_ctl(env, SPCMP, cmp, NULL) == 0 );+ t( sp_ctl(env, SPMERGE, 0) == 0 );+ void *db = sp_open(env);+ t( db != NULL );+ t( sp_begin(db) == 0 );+ uint32_t k = 1, v = 2;+ t( sp_set(db, &k, sizeof(k), &v, sizeof(v)) == 0 );+ k = 2;+ t( sp_set(db, &k, sizeof(k), &v, sizeof(v)) == 0 );+ k = 3;+ t( sp_set(db, &k, sizeof(k), &v, sizeof(v)) == 0 );+ t( sp_commit(db) == 0 );+ t( sp_destroy(db) == 0 );+ db = sp_open(env);+ t( db != NULL );+ k = 2;+ void *cur = sp_cursor(db, SPGT, &k, sizeof(k));+ t( cur != NULL );+ t( sp_fetch(cur) == 1 );+ t( *(uint32_t*)sp_key(cur) == 3 );+ t( sp_keysize(cur) == sizeof(k) );+ t( *(uint32_t*)sp_value(cur) == 2 );+ t( sp_valuesize(cur) == sizeof(v) );+ t( sp_fetch(cur) == 0 );+ t( sp_fetch(cur) == 0 );+ t( sp_destroy(cur) == 0 );+ t( sp_destroy(db) == 0 );+ t( sp_destroy(env) == 0 );+ t( rmrf(dbrep) == 0 );+}++static void+txn_recover_log_fetch_klte(void) {+ void *env = sp_env();+ t( env != NULL );+ t( sp_ctl(env, SPDIR, SPO_CREAT|SPO_RDWR, dbrep) == 0 );+ t( sp_ctl(env, SPCMP, cmp, NULL) == 0 );+ t( sp_ctl(env, SPMERGE, 0) == 0 );+ void *db = sp_open(env);+ t( db != NULL );+ t( sp_begin(db) == 0 );+ uint32_t k = 1, v = 2;+ t( sp_set(db, &k, sizeof(k), &v, sizeof(v)) == 0 );+ k = 2;+ t( sp_set(db, &k, sizeof(k), &v, sizeof(v)) == 0 );+ k = 3;+ t( sp_set(db, &k, sizeof(k), &v, sizeof(v)) == 0 );+ t( sp_commit(db) == 0 );+ t( sp_destroy(db) == 0 );+ db = sp_open(env);+ t( db != NULL );+ k = 2;+ void *cur = sp_cursor(db, SPLTE, &k, sizeof(k));+ t( cur != NULL );+ t( sp_fetch(cur) == 1 );+ t( *(uint32_t*)sp_key(cur) == 2 );+ t( sp_keysize(cur) == sizeof(k) );+ t( *(uint32_t*)sp_value(cur) == 2 );+ t( sp_valuesize(cur) == sizeof(v) );+ t( sp_fetch(cur) == 1 );+ t( *(uint32_t*)sp_key(cur) == 1 );+ t( sp_keysize(cur) == sizeof(k) );+ t( *(uint32_t*)sp_value(cur) == 2 );+ t( sp_valuesize(cur) == sizeof(v) );+ t( sp_fetch(cur) == 0 );+ t( sp_fetch(cur) == 0 );+ t( sp_destroy(cur) == 0 );+ t( sp_destroy(db) == 0 );+ t( sp_destroy(env) == 0 );+ t( rmrf(dbrep) == 0 );+}++static void+txn_recover_log_fetch_klt(void) {+ void *env = sp_env();+ t( env != NULL );+ t( sp_ctl(env, SPDIR, SPO_CREAT|SPO_RDWR, dbrep) == 0 );+ t( sp_ctl(env, SPCMP, cmp, NULL) == 0 );+ t( sp_ctl(env, SPMERGE, 0) == 0 );+ void *db = sp_open(env);+ t( db != NULL );+ t( sp_begin(db) == 0 );+ uint32_t k = 1, v = 2;+ t( sp_set(db, &k, sizeof(k), &v, sizeof(v)) == 0 );+ k = 2;+ t( sp_set(db, &k, sizeof(k), &v, sizeof(v)) == 0 );+ k = 3;+ t( sp_set(db, &k, sizeof(k), &v, sizeof(v)) == 0 );+ t( sp_commit(db) == 0 );+ t( sp_destroy(db) == 0 );+ db = sp_open(env);+ t( db != NULL );+ k = 2;+ void *cur = sp_cursor(db, SPLT, &k, sizeof(k));+ t( cur != NULL );+ t( sp_fetch(cur) == 1 );+ t( *(uint32_t*)sp_key(cur) == 1 );+ t( sp_keysize(cur) == sizeof(k) );+ t( *(uint32_t*)sp_value(cur) == 2 );+ t( sp_valuesize(cur) == sizeof(v) );+ t( sp_fetch(cur) == 0 );+ t( sp_keysize(cur) == 0 );+ t( sp_key(cur) == NULL );+ t( sp_valuesize(cur) == 0 );+ t( sp_value(cur) == NULL );+ t( sp_fetch(cur) == 0 );+ t( sp_keysize(cur) == 0 );+ t( sp_key(cur) == NULL );+ t( sp_valuesize(cur) == 0 );+ t( sp_value(cur) == NULL );+ t( sp_destroy(cur) == 0 );+ t( sp_destroy(db) == 0 );+ t( sp_destroy(env) == 0 );+ t( rmrf(dbrep) == 0 );+}++static void+txn_recover_log_n_get(void) {+ void *env = sp_env();+ t( env != NULL );+ t( sp_ctl(env, SPDIR, SPO_CREAT|SPO_RDWR, dbrep) == 0 );+ t( sp_ctl(env, SPCMP, cmp, NULL) == 0 );+ t( sp_ctl(env, SPGC, 0) == 0 );+ t( sp_ctl(env, SPMERGE, 0) == 0 );+ void *db = sp_open(env);+ t( db != NULL );+ t( sp_begin(db) == 0 );+ uint32_t k = 0, v;+ while (k < 12) {+ v = k;+ t( sp_set(db, &k, sizeof(k), &v, sizeof(v)) == 0);+ k++;+ }+ t( sp_commit(db) == 0 );+ t( sp_destroy(db) == 0 );+ t( exists(dbrep, 1, "log") == 1 );+ db = sp_open(env);+ t( db != NULL );+ k = 0;+ while (k < 12) {+ size_t vsize = 0;+ void *vp = NULL;+ t( sp_get(db, &k, sizeof(k), &vp, &vsize) == 1 );+ t( vsize == sizeof(v) );+ t( *(uint32_t*)vp == k );+ free(vp);+ k++;+ }+ t( sp_destroy(db) == 0 );+ t( sp_destroy(env) == 0 );+ t( rmrf(dbrep) == 0 );+}++static void+txn_recover_log_n_replace(void) {+ void *env = sp_env();+ t( env != NULL );+ t( sp_ctl(env, SPDIR, SPO_CREAT|SPO_RDWR, dbrep) == 0 );+ t( sp_ctl(env, SPCMP, cmp, NULL) == 0 );+ t( sp_ctl(env, SPGC, 0) == 0 );+ t( sp_ctl(env, SPMERGE, 0) == 0 );+ void *db = sp_open(env);+ t( exists(dbrep, 1, "log.incomplete") == 1 );+ t( db != NULL );+ t( sp_begin(db) == 0 );+ uint32_t k = 0, v = 1;+ while (k < 12) {+ t( sp_set(db, &k, sizeof(k), &v, sizeof(v)) == 0);+ k++;+ }+ k = 0;+ v = 2;+ while (k < 12) {+ t( sp_set(db, &k, sizeof(k), &v, sizeof(v)) == 0);+ k++;+ }+ t( sp_commit(db) == 0 );+ t( sp_destroy(db) == 0 );+ t( exists(dbrep, 1, "log") == 1 );+ db = sp_open(env);+ t( exists(dbrep, 1, "log") == 1 );+ t( exists(dbrep, 2, "log.incomplete") == 1 );+ t( db != NULL );+ k = 0;+ while (k < 12) {+ size_t vsize = 0;+ void *vp = NULL;+ t( sp_get(db, &k, sizeof(k), &vp, &vsize) == 1 );+ t( vsize == sizeof(v) );+ t( *(uint32_t*)vp == 2 );+ free(vp);+ k++;+ }+ t( sp_destroy(db) == 0 );+ t( exists(dbrep, 1, "log") == 1 );+ t( exists(dbrep, 2, "log") == 0 );+ t( exists(dbrep, 2, "log.incomplete") == 0 );+ t( sp_destroy(env) == 0 );+ t( rmrf(dbrep) == 0 );+}++static void+txn_recover_page_set_get(void) {+ void *env = sp_env();+ t( env != NULL );+ t( sp_ctl(env, SPDIR, SPO_CREAT|SPO_RDWR, dbrep) == 0 );+ t( sp_ctl(env, SPCMP, cmp, NULL) == 0 );+ t( sp_ctl(env, SPGC, 0) == 0 );+ t( sp_ctl(env, SPMERGE, 0) == 0 );+ void *db = sp_open(env);+ t( db != NULL );+ t( sp_begin(db) == 0 );+ uint32_t k = 1, v = 1;+ t( sp_set(db, &k, sizeof(k), &v, sizeof(v)) == 0);+ k = 2;+ t( sp_set(db, &k, sizeof(k), &v, sizeof(v)) == 0);+ k = 3;+ t( sp_set(db, &k, sizeof(k), &v, sizeof(v)) == 0);+ t( sp_commit(db) == 0 );+ t( sp_ctl(db, SPMERGEFORCE) == 0 );+ t( exists(dbrep, 1, "log.incomplete") == 0 );+ t( exists(dbrep, 1, "log") == 0 );+ t( exists(dbrep, 1, "db") == 1 );+ t( exists(dbrep, 2, "log.incomplete") == 1 );+ t( sp_destroy(db) == 0 );+ t( exists(dbrep, 1, "db") == 1 );+ t( exists(dbrep, 2, "log") == 0 );+ t( exists(dbrep, 2, "log.incomplete") == 0 );+ db = sp_open(env);+ t( db != NULL );+ size_t vsize = 0;+ void *vp = NULL;+ k = 1;+ t( sp_get(db, &k, sizeof(k), &vp, &vsize) == 1 );+ t( vsize == sizeof(v) );+ t( *(uint32_t*)vp == v );+ free(vp);+ k = 2;+ t( sp_get(db, &k, sizeof(k), &vp, &vsize) == 1 );+ t( vsize == sizeof(v) );+ t( *(uint32_t*)vp == v );+ free(vp);+ k = 3;+ t( sp_get(db, &k, sizeof(k), &vp, &vsize) == 1 );+ t( vsize == sizeof(v) );+ t( *(uint32_t*)vp == v );+ free(vp);+ t( sp_destroy(db) == 0 );+ t( sp_destroy(env) == 0 );+ t( rmrf(dbrep) == 0 );+}++static void+txn_recover_page_replace_get(void) {+ void *env = sp_env();+ t( env != NULL );+ t( sp_ctl(env, SPDIR, SPO_CREAT|SPO_RDWR, dbrep) == 0 );+ t( sp_ctl(env, SPCMP, cmp, NULL) == 0 );+ t( sp_ctl(env, SPMERGE, 0) == 0 );+ t( sp_ctl(env, SPGC, 0) == 0 );+ void *db = sp_open(env);+ t( db != NULL );+ t( sp_begin(db) == 0 );+ uint32_t k = 1, v = 1;+ t( sp_set(db, &k, sizeof(k), &v, sizeof(v)) == 0);+ v = 2;+ t( sp_set(db, &k, sizeof(k), &v, sizeof(v)) == 0);+ v = 3;+ t( sp_set(db, &k, sizeof(k), &v, sizeof(v)) == 0);+ t( sp_commit(db) == 0 );+ t( sp_ctl(db, SPMERGEFORCE) == 0 );+ t( exists(dbrep, 1, "log.incomplete") == 0 );+ t( exists(dbrep, 1, "log") == 0 );+ t( exists(dbrep, 2, "log.incomplete") == 1 );+ t( exists(dbrep, 2, "log") == 0 );+ t( exists(dbrep, 1, "db") == 1 );+ t( sp_destroy(db) == 0 );+ db = sp_open(env);+ t( db != NULL );+ size_t vsize = 0;+ void *vp = NULL;+ k = 1;+ t( sp_get(db, &k, sizeof(k), &vp, &vsize) == 1 );+ t( vsize == sizeof(v) );+ t( *(uint32_t*)vp == 3 );+ free(vp);+ t( sp_destroy(db) == 0 );+ t( sp_destroy(env) == 0 );+ t( rmrf(dbrep) == 0 );+}++static void+txn_recover_page_set_get_replace_get(void) {+ void *env = sp_env();+ t( env != NULL );+ t( sp_ctl(env, SPDIR, SPO_CREAT|SPO_RDWR, dbrep) == 0 );+ t( sp_ctl(env, SPCMP, cmp, NULL) == 0 );+ t( sp_ctl(env, SPMERGE, 0) == 0 );+ t( sp_ctl(env, SPGC, 0) == 0 );+ void *db = sp_open(env);+ t( db != NULL );+ t( sp_begin(db) == 0 );+ uint32_t k = 1, v = 1;+ t( sp_set(db, &k, sizeof(k), &v, sizeof(v)) == 0);+ t( sp_set(db, &k, sizeof(k), &v, sizeof(v)) == 0);+ v = 8;+ t( sp_set(db, &k, sizeof(k), &v, sizeof(v)) == 0);+ t( sp_ctl(db, SPMERGEFORCE) == 0 );+ t( sp_commit(db) == 0 );+ t( sp_destroy(db) == 0 );+ db = sp_open(env);+ t( db != NULL );+ t( sp_begin(db) == 0 );+ size_t vsize = 0;+ void *vp = NULL;+ k = 1;+ t( sp_get(db, &k, sizeof(k), &vp, &vsize) == 1 );+ t( vsize == sizeof(v) );+ t( *(uint32_t*)vp == 8 );+ free(vp);+ v = 2;+ t( sp_set(db, &k, sizeof(k), &v, sizeof(v)) == 0);+ k = 2;+ v = 3;+ t( sp_set(db, &k, sizeof(k), &v, sizeof(v)) == 0);+ t( sp_set(db, &k, sizeof(k), &v, sizeof(v)) == 0);+ vsize = 0;+ vp = NULL;+ t( sp_commit(db) == 0 );+ t( sp_ctl(db, SPMERGEFORCE) == 0 );+ k = 1;+ t( sp_get(db, &k, sizeof(k), &vp, &vsize) == 1 );+ t( vsize == sizeof(v) );+ t( *(uint32_t*)vp == 2 );+ free(vp);+ k = 2;+ t( sp_get(db, &k, sizeof(k), &vp, &vsize) == 1 );+ t( vsize == sizeof(v) );+ t( *(uint32_t*)vp == 3 );+ free(vp);+ t( sp_destroy(db) == 0 );+ db = sp_open(env);+ t( db != NULL );+ vsize = 0;+ vp = NULL;+ k = 1;+ t( sp_get(db, &k, sizeof(k), &vp, &vsize) == 1 );+ t( vsize == sizeof(v) );+ t( *(uint32_t*)vp == 2 );+ free(vp);+ k = 2;+ t( sp_get(db, &k, sizeof(k), &vp, &vsize) == 1 );+ t( vsize == sizeof(v) );+ t( *(uint32_t*)vp == 3 );+ free(vp);+ t( sp_destroy(db) == 0 );+ t( sp_destroy(env) == 0 );+ t( rmrf(dbrep) == 0 );+}++static void+txn_recover_page_delete_get(void) {+ void *env = sp_env();+ t( env != NULL );+ t( sp_ctl(env, SPDIR, SPO_CREAT|SPO_RDWR, dbrep) == 0 );+ t( sp_ctl(env, SPCMP, cmp, NULL) == 0 );+ t( sp_ctl(env, SPMERGE, 0) == 0 );+ t( sp_ctl(env, SPGC, 0) == 0 );+ void *db = sp_open(env);+ t( db != NULL );+ t( sp_begin(db) == 0 );+ uint32_t k = 1, v = 1;+ t( sp_set(db, &k, sizeof(k), &v, sizeof(v)) == 0 );+ t( sp_set(db, &k, sizeof(k), &v, sizeof(v)) == 0 );+ t( sp_delete(db, &k, sizeof(k)) == 0 );+ t( sp_commit(db) == 0 );+ t( sp_ctl(db, SPMERGEFORCE) == 0 );+ t( exists(dbrep, 1, "log.incomplete") == 0 );+ t( exists(dbrep, 1, "log") == 0 );+ t( exists(dbrep, 1, "db.incomplete") == 0 );+ t( exists(dbrep, 1, "db") == 0 );+ t( exists(dbrep, 2, "log.incomplete") == 1 );+ t( exists(dbrep, 2, "log") == 0 );+ t( sp_destroy(db) == 0 );+ db = sp_open(env);+ t( db != NULL );+ size_t vsize = 0;+ void *vp = NULL;+ t( sp_get(db, &k, sizeof(k), &vp, &vsize) == 0 );+ t( sp_destroy(db) == 0 );+ t( sp_destroy(env) == 0 );+ t( rmrf(dbrep) == 0 );+}++static void+txn_recover_page_delete_set_get(void) {+ void *env = sp_env();+ t( env != NULL );+ t( sp_ctl(env, SPDIR, SPO_CREAT|SPO_RDWR, dbrep) == 0 );+ t( sp_ctl(env, SPCMP, cmp, NULL) == 0 );+ t( sp_ctl(env, SPMERGE, 0) == 0 );+ t( sp_ctl(env, SPGC, 0) == 0 );+ void *db = sp_open(env);+ t( db != NULL );+ t( sp_begin(db) == 0 );+ uint32_t k = 1, v = 1;+ t( sp_set(db, &k, sizeof(k), &v, sizeof(v)) == 0 );+ t( sp_set(db, &k, sizeof(k), &v, sizeof(v)) == 0 );+ t( sp_delete(db, &k, sizeof(k)) == 0 );+ t( sp_commit(db) == 0 );+ t( sp_ctl(db, SPMERGEFORCE) == 0 );+ t( exists(dbrep, 1, "log.incomplete") == 0 );+ t( exists(dbrep, 1, "log") == 0 );+ t( exists(dbrep, 1, "db.incomplete") == 0 );+ t( exists(dbrep, 1, "db") == 0 );+ t( exists(dbrep, 2, "log.incomplete") == 1 );+ t( exists(dbrep, 2, "log") == 0 );+ t( sp_destroy(db) == 0 );+ t( exists(dbrep, 1, "log.incomplete") == 0 );+ t( exists(dbrep, 1, "log") == 0 );+ t( exists(dbrep, 1, "db.incomplete") == 0 );+ t( exists(dbrep, 1, "db") == 0 );+ t( exists(dbrep, 2, "log.incomplete") == 0 );+ t( exists(dbrep, 2, "log") == 0 );+ db = sp_open(env);+ t( db != NULL );+ t( sp_begin(db) == 0 );+ size_t vsize = 0;+ void *vp = NULL;+ t( sp_get(db, &k, sizeof(k), &vp, &vsize) == 0 );+ t( sp_set(db, &k, sizeof(k), &v, sizeof(v)) == 0 );+ k = 2;+ t( sp_set(db, &k, sizeof(k), &v, sizeof(v)) == 0 );+ t( sp_delete(db, &k, sizeof(k)) == 0 );+ t( sp_commit(db) == 0 );+ t( sp_ctl(db, SPMERGEFORCE) == 0 );+ t( exists(dbrep, 1, "log.incomplete") == 0 );+ t( exists(dbrep, 1, "log") == 0 );+ t( exists(dbrep, 1, "db.incomplete") == 0 );+ t( exists(dbrep, 1, "db") == 1 );+ t( exists(dbrep, 2, "log.incomplete") == 1 );+ t( exists(dbrep, 2, "log") == 0 );+ t( sp_destroy(db) == 0 );+ db = sp_open(env);+ t( db != NULL );+ vsize = 0;+ vp = NULL;+ k = 1;+ t( sp_get(db, &k, sizeof(k), &vp, &vsize) == 1 );+ t( vsize == sizeof(v) );+ t( *(uint32_t*)vp == 1 );+ free(vp);+ k = 2;+ t( sp_get(db, &k, sizeof(k), &vp, &vsize) == 0 );+ t( sp_destroy(db) == 0 );+ t( sp_destroy(env) == 0 );+ t( rmrf(dbrep) == 0 );+}++static void+txn_recover_page_fetch_gte(void) {+ void *env = sp_env();+ t( env != NULL );+ t( sp_ctl(env, SPDIR, SPO_CREAT|SPO_RDWR, dbrep) == 0 );+ t( sp_ctl(env, SPCMP, cmp, NULL) == 0 );+ t( sp_ctl(env, SPGC, 0) == 0 );+ t( sp_ctl(env, SPMERGE, 0) == 0 );+ void *db = sp_open(env);+ t( db != NULL );+ t( sp_begin(db) == 0 );+ uint32_t k = 1, v = 2;+ t( sp_set(db, &k, sizeof(k), &v, sizeof(v)) == 0 );+ k = 2;+ t( sp_set(db, &k, sizeof(k), &v, sizeof(v)) == 0 );+ k = 3;+ t( sp_set(db, &k, sizeof(k), &v, sizeof(v)) == 0 );+ t( sp_commit(db) == 0 );+ t( sp_ctl(db, SPMERGEFORCE) == 0 );+ t( sp_destroy(db) == 0 );+ db = sp_open(env);+ t( db != NULL );+ void *cur = sp_cursor(db, SPGTE, NULL, 0);+ t( cur != NULL );+ t( sp_fetch(cur) == 1 );+ t( *(uint32_t*)sp_key(cur) == 1 );+ t( sp_keysize(cur) == sizeof(k) );+ t( *(uint32_t*)sp_value(cur) == 2 );+ t( sp_valuesize(cur) == sizeof(v) );+ t( sp_fetch(cur) == 1 );+ t( *(uint32_t*)sp_key(cur) == 2 );+ t( sp_keysize(cur) == sizeof(k) );+ t( *(uint32_t*)sp_value(cur) == 2 );+ t( sp_valuesize(cur) == sizeof(v) );+ t( sp_fetch(cur) == 1 );+ t( *(uint32_t*)sp_key(cur) == 3 );+ t( sp_keysize(cur) == sizeof(k) );+ t( *(uint32_t*)sp_value(cur) == 2 );+ t( sp_valuesize(cur) == sizeof(v) );+ t( sp_fetch(cur) == 0 );+ t( sp_fetch(cur) == 0 );+ t( sp_destroy(cur) == 0 );+ t( sp_destroy(db) == 0 );+ t( sp_destroy(env) == 0 );+ t( rmrf(dbrep) == 0 );+}++static void+txn_recover_page_fetch_lte(void) {+ void *env = sp_env();+ t( env != NULL );+ t( sp_ctl(env, SPDIR, SPO_CREAT|SPO_RDWR, dbrep) == 0 );+ t( sp_ctl(env, SPCMP, cmp, NULL) == 0 );+ t( sp_ctl(env, SPGC, 0) == 0 );+ t( sp_ctl(env, SPMERGE, 0) == 0 );+ void *db = sp_open(env);+ t( db != NULL );+ t( sp_begin(db) == 0 );+ uint32_t k = 1, v = 2;+ t( sp_set(db, &k, sizeof(k), &v, sizeof(v)) == 0 );+ k = 2;+ t( sp_set(db, &k, sizeof(k), &v, sizeof(v)) == 0 );+ k = 3;+ t( sp_set(db, &k, sizeof(k), &v, sizeof(v)) == 0 );+ t( sp_commit(db) == 0 );+ t( sp_ctl(db, SPMERGEFORCE) == 0 );+ t( sp_destroy(db) == 0 );+ db = sp_open(env);+ t( db != NULL );+ void *cur = sp_cursor(db, SPLTE, NULL, 0);+ t( cur != NULL );+ t( sp_fetch(cur) == 1 );+ t( *(uint32_t*)sp_key(cur) == 3 );+ t( sp_keysize(cur) == sizeof(k) );+ t( *(uint32_t*)sp_value(cur) == 2 );+ t( sp_valuesize(cur) == sizeof(v) );+ t( sp_fetch(cur) == 1 );+ t( *(uint32_t*)sp_key(cur) == 2 );+ t( sp_keysize(cur) == sizeof(k) );+ t( *(uint32_t*)sp_value(cur) == 2 );+ t( sp_valuesize(cur) == sizeof(v) );+ t( sp_fetch(cur) == 1 );+ t( *(uint32_t*)sp_key(cur) == 1 );+ t( sp_keysize(cur) == sizeof(k) );+ t( *(uint32_t*)sp_value(cur) == 2 );+ t( sp_valuesize(cur) == sizeof(v) );+ t( sp_fetch(cur) == 0 );+ t( sp_fetch(cur) == 0 );+ t( sp_destroy(cur) == 0 );+ t( sp_destroy(db) == 0 );+ t( sp_destroy(env) == 0 );+ t( rmrf(dbrep) == 0 );+}++static void+txn_recover_page_fetch_kgte(void) {+ void *env = sp_env();+ t( env != NULL );+ t( sp_ctl(env, SPDIR, SPO_CREAT|SPO_RDWR, dbrep) == 0 );+ t( sp_ctl(env, SPCMP, cmp, NULL) == 0 );+ t( sp_ctl(env, SPGC, 0) == 0 );+ t( sp_ctl(env, SPMERGE, 0) == 0 );+ void *db = sp_open(env);+ t( db != NULL );+ t( sp_begin(db) == 0 );+ uint32_t k = 1, v = 2;+ t( sp_set(db, &k, sizeof(k), &v, sizeof(v)) == 0 );+ k = 2;+ t( sp_set(db, &k, sizeof(k), &v, sizeof(v)) == 0 );+ k = 3;+ t( sp_set(db, &k, sizeof(k), &v, sizeof(v)) == 0 );+ t( sp_commit(db) == 0 );+ t( sp_ctl(db, SPMERGEFORCE) == 0 );+ t( sp_destroy(db) == 0 );+ db = sp_open(env);+ t( db != NULL );+ k = 2;+ void *cur = sp_cursor(db, SPGTE, &k, sizeof(k));+ t( cur != NULL );+ t( sp_fetch(cur) == 1 );+ t( *(uint32_t*)sp_key(cur) == 2 );+ t( sp_keysize(cur) == sizeof(k) );+ t( *(uint32_t*)sp_value(cur) == 2 );+ t( sp_valuesize(cur) == sizeof(v) );+ t( sp_fetch(cur) == 1 );+ t( *(uint32_t*)sp_key(cur) == 3 );+ t( sp_keysize(cur) == sizeof(k) );+ t( *(uint32_t*)sp_value(cur) == 2 );+ t( sp_valuesize(cur) == sizeof(v) );+ t( sp_fetch(cur) == 0 );+ t( sp_fetch(cur) == 0 );+ t( sp_destroy(cur) == 0 );+ t( sp_destroy(db) == 0 );+ t( sp_destroy(env) == 0 );+ t( rmrf(dbrep) == 0 );+}++static void+txn_recover_page_fetch_kgt(void) {+ void *env = sp_env();+ t( env != NULL );+ t( sp_ctl(env, SPDIR, SPO_CREAT|SPO_RDWR, dbrep) == 0 );+ t( sp_ctl(env, SPCMP, cmp, NULL) == 0 );+ t( sp_ctl(env, SPGC, 0) == 0 );+ t( sp_ctl(env, SPMERGE, 0) == 0 );+ void *db = sp_open(env);+ t( db != NULL );+ t( sp_begin(db) == 0 );+ uint32_t k = 1, v = 2;+ t( sp_set(db, &k, sizeof(k), &v, sizeof(v)) == 0 );+ k = 2;+ t( sp_set(db, &k, sizeof(k), &v, sizeof(v)) == 0 );+ k = 3;+ t( sp_set(db, &k, sizeof(k), &v, sizeof(v)) == 0 );+ t( sp_commit(db) == 0 );+ t( sp_ctl(db, SPMERGEFORCE) == 0 );+ t( sp_destroy(db) == 0 );+ db = sp_open(env);+ t( db != NULL );+ k = 2;+ void *cur = sp_cursor(db, SPGT, &k, sizeof(k));+ t( cur != NULL );+ t( sp_fetch(cur) == 1 );+ t( *(uint32_t*)sp_key(cur) == 3 );+ t( sp_keysize(cur) == sizeof(k) );+ t( *(uint32_t*)sp_value(cur) == 2 );+ t( sp_valuesize(cur) == sizeof(v) );+ t( sp_fetch(cur) == 0 );+ t( sp_fetch(cur) == 0 );+ t( sp_destroy(cur) == 0 );+ t( sp_destroy(db) == 0 );+ t( sp_destroy(env) == 0 );+ t( rmrf(dbrep) == 0 );+}++static void+txn_recover_page_fetch_klte(void) {+ void *env = sp_env();+ t( env != NULL );+ t( sp_ctl(env, SPDIR, SPO_CREAT|SPO_RDWR, dbrep) == 0 );+ t( sp_ctl(env, SPCMP, cmp, NULL) == 0 );+ t( sp_ctl(env, SPGC, 0) == 0 );+ t( sp_ctl(env, SPMERGE, 0) == 0 );+ void *db = sp_open(env);+ t( db != NULL );+ t( sp_begin(db) == 0 );+ uint32_t k = 1, v = 2;+ t( sp_set(db, &k, sizeof(k), &v, sizeof(v)) == 0 );+ k = 2;+ t( sp_set(db, &k, sizeof(k), &v, sizeof(v)) == 0 );+ k = 3;+ t( sp_set(db, &k, sizeof(k), &v, sizeof(v)) == 0 );+ t( sp_commit(db) == 0 );+ t( sp_ctl(db, SPMERGEFORCE) == 0 );+ t( sp_destroy(db) == 0 );+ db = sp_open(env);+ t( db != NULL );+ k = 2;+ void *cur = sp_cursor(db, SPLTE, &k, sizeof(k));+ t( cur != NULL );+ t( sp_fetch(cur) == 1 );+ t( *(uint32_t*)sp_key(cur) == 2 );+ t( sp_keysize(cur) == sizeof(k) );+ t( *(uint32_t*)sp_value(cur) == 2 );+ t( sp_valuesize(cur) == sizeof(v) );+ t( sp_fetch(cur) == 1 );+ t( *(uint32_t*)sp_key(cur) == 1 );+ t( sp_keysize(cur) == sizeof(k) );+ t( *(uint32_t*)sp_value(cur) == 2 );+ t( sp_valuesize(cur) == sizeof(v) );+ t( sp_fetch(cur) == 0 );+ t( sp_fetch(cur) == 0 );+ t( sp_destroy(cur) == 0 );+ t( sp_destroy(db) == 0 );+ t( sp_destroy(env) == 0 );+ t( rmrf(dbrep) == 0 );+}++static void+txn_recover_page_fetch_klt(void) {+ void *env = sp_env();+ t( env != NULL );+ t( sp_ctl(env, SPDIR, SPO_CREAT|SPO_RDWR, dbrep) == 0 );+ t( sp_ctl(env, SPCMP, cmp, NULL) == 0 );+ t( sp_ctl(env, SPGC, 0) == 0 );+ t( sp_ctl(env, SPMERGE, 0) == 0 );+ void *db = sp_open(env);+ t( db != NULL );+ t( sp_begin(db) == 0 );+ uint32_t k = 1, v = 2;+ t( sp_set(db, &k, sizeof(k), &v, sizeof(v)) == 0 );+ k = 2;+ t( sp_set(db, &k, sizeof(k), &v, sizeof(v)) == 0 );+ k = 3;+ t( sp_set(db, &k, sizeof(k), &v, sizeof(v)) == 0 );+ t( sp_commit(db) == 0 );+ t( sp_ctl(db, SPMERGEFORCE) == 0 );+ t( sp_destroy(db) == 0 );+ db = sp_open(env);+ t( db != NULL );+ k = 2;+ void *cur = sp_cursor(db, SPLT, &k, sizeof(k));+ t( cur != NULL );+ t( sp_fetch(cur) == 1 );+ t( *(uint32_t*)sp_key(cur) == 1 );+ t( sp_keysize(cur) == sizeof(k) );+ t( *(uint32_t*)sp_value(cur) == 2 );+ t( sp_valuesize(cur) == sizeof(v) );+ t( sp_fetch(cur) == 0 );+ t( sp_fetch(cur) == 0 );+ t( sp_destroy(cur) == 0 );+ t( sp_destroy(db) == 0 );+ t( sp_destroy(env) == 0 );+ t( rmrf(dbrep) == 0 );+}++static void+txn_recover_page_n_get(void) {+ void *env = sp_env();+ t( env != NULL );+ t( sp_ctl(env, SPDIR, SPO_CREAT|SPO_RDWR, dbrep) == 0 );+ t( sp_ctl(env, SPCMP, cmp, NULL) == 0 );+ t( sp_ctl(env, SPGC, 0) == 0 );+ t( sp_ctl(env, SPMERGE, 0) == 0 );+ void *db = sp_open(env);+ t( db != NULL );+ t( sp_begin(db) == 0 );+ uint32_t k = 0, v;+ while (k < 12) {+ v = k;+ t( sp_set(db, &k, sizeof(k), &v, sizeof(v)) == 0);+ k++;+ }+ t( sp_commit(db) == 0 );+ t( sp_ctl(db, SPMERGEFORCE) == 0 );+ t( sp_destroy(db) == 0 );+ t( exists(dbrep, 1, "db") == 1 );+ t( exists(dbrep, 1, "log.incomplete") == 0 );+ t( exists(dbrep, 1, "log") == 0 );+ db = sp_open(env);+ t( db != NULL );+ k = 0;+ while (k < 12) {+ size_t vsize = 0;+ void *vp = NULL;+ t( sp_get(db, &k, sizeof(k), &vp, &vsize) == 1 );+ t( vsize == sizeof(v) );+ t( *(uint32_t*)vp == k );+ free(vp);+ k++;+ }+ t( sp_destroy(db) == 0 );+ t( sp_destroy(env) == 0 );+ t( rmrf(dbrep) == 0 );+}++static void+txn_recover_page_n_replace(void) {+ void *env = sp_env();+ t( env != NULL );+ t( sp_ctl(env, SPDIR, SPO_CREAT|SPO_RDWR, dbrep) == 0 );+ t( sp_ctl(env, SPCMP, cmp, NULL) == 0 );+ t( sp_ctl(env, SPGC, 0) == 0 );+ t( sp_ctl(env, SPMERGE, 0) == 0 );+ void *db = sp_open(env);+ t( db != NULL );+ t( sp_begin(db) == 0 );+ uint32_t k = 0, v = 1;+ while (k < 12) {+ t( sp_set(db, &k, sizeof(k), &v, sizeof(v)) == 0);+ k++;+ }+ t( sp_commit(db) == 0 );+ t( sp_ctl(db, SPMERGEFORCE) == 0 );+ t( sp_begin(db) == 0 );+ t( exists(dbrep, 1, "db") == 1 );+ k = 0;+ v = 2;+ while (k < 12) {+ t( sp_set(db, &k, sizeof(k), &v, sizeof(v)) == 0);+ k++;+ }+ t( sp_commit(db) == 0 );+ t( sp_ctl(db, SPMERGEFORCE) == 0 );+ t( sp_destroy(db) == 0 );+ t( exists(dbrep, 2, "db") == 1 );+ t( exists(dbrep, 1, "log") == 0 );+ t( exists(dbrep, 2, "log") == 0 );+ db = sp_open(env);+ t( db != NULL );+ k = 0;+ while (k < 12) {+ size_t vsize = 0;+ void *vp = NULL;+ t( sp_get(db, &k, sizeof(k), &vp, &vsize) == 1 );+ t( vsize == sizeof(v) );+ t( *(uint32_t*)vp == 2 );+ free(vp);+ k++;+ }+ t( sp_destroy(db) == 0 );+ t( sp_destroy(env) == 0 );+ t( rmrf(dbrep) == 0 );+}++static void+txn_recover_page_log_fetch_gte(void) {+ void *env = sp_env();+ t( env != NULL );+ t( sp_ctl(env, SPDIR, SPO_CREAT|SPO_RDWR, dbrep) == 0 );+ t( sp_ctl(env, SPCMP, cmp, NULL) == 0 );+ t( sp_ctl(env, SPGC, 0) == 0 );+ t( sp_ctl(env, SPMERGE, 0) == 0 );+ void *db = sp_open(env);+ t( db != NULL );+ t( sp_begin(db) == 0 );+ uint32_t k = 1, v = 2;+ t( sp_set(db, &k, sizeof(k), &v, sizeof(v)) == 0 );+ k = 2;+ t( sp_set(db, &k, sizeof(k), &v, sizeof(v)) == 0 );+ k = 3;+ t( sp_set(db, &k, sizeof(k), &v, sizeof(v)) == 0 );+ t( sp_commit(db) == 0 );+ t( sp_ctl(db, SPMERGEFORCE) == 0 );+ t( sp_begin(db) == 0 );+ k = 4;+ t( sp_set(db, &k, sizeof(k), &v, sizeof(v)) == 0 );+ k = 5;+ t( sp_set(db, &k, sizeof(k), &v, sizeof(v)) == 0 );+ k = 6;+ t( sp_set(db, &k, sizeof(k), &v, sizeof(v)) == 0 );+ t( sp_commit(db) == 0 );+ t( sp_destroy(db) == 0 );+ db = sp_open(env);+ t( db != NULL );+ void *cur = sp_cursor(db, SPGTE, NULL, 0);+ t( cur != NULL );+ t( sp_fetch(cur) == 1 );+ t( *(uint32_t*)sp_key(cur) == 1 );+ t( sp_keysize(cur) == sizeof(k) );+ t( *(uint32_t*)sp_value(cur) == 2 );+ t( sp_valuesize(cur) == sizeof(v) );+ t( sp_fetch(cur) == 1 );+ t( *(uint32_t*)sp_key(cur) == 2 );+ t( sp_keysize(cur) == sizeof(k) );+ t( *(uint32_t*)sp_value(cur) == 2 );+ t( sp_valuesize(cur) == sizeof(v) );+ t( sp_fetch(cur) == 1 );+ t( *(uint32_t*)sp_key(cur) == 3 );+ t( sp_keysize(cur) == sizeof(k) );+ t( *(uint32_t*)sp_value(cur) == 2 );+ t( sp_valuesize(cur) == sizeof(v) );+ t( sp_fetch(cur) == 1 );+ t( *(uint32_t*)sp_key(cur) == 4 );+ t( sp_keysize(cur) == sizeof(k) );+ t( *(uint32_t*)sp_value(cur) == 2 );+ t( sp_valuesize(cur) == sizeof(v) );+ t( sp_fetch(cur) == 1 );+ t( *(uint32_t*)sp_key(cur) == 5 );+ t( sp_keysize(cur) == sizeof(k) );+ t( *(uint32_t*)sp_value(cur) == 2 );+ t( sp_valuesize(cur) == sizeof(v) );+ t( sp_fetch(cur) == 1 );+ t( *(uint32_t*)sp_key(cur) == 6 );+ t( sp_keysize(cur) == sizeof(k) );+ t( *(uint32_t*)sp_value(cur) == 2 );+ t( sp_valuesize(cur) == sizeof(v) );+ t( sp_fetch(cur) == 0 );+ t( sp_destroy(cur) == 0 );+ t( sp_destroy(db) == 0 );+ t( sp_destroy(env) == 0 );+ t( rmrf(dbrep) == 0 );+}++static void+txn_recover_page_log_fetch_lte(void) {+ void *env = sp_env();+ t( env != NULL );+ t( sp_ctl(env, SPDIR, SPO_CREAT|SPO_RDWR, dbrep) == 0 );+ t( sp_ctl(env, SPCMP, cmp, NULL) == 0 );+ t( sp_ctl(env, SPGC, 0) == 0 );+ t( sp_ctl(env, SPMERGE, 0) == 0 );+ void *db = sp_open(env);+ t( db != NULL );+ t( sp_begin(db) == 0 );+ uint32_t k = 1, v = 2;+ t( sp_set(db, &k, sizeof(k), &v, sizeof(v)) == 0 );+ k = 2;+ t( sp_set(db, &k, sizeof(k), &v, sizeof(v)) == 0 );+ k = 3;+ t( sp_set(db, &k, sizeof(k), &v, sizeof(v)) == 0 );+ t( sp_commit(db) == 0 );+ t( sp_ctl(db, SPMERGEFORCE) == 0 );+ t( sp_begin(db) == 0 );+ k = 4;+ t( sp_set(db, &k, sizeof(k), &v, sizeof(v)) == 0 );+ k = 5;+ t( sp_set(db, &k, sizeof(k), &v, sizeof(v)) == 0 );+ k = 6;+ t( sp_set(db, &k, sizeof(k), &v, sizeof(v)) == 0 );+ t( sp_commit(db) == 0 );+ t( sp_destroy(db) == 0 );+ db = sp_open(env);+ t( db != NULL );+ void *cur = sp_cursor(db, SPLTE, NULL, 0);+ t( cur != NULL );+ t( sp_fetch(cur) == 1 );+ t( *(uint32_t*)sp_key(cur) == 6 );+ t( sp_keysize(cur) == sizeof(k) );+ t( *(uint32_t*)sp_value(cur) == 2 );+ t( sp_valuesize(cur) == sizeof(v) );+ t( sp_fetch(cur) == 1 );+ t( *(uint32_t*)sp_key(cur) == 5 );+ t( sp_keysize(cur) == sizeof(k) );+ t( *(uint32_t*)sp_value(cur) == 2 );+ t( sp_valuesize(cur) == sizeof(v) );+ t( sp_fetch(cur) == 1 );+ t( *(uint32_t*)sp_key(cur) == 4 );+ t( sp_keysize(cur) == sizeof(k) );+ t( *(uint32_t*)sp_value(cur) == 2 );+ t( sp_valuesize(cur) == sizeof(v) );+ t( sp_fetch(cur) == 1 );+ t( *(uint32_t*)sp_key(cur) == 3 );+ t( sp_keysize(cur) == sizeof(k) );+ t( *(uint32_t*)sp_value(cur) == 2 );+ t( sp_valuesize(cur) == sizeof(v) );+ t( sp_fetch(cur) == 1 );+ t( *(uint32_t*)sp_key(cur) == 2 );+ t( sp_keysize(cur) == sizeof(k) );+ t( *(uint32_t*)sp_value(cur) == 2 );+ t( sp_valuesize(cur) == sizeof(v) );+ t( sp_fetch(cur) == 1 );+ t( *(uint32_t*)sp_key(cur) == 1 );+ t( sp_keysize(cur) == sizeof(k) );+ t( *(uint32_t*)sp_value(cur) == 2 );+ t( sp_valuesize(cur) == sizeof(v) );+ t( sp_fetch(cur) == 0 );+ t( sp_destroy(cur) == 0 );+ t( sp_destroy(db) == 0 );+ t( sp_destroy(env) == 0 );+ t( rmrf(dbrep) == 0 );+}++static void+txn_recover_page_log_fetch_kgte(void) {+ void *env = sp_env();+ t( env != NULL );+ t( sp_ctl(env, SPDIR, SPO_CREAT|SPO_RDWR, dbrep) == 0 );+ t( sp_ctl(env, SPCMP, cmp, NULL) == 0 );+ t( sp_ctl(env, SPGC, 0) == 0 );+ t( sp_ctl(env, SPMERGE, 0) == 0 );+ void *db = sp_open(env);+ t( db != NULL );+ t( sp_begin(db) == 0 );+ uint32_t k = 1, v = 2;+ t( sp_set(db, &k, sizeof(k), &v, sizeof(v)) == 0 );+ k = 2;+ t( sp_set(db, &k, sizeof(k), &v, sizeof(v)) == 0 );+ k = 3;+ t( sp_set(db, &k, sizeof(k), &v, sizeof(v)) == 0 );+ t( sp_commit(db) == 0 );+ t( sp_ctl(db, SPMERGEFORCE) == 0 );+ t( sp_begin(db) == 0 );+ k = 4;+ t( sp_set(db, &k, sizeof(k), &v, sizeof(v)) == 0 );+ k = 5;+ t( sp_set(db, &k, sizeof(k), &v, sizeof(v)) == 0 );+ k = 6;+ t( sp_set(db, &k, sizeof(k), &v, sizeof(v)) == 0 );+ t( sp_commit(db) == 0 );+ t( sp_destroy(db) == 0 );+ db = sp_open(env);+ t( db != NULL );+ k = 2;+ void *cur = sp_cursor(db, SPGTE, &k, sizeof(k));+ t( cur != NULL );+ t( sp_fetch(cur) == 1 );+ t( *(uint32_t*)sp_key(cur) == 2 );+ t( sp_keysize(cur) == sizeof(k) );+ t( *(uint32_t*)sp_value(cur) == 2 );+ t( sp_valuesize(cur) == sizeof(v) );+ t( sp_fetch(cur) == 1 );+ t( *(uint32_t*)sp_key(cur) == 3 );+ t( sp_keysize(cur) == sizeof(k) );+ t( *(uint32_t*)sp_value(cur) == 2 );+ t( sp_valuesize(cur) == sizeof(v) );+ t( sp_fetch(cur) == 1 );+ t( *(uint32_t*)sp_key(cur) == 4 );+ t( sp_keysize(cur) == sizeof(k) );+ t( *(uint32_t*)sp_value(cur) == 2 );+ t( sp_valuesize(cur) == sizeof(v) );+ t( sp_fetch(cur) == 1 );+ t( *(uint32_t*)sp_key(cur) == 5 );+ t( sp_keysize(cur) == sizeof(k) );+ t( *(uint32_t*)sp_value(cur) == 2 );+ t( sp_valuesize(cur) == sizeof(v) );+ t( sp_fetch(cur) == 1 );+ t( *(uint32_t*)sp_key(cur) == 6 );+ t( sp_keysize(cur) == sizeof(k) );+ t( *(uint32_t*)sp_value(cur) == 2 );+ t( sp_valuesize(cur) == sizeof(v) );+ t( sp_fetch(cur) == 0 );+ t( sp_fetch(cur) == 0 );+ t( sp_destroy(cur) == 0 );+ t( sp_destroy(db) == 0 );+ t( sp_destroy(env) == 0 );+ t( rmrf(dbrep) == 0 );+}++static void+txn_recover_page_log_fetch_kgt(void) {+ void *env = sp_env();+ t( env != NULL );+ t( sp_ctl(env, SPDIR, SPO_CREAT|SPO_RDWR, dbrep) == 0 );+ t( sp_ctl(env, SPCMP, cmp, NULL) == 0 );+ t( sp_ctl(env, SPGC, 0) == 0 );+ t( sp_ctl(env, SPMERGE, 0) == 0 );+ void *db = sp_open(env);+ t( db != NULL );+ t( sp_begin(db) == 0 );+ uint32_t k = 1, v = 2;+ t( sp_set(db, &k, sizeof(k), &v, sizeof(v)) == 0 );+ k = 2;+ t( sp_set(db, &k, sizeof(k), &v, sizeof(v)) == 0 );+ k = 3;+ t( sp_set(db, &k, sizeof(k), &v, sizeof(v)) == 0 );+ t( sp_commit(db) == 0 );+ t( sp_ctl(db, SPMERGEFORCE) == 0 );+ t( sp_begin(db) == 0 );+ k = 4;+ t( sp_set(db, &k, sizeof(k), &v, sizeof(v)) == 0 );+ k = 5;+ t( sp_set(db, &k, sizeof(k), &v, sizeof(v)) == 0 );+ k = 6;+ t( sp_set(db, &k, sizeof(k), &v, sizeof(v)) == 0 );+ t( sp_commit(db) == 0 );+ t( sp_destroy(db) == 0 );+ db = sp_open(env);+ t( db != NULL );+ k = 2;+ void *cur = sp_cursor(db, SPGT, &k, sizeof(k));+ t( cur != NULL );+ t( sp_fetch(cur) == 1 );+ t( *(uint32_t*)sp_key(cur) == 3 );+ t( sp_keysize(cur) == sizeof(k) );+ t( *(uint32_t*)sp_value(cur) == 2 );+ t( sp_valuesize(cur) == sizeof(v) );+ t( sp_fetch(cur) == 1 );+ t( *(uint32_t*)sp_key(cur) == 4 );+ t( sp_keysize(cur) == sizeof(k) );+ t( *(uint32_t*)sp_value(cur) == 2 );+ t( sp_valuesize(cur) == sizeof(v) );+ t( sp_fetch(cur) == 1 );+ t( *(uint32_t*)sp_key(cur) == 5 );+ t( sp_keysize(cur) == sizeof(k) );+ t( *(uint32_t*)sp_value(cur) == 2 );+ t( sp_valuesize(cur) == sizeof(v) );+ t( sp_fetch(cur) == 1 );+ t( *(uint32_t*)sp_key(cur) == 6 );+ t( sp_keysize(cur) == sizeof(k) );+ t( *(uint32_t*)sp_value(cur) == 2 );+ t( sp_valuesize(cur) == sizeof(v) );+ t( sp_fetch(cur) == 0 );+ t( sp_fetch(cur) == 0 );+ t( sp_destroy(cur) == 0 );+ t( sp_destroy(db) == 0 );+ t( sp_destroy(env) == 0 );+ t( rmrf(dbrep) == 0 );+}++static void+txn_recover_page_log_fetch_klte(void) {+ void *env = sp_env();+ t( env != NULL );+ t( sp_ctl(env, SPDIR, SPO_CREAT|SPO_RDWR, dbrep) == 0 );+ t( sp_ctl(env, SPCMP, cmp, NULL) == 0 );+ t( sp_ctl(env, SPGC, 0) == 0 );+ t( sp_ctl(env, SPMERGE, 0) == 0 );+ void *db = sp_open(env);+ t( db != NULL );+ t( sp_begin(db) == 0 );+ uint32_t k = 1, v = 2;+ t( sp_set(db, &k, sizeof(k), &v, sizeof(v)) == 0 );+ k = 2;+ t( sp_set(db, &k, sizeof(k), &v, sizeof(v)) == 0 );+ k = 3;+ t( sp_set(db, &k, sizeof(k), &v, sizeof(v)) == 0 );+ t( sp_commit(db) == 0 );+ t( sp_ctl(db, SPMERGEFORCE) == 0 );+ t( sp_begin(db) == 0 );+ k = 4;+ t( sp_set(db, &k, sizeof(k), &v, sizeof(v)) == 0 );+ k = 5;+ t( sp_set(db, &k, sizeof(k), &v, sizeof(v)) == 0 );+ k = 6;+ t( sp_set(db, &k, sizeof(k), &v, sizeof(v)) == 0 );+ t( sp_commit(db) == 0 );+ t( sp_destroy(db) == 0 );+ db = sp_open(env);+ t( db != NULL );+ k = 4;+ void *cur = sp_cursor(db, SPLTE, &k, sizeof(k));+ t( cur != NULL );+ t( sp_fetch(cur) == 1 );+ t( *(uint32_t*)sp_key(cur) == 4 );+ t( sp_keysize(cur) == sizeof(k) );+ t( *(uint32_t*)sp_value(cur) == 2 );+ t( sp_valuesize(cur) == sizeof(v) );+ t( sp_fetch(cur) == 1 );+ t( *(uint32_t*)sp_key(cur) == 3 );+ t( sp_keysize(cur) == sizeof(k) );+ t( *(uint32_t*)sp_value(cur) == 2 );+ t( sp_valuesize(cur) == sizeof(v) );+ t( sp_fetch(cur) == 1 );+ t( *(uint32_t*)sp_key(cur) == 2 );+ t( sp_keysize(cur) == sizeof(k) );+ t( *(uint32_t*)sp_value(cur) == 2 );+ t( sp_valuesize(cur) == sizeof(v) );+ t( sp_fetch(cur) == 1 );+ t( *(uint32_t*)sp_key(cur) == 1 );+ t( sp_keysize(cur) == sizeof(k) );+ t( *(uint32_t*)sp_value(cur) == 2 );+ t( sp_valuesize(cur) == sizeof(v) );+ t( sp_fetch(cur) == 0 );+ t( sp_fetch(cur) == 0 );+ t( sp_destroy(cur) == 0 );+ t( sp_destroy(db) == 0 );+ t( sp_destroy(env) == 0 );+ t( rmrf(dbrep) == 0 );+}++static void+txn_recover_page_log_fetch_klt(void) {+ void *env = sp_env();+ t( env != NULL );+ t( sp_ctl(env, SPDIR, SPO_CREAT|SPO_RDWR, dbrep) == 0 );+ t( sp_ctl(env, SPCMP, cmp, NULL) == 0 );+ t( sp_ctl(env, SPGC, 0) == 0 );+ t( sp_ctl(env, SPMERGE, 0) == 0 );+ void *db = sp_open(env);+ t( db != NULL );+ t( sp_begin(db) == 0 );+ uint32_t k = 1, v = 2;+ t( sp_set(db, &k, sizeof(k), &v, sizeof(v)) == 0 );+ k = 2;+ t( sp_set(db, &k, sizeof(k), &v, sizeof(v)) == 0 );+ k = 3;+ t( sp_set(db, &k, sizeof(k), &v, sizeof(v)) == 0 );+ t( sp_commit(db) == 0 );+ t( sp_ctl(db, SPMERGEFORCE) == 0 );+ t( sp_begin(db) == 0 );+ k = 4;+ t( sp_set(db, &k, sizeof(k), &v, sizeof(v)) == 0 );+ k = 5;+ t( sp_set(db, &k, sizeof(k), &v, sizeof(v)) == 0 );+ k = 6;+ t( sp_set(db, &k, sizeof(k), &v, sizeof(v)) == 0 );+ t( sp_commit(db) == 0 );+ t( sp_destroy(db) == 0 );+ db = sp_open(env);+ t( db != NULL );+ k = 4;+ void *cur = sp_cursor(db, SPLT, &k, sizeof(k));+ t( cur != NULL );+ t( sp_fetch(cur) == 1 );+ t( *(uint32_t*)sp_key(cur) == 3 );+ t( sp_keysize(cur) == sizeof(k) );+ t( *(uint32_t*)sp_value(cur) == 2 );+ t( sp_valuesize(cur) == sizeof(v) );+ t( sp_fetch(cur) == 1 );+ t( *(uint32_t*)sp_key(cur) == 2 );+ t( sp_keysize(cur) == sizeof(k) );+ t( *(uint32_t*)sp_value(cur) == 2 );+ t( sp_valuesize(cur) == sizeof(v) );+ t( sp_fetch(cur) == 1 );+ t( *(uint32_t*)sp_key(cur) == 1 );+ t( sp_keysize(cur) == sizeof(k) );+ t( *(uint32_t*)sp_value(cur) == 2 );+ t( sp_valuesize(cur) == sizeof(v) );+ t( sp_fetch(cur) == 0 );+ t( sp_fetch(cur) == 0 );+ t( sp_destroy(cur) == 0 );+ t( sp_destroy(db) == 0 );+ t( sp_destroy(env) == 0 );+ t( rmrf(dbrep) == 0 );+}++static void+txn_recover_page_log_n_replace(void) {+ void *env = sp_env();+ t( env != NULL );+ t( sp_ctl(env, SPDIR, SPO_CREAT|SPO_RDWR, dbrep) == 0 );+ t( sp_ctl(env, SPCMP, cmp, NULL) == 0 );+ t( sp_ctl(env, SPGC, 0) == 0 );+ t( sp_ctl(env, SPMERGE, 0) == 0 );+ void *db = sp_open(env);+ t( db != NULL );+ t( sp_begin(db) == 0 );+ uint32_t k = 0, v = 1;+ while (k < 12) {+ t( sp_set(db, &k, sizeof(k), &v, sizeof(v)) == 0);+ if (k > 0 && k % 3 == 0) {+ t( sp_commit(db) == 0 );+ t( sp_ctl(db, SPMERGEFORCE) == 0 );+ t( sp_begin(db) == 0 );+ }+ k++;+ }+ t( sp_commit(db) == 0 );+ k = 0;+ v = 2;+ while (k < 12) {+ t( sp_set(db, &k, sizeof(k), &v, sizeof(v)) == 0);+ k++;+ }+ t( sp_destroy(db) == 0 );+ t( exists(dbrep, 1, "db") == 1 );+ t( exists(dbrep, 2, "db") == 1 );+ t( exists(dbrep, 3, "db") == 1 );+ t( exists(dbrep, 4, "log") == 1 );+ db = sp_open(env);+ t( db != NULL );+ k = 0;+ while (k < 12) {+ size_t vsize = 0;+ void *vp = NULL;+ t( sp_get(db, &k, sizeof(k), &vp, &vsize) == 1 );+ t( vsize == sizeof(v) );+ t( *(uint32_t*)vp == 2 );+ free(vp);+ k++;+ }+ t( sp_destroy(db) == 0 );+ db = sp_open(env);+ t( db != NULL );+ t( sp_ctl(db, SPMERGEFORCE) == 0 );+ t( exists(dbrep, 5, "db") == 1 );+ t( exists(dbrep, 5, "log") == 0 );+ t( exists(dbrep, 5, "log.incomplete") == 0 );+ t( exists(dbrep, 6, "log.incomplete") == 1 );+ k = 0;+ while (k < 12) {+ size_t vsize = 0;+ void *vp = NULL;+ t( sp_get(db, &k, sizeof(k), &vp, &vsize) == 1 );+ t( vsize == sizeof(v) );+ t( *(uint32_t*)vp == 2 );+ free(vp);+ k++;+ }+ t( sp_destroy(db) == 0 );+ t( sp_destroy(env) == 0 );+ t( rmrf(dbrep) == 0 );+}++int+main(int argc, char *argv[])+{+ rmrf(dbrep);++ test(recover_log_set_get);+ test(recover_log_replace_get);+ test(recover_log_set_get_replace_get);+ test(recover_log_delete_get);+ test(recover_log_delete_set_get);+ test(recover_log_fetch_gte);+ test(recover_log_fetch_lte);+ test(recover_log_fetch_kgte);+ test(recover_log_fetch_kgt);+ test(recover_log_fetch_klte);+ test(recover_log_fetch_klt);+ test(recover_log_n_get);+ test(recover_log_n_replace);++ test(recover_page_set_get);+ test(recover_page_replace_get);+ test(recover_page_set_get_replace_get);+ test(recover_page_delete_get);+ test(recover_page_delete_set_get);+ test(recover_page_fetch_gte);+ test(recover_page_fetch_lte);+ test(recover_page_fetch_kgte);+ test(recover_page_fetch_kgt);+ test(recover_page_fetch_klte);+ test(recover_page_fetch_klt);+ test(recover_page_n_get);+ test(recover_page_n_replace);++ test(recover_page_log_fetch_gte);+ test(recover_page_log_fetch_lte);+ test(recover_page_log_fetch_kgte);+ test(recover_page_log_fetch_kgt);+ test(recover_page_log_fetch_klte);+ test(recover_page_log_fetch_klt);+ test(recover_page_log_n_replace);++ test(txn_recover_log_set_get);+ test(txn_recover_log_replace_get);+ test(txn_recover_log_set_get_replace_get);+ test(txn_recover_log_delete_get);+ test(txn_recover_log_delete_set_get);+ test(txn_recover_log_fetch_gte);+ test(txn_recover_log_fetch_lte);+ test(txn_recover_log_fetch_kgte);+ test(txn_recover_log_fetch_kgt);+ test(txn_recover_log_fetch_klte);+ test(txn_recover_log_fetch_klt);+ test(txn_recover_log_n_get);+ test(txn_recover_log_n_replace);++ test(txn_recover_page_set_get);+ test(txn_recover_page_replace_get);+ test(txn_recover_page_set_get_replace_get);+ test(txn_recover_page_delete_get);+ test(txn_recover_page_delete_set_get);+ test(txn_recover_page_fetch_gte);+ test(txn_recover_page_fetch_lte);+ test(txn_recover_page_fetch_kgte);+ test(txn_recover_page_fetch_kgt);+ test(txn_recover_page_fetch_klte);+ test(txn_recover_page_fetch_klt);+ test(txn_recover_page_n_get);+ test(txn_recover_page_n_replace);++ test(txn_recover_page_log_fetch_gte);+ test(txn_recover_page_log_fetch_lte);+ test(txn_recover_page_log_fetch_kgte);+ test(txn_recover_page_log_fetch_kgt);+ test(txn_recover_page_log_fetch_klte);+ test(txn_recover_page_log_fetch_klt);+ test(txn_recover_page_log_n_replace); return 0; }
+ sophia/test/transaction.c view
@@ -0,0 +1,1015 @@++/*+ * sophia database+ * sphia.org+ *+ * Copyright (c) Dmitry Simonenko+ * BSD License+*/++#include <sophia.h>+#include "test.h"++static char *dbrep = "./rep";++static inline int+cmp(char *a, size_t asz, char *b, size_t bsz, void *arg) {+ register uint32_t av = *(uint32_t*)a;+ register uint32_t bv = *(uint32_t*)b;+ if (av == bv)+ return 0;+ return (av > bv) ? 1 : -1;+}++static void+begin(void) {+ void *env = sp_env();+ t( env != NULL );+ t( sp_ctl(env, SPDIR, SPO_CREAT|SPO_RDWR, dbrep) == 0 );+ t( sp_ctl(env, SPCMP, cmp, NULL) == 0 );+ void *db = sp_open(env);+ t( db != NULL );+ t( sp_begin(db) == 0 );+ t( sp_destroy(db) == 0 );+ t( sp_destroy(env) == 0 );+ rmrf(dbrep);+}++static void+begin_begin(void) {+ void *env = sp_env();+ t( env != NULL );+ t( sp_ctl(env, SPDIR, SPO_CREAT|SPO_RDWR, dbrep) == 0 );+ t( sp_ctl(env, SPCMP, cmp, NULL) == 0 );+ void *db = sp_open(env);+ t( db != NULL );+ t( sp_begin(db) == 0 );+ t( sp_begin(db) == -1 );+ t( sp_destroy(db) == 0 );+ t( sp_destroy(env) == 0 );+ rmrf(dbrep);+}++static void+commit(void) {+ void *env = sp_env();+ t( env != NULL );+ t( sp_ctl(env, SPDIR, SPO_CREAT|SPO_RDWR, dbrep) == 0 );+ t( sp_ctl(env, SPCMP, cmp, NULL) == 0 );+ void *db = sp_open(env);+ t( db != NULL );+ t( sp_commit(db) == -1 );+ t( sp_destroy(db) == 0 );+ t( sp_destroy(env) == 0 );+ rmrf(dbrep);+}++static void+rollback(void) {+ void *env = sp_env();+ t( env != NULL );+ t( sp_ctl(env, SPDIR, SPO_CREAT|SPO_RDWR, dbrep) == 0 );+ t( sp_ctl(env, SPCMP, cmp, NULL) == 0 );+ void *db = sp_open(env);+ t( db != NULL );+ t( sp_rollback(db) == -1 );+ t( sp_destroy(db) == 0 );+ t( sp_destroy(env) == 0 );+ rmrf(dbrep);+}++static void+begin_commit_empty(void) {+ void *env = sp_env();+ t( env != NULL );+ t( sp_ctl(env, SPDIR, SPO_CREAT|SPO_RDWR, dbrep) == 0 );+ t( sp_ctl(env, SPCMP, cmp, NULL) == 0 );+ void *db = sp_open(env);+ t( db != NULL );+ t( sp_begin(db) == 0 );+ t( sp_commit(db) == 0 );+ t( sp_destroy(db) == 0 );+ t( sp_destroy(env) == 0 );+ rmrf(dbrep);+}++static void+begin_rollback_empty(void) {+ void *env = sp_env();+ t( env != NULL );+ t( sp_ctl(env, SPDIR, SPO_CREAT|SPO_RDWR, dbrep) == 0 );+ t( sp_ctl(env, SPCMP, cmp, NULL) == 0 );+ void *db = sp_open(env);+ t( db != NULL );+ t( sp_begin(db) == 0 );+ t( sp_commit(db) == 0 );+ t( sp_destroy(db) == 0 );+ t( sp_destroy(env) == 0 );+ rmrf(dbrep);+}++static void+begin_commit(void) {+ void *env = sp_env();+ t( env != NULL );+ t( sp_ctl(env, SPDIR, SPO_CREAT|SPO_RDWR, dbrep) == 0 );+ t( sp_ctl(env, SPCMP, cmp, NULL) == 0 );+ void *db = sp_open(env);+ t( db != NULL );+ t( sp_begin(db) == 0 );+ uint32_t k = 1;+ t( sp_set(db, &k, sizeof(k), &k, sizeof(k)) == 0);+ k = 2;+ t( sp_set(db, &k, sizeof(k), &k, sizeof(k)) == 0);+ k = 3;+ t( sp_set(db, &k, sizeof(k), &k, sizeof(k)) == 0);+ t( sp_commit(db) == 0 );+ t( sp_destroy(db) == 0 );+ t( sp_destroy(env) == 0 );+ rmrf(dbrep);+}++static void+begin_commit_get(void) {+ void *env = sp_env();+ t( env != NULL );+ t( sp_ctl(env, SPDIR, SPO_CREAT|SPO_RDWR, dbrep) == 0 );+ t( sp_ctl(env, SPCMP, cmp, NULL) == 0 );+ void *db = sp_open(env);+ t( db != NULL );+ t( sp_begin(db) == 0 );+ uint32_t k = 1;+ t( sp_set(db, &k, sizeof(k), &k, sizeof(k)) == 0);+ k = 2;+ t( sp_set(db, &k, sizeof(k), &k, sizeof(k)) == 0);+ k = 3;+ t( sp_set(db, &k, sizeof(k), &k, sizeof(k)) == 0);+ t( sp_commit(db) == 0 );+ size_t vsize = 0;+ void *vp = NULL;+ k = 1;+ t( sp_get(db, &k, sizeof(k), &vp, &vsize) == 1 );+ t( vsize == sizeof(k) );+ t( *(uint32_t*)vp == k );+ free(vp);+ k = 2;+ t( sp_get(db, &k, sizeof(k), &vp, &vsize) == 1 );+ t( vsize == sizeof(k) );+ t( *(uint32_t*)vp == k );+ free(vp);+ k = 3;+ t( sp_get(db, &k, sizeof(k), &vp, &vsize) == 1 );+ t( vsize == sizeof(k) );+ t( *(uint32_t*)vp == k );+ free(vp);+ t( sp_destroy(db) == 0 );+ t( sp_destroy(env) == 0 );+ rmrf(dbrep);+}++static void+begin_get_commit_get(void) {+ void *env = sp_env();+ t( env != NULL );+ t( sp_ctl(env, SPDIR, SPO_CREAT|SPO_RDWR, dbrep) == 0 );+ t( sp_ctl(env, SPCMP, cmp, NULL) == 0 );+ void *db = sp_open(env);+ t( db != NULL );+ t( sp_begin(db) == 0 );+ uint32_t k = 1;+ t( sp_set(db, &k, sizeof(k), &k, sizeof(k)) == 0);+ size_t vsize = 0;+ void *vp = NULL;+ t( sp_get(db, &k, sizeof(k), &vp, &vsize) == 1 );+ t( vsize == sizeof(k) );+ t( *(uint32_t*)vp == k );+ free(vp);+ t( sp_commit(db) == 0 );+ vsize = 0;+ vp = NULL;+ t( sp_get(db, &k, sizeof(k), &vp, &vsize) == 1 );+ t( vsize == sizeof(k) );+ t( *(uint32_t*)vp == k );+ free(vp);+ t( sp_destroy(db) == 0 );+ t( sp_destroy(env) == 0 );+ rmrf(dbrep);+}++static void+begin_rollback(void) {+ void *env = sp_env();+ t( env != NULL );+ t( sp_ctl(env, SPDIR, SPO_CREAT|SPO_RDWR, dbrep) == 0 );+ t( sp_ctl(env, SPCMP, cmp, NULL) == 0 );+ void *db = sp_open(env);+ t( db != NULL );+ t( sp_begin(db) == 0 );+ uint32_t k = 1;+ t( sp_set(db, &k, sizeof(k), &k, sizeof(k)) == 0);+ k = 2;+ t( sp_set(db, &k, sizeof(k), &k, sizeof(k)) == 0);+ k = 3;+ t( sp_set(db, &k, sizeof(k), &k, sizeof(k)) == 0);+ t( sp_rollback(db) == 0 );+ t( sp_destroy(db) == 0 );+ t( sp_destroy(env) == 0 );+ rmrf(dbrep);+}++static void+begin_rollback_get(void) {+ void *env = sp_env();+ t( env != NULL );+ t( sp_ctl(env, SPDIR, SPO_CREAT|SPO_RDWR, dbrep) == 0 );+ t( sp_ctl(env, SPCMP, cmp, NULL) == 0 );+ void *db = sp_open(env);+ t( db != NULL );+ t( sp_begin(db) == 0 );+ uint32_t k = 1;+ t( sp_set(db, &k, sizeof(k), &k, sizeof(k)) == 0);+ k = 2;+ t( sp_set(db, &k, sizeof(k), &k, sizeof(k)) == 0);+ k = 3;+ t( sp_set(db, &k, sizeof(k), &k, sizeof(k)) == 0);+ t( sp_rollback(db) == 0 );+ size_t vsize = 0;+ void *vp = NULL;+ k = 1;+ t( sp_get(db, &k, sizeof(k), &vp, &vsize) == 0 );+ k = 2;+ t( sp_get(db, &k, sizeof(k), &vp, &vsize) == 0 );+ k = 3;+ t( sp_get(db, &k, sizeof(k), &vp, &vsize) == 0 );+ t( sp_destroy(db) == 0 );+ t( sp_destroy(env) == 0 );+ rmrf(dbrep);+}++static void+begin_rollback_consistent(void) {+ void *env = sp_env();+ t( env != NULL );+ t( sp_ctl(env, SPDIR, SPO_CREAT|SPO_RDWR, dbrep) == 0 );+ t( sp_ctl(env, SPCMP, cmp, NULL) == 0 );+ void *db = sp_open(env);+ t( db != NULL );+ uint32_t k = 1;+ t( sp_set(db, &k, sizeof(k), &k, sizeof(k)) == 0);+ k = 2;+ t( sp_set(db, &k, sizeof(k), &k, sizeof(k)) == 0);+ k = 3;+ t( sp_set(db, &k, sizeof(k), &k, sizeof(k)) == 0);+ t( sp_begin(db) == 0 );+ k = 1;+ uint32_t v = k + 1;+ t( sp_set(db, &k, sizeof(k), &v, sizeof(v)) == 0);+ k = 2;+ v = k + 1;+ t( sp_set(db, &k, sizeof(k), &v, sizeof(v)) == 0);+ k = 3;+ v = k + 1;+ t( sp_set(db, &k, sizeof(k), &v, sizeof(v)) == 0);++ size_t vsize = 0;+ void *vp = NULL;+ k = 1;+ t( sp_get(db, &k, sizeof(k), &vp, &vsize) == 1 );+ t( vsize == sizeof(k) );+ t( *(uint32_t*)vp == k + 1);+ free(vp);+ k = 2;+ t( sp_get(db, &k, sizeof(k), &vp, &vsize) == 1 );+ t( vsize == sizeof(k) );+ t( *(uint32_t*)vp == k + 1);+ free(vp);+ k = 3;+ t( sp_get(db, &k, sizeof(k), &vp, &vsize) == 1 );+ t( vsize == sizeof(k) );+ t( *(uint32_t*)vp == k + 1);+ free(vp);+ t( sp_rollback(db) == 0 );++ vsize = 0;+ vp = NULL;+ k = 1;+ t( sp_get(db, &k, sizeof(k), &vp, &vsize) == 1 );+ t( vsize == sizeof(k) );+ t( *(uint32_t*)vp == k);+ free(vp);+ k = 2;+ t( sp_get(db, &k, sizeof(k), &vp, &vsize) == 1 );+ t( vsize == sizeof(k) );+ t( *(uint32_t*)vp == k);+ free(vp);+ k = 3;+ t( sp_get(db, &k, sizeof(k), &vp, &vsize) == 1 );+ t( vsize == sizeof(k) );+ t( *(uint32_t*)vp == k);+ free(vp);++ t( sp_destroy(db) == 0 );+ t( sp_destroy(env) == 0 );+ rmrf(dbrep);+}++static void+begin_commit_2x(void) {+ void *env = sp_env();+ t( env != NULL );+ t( sp_ctl(env, SPDIR, SPO_CREAT|SPO_RDWR, dbrep) == 0 );+ t( sp_ctl(env, SPCMP, cmp, NULL) == 0 );+ void *db = sp_open(env);+ t( db != NULL );+ t( sp_begin(db) == 0 );+ uint32_t k = 1;+ t( sp_set(db, &k, sizeof(k), &k, sizeof(k)) == 0);+ k = 2;+ t( sp_set(db, &k, sizeof(k), &k, sizeof(k)) == 0);+ k = 3;+ t( sp_set(db, &k, sizeof(k), &k, sizeof(k)) == 0);+ t( sp_commit(db) == 0 );+ t( sp_begin(db) == 0 );+ k = 1;+ t( sp_set(db, &k, sizeof(k), &k, sizeof(k)) == 0);+ k = 2;+ t( sp_set(db, &k, sizeof(k), &k, sizeof(k)) == 0);+ k = 3;+ t( sp_set(db, &k, sizeof(k), &k, sizeof(k)) == 0);+ t( sp_commit(db) == 0 );+ t( sp_destroy(db) == 0 );+ t( sp_destroy(env) == 0 );+ rmrf(dbrep);+}++static void+rollback_on_close(void) {+ void *env = sp_env();+ t( env != NULL );+ t( sp_ctl(env, SPDIR, SPO_CREAT|SPO_RDWR, dbrep) == 0 );+ t( sp_ctl(env, SPCMP, cmp, NULL) == 0 );+ void *db = sp_open(env);+ t( db != NULL );+ t( sp_begin(db) == 0 );+ uint32_t k = 1;+ t( sp_set(db, &k, sizeof(k), &k, sizeof(k)) == 0);+ k = 2;+ t( sp_set(db, &k, sizeof(k), &k, sizeof(k)) == 0);+ k = 3;+ t( sp_set(db, &k, sizeof(k), &k, sizeof(k)) == 0);+ t( sp_destroy(db) == 0 );+ db = sp_open(env);+ t( db != NULL );+ size_t vsize = 0;+ void *vp = NULL;+ k = 1;+ t( sp_get(db, &k, sizeof(k), &vp, &vsize) == 0 );+ k = 2;+ t( sp_get(db, &k, sizeof(k), &vp, &vsize) == 0 );+ k = 3;+ t( sp_get(db, &k, sizeof(k), &vp, &vsize) == 0 );+ t( sp_destroy(db) == 0 );+ t( sp_destroy(env) == 0 );+ rmrf(dbrep);+}++static void+rollback_recover(void) {+ void *env = sp_env();+ t( env != NULL );+ t( sp_ctl(env, SPDIR, SPO_CREAT|SPO_RDWR, dbrep) == 0 );+ t( sp_ctl(env, SPCMP, cmp, NULL) == 0 );+ void *db = sp_open(env);+ t( db != NULL );+ t( sp_begin(db) == 0 );+ uint32_t k = 1;+ t( sp_set(db, &k, sizeof(k), &k, sizeof(k)) == 0);+ k = 2;+ t( sp_set(db, &k, sizeof(k), &k, sizeof(k)) == 0);+ k = 3;+ t( sp_set(db, &k, sizeof(k), &k, sizeof(k)) == 0);+ t( sp_rollback(db) == 0);+ t( sp_destroy(db) == 0 );+ db = sp_open(env);+ t( db != NULL );+ size_t vsize = 0;+ void *vp = NULL;+ k = 1;+ t( sp_get(db, &k, sizeof(k), &vp, &vsize) == 0 );+ k = 2;+ t( sp_get(db, &k, sizeof(k), &vp, &vsize) == 0 );+ k = 3;+ t( sp_get(db, &k, sizeof(k), &vp, &vsize) == 0 );+ t( sp_destroy(db) == 0 );+ t( sp_destroy(env) == 0 );+ rmrf(dbrep);+}++static void+commit_recover(void) {+ void *env = sp_env();+ t( env != NULL );+ t( sp_ctl(env, SPDIR, SPO_CREAT|SPO_RDWR, dbrep) == 0 );+ t( sp_ctl(env, SPCMP, cmp, NULL) == 0 );+ void *db = sp_open(env);+ t( db != NULL );+ t( sp_begin(db) == 0 );+ uint32_t k = 1;+ t( sp_set(db, &k, sizeof(k), &k, sizeof(k)) == 0);+ k = 2;+ t( sp_set(db, &k, sizeof(k), &k, sizeof(k)) == 0);+ k = 3;+ t( sp_set(db, &k, sizeof(k), &k, sizeof(k)) == 0);+ t( sp_commit(db) == 0 );+ t( sp_destroy(db) == 0 );+ db = sp_open(env);+ t( db != NULL );+ size_t vsize = 0;+ void *vp = NULL;+ k = 1;+ t( sp_get(db, &k, sizeof(k), &vp, &vsize) == 1 );+ t( vsize == sizeof(k) );+ t( *(uint32_t*)vp == k);+ free(vp);+ k = 2;+ t( sp_get(db, &k, sizeof(k), &vp, &vsize) == 1 );+ t( vsize == sizeof(k) );+ t( *(uint32_t*)vp == k);+ free(vp);+ k = 3;+ t( sp_get(db, &k, sizeof(k), &vp, &vsize) == 1 );+ t( vsize == sizeof(k) );+ t( *(uint32_t*)vp == k);+ free(vp);+ t( sp_destroy(db) == 0 );+ t( sp_destroy(env) == 0 );+ rmrf(dbrep);+}++static void+cursor_begin(void) {+ void *env = sp_env();+ t( env != NULL );+ t( sp_ctl(env, SPDIR, SPO_CREAT|SPO_RDWR, dbrep) == 0 );+ t( sp_ctl(env, SPCMP, cmp, NULL) == 0 );+ void *db = sp_open(env);+ t( db != NULL );+ void *cur = sp_cursor(db, SPGTE, NULL, 0);+ t( cur != NULL );+ t( sp_begin(db) == -1 );+ t( sp_commit(db) == -1 );+ t( sp_destroy(cur) == 0 );+ t( sp_destroy(db) == 0 );+ t( sp_destroy(env) == 0 );+ rmrf(dbrep);+}++static void+cursor_commit(void) {+ void *env = sp_env();+ t( env != NULL );+ t( sp_ctl(env, SPDIR, SPO_CREAT|SPO_RDWR, dbrep) == 0 );+ t( sp_ctl(env, SPCMP, cmp, NULL) == 0 );+ void *db = sp_open(env);+ t( db != NULL );+ t( sp_begin(db) == 0 );+ void *cur = sp_cursor(db, SPGTE, NULL, 0);+ t( cur != NULL );+ t( sp_commit(db) == -1 );+ t( sp_destroy(cur) == 0 );+ t( sp_destroy(db) == 0 );+ t( sp_destroy(env) == 0 );+ rmrf(dbrep);+}++static void+cursor_closed_commit(void) {+ void *env = sp_env();+ t( env != NULL );+ t( sp_ctl(env, SPDIR, SPO_CREAT|SPO_RDWR, dbrep) == 0 );+ t( sp_ctl(env, SPCMP, cmp, NULL) == 0 );+ void *db = sp_open(env);+ t( db != NULL );+ t( sp_begin(db) == 0 );+ void *cur = sp_cursor(db, SPGTE, NULL, 0);+ t( cur != NULL );+ t( sp_destroy(cur) == 0 );+ t( sp_commit(db) == 0 );+ t( sp_destroy(db) == 0 );+ t( sp_destroy(env) == 0 );+ rmrf(dbrep);+}++static void+cursor_rollback(void) {+ void *env = sp_env();+ t( env != NULL );+ t( sp_ctl(env, SPDIR, SPO_CREAT|SPO_RDWR, dbrep) == 0 );+ t( sp_ctl(env, SPCMP, cmp, NULL) == 0 );+ void *db = sp_open(env);+ t( db != NULL );+ t( sp_begin(db) == 0 );+ void *cur = sp_cursor(db, SPGTE, NULL, 0);+ t( cur != NULL );+ t( sp_rollback(db) == -1 );+ t( sp_destroy(cur) == 0 );+ t( sp_destroy(db) == 0 );+ t( sp_destroy(env) == 0 );+ rmrf(dbrep);+}++static void+begin_fetch_gte_commit(void) {+ void *env = sp_env();+ t( env != NULL );+ t( sp_ctl(env, SPDIR, SPO_CREAT|SPO_RDWR, dbrep) == 0 );+ t( sp_ctl(env, SPCMP, cmp, NULL) == 0 );+ void *db = sp_open(env);+ t( db != NULL );+ t( sp_begin(db) == 0 );+ uint32_t k = 1, v = 2;+ t( sp_set(db, &k, sizeof(k), &v, sizeof(v)) == 0 );+ k = 2;+ t( sp_set(db, &k, sizeof(k), &v, sizeof(v)) == 0 );+ k = 3;+ t( sp_set(db, &k, sizeof(k), &v, sizeof(v)) == 0 );+ void *cur = sp_cursor(db, SPGTE, NULL, 0);+ t( cur != NULL );+ t( sp_fetch(cur) == 1 );+ t( *(uint32_t*)sp_key(cur) == 1 );+ t( sp_keysize(cur) == sizeof(k) );+ t( *(uint32_t*)sp_value(cur) == 2 );+ t( sp_valuesize(cur) == sizeof(v) );+ t( sp_fetch(cur) == 1 );+ t( *(uint32_t*)sp_key(cur) == 2 );+ t( sp_keysize(cur) == sizeof(k) );+ t( *(uint32_t*)sp_value(cur) == 2 );+ t( sp_valuesize(cur) == sizeof(v) );+ t( sp_fetch(cur) == 1 );+ t( *(uint32_t*)sp_key(cur) == 3 );+ t( sp_keysize(cur) == sizeof(k) );+ t( *(uint32_t*)sp_value(cur) == 2 );+ t( sp_valuesize(cur) == sizeof(v) );+ t( sp_fetch(cur) == 0 );+ t( sp_fetch(cur) == 0 );+ t( sp_destroy(cur) == 0 );+ t( sp_commit(db) == 0 );+ t( sp_destroy(db) == 0 );+ t( sp_destroy(env) == 0 );+ t( rmrf(dbrep) == 0 );+}++static void+begin_fetch_gt_commit(void) {+ void *env = sp_env();+ t( env != NULL );+ t( sp_ctl(env, SPDIR, SPO_CREAT|SPO_RDWR, dbrep) == 0 );+ t( sp_ctl(env, SPCMP, cmp, NULL) == 0 );+ void *db = sp_open(env);+ t( db != NULL );+ t( sp_begin(db) == 0 );+ uint32_t k = 1, v = 2;+ t( sp_set(db, &k, sizeof(k), &v, sizeof(v)) == 0 );+ k = 2;+ t( sp_set(db, &k, sizeof(k), &v, sizeof(v)) == 0 );+ k = 3;+ t( sp_set(db, &k, sizeof(k), &v, sizeof(v)) == 0 );+ void *cur = sp_cursor(db, SPGT, NULL, 0);+ t( cur != NULL );+ t( sp_fetch(cur) == 1 );+ t( *(uint32_t*)sp_key(cur) == 1 );+ t( sp_keysize(cur) == sizeof(k) );+ t( *(uint32_t*)sp_value(cur) == 2 );+ t( sp_valuesize(cur) == sizeof(v) );+ t( sp_fetch(cur) == 1 );+ t( *(uint32_t*)sp_key(cur) == 2 );+ t( sp_keysize(cur) == sizeof(k) );+ t( *(uint32_t*)sp_value(cur) == 2 );+ t( sp_valuesize(cur) == sizeof(v) );+ t( sp_fetch(cur) == 1 );+ t( *(uint32_t*)sp_key(cur) == 3 );+ t( sp_keysize(cur) == sizeof(k) );+ t( *(uint32_t*)sp_value(cur) == 2 );+ t( sp_valuesize(cur) == sizeof(v) );+ t( sp_fetch(cur) == 0 );+ t( sp_fetch(cur) == 0 );+ t( sp_destroy(cur) == 0 );+ t( sp_commit(db) == 0 );+ t( sp_destroy(db) == 0 );+ t( sp_destroy(env) == 0 );+ t( rmrf(dbrep) == 0 );+}++static void+begin_fetch_lte_commit(void) {+ void *env = sp_env();+ t( env != NULL );+ t( sp_ctl(env, SPDIR, SPO_CREAT|SPO_RDWR, dbrep) == 0 );+ t( sp_ctl(env, SPCMP, cmp, NULL) == 0 );+ void *db = sp_open(env);+ t( db != NULL );+ t( sp_begin(db) == 0 );+ uint32_t k = 1, v = 2;+ t( sp_set(db, &k, sizeof(k), &v, sizeof(v)) == 0 );+ k = 2;+ t( sp_set(db, &k, sizeof(k), &v, sizeof(v)) == 0 );+ k = 3;+ t( sp_set(db, &k, sizeof(k), &v, sizeof(v)) == 0 );+ void *cur = sp_cursor(db, SPLTE, NULL, 0);+ t( cur != NULL );+ t( sp_fetch(cur) == 1 );+ t( *(uint32_t*)sp_key(cur) == 3 );+ t( sp_keysize(cur) == sizeof(k) );+ t( *(uint32_t*)sp_value(cur) == 2 );+ t( sp_valuesize(cur) == sizeof(v) );+ t( sp_fetch(cur) == 1 );+ t( *(uint32_t*)sp_key(cur) == 2 );+ t( sp_keysize(cur) == sizeof(k) );+ t( *(uint32_t*)sp_value(cur) == 2 );+ t( sp_valuesize(cur) == sizeof(v) );+ t( sp_fetch(cur) == 1 );+ t( *(uint32_t*)sp_key(cur) == 1 );+ t( sp_keysize(cur) == sizeof(k) );+ t( *(uint32_t*)sp_value(cur) == 2 );+ t( sp_valuesize(cur) == sizeof(v) );+ t( sp_fetch(cur) == 0 );+ t( sp_fetch(cur) == 0 );+ t( sp_destroy(cur) == 0 );+ t( sp_commit(db) == 0 );+ t( sp_destroy(db) == 0 );+ t( sp_destroy(env) == 0 );+ t( rmrf(dbrep) == 0 );+}++static void+begin_fetch_lt_commit(void) {+ void *env = sp_env();+ t( env != NULL );+ t( sp_ctl(env, SPDIR, SPO_CREAT|SPO_RDWR, dbrep) == 0 );+ t( sp_ctl(env, SPCMP, cmp, NULL) == 0 );+ void *db = sp_open(env);+ t( db != NULL );+ t( sp_begin(db) == 0 );+ uint32_t k = 1, v = 2;+ t( sp_set(db, &k, sizeof(k), &v, sizeof(v)) == 0 );+ k = 2;+ t( sp_set(db, &k, sizeof(k), &v, sizeof(v)) == 0 );+ k = 3;+ t( sp_set(db, &k, sizeof(k), &v, sizeof(v)) == 0 );+ void *cur = sp_cursor(db, SPLT, NULL, 0);+ t( cur != NULL );+ t( sp_fetch(cur) == 1 );+ t( *(uint32_t*)sp_key(cur) == 3 );+ t( sp_keysize(cur) == sizeof(k) );+ t( *(uint32_t*)sp_value(cur) == 2 );+ t( sp_valuesize(cur) == sizeof(v) );+ t( sp_fetch(cur) == 1 );+ t( *(uint32_t*)sp_key(cur) == 2 );+ t( sp_keysize(cur) == sizeof(k) );+ t( *(uint32_t*)sp_value(cur) == 2 );+ t( sp_valuesize(cur) == sizeof(v) );+ t( sp_fetch(cur) == 1 );+ t( *(uint32_t*)sp_key(cur) == 1 );+ t( sp_keysize(cur) == sizeof(k) );+ t( *(uint32_t*)sp_value(cur) == 2 );+ t( sp_valuesize(cur) == sizeof(v) );+ t( sp_fetch(cur) == 0 );+ t( sp_fetch(cur) == 0 );+ t( sp_destroy(cur) == 0 );+ t( sp_commit(db) == 0 );+ t( sp_destroy(db) == 0 );+ t( sp_destroy(env) == 0 );+ t( rmrf(dbrep) == 0 );+}++static void+begin_fetch_kgte_commit(void) {+ void *env = sp_env();+ t( env != NULL );+ t( sp_ctl(env, SPDIR, SPO_CREAT|SPO_RDWR, dbrep) == 0 );+ t( sp_ctl(env, SPCMP, cmp, NULL) == 0 );+ void *db = sp_open(env);+ t( db != NULL );+ t( sp_begin(db) == 0 );+ uint32_t k = 1, v = 2;+ t( sp_set(db, &k, sizeof(k), &v, sizeof(v)) == 0 );+ k = 2;+ t( sp_set(db, &k, sizeof(k), &v, sizeof(v)) == 0 );+ k = 3;+ t( sp_set(db, &k, sizeof(k), &v, sizeof(v)) == 0 );++ k = 2;+ void *cur = sp_cursor(db, SPGTE, &k, sizeof(k));+ t( cur != NULL );+ t( sp_fetch(cur) == 1 );+ t( *(uint32_t*)sp_key(cur) == 2 );+ t( sp_keysize(cur) == sizeof(k) );+ t( *(uint32_t*)sp_value(cur) == 2 );+ t( sp_valuesize(cur) == sizeof(v) );+ t( sp_fetch(cur) == 1 );+ t( *(uint32_t*)sp_key(cur) == 3 );+ t( sp_keysize(cur) == sizeof(k) );+ t( *(uint32_t*)sp_value(cur) == 2 );+ t( sp_valuesize(cur) == sizeof(v) );+ t( sp_fetch(cur) == 0 );+ t( sp_fetch(cur) == 0 );+ t( sp_destroy(cur) == 0 );+ t( sp_commit(db) == 0 );+ t( sp_destroy(db) == 0 );+ t( sp_destroy(env) == 0 );+ t( rmrf(dbrep) == 0 );+}++static void+begin_fetch_kgt_commit(void) {+ void *env = sp_env();+ t( env != NULL );+ t( sp_ctl(env, SPDIR, SPO_CREAT|SPO_RDWR, dbrep) == 0 );+ t( sp_ctl(env, SPCMP, cmp, NULL) == 0 );+ void *db = sp_open(env);+ t( db != NULL );+ t( sp_begin(db) == 0 );+ uint32_t k = 1, v = 2;+ t( sp_set(db, &k, sizeof(k), &v, sizeof(v)) == 0 );+ k = 2;+ t( sp_set(db, &k, sizeof(k), &v, sizeof(v)) == 0 );+ k = 3;+ t( sp_set(db, &k, sizeof(k), &v, sizeof(v)) == 0 );+ k = 2;+ void *cur = sp_cursor(db, SPGT, &k, sizeof(k));+ t( cur != NULL );+ t( sp_fetch(cur) == 1 );+ t( *(uint32_t*)sp_key(cur) == 3 );+ t( sp_keysize(cur) == sizeof(k) );+ t( *(uint32_t*)sp_value(cur) == 2 );+ t( sp_valuesize(cur) == sizeof(v) );+ t( sp_fetch(cur) == 0 );+ t( sp_fetch(cur) == 0 );+ t( sp_destroy(cur) == 0 );+ t( sp_commit(db) == 0 );+ t( sp_destroy(db) == 0 );+ t( sp_destroy(env) == 0 );+ t( rmrf(dbrep) == 0 );+}++static void+begin_fetch_klte_commit(void) {+ void *env = sp_env();+ t( env != NULL );+ t( sp_ctl(env, SPDIR, SPO_CREAT|SPO_RDWR, dbrep) == 0 );+ t( sp_ctl(env, SPCMP, cmp, NULL) == 0 );+ void *db = sp_open(env);+ t( db != NULL );+ t( sp_begin(db) == 0 );+ uint32_t k = 1, v = 2;+ t( sp_set(db, &k, sizeof(k), &v, sizeof(v)) == 0 );+ k = 2;+ t( sp_set(db, &k, sizeof(k), &v, sizeof(v)) == 0 );+ k = 3;+ t( sp_set(db, &k, sizeof(k), &v, sizeof(v)) == 0 );+ k = 2;+ void *cur = sp_cursor(db, SPLTE, &k, sizeof(k) );+ t( cur != NULL );+ t( sp_fetch(cur) == 1 );+ t( *(uint32_t*)sp_key(cur) == 2 );+ t( sp_keysize(cur) == sizeof(k) );+ t( *(uint32_t*)sp_value(cur) == 2 );+ t( sp_valuesize(cur) == sizeof(v) );+ t( sp_fetch(cur) == 1 );+ t( *(uint32_t*)sp_key(cur) == 1 );+ t( sp_keysize(cur) == sizeof(k) );+ t( *(uint32_t*)sp_value(cur) == 2 );+ t( sp_valuesize(cur) == sizeof(v) );+ t( sp_fetch(cur) == 0 );+ t( sp_fetch(cur) == 0 );+ t( sp_destroy(cur) == 0 );+ t( sp_commit(db) == 0 );+ t( sp_destroy(db) == 0 );+ t( sp_destroy(env) == 0 );+ t( rmrf(dbrep) == 0 );+}++static void+begin_fetch_klt_commit(void) {+ void *env = sp_env();+ t( env != NULL );+ t( sp_ctl(env, SPDIR, SPO_CREAT|SPO_RDWR, dbrep) == 0 );+ t( sp_ctl(env, SPCMP, cmp, NULL) == 0 );+ void *db = sp_open(env);+ t( db != NULL );+ t( sp_begin(db) == 0 );+ uint32_t k = 1, v = 2;+ t( sp_set(db, &k, sizeof(k), &v, sizeof(v)) == 0 );+ k = 2;+ t( sp_set(db, &k, sizeof(k), &v, sizeof(v)) == 0 );+ k = 3;+ t( sp_set(db, &k, sizeof(k), &v, sizeof(v)) == 0 );+ k = 2;+ void *cur = sp_cursor(db, SPLT, &k, sizeof(k) );+ t( cur != NULL );+ t( sp_fetch(cur) == 1 );+ t( *(uint32_t*)sp_key(cur) == 1 );+ t( sp_keysize(cur) == sizeof(k) );+ t( *(uint32_t*)sp_value(cur) == 2 );+ t( sp_valuesize(cur) == sizeof(v) );+ t( sp_fetch(cur) == 0 );+ t( sp_fetch(cur) == 0 );+ t( sp_destroy(cur) == 0 );+ t( sp_commit(db) == 0 );+ t( sp_destroy(db) == 0 );+ t( sp_destroy(env) == 0 );+ t( rmrf(dbrep) == 0 );+}++static void+begin_fetch_after_end_commit(void) {+ void *env = sp_env();+ t( env != NULL );+ t( sp_ctl(env, SPDIR, SPO_CREAT|SPO_RDWR, dbrep) == 0 );+ t( sp_ctl(env, SPCMP, cmp, NULL) == 0 );+ void *db = sp_open(env);+ t( db != NULL );+ t( sp_begin(db) == 0 );+ uint32_t k = 1, v = 2;+ t( sp_set(db, &k, sizeof(k), &v, sizeof(v)) == 0 );+ k = 2;+ t( sp_set(db, &k, sizeof(k), &v, sizeof(v)) == 0 );+ k = 3;+ t( sp_set(db, &k, sizeof(k), &v, sizeof(v)) == 0 );+ void *cur = sp_cursor(db, SPGTE, NULL, 0);+ t( cur != NULL );+ t( sp_fetch(cur) == 1 );+ t( *(uint32_t*)sp_key(cur) == 1 );+ t( sp_keysize(cur) == sizeof(k) );+ t( *(uint32_t*)sp_value(cur) == 2 );+ t( sp_valuesize(cur) == sizeof(v) );+ t( sp_fetch(cur) == 1 );+ t( *(uint32_t*)sp_key(cur) == 2 );+ t( sp_keysize(cur) == sizeof(k) );+ t( *(uint32_t*)sp_value(cur) == 2 );+ t( sp_valuesize(cur) == sizeof(v) );+ t( sp_fetch(cur) == 1 );+ t( *(uint32_t*)sp_key(cur) == 3 );+ t( sp_keysize(cur) == sizeof(k) );+ t( *(uint32_t*)sp_value(cur) == 2 );+ t( sp_valuesize(cur) == sizeof(v) );+ t( sp_fetch(cur) == 0 );+ t( sp_keysize(cur) == 0 );+ t( sp_key(cur) == NULL );+ t( sp_valuesize(cur) == 0 );+ t( sp_value(cur) == NULL );+ t( sp_destroy(cur) == 0 );+ t( sp_commit(db) == 0 );+ t( sp_destroy(db) == 0 );+ t( sp_destroy(env) == 0 );+ t( rmrf(dbrep) == 0 );+}++static void+begin_fetch_gte_rollback(void) {+ void *env = sp_env();+ t( env != NULL );+ t( sp_ctl(env, SPDIR, SPO_CREAT|SPO_RDWR, dbrep) == 0 );+ t( sp_ctl(env, SPCMP, cmp, NULL) == 0 );+ void *db = sp_open(env);+ t( db != NULL );+ t( sp_begin(db) == 0 );+ uint32_t k = 1, v = 2;+ t( sp_set(db, &k, sizeof(k), &v, sizeof(v)) == 0 );+ k = 2;+ t( sp_set(db, &k, sizeof(k), &v, sizeof(v)) == 0 );+ k = 3;+ t( sp_set(db, &k, sizeof(k), &v, sizeof(v)) == 0 );+ void *cur = sp_cursor(db, SPGTE, NULL, 0);+ t( cur != NULL );+ t( sp_fetch(cur) == 1 );+ t( *(uint32_t*)sp_key(cur) == 1 );+ t( sp_keysize(cur) == sizeof(k) );+ t( *(uint32_t*)sp_value(cur) == 2 );+ t( sp_valuesize(cur) == sizeof(v) );+ t( sp_fetch(cur) == 1 );+ t( *(uint32_t*)sp_key(cur) == 2 );+ t( sp_keysize(cur) == sizeof(k) );+ t( *(uint32_t*)sp_value(cur) == 2 );+ t( sp_valuesize(cur) == sizeof(v) );+ t( sp_fetch(cur) == 1 );+ t( *(uint32_t*)sp_key(cur) == 3 );+ t( sp_keysize(cur) == sizeof(k) );+ t( *(uint32_t*)sp_value(cur) == 2 );+ t( sp_valuesize(cur) == sizeof(v) );+ t( sp_fetch(cur) == 0 );+ t( sp_fetch(cur) == 0 );+ t( sp_destroy(cur) == 0 );+ t( sp_rollback(db) == 0 );+ t( sp_destroy(db) == 0 );+ t( sp_destroy(env) == 0 );+ t( rmrf(dbrep) == 0 );+}++static void+begin_commit_fetch(void) {+ void *env = sp_env();+ t( env != NULL );+ t( sp_ctl(env, SPDIR, SPO_CREAT|SPO_RDWR, dbrep) == 0 );+ t( sp_ctl(env, SPCMP, cmp, NULL) == 0 );+ void *db = sp_open(env);+ t( db != NULL );+ t( sp_begin(db) == 0 );+ uint32_t k = 1, v = 2;+ t( sp_set(db, &k, sizeof(k), &v, sizeof(v)) == 0 );+ k = 2;+ t( sp_set(db, &k, sizeof(k), &v, sizeof(v)) == 0 );+ k = 3;+ t( sp_set(db, &k, sizeof(k), &v, sizeof(v)) == 0 );+ t( sp_commit(db) == 0 );+ void *cur = sp_cursor(db, SPGTE, NULL, 0);+ t( cur != NULL );+ t( sp_fetch(cur) == 1 );+ t( *(uint32_t*)sp_key(cur) == 1 );+ t( sp_keysize(cur) == sizeof(k) );+ t( *(uint32_t*)sp_value(cur) == 2 );+ t( sp_valuesize(cur) == sizeof(v) );+ t( sp_fetch(cur) == 1 );+ t( *(uint32_t*)sp_key(cur) == 2 );+ t( sp_keysize(cur) == sizeof(k) );+ t( *(uint32_t*)sp_value(cur) == 2 );+ t( sp_valuesize(cur) == sizeof(v) );+ t( sp_fetch(cur) == 1 );+ t( *(uint32_t*)sp_key(cur) == 3 );+ t( sp_keysize(cur) == sizeof(k) );+ t( *(uint32_t*)sp_value(cur) == 2 );+ t( sp_valuesize(cur) == sizeof(v) );+ t( sp_fetch(cur) == 0 );+ t( sp_fetch(cur) == 0 );+ t( sp_destroy(cur) == 0 );+ t( sp_destroy(db) == 0 );+ t( sp_destroy(env) == 0 );+ t( rmrf(dbrep) == 0 );+}++static void+begin_rollback_fetch(void) {+ void *env = sp_env();+ t( env != NULL );+ t( sp_ctl(env, SPDIR, SPO_CREAT|SPO_RDWR, dbrep) == 0 );+ t( sp_ctl(env, SPCMP, cmp, NULL) == 0 );+ void *db = sp_open(env);+ t( db != NULL );+ t( sp_begin(db) == 0 );+ uint32_t k = 1, v = 2;+ t( sp_set(db, &k, sizeof(k), &v, sizeof(v)) == 0 );+ k = 2;+ t( sp_set(db, &k, sizeof(k), &v, sizeof(v)) == 0 );+ k = 3;+ t( sp_set(db, &k, sizeof(k), &v, sizeof(v)) == 0 );+ t( sp_rollback(db) == 0 );+ void *cur = sp_cursor(db, SPGTE, NULL, 0);+ t( cur != NULL );+ t( sp_fetch(cur) == 0 );+ t( sp_fetch(cur) == 0 );+ t( sp_destroy(cur) == 0 );+ t( sp_destroy(db) == 0 );+ t( sp_destroy(env) == 0 );+ t( rmrf(dbrep) == 0 );+}++int+main(int argc, char *argv[])+{+ rmrf(dbrep);++ test(begin);+ test(begin_begin);+ test(begin);+ test(commit);+ test(rollback);+ test(begin_commit_empty);+ test(begin_rollback_empty);+ test(begin_commit);+ test(begin_commit_get);+ test(begin_get_commit_get);+ test(begin_rollback);+ test(begin_rollback_get);+ test(begin_rollback_consistent);+ test(begin_commit_2x);+ test(rollback_on_close);+ test(rollback_recover);+ test(commit_recover);+ test(cursor_begin);+ test(cursor_commit);+ test(cursor_closed_commit);+ test(cursor_rollback);+ test(begin_fetch_gte_commit);+ test(begin_fetch_gt_commit);+ test(begin_fetch_lte_commit);+ test(begin_fetch_lt_commit);+ test(begin_fetch_kgte_commit);+ test(begin_fetch_kgt_commit);+ test(begin_fetch_klte_commit);+ test(begin_fetch_klt_commit);+ test(begin_fetch_after_end_commit);+ test(begin_fetch_gte_rollback);+ test(begin_commit_fetch);+ test(begin_rollback_fetch);+ return 0;+}