Index: main.mk ================================================================== --- main.mk +++ main.mk @@ -76,19 +76,19 @@ LIBOBJ+= alter.o analyze.o attach.o auth.o \ build.o \ callback.o complete.o ctime.o date.o delete.o expr.o fault.o fkey.o \ $(FTS3_OBJ) \ func.o global.o hash.o \ - icu.o insert.o kvlsm.o kvmem.o legacy.o \ + icu.o insert.o kv.o kvlsm.o kvmem.o legacy.o \ lsm_ckpt.o lsm_file.o lsm_log.o lsm_main.o lsm_mem.o lsm_mutex.o \ lsm_shared.o lsm_str.o lsm_sorted.o lsm_tree.o \ lsm_unix.o lsm_varint.o \ main.o malloc.o math.o mem0.o mem1.o mem2.o mem3.o mem5.o \ mutex.o mutex_noop.o mutex_unix.o mutex_w32.o \ opcodes.o os.o \ parse.o pragma.o prepare.o printf.o \ - random.o resolve.o rowset.o rtree.o select.o status.o storage.o \ + random.o resolve.o rowset.o rtree.o select.o status.o \ tokenize.o trigger.o \ update.o util.o varint.o \ vdbe.o vdbeapi.o vdbeaux.o vdbecodec.o vdbecursor.o \ vdbemem.o vdbetrace.o \ walker.o where.o utf.o @@ -113,10 +113,11 @@ $(TOP)/src/global.c \ $(TOP)/src/hash.c \ $(TOP)/src/hash.h \ $(TOP)/src/hwtime.h \ $(TOP)/src/insert.c \ + $(TOP)/src/kv.c \ $(TOP)/src/kv.h \ $(TOP)/src/kvlsm.c \ $(TOP)/src/kvmem.c \ $(TOP)/src/legacy.c \ $(TOP)/src/lsm.h \ @@ -159,11 +160,10 @@ $(TOP)/src/shell.c \ $(TOP)/src/sqlite.h.in \ $(TOP)/src/sqliteInt.h \ $(TOP)/src/sqliteLimit.h \ $(TOP)/src/status.c \ - $(TOP)/src/storage.c \ $(TOP)/src/tclsqlite.c \ $(TOP)/src/tokenize.c \ $(TOP)/src/trigger.c \ $(TOP)/src/utf.c \ $(TOP)/src/update.c \ @@ -228,15 +228,15 @@ $(TOP)/test/test_misc1.c \ $(TOP)/test/test_config.c \ $(TOP)/test/test_func.c \ $(TOP)/test/test_hexio.c \ $(TOP)/test/test_lsm.c \ + $(TOP)/test/test_kv.c \ + $(TOP)/test/test_kv2.c \ $(TOP)/test/test_malloc.c \ $(TOP)/test/test_mem.c \ $(TOP)/test/test_mutex.c \ - $(TOP)/test/test_storage.c \ - $(TOP)/test/test_storage2.c \ $(TOP)/test/test_thread.c \ $(TOP)/test/test_wsd.c #TESTSRC += $(TOP)/ext/fts2/fts2_tokenizer.c #TESTSRC += $(TOP)/ext/fts3/fts3_tokenizer.c ADDED src/kv.c Index: src/kv.c ================================================================== --- /dev/null +++ src/kv.c @@ -0,0 +1,496 @@ +/* +** 2012 January 21 +** +** The author disclaims copyright to this source code. In place of +** a legal notice, here is a blessing: +** +** May you do good and not evil. +** May you find forgiveness for yourself and forgive others. +** May you share freely, never taking more than you give. +** +************************************************************************* +** +** General wrapper functions around the various KV storage engine +** implementations. It also implements tracing of calls to the KV +** engine and some higher-level ensembles of the low-level storage +** calls. +*/ +#include "sqliteInt.h" + +/* +** Names of error codes used for tracing. +*/ +static const char *kvErrName(int e){ + const char *zName; + switch( e ){ + case SQLITE4_OK: zName = "OK"; break; + case SQLITE4_ERROR: zName = "ERROR"; break; + case SQLITE4_INTERNAL: zName = "INTERNAL"; break; + case SQLITE4_PERM: zName = "PERM"; break; + case SQLITE4_ABORT: zName = "ABORT"; break; + case SQLITE4_BUSY: zName = "BUSY"; break; + case SQLITE4_LOCKED: zName = "LOCKED"; break; + case SQLITE4_NOMEM: zName = "NOMEM"; break; + case SQLITE4_READONLY: zName = "READONLY"; break; + case SQLITE4_INTERRUPT: zName = "INTERRUPT"; break; + case SQLITE4_IOERR: zName = "IOERR"; break; + case SQLITE4_CORRUPT: zName = "CORRUPT"; break; + case SQLITE4_NOTFOUND: zName = "NOTFOUND"; break; + case SQLITE4_FULL: zName = "FULL"; break; + case SQLITE4_CANTOPEN: zName = "CANTOPEN"; break; + case SQLITE4_PROTOCOL: zName = "PROTOCOL"; break; + case SQLITE4_EMPTY: zName = "EMPTY"; break; + case SQLITE4_SCHEMA: zName = "SCHEMA"; break; + case SQLITE4_TOOBIG: zName = "TOOBIG"; break; + case SQLITE4_CONSTRAINT: zName = "CONSTRAINT"; break; + case SQLITE4_MISMATCH: zName = "MISMATCH"; break; + case SQLITE4_MISUSE: zName = "MISUSE"; break; + case SQLITE4_NOLFS: zName = "NOLFS"; break; + case SQLITE4_AUTH: zName = "AUTH"; break; + case SQLITE4_FORMAT: zName = "FORMAT"; break; + case SQLITE4_RANGE: zName = "RANGE"; break; + case SQLITE4_NOTADB: zName = "NOTADB"; break; + case SQLITE4_ROW: zName = "ROW"; break; + case SQLITE4_DONE: zName = "DONE"; break; + case SQLITE4_INEXACT: zName = "INEXACT"; break; + default: zName = "???"; break; + } + return zName; +} + +/* +** Do any requested tracing +*/ +static void kvTrace(KVStore *p, const char *zFormat, ...){ + if( p->fTrace ){ + va_list ap; + char *z; + + va_start(ap, zFormat); + z = sqlite4_vmprintf(p->pEnv, zFormat, ap); + va_end(ap); + printf("%s.%s\n", p->zKVName, z); + fflush(stdout); + sqlite4_free(p->pEnv, z); + } +} + +/* +** Open a storage engine via URI +*/ +int sqlite4KVStoreOpen( + sqlite4 *db, /* The database connection doing the open */ + const char *zName, /* Symbolic name for this database */ + const char *zUri, /* URI for this database */ + KVStore **ppKVStore, /* Write the new KVStore object here */ + unsigned flags /* Option flags */ +){ + KVStore *pNew = 0; + int rc; + sqlite4_env *pEnv = &sqlite4DefaultEnv; /* OR db->pEnv */ + const char *zStorageName; + KVFactory *pMkr; + int (*xFactory)(sqlite4_env*,sqlite4_kvstore**,const char*,unsigned); + + if( (flags & SQLITE4_KVOPEN_TEMPORARY)!=0 || zUri==0 || zUri[0]==0 ){ + zStorageName = "temp"; + }else{ + zStorageName = sqlite4_uri_parameter(zName, "kv"); + if( zStorageName==0 ){ + if( memcmp(":memory:", zUri, 8)==0 ){ + zStorageName = "temp"; + }else{ + zStorageName = "main"; + } + } + } + *ppKVStore = 0; + sqlite4_mutex_enter(pEnv->pFactoryMutex); + for(pMkr=pEnv->pFactory; pMkr && strcmp(zStorageName,pMkr->zName); + pMkr=pMkr->pNext){} + xFactory = pMkr ? pMkr->xFactory : 0; + sqlite4_mutex_leave(pEnv->pFactoryMutex); + if( xFactory==0 ){ + return SQLITE4_ERROR; + } + rc = xFactory(pEnv, &pNew, zUri, flags); + *ppKVStore = pNew; + if( pNew ){ + sqlite4_randomness(pEnv, sizeof(pNew->kvId), &pNew->kvId); + sqlite4_snprintf(pNew->zKVName, sizeof(pNew->zKVName), + "%s", zName); + pNew->fTrace = (db->flags & SQLITE4_KvTrace)!=0; + kvTrace(pNew, "open(%s,%d,0x%04x)", zUri, pNew->kvId, flags); + } + return rc; +} + +/* Convert binary data to hex for display in trace messages */ +static void binToHex(char *zOut, int mxOut, const KVByteArray *a, KVSize n){ + int i; + if( n>mxOut/2-1 ) n = mxOut/2-1; + for(i=0; i>4)&0xf]; + zOut[i*2+1] = "0123456789abcdef"[a[i]&0xf]; + } + zOut[i*2] = 0; +} + +/* +** The following wrapper functions invoke the underlying methods of +** the storage object and add optional tracing. +*/ +int sqlite4KVStoreReplace( + KVStore *p, + const KVByteArray *pKey, KVSize nKey, + const KVByteArray *pData, KVSize nData +){ + if( p->fTrace ){ + char zKey[52], zData[52]; + binToHex(zKey, sizeof(zKey), pKey, nKey); + binToHex(zData, sizeof(zData), pData, nData); + kvTrace(p, "xReplace(%d,%s,%d,%s,%d)", + p->kvId, zKey, (int)nKey, zData, (int)nData); + } + return p->pStoreVfunc->xReplace(p,pKey,nKey,pData,nData); +} +int sqlite4KVStoreOpenCursor(KVStore *p, KVCursor **ppKVCursor){ + KVCursor *pCur; + int rc; + + rc = p->pStoreVfunc->xOpenCursor(p, &pCur); + *ppKVCursor = pCur; + if( pCur ){ + sqlite4_randomness(pCur->pEnv, sizeof(pCur->curId), &pCur->curId); + pCur->fTrace = p->fTrace; + pCur->pStore = p; + } + kvTrace(p, "xOpenCursor(%d,%d) -> %s", + p->kvId, pCur?pCur->curId:-1, kvErrName(rc)); + return rc; +} +int sqlite4KVCursorSeek( + KVCursor *p, + const KVByteArray *pKey, KVSize nKey, + int dir +){ + int rc; + assert( dir==0 || dir==(+1) || dir==(-1) || dir==(-2) ); + rc = p->pStoreVfunc->xSeek(p,pKey,nKey,dir); + if( p->fTrace ){ + char zKey[52]; + binToHex(zKey, sizeof(zKey), pKey, nKey); + kvTrace(p->pStore, "xSeek(%d,%s,%d,%d) -> %s", + p->curId, zKey, (int)nKey, dir, kvErrName(rc)); + } + return rc; +} +int sqlite4KVCursorNext(KVCursor *p){ + int rc; + rc = p->pStoreVfunc->xNext(p); + kvTrace(p->pStore, "xNext(%d) -> %s", p->curId, kvErrName(rc)); + return rc; +} +int sqlite4KVCursorPrev(KVCursor *p){ + int rc; + rc = p->pStoreVfunc->xPrev(p); + kvTrace(p->pStore, "xPrev(%d) -> %s", p->curId, kvErrName(rc)); + return rc; +} +int sqlite4KVCursorDelete(KVCursor *p){ + int rc; + rc = p->pStoreVfunc->xDelete(p); + kvTrace(p->pStore, "xDelete(%d) -> %s", p->curId, kvErrName(rc)); + return rc; +} +int sqlite4KVCursorReset(KVCursor *p){ + int rc; + rc = p->pStoreVfunc->xReset(p); + kvTrace(p->pStore, "xReset(%d) -> %s", p->curId, kvErrName(rc)); + return rc; +} +int sqlite4KVCursorKey(KVCursor *p, const KVByteArray **ppKey, KVSize *pnKey){ + int rc; + rc = p->pStoreVfunc->xKey(p, ppKey, pnKey); + if( p->fTrace ){ + if( rc==SQLITE4_OK ){ + char zKey[52]; + binToHex(zKey, sizeof(zKey), *ppKey, *pnKey); + kvTrace(p->pStore, "xKey(%d,%s,%d)", p->curId, zKey, (int)*pnKey); + }else{ + kvTrace(p->pStore, "xKey(%d,)", p->curId, rc); + } + } + return rc; +} +int sqlite4KVCursorData( + KVCursor *p, + KVSize ofst, + KVSize n, + const KVByteArray **ppData, + KVSize *pnData +){ + int rc; + rc = p->pStoreVfunc->xData(p, ofst, n, ppData, pnData); + if( p->fTrace ){ + if( rc==SQLITE4_OK ){ + char zData[52]; + binToHex(zData, sizeof(zData), *ppData, *pnData); + kvTrace(p->pStore, "xData(%d,%d,%d,%s,%d)", + p->curId, (int)ofst, (int)n, zData, (int)*pnData); + }else{ + kvTrace(p->pStore, "xData(%d,%d,%d,)", + p->curId, (int)ofst, (int)n, rc); + } + } + return rc; +} +int sqlite4KVCursorClose(KVCursor *p){ + int rc = SQLITE4_OK; + if( p ){ + KVStore *pStore = p->pStore; + int curId = p->curId; + rc = p->pStoreVfunc->xCloseCursor(p); + kvTrace(pStore, "xCloseCursor(%d) -> %s", curId, kvErrName(rc)); + } + return rc; +} +int sqlite4KVStoreBegin(KVStore *p, int iLevel){ + int rc; + rc = p->pStoreVfunc->xBegin(p, iLevel); + kvTrace(p, "xBegin(%d,%d) -> %s", p->kvId, iLevel, kvErrName(rc)); + assert( p->iTransLevel==iLevel || rc!=SQLITE4_OK ); + return rc; +} +int sqlite4KVStoreCommitPhaseOne(KVStore *p, int iLevel){ + int rc; + assert( iLevel>=0 ); + assert( iLevel<=p->iTransLevel ); + if( p->iTransLevel==iLevel ) return SQLITE4_OK; + if( p->pStoreVfunc->xCommitPhaseOne ){ + rc = p->pStoreVfunc->xCommitPhaseOne(p, iLevel); + }else{ + rc = SQLITE4_OK; + } + kvTrace(p, "xCommitPhaseOne(%d,%d) -> %s", p->kvId, iLevel, kvErrName(rc)); + assert( p->iTransLevel>iLevel ); + return rc; +} +int sqlite4KVStoreCommitPhaseTwo(KVStore *p, int iLevel){ + int rc; + assert( iLevel>=0 ); + assert( iLevel<=p->iTransLevel ); + if( p->iTransLevel==iLevel ) return SQLITE4_OK; + rc = p->pStoreVfunc->xCommitPhaseTwo(p, iLevel); + kvTrace(p, "xCommitPhaseTwo(%d,%d) -> %s", p->kvId, iLevel, kvErrName(rc)); + assert( p->iTransLevel==iLevel || rc!=SQLITE4_OK ); + return rc; +} +int sqlite4KVStoreCommit(KVStore *p, int iLevel){ + int rc; + rc = sqlite4KVStoreCommitPhaseOne(p, iLevel); + if( rc==SQLITE4_OK ) rc = sqlite4KVStoreCommitPhaseTwo(p, iLevel); + return rc; +} +int sqlite4KVStoreRollback(KVStore *p, int iLevel){ + int rc; + assert( iLevel>=0 ); + assert( iLevel<=p->iTransLevel ); + rc = p->pStoreVfunc->xRollback(p, iLevel); + kvTrace(p, "xRollback(%d,%d) -> %s", p->kvId, iLevel, kvErrName(rc)); + assert( p->iTransLevel==iLevel || rc!=SQLITE4_OK ); + return rc; +} +int sqlite4KVStoreRevert(KVStore *p, int iLevel){ + int rc; + assert( iLevel>0 ); + assert( iLevel<=p->iTransLevel ); + if( p->pStoreVfunc->xRevert ){ + rc = p->pStoreVfunc->xRevert(p, iLevel); + kvTrace(p, "xRevert(%d,%d) -> %s", p->kvId, iLevel, kvErrName(rc)); + }else{ + rc = sqlite4KVStoreRollback(p, iLevel-1); + if( rc==SQLITE4_OK ){ + rc = sqlite4KVStoreBegin(p, iLevel); + } + } + assert( p->iTransLevel==iLevel || rc!=SQLITE4_OK ); + return rc; +} +int sqlite4KVStoreClose(KVStore *p){ + int rc; + if( p ){ + kvTrace(p, "xClose(%d)", p->kvId); + rc = p->pStoreVfunc->xClose(p); + } + return rc; +} + +/* +** Key for the meta-data +*/ +static const KVByteArray metadataKey[] = { 0x00, 0x00 }; + +static void writeMetaArray(KVByteArray *aMeta, int iElem, u32 iVal){ + int i = sizeof(u32) * iElem; + aMeta[i+0] = (iVal>>24)&0xff; + aMeta[i+1] = (iVal>>16)&0xff; + aMeta[i+2] = (iVal>>8) &0xff; + aMeta[i+3] = (iVal>>0) &0xff; +} + +/* +** Read nMeta unsigned 32-bit integers of metadata beginning at iStart. +*/ +int sqlite4KVStoreGetMeta(KVStore *p, int iStart, int nMeta, unsigned int *a){ + KVCursor *pCur; + int rc; + int i, j; + KVSize nData; + const KVByteArray *aData; + + rc = sqlite4KVStoreOpenCursor(p, &pCur); + if( rc==SQLITE4_OK ){ + rc = sqlite4KVCursorSeek(pCur, metadataKey, sizeof(metadataKey), 0); + if( rc==SQLITE4_NOTFOUND ){ + rc = SQLITE4_OK; + nData = 0; + }else if( rc==SQLITE4_OK ){ + rc = sqlite4KVCursorData(pCur, 0, -1, &aData, &nData); + } + if( rc==SQLITE4_OK ){ + i = 0; + j = iStart*4; + while( i=0 ){ + for(i=0; i<16 && i>4]; + zOut[i*3+1] = base16[v&0xf]; + zOut[16*3+3+i] = (v>=0x20 && v<=0x7e) ? v : '.'; + } + while( i<16 ){ + zOut[i*3] = ' '; + zOut[i*3+1] = ' '; + zOut[16*3+3+i] = ' '; + i++; + } + sqlite4DebugPrintf("%.3s %s\n", zPrefix, zOut); + n -= 16; + if( n<=0 ) break; + a += 16; + zPrefix = " "; + } +} + +/* +** Dump the entire content of a key-value database +*/ +void sqlite4KVStoreDump(KVStore *pStore){ + int rc; + int nRow = 0; + KVCursor *pCur; + KVSize nKey, nData; + KVByteArray const *aKey; + KVByteArray const *aData; + static const KVByteArray aProbe[] = { 0x00 }; + + rc = sqlite4KVStoreOpenCursor(pStore, &pCur); + if( rc==SQLITE4_OK ){ + rc = sqlite4KVCursorSeek(pCur, aProbe, 1, +1); + while( rc!=SQLITE4_NOTFOUND ){ + rc = sqlite4KVCursorKey(pCur, &aKey, &nKey); + if( rc ) break; + if( nRow>0 ) sqlite4DebugPrintf("\n"); + nRow++; + outputBinary(aKey, nKey, "K: "); + rc = sqlite4KVCursorData(pCur, 0, -1, &aData, &nData); + if( rc ) break; + outputBinary(aData, nData, "V: "); + rc = sqlite4KVCursorNext(pCur); + } + sqlite4KVCursorClose(pCur); + } +} +#endif /* SQLITE4_DEBUG */ DELETED src/storage.c Index: src/storage.c ================================================================== --- src/storage.c +++ /dev/null @@ -1,496 +0,0 @@ -/* -** 2012 January 21 -** -** The author disclaims copyright to this source code. In place of -** a legal notice, here is a blessing: -** -** May you do good and not evil. -** May you find forgiveness for yourself and forgive others. -** May you share freely, never taking more than you give. -** -************************************************************************* -** -** General wrapper functions around the various KV storage engine -** implementations. It also implements tracing of calls to the KV -** engine and some higher-level ensembles of the low-level storage -** calls. -*/ -#include "sqliteInt.h" - -/* -** Names of error codes used for tracing. -*/ -static const char *kvErrName(int e){ - const char *zName; - switch( e ){ - case SQLITE4_OK: zName = "OK"; break; - case SQLITE4_ERROR: zName = "ERROR"; break; - case SQLITE4_INTERNAL: zName = "INTERNAL"; break; - case SQLITE4_PERM: zName = "PERM"; break; - case SQLITE4_ABORT: zName = "ABORT"; break; - case SQLITE4_BUSY: zName = "BUSY"; break; - case SQLITE4_LOCKED: zName = "LOCKED"; break; - case SQLITE4_NOMEM: zName = "NOMEM"; break; - case SQLITE4_READONLY: zName = "READONLY"; break; - case SQLITE4_INTERRUPT: zName = "INTERRUPT"; break; - case SQLITE4_IOERR: zName = "IOERR"; break; - case SQLITE4_CORRUPT: zName = "CORRUPT"; break; - case SQLITE4_NOTFOUND: zName = "NOTFOUND"; break; - case SQLITE4_FULL: zName = "FULL"; break; - case SQLITE4_CANTOPEN: zName = "CANTOPEN"; break; - case SQLITE4_PROTOCOL: zName = "PROTOCOL"; break; - case SQLITE4_EMPTY: zName = "EMPTY"; break; - case SQLITE4_SCHEMA: zName = "SCHEMA"; break; - case SQLITE4_TOOBIG: zName = "TOOBIG"; break; - case SQLITE4_CONSTRAINT: zName = "CONSTRAINT"; break; - case SQLITE4_MISMATCH: zName = "MISMATCH"; break; - case SQLITE4_MISUSE: zName = "MISUSE"; break; - case SQLITE4_NOLFS: zName = "NOLFS"; break; - case SQLITE4_AUTH: zName = "AUTH"; break; - case SQLITE4_FORMAT: zName = "FORMAT"; break; - case SQLITE4_RANGE: zName = "RANGE"; break; - case SQLITE4_NOTADB: zName = "NOTADB"; break; - case SQLITE4_ROW: zName = "ROW"; break; - case SQLITE4_DONE: zName = "DONE"; break; - case SQLITE4_INEXACT: zName = "INEXACT"; break; - default: zName = "???"; break; - } - return zName; -} - -/* -** Do any requested tracing -*/ -static void kvTrace(KVStore *p, const char *zFormat, ...){ - if( p->fTrace ){ - va_list ap; - char *z; - - va_start(ap, zFormat); - z = sqlite4_vmprintf(p->pEnv, zFormat, ap); - va_end(ap); - printf("%s.%s\n", p->zKVName, z); - fflush(stdout); - sqlite4_free(p->pEnv, z); - } -} - -/* -** Open a storage engine via URI -*/ -int sqlite4KVStoreOpen( - sqlite4 *db, /* The database connection doing the open */ - const char *zName, /* Symbolic name for this database */ - const char *zUri, /* URI for this database */ - KVStore **ppKVStore, /* Write the new KVStore object here */ - unsigned flags /* Option flags */ -){ - KVStore *pNew = 0; - int rc; - sqlite4_env *pEnv = &sqlite4DefaultEnv; /* OR db->pEnv */ - const char *zStorageName; - KVFactory *pMkr; - int (*xFactory)(sqlite4_env*,sqlite4_kvstore**,const char*,unsigned); - - if( (flags & SQLITE4_KVOPEN_TEMPORARY)!=0 || zUri==0 || zUri[0]==0 ){ - zStorageName = "temp"; - }else{ - zStorageName = sqlite4_uri_parameter(zName, "kv"); - if( zStorageName==0 ){ - if( memcmp(":memory:", zUri, 8)==0 ){ - zStorageName = "temp"; - }else{ - zStorageName = "main"; - } - } - } - *ppKVStore = 0; - sqlite4_mutex_enter(pEnv->pFactoryMutex); - for(pMkr=pEnv->pFactory; pMkr && strcmp(zStorageName,pMkr->zName); - pMkr=pMkr->pNext){} - xFactory = pMkr ? pMkr->xFactory : 0; - sqlite4_mutex_leave(pEnv->pFactoryMutex); - if( xFactory==0 ){ - return SQLITE4_ERROR; - } - rc = xFactory(pEnv, &pNew, zUri, flags); - *ppKVStore = pNew; - if( pNew ){ - sqlite4_randomness(pEnv, sizeof(pNew->kvId), &pNew->kvId); - sqlite4_snprintf(pNew->zKVName, sizeof(pNew->zKVName), - "%s", zName); - pNew->fTrace = (db->flags & SQLITE4_KvTrace)!=0; - kvTrace(pNew, "open(%s,%d,0x%04x)", zUri, pNew->kvId, flags); - } - return rc; -} - -/* Convert binary data to hex for display in trace messages */ -static void binToHex(char *zOut, int mxOut, const KVByteArray *a, KVSize n){ - int i; - if( n>mxOut/2-1 ) n = mxOut/2-1; - for(i=0; i>4)&0xf]; - zOut[i*2+1] = "0123456789abcdef"[a[i]&0xf]; - } - zOut[i*2] = 0; -} - -/* -** The following wrapper functions invoke the underlying methods of -** the storage object and add optional tracing. -*/ -int sqlite4KVStoreReplace( - KVStore *p, - const KVByteArray *pKey, KVSize nKey, - const KVByteArray *pData, KVSize nData -){ - if( p->fTrace ){ - char zKey[52], zData[52]; - binToHex(zKey, sizeof(zKey), pKey, nKey); - binToHex(zData, sizeof(zData), pData, nData); - kvTrace(p, "xReplace(%d,%s,%d,%s,%d)", - p->kvId, zKey, (int)nKey, zData, (int)nData); - } - return p->pStoreVfunc->xReplace(p,pKey,nKey,pData,nData); -} -int sqlite4KVStoreOpenCursor(KVStore *p, KVCursor **ppKVCursor){ - KVCursor *pCur; - int rc; - - rc = p->pStoreVfunc->xOpenCursor(p, &pCur); - *ppKVCursor = pCur; - if( pCur ){ - sqlite4_randomness(pCur->pEnv, sizeof(pCur->curId), &pCur->curId); - pCur->fTrace = p->fTrace; - pCur->pStore = p; - } - kvTrace(p, "xOpenCursor(%d,%d) -> %s", - p->kvId, pCur?pCur->curId:-1, kvErrName(rc)); - return rc; -} -int sqlite4KVCursorSeek( - KVCursor *p, - const KVByteArray *pKey, KVSize nKey, - int dir -){ - int rc; - assert( dir==0 || dir==(+1) || dir==(-1) || dir==(-2) ); - rc = p->pStoreVfunc->xSeek(p,pKey,nKey,dir); - if( p->fTrace ){ - char zKey[52]; - binToHex(zKey, sizeof(zKey), pKey, nKey); - kvTrace(p->pStore, "xSeek(%d,%s,%d,%d) -> %s", - p->curId, zKey, (int)nKey, dir, kvErrName(rc)); - } - return rc; -} -int sqlite4KVCursorNext(KVCursor *p){ - int rc; - rc = p->pStoreVfunc->xNext(p); - kvTrace(p->pStore, "xNext(%d) -> %s", p->curId, kvErrName(rc)); - return rc; -} -int sqlite4KVCursorPrev(KVCursor *p){ - int rc; - rc = p->pStoreVfunc->xPrev(p); - kvTrace(p->pStore, "xPrev(%d) -> %s", p->curId, kvErrName(rc)); - return rc; -} -int sqlite4KVCursorDelete(KVCursor *p){ - int rc; - rc = p->pStoreVfunc->xDelete(p); - kvTrace(p->pStore, "xDelete(%d) -> %s", p->curId, kvErrName(rc)); - return rc; -} -int sqlite4KVCursorReset(KVCursor *p){ - int rc; - rc = p->pStoreVfunc->xReset(p); - kvTrace(p->pStore, "xReset(%d) -> %s", p->curId, kvErrName(rc)); - return rc; -} -int sqlite4KVCursorKey(KVCursor *p, const KVByteArray **ppKey, KVSize *pnKey){ - int rc; - rc = p->pStoreVfunc->xKey(p, ppKey, pnKey); - if( p->fTrace ){ - if( rc==SQLITE4_OK ){ - char zKey[52]; - binToHex(zKey, sizeof(zKey), *ppKey, *pnKey); - kvTrace(p->pStore, "xKey(%d,%s,%d)", p->curId, zKey, (int)*pnKey); - }else{ - kvTrace(p->pStore, "xKey(%d,)", p->curId, rc); - } - } - return rc; -} -int sqlite4KVCursorData( - KVCursor *p, - KVSize ofst, - KVSize n, - const KVByteArray **ppData, - KVSize *pnData -){ - int rc; - rc = p->pStoreVfunc->xData(p, ofst, n, ppData, pnData); - if( p->fTrace ){ - if( rc==SQLITE4_OK ){ - char zData[52]; - binToHex(zData, sizeof(zData), *ppData, *pnData); - kvTrace(p->pStore, "xData(%d,%d,%d,%s,%d)", - p->curId, (int)ofst, (int)n, zData, (int)*pnData); - }else{ - kvTrace(p->pStore, "xData(%d,%d,%d,)", - p->curId, (int)ofst, (int)n, rc); - } - } - return rc; -} -int sqlite4KVCursorClose(KVCursor *p){ - int rc = SQLITE4_OK; - if( p ){ - KVStore *pStore = p->pStore; - int curId = p->curId; - rc = p->pStoreVfunc->xCloseCursor(p); - kvTrace(pStore, "xCloseCursor(%d) -> %s", curId, kvErrName(rc)); - } - return rc; -} -int sqlite4KVStoreBegin(KVStore *p, int iLevel){ - int rc; - rc = p->pStoreVfunc->xBegin(p, iLevel); - kvTrace(p, "xBegin(%d,%d) -> %s", p->kvId, iLevel, kvErrName(rc)); - assert( p->iTransLevel==iLevel || rc!=SQLITE4_OK ); - return rc; -} -int sqlite4KVStoreCommitPhaseOne(KVStore *p, int iLevel){ - int rc; - assert( iLevel>=0 ); - assert( iLevel<=p->iTransLevel ); - if( p->iTransLevel==iLevel ) return SQLITE4_OK; - if( p->pStoreVfunc->xCommitPhaseOne ){ - rc = p->pStoreVfunc->xCommitPhaseOne(p, iLevel); - }else{ - rc = SQLITE4_OK; - } - kvTrace(p, "xCommitPhaseOne(%d,%d) -> %s", p->kvId, iLevel, kvErrName(rc)); - assert( p->iTransLevel>iLevel ); - return rc; -} -int sqlite4KVStoreCommitPhaseTwo(KVStore *p, int iLevel){ - int rc; - assert( iLevel>=0 ); - assert( iLevel<=p->iTransLevel ); - if( p->iTransLevel==iLevel ) return SQLITE4_OK; - rc = p->pStoreVfunc->xCommitPhaseTwo(p, iLevel); - kvTrace(p, "xCommitPhaseTwo(%d,%d) -> %s", p->kvId, iLevel, kvErrName(rc)); - assert( p->iTransLevel==iLevel || rc!=SQLITE4_OK ); - return rc; -} -int sqlite4KVStoreCommit(KVStore *p, int iLevel){ - int rc; - rc = sqlite4KVStoreCommitPhaseOne(p, iLevel); - if( rc==SQLITE4_OK ) rc = sqlite4KVStoreCommitPhaseTwo(p, iLevel); - return rc; -} -int sqlite4KVStoreRollback(KVStore *p, int iLevel){ - int rc; - assert( iLevel>=0 ); - assert( iLevel<=p->iTransLevel ); - rc = p->pStoreVfunc->xRollback(p, iLevel); - kvTrace(p, "xRollback(%d,%d) -> %s", p->kvId, iLevel, kvErrName(rc)); - assert( p->iTransLevel==iLevel || rc!=SQLITE4_OK ); - return rc; -} -int sqlite4KVStoreRevert(KVStore *p, int iLevel){ - int rc; - assert( iLevel>0 ); - assert( iLevel<=p->iTransLevel ); - if( p->pStoreVfunc->xRevert ){ - rc = p->pStoreVfunc->xRevert(p, iLevel); - kvTrace(p, "xRevert(%d,%d) -> %s", p->kvId, iLevel, kvErrName(rc)); - }else{ - rc = sqlite4KVStoreRollback(p, iLevel-1); - if( rc==SQLITE4_OK ){ - rc = sqlite4KVStoreBegin(p, iLevel); - } - } - assert( p->iTransLevel==iLevel || rc!=SQLITE4_OK ); - return rc; -} -int sqlite4KVStoreClose(KVStore *p){ - int rc; - if( p ){ - kvTrace(p, "xClose(%d)", p->kvId); - rc = p->pStoreVfunc->xClose(p); - } - return rc; -} - -/* -** Key for the meta-data -*/ -static const KVByteArray metadataKey[] = { 0x00, 0x00 }; - -static void writeMetaArray(KVByteArray *aMeta, int iElem, u32 iVal){ - int i = sizeof(u32) * iElem; - aMeta[i+0] = (iVal>>24)&0xff; - aMeta[i+1] = (iVal>>16)&0xff; - aMeta[i+2] = (iVal>>8) &0xff; - aMeta[i+3] = (iVal>>0) &0xff; -} - -/* -** Read nMeta unsigned 32-bit integers of metadata beginning at iStart. -*/ -int sqlite4KVStoreGetMeta(KVStore *p, int iStart, int nMeta, unsigned int *a){ - KVCursor *pCur; - int rc; - int i, j; - KVSize nData; - const KVByteArray *aData; - - rc = sqlite4KVStoreOpenCursor(p, &pCur); - if( rc==SQLITE4_OK ){ - rc = sqlite4KVCursorSeek(pCur, metadataKey, sizeof(metadataKey), 0); - if( rc==SQLITE4_NOTFOUND ){ - rc = SQLITE4_OK; - nData = 0; - }else if( rc==SQLITE4_OK ){ - rc = sqlite4KVCursorData(pCur, 0, -1, &aData, &nData); - } - if( rc==SQLITE4_OK ){ - i = 0; - j = iStart*4; - while( i=0 ){ - for(i=0; i<16 && i>4]; - zOut[i*3+1] = base16[v&0xf]; - zOut[16*3+3+i] = (v>=0x20 && v<=0x7e) ? v : '.'; - } - while( i<16 ){ - zOut[i*3] = ' '; - zOut[i*3+1] = ' '; - zOut[16*3+3+i] = ' '; - i++; - } - sqlite4DebugPrintf("%.3s %s\n", zPrefix, zOut); - n -= 16; - if( n<=0 ) break; - a += 16; - zPrefix = " "; - } -} - -/* -** Dump the entire content of a key-value database -*/ -void sqlite4KVStoreDump(KVStore *pStore){ - int rc; - int nRow = 0; - KVCursor *pCur; - KVSize nKey, nData; - KVByteArray const *aKey; - KVByteArray const *aData; - static const KVByteArray aProbe[] = { 0x00 }; - - rc = sqlite4KVStoreOpenCursor(pStore, &pCur); - if( rc==SQLITE4_OK ){ - rc = sqlite4KVCursorSeek(pCur, aProbe, 1, +1); - while( rc!=SQLITE4_NOTFOUND ){ - rc = sqlite4KVCursorKey(pCur, &aKey, &nKey); - if( rc ) break; - if( nRow>0 ) sqlite4DebugPrintf("\n"); - nRow++; - outputBinary(aKey, nKey, "K: "); - rc = sqlite4KVCursorData(pCur, 0, -1, &aData, &nData); - if( rc ) break; - outputBinary(aData, nData, "V: "); - rc = sqlite4KVCursorNext(pCur); - } - sqlite4KVCursorClose(pCur); - } -} -#endif /* SQLITE4_DEBUG */ ADDED test/test_kv.c Index: test/test_kv.c ================================================================== --- /dev/null +++ test/test_kv.c @@ -0,0 +1,495 @@ +/* +** 2011 January 21 +** +** The author disclaims copyright to this source code. In place of +** a legal notice, here is a blessing: +** +** May you do good and not evil. +** May you find forgiveness for yourself and forgive others. +** May you share freely, never taking more than you give. +** +************************************************************************* +** +** Code for testing the storage subsystem using the Storage interface. +*/ +#include "sqliteInt.h" + +/* Defined in test1.c */ +extern void *sqlite4TestTextToPtr(const char*); + +/* Defined in test_hexio.c */ +extern void sqlite4TestBinToHex(unsigned char*,int); +extern int sqlite4TestHexToBin(const unsigned char *in,int,unsigned char *out); + +/* Set the TCL result to an integer. +*/ +static void storageSetTclErrorName(Tcl_Interp *interp, int rc){ + extern const char *sqlite4TestErrorName(int); + Tcl_SetObjResult(interp, Tcl_NewStringObj(sqlite4TestErrorName(rc), -1)); +} + +/* +** TCLCMD: storage_open URI ?FLAGS? +** +** Return a string that identifies the new storage object. +*/ +static int test_storage_open( + void * clientData, + Tcl_Interp *interp, + int objc, + Tcl_Obj *CONST objv[] +){ + KVStore *pNew = 0; + int rc; + int flags = 0; + sqlite4 db; + char zRes[50]; + if( objc!=2 && objc!=3 ){ + Tcl_WrongNumArgs(interp, 1, objv, "URI ?FLAGS?"); + return TCL_ERROR; + } + if( objc==3 && Tcl_GetIntFromObj(interp, objv[2], &flags) ){ + return TCL_ERROR; + } + memset(&db, 0, sizeof(db)); + rc = sqlite4KVStoreOpen(&db, "test", Tcl_GetString(objv[1]), &pNew, flags); + if( rc ){ + sqlite4KVStoreClose(pNew); + storageSetTclErrorName(interp, rc); + return TCL_ERROR; + } + sqlite4_snprintf(zRes,sizeof(zRes), "%p", pNew); + Tcl_SetObjResult(interp, Tcl_NewStringObj(zRes,-1)); + return TCL_OK; +} + +/* +** TCLCMD: storage_close STORAGE +** +** Close a storage object. +*/ +static int test_storage_close( + void * clientData, + Tcl_Interp *interp, + int objc, + Tcl_Obj *CONST objv[] +){ + KVStore *pOld = 0; + int rc; + if( objc!=2 ){ + Tcl_WrongNumArgs(interp, 2, objv, "STORAGE"); + return TCL_ERROR; + } + pOld = sqlite4TestTextToPtr(Tcl_GetString(objv[1])); + rc = sqlite4KVStoreClose(pOld); + if( rc ){ + storageSetTclErrorName(interp, rc); + return TCL_ERROR; + } + return TCL_OK; +} + +/* +** TCLCMD: storage_open_cursor STORAGE +** +** Return a string that identifies the new storage cursor +*/ +static int test_storage_open_cursor( + void * clientData, + Tcl_Interp *interp, + int objc, + Tcl_Obj *CONST objv[] +){ + KVCursor *pNew = 0; + KVStore *pStore = 0; + int rc; + char zRes[50]; + if( objc!=2 ){ + Tcl_WrongNumArgs(interp, 2, objv, "STORAGE"); + return TCL_ERROR; + } + pStore = sqlite4TestTextToPtr(Tcl_GetString(objv[1])); + rc = sqlite4KVStoreOpenCursor(pStore, &pNew); + if( rc ){ + sqlite4KVCursorClose(pNew); + storageSetTclErrorName(interp, rc); + return TCL_ERROR; + } + sqlite4_snprintf(zRes,sizeof(zRes), "%p", pNew); + Tcl_SetObjResult(interp, Tcl_NewStringObj(zRes,-1)); + return TCL_OK; +} + +/* +** TCLCMD: storage_close_cursor CURSOR +** +** Close a cursor object. +*/ +static int test_storage_close_cursor( + void * clientData, + Tcl_Interp *interp, + int objc, + Tcl_Obj *CONST objv[] +){ + KVCursor *pOld = 0; + int rc; + if( objc!=2 ){ + Tcl_WrongNumArgs(interp, 2, objv, "CURSOR"); + return TCL_ERROR; + } + pOld = sqlite4TestTextToPtr(Tcl_GetString(objv[1])); + rc = sqlite4KVCursorClose(pOld); + if( rc ){ + storageSetTclErrorName(interp, rc); + return TCL_ERROR; + } + return TCL_OK; +} + +/* Decode hex into a binary key */ +static void sqlite4DecodeHex(Tcl_Obj *pObj, unsigned char *a, int *pN){ + const unsigned char *pIn; + int nIn; + pIn = (const unsigned char*)Tcl_GetStringFromObj(pObj, &nIn); + *pN = sqlite4TestHexToBin(pIn, nIn, a); +} + +/* +** TCLCMD: storage_replace STORAGE KEY VALUE +** +** Insert content into a KV storage object. KEY and VALUE are hex. +*/ +static int test_storage_replace( + void * clientData, + Tcl_Interp *interp, + int objc, + Tcl_Obj *CONST objv[] +){ + KVStore *p = 0; + int rc; + int nKey, nData; + unsigned char zKey[200]; + unsigned char zData[200]; + if( objc!=4 ){ + Tcl_WrongNumArgs(interp, 2, objv, "STORAGE KEY VALUE"); + return TCL_ERROR; + } + p = sqlite4TestTextToPtr(Tcl_GetString(objv[1])); + sqlite4DecodeHex(objv[2], zKey, &nKey); + sqlite4DecodeHex(objv[3], zData, &nData); + rc = sqlite4KVStoreReplace(p, zKey, nKey, zData, nData); + if( rc ){ + storageSetTclErrorName(interp, rc); + return TCL_ERROR; + } + return TCL_OK; +} + +/* +** TCLCMD: storage_begin STORAGE LEVEL +** +** Increase the transaction level +*/ +static int test_storage_begin( + void * clientData, + Tcl_Interp *interp, + int objc, + Tcl_Obj *CONST objv[] +){ + KVStore *p = 0; + int rc; + int iLevel; + if( objc!=3 ){ + Tcl_WrongNumArgs(interp, 2, objv, "STORAGE LEVEL"); + return TCL_ERROR; + } + p = sqlite4TestTextToPtr(Tcl_GetString(objv[1])); + if( Tcl_GetIntFromObj(interp, objv[2], &iLevel) ) return TCL_ERROR; + rc = sqlite4KVStoreBegin(p, iLevel); + if( rc ){ + storageSetTclErrorName(interp, rc); + return TCL_ERROR; + } + return TCL_OK; +} + +/* +** TCLCMD: storage_commit STORAGE LEVEL +** +** Increase the transaction level +*/ +static int test_storage_commit( + void * clientData, + Tcl_Interp *interp, + int objc, + Tcl_Obj *CONST objv[] +){ + KVStore *p = 0; + int rc; + int iLevel; + if( objc!=3 ){ + Tcl_WrongNumArgs(interp, 2, objv, "STORAGE LEVEL"); + return TCL_ERROR; + } + p = sqlite4TestTextToPtr(Tcl_GetString(objv[1])); + if( Tcl_GetIntFromObj(interp, objv[2], &iLevel) ) return TCL_ERROR; + rc = sqlite4KVStoreCommitPhaseOne(p, iLevel); + if( rc ){ + storageSetTclErrorName(interp, rc); + return TCL_ERROR; + } + rc = sqlite4KVStoreCommitPhaseTwo(p, iLevel); + if( rc ){ + storageSetTclErrorName(interp, rc); + return TCL_ERROR; + } + return TCL_OK; +} + +/* +** TCLCMD: storage_rollback STORAGE LEVEL +** +** Increase the transaction level +*/ +static int test_storage_rollback( + void * clientData, + Tcl_Interp *interp, + int objc, + Tcl_Obj *CONST objv[] +){ + KVStore *p = 0; + int rc; + int iLevel; + if( objc!=3 ){ + Tcl_WrongNumArgs(interp, 2, objv, "STORAGE LEVEL"); + return TCL_ERROR; + } + p = sqlite4TestTextToPtr(Tcl_GetString(objv[1])); + if( Tcl_GetIntFromObj(interp, objv[2], &iLevel) ) return TCL_ERROR; + rc = sqlite4KVStoreRollback(p, iLevel); + if( rc ){ + storageSetTclErrorName(interp, rc); + return TCL_ERROR; + } + return TCL_OK; +} + +/* +** TCLCMD: storage_seek CURSOR KEY DIRECTION +** +** Move a cursor object +*/ +static int test_storage_seek( + void * clientData, + Tcl_Interp *interp, + int objc, + Tcl_Obj *CONST objv[] +){ + KVCursor *p = 0; + int rc; + int nKey, dir; + unsigned char aKey[100]; + if( objc!=4 ){ + Tcl_WrongNumArgs(interp, 4, objv, "CURSOR KEY DIRECTION"); + return TCL_ERROR; + } + p = sqlite4TestTextToPtr(Tcl_GetString(objv[1])); + sqlite4DecodeHex(objv[2], aKey, &nKey); + if( Tcl_GetIntFromObj(interp, objv[3], &dir) ) return TCL_ERROR; + rc = sqlite4KVCursorSeek(p, aKey, nKey, dir); + storageSetTclErrorName(interp, rc); + return TCL_OK; +} + +/* +** TCLCMD: storage_next CURSOR +** +** Move the cursor to the next entry +*/ +static int test_storage_next( + void * clientData, + Tcl_Interp *interp, + int objc, + Tcl_Obj *CONST objv[] +){ + KVCursor *p = 0; + int rc; + if( objc!=2 ){ + Tcl_WrongNumArgs(interp, 2, objv, "CURSOR"); + return TCL_ERROR; + } + p = sqlite4TestTextToPtr(Tcl_GetString(objv[1])); + rc = sqlite4KVCursorNext(p); + storageSetTclErrorName(interp, rc); + return TCL_OK; +} + +/* +** TCLCMD: storage_prev CURSOR +** +** Move the cursor to the previous entry +*/ +static int test_storage_prev( + void * clientData, + Tcl_Interp *interp, + int objc, + Tcl_Obj *CONST objv[] +){ + KVCursor *p = 0; + int rc; + if( objc!=2 ){ + Tcl_WrongNumArgs(interp, 2, objv, "CURSOR"); + return TCL_ERROR; + } + p = sqlite4TestTextToPtr(Tcl_GetString(objv[1])); + rc = sqlite4KVCursorPrev(p); + storageSetTclErrorName(interp, rc); + return TCL_OK; +} + +/* +** TCLCMD: storage_delete CURSOR +** +** delete the entry under the cursor +*/ +static int test_storage_delete( + void * clientData, + Tcl_Interp *interp, + int objc, + Tcl_Obj *CONST objv[] +){ + KVCursor *p = 0; + int rc; + if( objc!=2 ){ + Tcl_WrongNumArgs(interp, 2, objv, "CURSOR"); + return TCL_ERROR; + } + p = sqlite4TestTextToPtr(Tcl_GetString(objv[1])); + rc = sqlite4KVCursorDelete(p); + storageSetTclErrorName(interp, rc); + return TCL_OK; +} + +/* +** TCLCMD: storage_reset CURSOR +** +** Reset the cursor +*/ +static int test_storage_reset( + void * clientData, + Tcl_Interp *interp, + int objc, + Tcl_Obj *CONST objv[] +){ + KVCursor *p = 0; + int rc; + if( objc!=2 ){ + Tcl_WrongNumArgs(interp, 2, objv, "CURSOR"); + return TCL_ERROR; + } + p = sqlite4TestTextToPtr(Tcl_GetString(objv[1])); + rc = sqlite4KVCursorReset(p); + storageSetTclErrorName(interp, rc); + return TCL_OK; +} + +/* +** TCLCMD: storage_key CURSOR +** +** Return the complete key of the cursor +*/ +static int test_storage_key( + void * clientData, + Tcl_Interp *interp, + int objc, + Tcl_Obj *CONST objv[] +){ + KVCursor *p = 0; + int rc; + const unsigned char *aKey; + int nKey; + if( objc!=2 ){ + Tcl_WrongNumArgs(interp, 2, objv, "CURSOR"); + return TCL_ERROR; + } + p = sqlite4TestTextToPtr(Tcl_GetString(objv[1])); + rc = sqlite4KVCursorKey(p, &aKey, &nKey); + if( rc ){ + storageSetTclErrorName(interp, rc); + }else{ + unsigned char zBuf[500]; + memcpy(zBuf, aKey, nKey); + sqlite4TestBinToHex(zBuf, nKey); + Tcl_SetObjResult(interp, Tcl_NewStringObj((char*)zBuf, -1)); + } + return TCL_OK; +} + +/* +** TCLCMD: storage_data CURSOR +** +** Return the complete data of the cursor +*/ +static int test_storage_data( + void * clientData, + Tcl_Interp *interp, + int objc, + Tcl_Obj *CONST objv[] +){ + KVCursor *p = 0; + int rc; + const unsigned char *aData; + int nData; + if( objc!=2 ){ + Tcl_WrongNumArgs(interp, 2, objv, "CURSOR"); + return TCL_ERROR; + } + p = sqlite4TestTextToPtr(Tcl_GetString(objv[1])); + rc = sqlite4KVCursorData(p, 0, 0, &aData, &nData); + if( rc ){ + storageSetTclErrorName(interp, rc); + }else{ + unsigned char zBuf[500]; + memcpy(zBuf, aData, nData); + sqlite4TestBinToHex(zBuf, nData); + Tcl_SetObjResult(interp, Tcl_NewStringObj((char*)zBuf, -1)); + } + return TCL_OK; +} + + + +/* +** Register the TCL commands defined above with the TCL interpreter. +** +** This routine should be the only externally visible symbol in this +** source code file. +*/ +int Sqliteteststorage_Init(Tcl_Interp *interp){ + struct SyscallCmd { + const char *zName; + Tcl_ObjCmdProc *xCmd; + } aCmd[] = { + { "storage_open", test_storage_open }, + { "storage_close", test_storage_close }, + { "storage_open_cursor", test_storage_open_cursor }, + { "storage_close_cursor", test_storage_close_cursor }, + { "storage_replace", test_storage_replace }, + { "storage_begin", test_storage_begin }, + { "storage_commit", test_storage_commit }, + { "storage_rollback", test_storage_rollback }, + { "storage_seek", test_storage_seek }, + { "storage_next", test_storage_next }, + { "storage_prev", test_storage_prev }, + { "storage_delete", test_storage_delete }, + { "storage_reset", test_storage_reset }, + { "storage_key", test_storage_key }, + { "storage_data", test_storage_data }, + }; + int i; + + for(i=0; ipReal->pStoreVfunc->xBegin(p->pReal, iLevel); + p->base.iTransLevel = p->pReal->iTransLevel; + return rc; +} + +static int kvwrapCommitPhaseOne(KVStore *pKVStore, int iLevel){ + int rc; + KVWrap *p = (KVWrap *)pKVStore; + rc = p->pReal->pStoreVfunc->xCommitPhaseOne(p->pReal, iLevel); + p->base.iTransLevel = p->pReal->iTransLevel; + return rc; +} + +static int kvwrapCommitPhaseTwo(KVStore *pKVStore, int iLevel){ + int rc; + KVWrap *p = (KVWrap *)pKVStore; + rc = p->pReal->pStoreVfunc->xCommitPhaseTwo(p->pReal, iLevel); + p->base.iTransLevel = p->pReal->iTransLevel; + return rc; +} + +static int kvwrapRollback(KVStore *pKVStore, int iLevel){ + int rc; + KVWrap *p = (KVWrap *)pKVStore; + rc = p->pReal->pStoreVfunc->xRollback(p->pReal, iLevel); + p->base.iTransLevel = p->pReal->iTransLevel; + return rc; +} + +static int kvwrapRevert(KVStore *pKVStore, int iLevel){ + int rc; + KVWrap *p = (KVWrap *)pKVStore; + rc = p->pReal->pStoreVfunc->xRevert(p->pReal, iLevel); + p->base.iTransLevel = p->pReal->iTransLevel; + return rc; +} + +static int kvwrapReplace( + KVStore *pKVStore, + const KVByteArray *aKey, KVSize nKey, + const KVByteArray *aData, KVSize nData +){ + KVWrap *p = (KVWrap *)pKVStore; + return p->pReal->pStoreVfunc->xReplace(p->pReal, aKey, nKey, aData, nData); +} + +/* +** Create a new cursor object. +*/ +static int kvwrapOpenCursor(KVStore *pKVStore, KVCursor **ppKVCursor){ + int rc = SQLITE4_OK; + KVWrap *p = (KVWrap *)pKVStore; + KVWrapCsr *pCsr; + + pCsr = (KVWrapCsr *)sqlite4_malloc(0, sizeof(KVWrapCsr)); + if( pCsr==0 ){ + rc = SQLITE4_NOMEM; + }else{ + memset(pCsr, 0, sizeof(KVWrapCsr)); + rc = p->pReal->pStoreVfunc->xOpenCursor(p->pReal, &pCsr->pReal); + if( rc!=SQLITE4_OK ){ + sqlite4_free(0, pCsr); + pCsr = 0; + }else{ + pCsr->base.pStore = pKVStore; + pCsr->base.pStoreVfunc = pKVStore->pStoreVfunc; + } + } + + *ppKVCursor = (KVCursor*)pCsr; + return rc; +} + +/* +** Reset a cursor +*/ +static int kvwrapReset(KVCursor *pKVCursor){ + KVWrap *p = (KVWrap *)(pKVCursor->pStore); + KVWrapCsr *pCsr = (KVWrapCsr *)pKVCursor; + return p->pReal->pStoreVfunc->xReset(pCsr->pReal); +} + +/* +** Destroy a cursor object +*/ +static int kvwrapCloseCursor(KVCursor *pKVCursor){ + int rc; + KVWrap *p = (KVWrap *)(pKVCursor->pStore); + KVWrapCsr *pCsr = (KVWrapCsr *)pKVCursor; + rc = p->pReal->pStoreVfunc->xCloseCursor(pCsr->pReal); + sqlite4_free(0, pCsr); + return rc; +} + +/* +** Move a cursor to the next non-deleted node. +*/ +static int kvwrapNextEntry(KVCursor *pKVCursor){ + int rc; + KVWrap *p = (KVWrap *)(pKVCursor->pStore); + KVWrapCsr *pCsr = (KVWrapCsr *)pKVCursor; + rc = p->pReal->pStoreVfunc->xNext(pCsr->pReal); + kvwg.nStep++; + return rc; +} + +/* +** Move a cursor to the previous non-deleted node. +*/ +static int kvwrapPrevEntry(KVCursor *pKVCursor){ + int rc; + KVWrap *p = (KVWrap *)(pKVCursor->pStore); + KVWrapCsr *pCsr = (KVWrapCsr *)pKVCursor; + rc = p->pReal->pStoreVfunc->xPrev(pCsr->pReal); + kvwg.nStep++; + return rc; +} + +/* +** Seek a cursor. +*/ +static int kvwrapSeek( + KVCursor *pKVCursor, + const KVByteArray *aKey, + KVSize nKey, + int dir +){ + KVWrap *p = (KVWrap *)(pKVCursor->pStore); + KVWrapCsr *pCsr = (KVWrapCsr *)pKVCursor; + + /* If aKey[0]==0, this is a seek to retrieve meta-data. Don't count this. */ + if( aKey[0] ) kvwg.nSeek++; + + return p->pReal->pStoreVfunc->xSeek(pCsr->pReal, aKey, nKey, dir); +} + +/* +** Delete the entry that the cursor is pointing to. +** +** Though the entry is "deleted", it still continues to exist as a +** phantom. Subsequent xNext or xPrev calls will work, as will +** calls to xKey and xData, thought the result from xKey and xData +** are undefined. +*/ +static int kvwrapDelete(KVCursor *pKVCursor){ + KVWrap *p = (KVWrap *)(pKVCursor->pStore); + KVWrapCsr *pCsr = (KVWrapCsr *)pKVCursor; + return p->pReal->pStoreVfunc->xDelete(pCsr->pReal); +} + +/* +** Return the key of the node the cursor is pointing to. +*/ +static int kvwrapKey( + KVCursor *pKVCursor, /* The cursor whose key is desired */ + const KVByteArray **paKey, /* Make this point to the key */ + KVSize *pN /* Make this point to the size of the key */ +){ + KVWrap *p = (KVWrap *)(pKVCursor->pStore); + KVWrapCsr *pCsr = (KVWrapCsr *)pKVCursor; + return p->pReal->pStoreVfunc->xKey(pCsr->pReal, paKey, pN); +} + +/* +** Return the data of the node the cursor is pointing to. +*/ +static int kvwrapData( + KVCursor *pKVCursor, /* The cursor from which to take the data */ + KVSize ofst, /* Offset into the data to begin reading */ + KVSize n, /* Number of bytes requested */ + const KVByteArray **paData, /* Pointer to the data written here */ + KVSize *pNData /* Number of bytes delivered */ +){ + KVWrap *p = (KVWrap *)(pKVCursor->pStore); + KVWrapCsr *pCsr = (KVWrapCsr *)pKVCursor; + return p->pReal->pStoreVfunc->xData(pCsr->pReal, ofst, n, paData, pNData); +} + +/* +** Destructor for the entire in-memory storage tree. +*/ +static int kvwrapClose(KVStore *pKVStore){ + int rc; + KVWrap *p = (KVWrap *)pKVStore; + rc = p->pReal->pStoreVfunc->xClose(p->pReal); + sqlite4_free(0, p); + return rc; +} + +/* +** Invoke the xControl() method of the underlying KVStore object. +*/ +static int kvwrapControl(KVStore *pKVStore, int op, void *pArg){ + KVWrap *p = (KVWrap *)pKVStore; + return p->pReal->pStoreVfunc->xControl(p->pReal, op, pArg); +} + +static int newFileStorage( + sqlite4_env *pEnv, + KVStore **ppKVStore, + const char *zName, + unsigned openFlags +){ + + /* Virtual methods for an LSM data store */ + static const KVStoreMethods kvwrapMethods = { + 1, + sizeof(KVStoreMethods), + kvwrapReplace, + kvwrapOpenCursor, + kvwrapSeek, + kvwrapNextEntry, + kvwrapPrevEntry, + kvwrapDelete, + kvwrapKey, + kvwrapData, + kvwrapReset, + kvwrapCloseCursor, + kvwrapBegin, + kvwrapCommitPhaseOne, + kvwrapCommitPhaseTwo, + kvwrapRollback, + kvwrapRevert, + kvwrapClose, + kvwrapControl + }; + + KVWrap *pNew; + int rc = SQLITE4_OK; + + pNew = (KVWrap *)sqlite4_malloc(0, sizeof(KVWrap)); + if( pNew==0 ){ + rc = SQLITE4_NOMEM; + }else{ + memset(pNew, 0, sizeof(KVWrap)); + pNew->base.pStoreVfunc = &kvwrapMethods; + rc = kvwg.xFactory(pEnv, &pNew->pReal, zName, openFlags); + if( rc!=SQLITE4_OK ){ + sqlite4_free(0, pNew); + pNew = 0; + } + } + + *ppKVStore = (KVStore*)pNew; + return rc; +} + +static int kvwrap_install_cmd(Tcl_Interp *interp, int objc, Tcl_Obj **objv){ + if( objc!=2 ){ + Tcl_WrongNumArgs(interp, 2, objv, ""); + return TCL_ERROR; + } + + if( kvwg.xFactory==0 ){ + sqlite4_env_config(0, SQLITE4_ENVCONFIG_KVSTORE_GET, "main", &kvwg.xFactory); + sqlite4_env_config(0, SQLITE4_ENVCONFIG_KVSTORE_PUSH, "main",newFileStorage); + } + return TCL_OK; +} + +static int kvwrap_seek_cmd(Tcl_Interp *interp, int objc, Tcl_Obj **objv){ + if( objc!=2 ){ + Tcl_WrongNumArgs(interp, 2, objv, ""); + return TCL_ERROR; + } + + Tcl_SetObjResult(interp, Tcl_NewIntObj(kvwg.nSeek)); + return TCL_OK; +} + +static int kvwrap_step_cmd(Tcl_Interp *interp, int objc, Tcl_Obj **objv){ + if( objc!=2 ){ + Tcl_WrongNumArgs(interp, 2, objv, ""); + return TCL_ERROR; + } + + Tcl_SetObjResult(interp, Tcl_NewIntObj(kvwg.nStep)); + return TCL_OK; +} + +static int kvwrap_reset_cmd(Tcl_Interp *interp, int objc, Tcl_Obj **objv){ + if( objc!=2 ){ + Tcl_WrongNumArgs(interp, 2, objv, ""); + return TCL_ERROR; + } + + kvwg.nStep = 0; + kvwg.nSeek = 0; + + Tcl_ResetResult(interp); + return TCL_OK; +} + + +/* +** TCLCMD: kvwrap SUB-COMMAND +*/ +static int kvwrap_command( + void * clientData, + Tcl_Interp *interp, + int objc, + Tcl_Obj *CONST objv[] +){ + struct Subcmd { + const char *zCmd; + int (*xCmd)(Tcl_Interp *, int, Tcl_Obj **); + } aSub[] = { + { "install", kvwrap_install_cmd }, + { "step", kvwrap_step_cmd }, + { "seek", kvwrap_seek_cmd }, + { "reset", kvwrap_reset_cmd }, + }; + int iSub; + int rc; + + rc = Tcl_GetIndexFromObjStruct( + interp, objv[1], aSub, sizeof(aSub[0]), "sub-command", 0, &iSub + ); + if( rc==TCL_OK ){ + rc = aSub[iSub].xCmd(interp, objc, (Tcl_Obj **)objv); + } + + return rc; +} + +/* +** Register the TCL commands defined above with the TCL interpreter. +** +** This routine should be the only externally visible symbol in this +** source code file. +*/ +int Sqliteteststorage2_Init(Tcl_Interp *interp){ + Tcl_CreateObjCommand(interp, "kvwrap", kvwrap_command, 0, 0); + return TCL_OK; +} DELETED test/test_storage.c Index: test/test_storage.c ================================================================== --- test/test_storage.c +++ /dev/null @@ -1,495 +0,0 @@ -/* -** 2011 January 21 -** -** The author disclaims copyright to this source code. In place of -** a legal notice, here is a blessing: -** -** May you do good and not evil. -** May you find forgiveness for yourself and forgive others. -** May you share freely, never taking more than you give. -** -************************************************************************* -** -** Code for testing the storage subsystem using the Storage interface. -*/ -#include "sqliteInt.h" - -/* Defined in test1.c */ -extern void *sqlite4TestTextToPtr(const char*); - -/* Defined in test_hexio.c */ -extern void sqlite4TestBinToHex(unsigned char*,int); -extern int sqlite4TestHexToBin(const unsigned char *in,int,unsigned char *out); - -/* Set the TCL result to an integer. -*/ -static void storageSetTclErrorName(Tcl_Interp *interp, int rc){ - extern const char *sqlite4TestErrorName(int); - Tcl_SetObjResult(interp, Tcl_NewStringObj(sqlite4TestErrorName(rc), -1)); -} - -/* -** TCLCMD: storage_open URI ?FLAGS? -** -** Return a string that identifies the new storage object. -*/ -static int test_storage_open( - void * clientData, - Tcl_Interp *interp, - int objc, - Tcl_Obj *CONST objv[] -){ - KVStore *pNew = 0; - int rc; - int flags = 0; - sqlite4 db; - char zRes[50]; - if( objc!=2 && objc!=3 ){ - Tcl_WrongNumArgs(interp, 1, objv, "URI ?FLAGS?"); - return TCL_ERROR; - } - if( objc==3 && Tcl_GetIntFromObj(interp, objv[2], &flags) ){ - return TCL_ERROR; - } - memset(&db, 0, sizeof(db)); - rc = sqlite4KVStoreOpen(&db, "test", Tcl_GetString(objv[1]), &pNew, flags); - if( rc ){ - sqlite4KVStoreClose(pNew); - storageSetTclErrorName(interp, rc); - return TCL_ERROR; - } - sqlite4_snprintf(zRes,sizeof(zRes), "%p", pNew); - Tcl_SetObjResult(interp, Tcl_NewStringObj(zRes,-1)); - return TCL_OK; -} - -/* -** TCLCMD: storage_close STORAGE -** -** Close a storage object. -*/ -static int test_storage_close( - void * clientData, - Tcl_Interp *interp, - int objc, - Tcl_Obj *CONST objv[] -){ - KVStore *pOld = 0; - int rc; - if( objc!=2 ){ - Tcl_WrongNumArgs(interp, 2, objv, "STORAGE"); - return TCL_ERROR; - } - pOld = sqlite4TestTextToPtr(Tcl_GetString(objv[1])); - rc = sqlite4KVStoreClose(pOld); - if( rc ){ - storageSetTclErrorName(interp, rc); - return TCL_ERROR; - } - return TCL_OK; -} - -/* -** TCLCMD: storage_open_cursor STORAGE -** -** Return a string that identifies the new storage cursor -*/ -static int test_storage_open_cursor( - void * clientData, - Tcl_Interp *interp, - int objc, - Tcl_Obj *CONST objv[] -){ - KVCursor *pNew = 0; - KVStore *pStore = 0; - int rc; - char zRes[50]; - if( objc!=2 ){ - Tcl_WrongNumArgs(interp, 2, objv, "STORAGE"); - return TCL_ERROR; - } - pStore = sqlite4TestTextToPtr(Tcl_GetString(objv[1])); - rc = sqlite4KVStoreOpenCursor(pStore, &pNew); - if( rc ){ - sqlite4KVCursorClose(pNew); - storageSetTclErrorName(interp, rc); - return TCL_ERROR; - } - sqlite4_snprintf(zRes,sizeof(zRes), "%p", pNew); - Tcl_SetObjResult(interp, Tcl_NewStringObj(zRes,-1)); - return TCL_OK; -} - -/* -** TCLCMD: storage_close_cursor CURSOR -** -** Close a cursor object. -*/ -static int test_storage_close_cursor( - void * clientData, - Tcl_Interp *interp, - int objc, - Tcl_Obj *CONST objv[] -){ - KVCursor *pOld = 0; - int rc; - if( objc!=2 ){ - Tcl_WrongNumArgs(interp, 2, objv, "CURSOR"); - return TCL_ERROR; - } - pOld = sqlite4TestTextToPtr(Tcl_GetString(objv[1])); - rc = sqlite4KVCursorClose(pOld); - if( rc ){ - storageSetTclErrorName(interp, rc); - return TCL_ERROR; - } - return TCL_OK; -} - -/* Decode hex into a binary key */ -static void sqlite4DecodeHex(Tcl_Obj *pObj, unsigned char *a, int *pN){ - const unsigned char *pIn; - int nIn; - pIn = (const unsigned char*)Tcl_GetStringFromObj(pObj, &nIn); - *pN = sqlite4TestHexToBin(pIn, nIn, a); -} - -/* -** TCLCMD: storage_replace STORAGE KEY VALUE -** -** Insert content into a KV storage object. KEY and VALUE are hex. -*/ -static int test_storage_replace( - void * clientData, - Tcl_Interp *interp, - int objc, - Tcl_Obj *CONST objv[] -){ - KVStore *p = 0; - int rc; - int nKey, nData; - unsigned char zKey[200]; - unsigned char zData[200]; - if( objc!=4 ){ - Tcl_WrongNumArgs(interp, 2, objv, "STORAGE KEY VALUE"); - return TCL_ERROR; - } - p = sqlite4TestTextToPtr(Tcl_GetString(objv[1])); - sqlite4DecodeHex(objv[2], zKey, &nKey); - sqlite4DecodeHex(objv[3], zData, &nData); - rc = sqlite4KVStoreReplace(p, zKey, nKey, zData, nData); - if( rc ){ - storageSetTclErrorName(interp, rc); - return TCL_ERROR; - } - return TCL_OK; -} - -/* -** TCLCMD: storage_begin STORAGE LEVEL -** -** Increase the transaction level -*/ -static int test_storage_begin( - void * clientData, - Tcl_Interp *interp, - int objc, - Tcl_Obj *CONST objv[] -){ - KVStore *p = 0; - int rc; - int iLevel; - if( objc!=3 ){ - Tcl_WrongNumArgs(interp, 2, objv, "STORAGE LEVEL"); - return TCL_ERROR; - } - p = sqlite4TestTextToPtr(Tcl_GetString(objv[1])); - if( Tcl_GetIntFromObj(interp, objv[2], &iLevel) ) return TCL_ERROR; - rc = sqlite4KVStoreBegin(p, iLevel); - if( rc ){ - storageSetTclErrorName(interp, rc); - return TCL_ERROR; - } - return TCL_OK; -} - -/* -** TCLCMD: storage_commit STORAGE LEVEL -** -** Increase the transaction level -*/ -static int test_storage_commit( - void * clientData, - Tcl_Interp *interp, - int objc, - Tcl_Obj *CONST objv[] -){ - KVStore *p = 0; - int rc; - int iLevel; - if( objc!=3 ){ - Tcl_WrongNumArgs(interp, 2, objv, "STORAGE LEVEL"); - return TCL_ERROR; - } - p = sqlite4TestTextToPtr(Tcl_GetString(objv[1])); - if( Tcl_GetIntFromObj(interp, objv[2], &iLevel) ) return TCL_ERROR; - rc = sqlite4KVStoreCommitPhaseOne(p, iLevel); - if( rc ){ - storageSetTclErrorName(interp, rc); - return TCL_ERROR; - } - rc = sqlite4KVStoreCommitPhaseTwo(p, iLevel); - if( rc ){ - storageSetTclErrorName(interp, rc); - return TCL_ERROR; - } - return TCL_OK; -} - -/* -** TCLCMD: storage_rollback STORAGE LEVEL -** -** Increase the transaction level -*/ -static int test_storage_rollback( - void * clientData, - Tcl_Interp *interp, - int objc, - Tcl_Obj *CONST objv[] -){ - KVStore *p = 0; - int rc; - int iLevel; - if( objc!=3 ){ - Tcl_WrongNumArgs(interp, 2, objv, "STORAGE LEVEL"); - return TCL_ERROR; - } - p = sqlite4TestTextToPtr(Tcl_GetString(objv[1])); - if( Tcl_GetIntFromObj(interp, objv[2], &iLevel) ) return TCL_ERROR; - rc = sqlite4KVStoreRollback(p, iLevel); - if( rc ){ - storageSetTclErrorName(interp, rc); - return TCL_ERROR; - } - return TCL_OK; -} - -/* -** TCLCMD: storage_seek CURSOR KEY DIRECTION -** -** Move a cursor object -*/ -static int test_storage_seek( - void * clientData, - Tcl_Interp *interp, - int objc, - Tcl_Obj *CONST objv[] -){ - KVCursor *p = 0; - int rc; - int nKey, dir; - unsigned char aKey[100]; - if( objc!=4 ){ - Tcl_WrongNumArgs(interp, 4, objv, "CURSOR KEY DIRECTION"); - return TCL_ERROR; - } - p = sqlite4TestTextToPtr(Tcl_GetString(objv[1])); - sqlite4DecodeHex(objv[2], aKey, &nKey); - if( Tcl_GetIntFromObj(interp, objv[3], &dir) ) return TCL_ERROR; - rc = sqlite4KVCursorSeek(p, aKey, nKey, dir); - storageSetTclErrorName(interp, rc); - return TCL_OK; -} - -/* -** TCLCMD: storage_next CURSOR -** -** Move the cursor to the next entry -*/ -static int test_storage_next( - void * clientData, - Tcl_Interp *interp, - int objc, - Tcl_Obj *CONST objv[] -){ - KVCursor *p = 0; - int rc; - if( objc!=2 ){ - Tcl_WrongNumArgs(interp, 2, objv, "CURSOR"); - return TCL_ERROR; - } - p = sqlite4TestTextToPtr(Tcl_GetString(objv[1])); - rc = sqlite4KVCursorNext(p); - storageSetTclErrorName(interp, rc); - return TCL_OK; -} - -/* -** TCLCMD: storage_prev CURSOR -** -** Move the cursor to the previous entry -*/ -static int test_storage_prev( - void * clientData, - Tcl_Interp *interp, - int objc, - Tcl_Obj *CONST objv[] -){ - KVCursor *p = 0; - int rc; - if( objc!=2 ){ - Tcl_WrongNumArgs(interp, 2, objv, "CURSOR"); - return TCL_ERROR; - } - p = sqlite4TestTextToPtr(Tcl_GetString(objv[1])); - rc = sqlite4KVCursorPrev(p); - storageSetTclErrorName(interp, rc); - return TCL_OK; -} - -/* -** TCLCMD: storage_delete CURSOR -** -** delete the entry under the cursor -*/ -static int test_storage_delete( - void * clientData, - Tcl_Interp *interp, - int objc, - Tcl_Obj *CONST objv[] -){ - KVCursor *p = 0; - int rc; - if( objc!=2 ){ - Tcl_WrongNumArgs(interp, 2, objv, "CURSOR"); - return TCL_ERROR; - } - p = sqlite4TestTextToPtr(Tcl_GetString(objv[1])); - rc = sqlite4KVCursorDelete(p); - storageSetTclErrorName(interp, rc); - return TCL_OK; -} - -/* -** TCLCMD: storage_reset CURSOR -** -** Reset the cursor -*/ -static int test_storage_reset( - void * clientData, - Tcl_Interp *interp, - int objc, - Tcl_Obj *CONST objv[] -){ - KVCursor *p = 0; - int rc; - if( objc!=2 ){ - Tcl_WrongNumArgs(interp, 2, objv, "CURSOR"); - return TCL_ERROR; - } - p = sqlite4TestTextToPtr(Tcl_GetString(objv[1])); - rc = sqlite4KVCursorReset(p); - storageSetTclErrorName(interp, rc); - return TCL_OK; -} - -/* -** TCLCMD: storage_key CURSOR -** -** Return the complete key of the cursor -*/ -static int test_storage_key( - void * clientData, - Tcl_Interp *interp, - int objc, - Tcl_Obj *CONST objv[] -){ - KVCursor *p = 0; - int rc; - const unsigned char *aKey; - int nKey; - if( objc!=2 ){ - Tcl_WrongNumArgs(interp, 2, objv, "CURSOR"); - return TCL_ERROR; - } - p = sqlite4TestTextToPtr(Tcl_GetString(objv[1])); - rc = sqlite4KVCursorKey(p, &aKey, &nKey); - if( rc ){ - storageSetTclErrorName(interp, rc); - }else{ - unsigned char zBuf[500]; - memcpy(zBuf, aKey, nKey); - sqlite4TestBinToHex(zBuf, nKey); - Tcl_SetObjResult(interp, Tcl_NewStringObj((char*)zBuf, -1)); - } - return TCL_OK; -} - -/* -** TCLCMD: storage_data CURSOR -** -** Return the complete data of the cursor -*/ -static int test_storage_data( - void * clientData, - Tcl_Interp *interp, - int objc, - Tcl_Obj *CONST objv[] -){ - KVCursor *p = 0; - int rc; - const unsigned char *aData; - int nData; - if( objc!=2 ){ - Tcl_WrongNumArgs(interp, 2, objv, "CURSOR"); - return TCL_ERROR; - } - p = sqlite4TestTextToPtr(Tcl_GetString(objv[1])); - rc = sqlite4KVCursorData(p, 0, 0, &aData, &nData); - if( rc ){ - storageSetTclErrorName(interp, rc); - }else{ - unsigned char zBuf[500]; - memcpy(zBuf, aData, nData); - sqlite4TestBinToHex(zBuf, nData); - Tcl_SetObjResult(interp, Tcl_NewStringObj((char*)zBuf, -1)); - } - return TCL_OK; -} - - - -/* -** Register the TCL commands defined above with the TCL interpreter. -** -** This routine should be the only externally visible symbol in this -** source code file. -*/ -int Sqliteteststorage_Init(Tcl_Interp *interp){ - struct SyscallCmd { - const char *zName; - Tcl_ObjCmdProc *xCmd; - } aCmd[] = { - { "storage_open", test_storage_open }, - { "storage_close", test_storage_close }, - { "storage_open_cursor", test_storage_open_cursor }, - { "storage_close_cursor", test_storage_close_cursor }, - { "storage_replace", test_storage_replace }, - { "storage_begin", test_storage_begin }, - { "storage_commit", test_storage_commit }, - { "storage_rollback", test_storage_rollback }, - { "storage_seek", test_storage_seek }, - { "storage_next", test_storage_next }, - { "storage_prev", test_storage_prev }, - { "storage_delete", test_storage_delete }, - { "storage_reset", test_storage_reset }, - { "storage_key", test_storage_key }, - { "storage_data", test_storage_data }, - }; - int i; - - for(i=0; ipReal->pStoreVfunc->xBegin(p->pReal, iLevel); - p->base.iTransLevel = p->pReal->iTransLevel; - return rc; -} - -static int kvwrapCommitPhaseOne(KVStore *pKVStore, int iLevel){ - int rc; - KVWrap *p = (KVWrap *)pKVStore; - rc = p->pReal->pStoreVfunc->xCommitPhaseOne(p->pReal, iLevel); - p->base.iTransLevel = p->pReal->iTransLevel; - return rc; -} - -static int kvwrapCommitPhaseTwo(KVStore *pKVStore, int iLevel){ - int rc; - KVWrap *p = (KVWrap *)pKVStore; - rc = p->pReal->pStoreVfunc->xCommitPhaseTwo(p->pReal, iLevel); - p->base.iTransLevel = p->pReal->iTransLevel; - return rc; -} - -static int kvwrapRollback(KVStore *pKVStore, int iLevel){ - int rc; - KVWrap *p = (KVWrap *)pKVStore; - rc = p->pReal->pStoreVfunc->xRollback(p->pReal, iLevel); - p->base.iTransLevel = p->pReal->iTransLevel; - return rc; -} - -static int kvwrapRevert(KVStore *pKVStore, int iLevel){ - int rc; - KVWrap *p = (KVWrap *)pKVStore; - rc = p->pReal->pStoreVfunc->xRevert(p->pReal, iLevel); - p->base.iTransLevel = p->pReal->iTransLevel; - return rc; -} - -static int kvwrapReplace( - KVStore *pKVStore, - const KVByteArray *aKey, KVSize nKey, - const KVByteArray *aData, KVSize nData -){ - KVWrap *p = (KVWrap *)pKVStore; - return p->pReal->pStoreVfunc->xReplace(p->pReal, aKey, nKey, aData, nData); -} - -/* -** Create a new cursor object. -*/ -static int kvwrapOpenCursor(KVStore *pKVStore, KVCursor **ppKVCursor){ - int rc = SQLITE4_OK; - KVWrap *p = (KVWrap *)pKVStore; - KVWrapCsr *pCsr; - - pCsr = (KVWrapCsr *)sqlite4_malloc(0, sizeof(KVWrapCsr)); - if( pCsr==0 ){ - rc = SQLITE4_NOMEM; - }else{ - memset(pCsr, 0, sizeof(KVWrapCsr)); - rc = p->pReal->pStoreVfunc->xOpenCursor(p->pReal, &pCsr->pReal); - if( rc!=SQLITE4_OK ){ - sqlite4_free(0, pCsr); - pCsr = 0; - }else{ - pCsr->base.pStore = pKVStore; - pCsr->base.pStoreVfunc = pKVStore->pStoreVfunc; - } - } - - *ppKVCursor = (KVCursor*)pCsr; - return rc; -} - -/* -** Reset a cursor -*/ -static int kvwrapReset(KVCursor *pKVCursor){ - KVWrap *p = (KVWrap *)(pKVCursor->pStore); - KVWrapCsr *pCsr = (KVWrapCsr *)pKVCursor; - return p->pReal->pStoreVfunc->xReset(pCsr->pReal); -} - -/* -** Destroy a cursor object -*/ -static int kvwrapCloseCursor(KVCursor *pKVCursor){ - int rc; - KVWrap *p = (KVWrap *)(pKVCursor->pStore); - KVWrapCsr *pCsr = (KVWrapCsr *)pKVCursor; - rc = p->pReal->pStoreVfunc->xCloseCursor(pCsr->pReal); - sqlite4_free(0, pCsr); - return rc; -} - -/* -** Move a cursor to the next non-deleted node. -*/ -static int kvwrapNextEntry(KVCursor *pKVCursor){ - int rc; - KVWrap *p = (KVWrap *)(pKVCursor->pStore); - KVWrapCsr *pCsr = (KVWrapCsr *)pKVCursor; - rc = p->pReal->pStoreVfunc->xNext(pCsr->pReal); - kvwg.nStep++; - return rc; -} - -/* -** Move a cursor to the previous non-deleted node. -*/ -static int kvwrapPrevEntry(KVCursor *pKVCursor){ - int rc; - KVWrap *p = (KVWrap *)(pKVCursor->pStore); - KVWrapCsr *pCsr = (KVWrapCsr *)pKVCursor; - rc = p->pReal->pStoreVfunc->xPrev(pCsr->pReal); - kvwg.nStep++; - return rc; -} - -/* -** Seek a cursor. -*/ -static int kvwrapSeek( - KVCursor *pKVCursor, - const KVByteArray *aKey, - KVSize nKey, - int dir -){ - KVWrap *p = (KVWrap *)(pKVCursor->pStore); - KVWrapCsr *pCsr = (KVWrapCsr *)pKVCursor; - - /* If aKey[0]==0, this is a seek to retrieve meta-data. Don't count this. */ - if( aKey[0] ) kvwg.nSeek++; - - return p->pReal->pStoreVfunc->xSeek(pCsr->pReal, aKey, nKey, dir); -} - -/* -** Delete the entry that the cursor is pointing to. -** -** Though the entry is "deleted", it still continues to exist as a -** phantom. Subsequent xNext or xPrev calls will work, as will -** calls to xKey and xData, thought the result from xKey and xData -** are undefined. -*/ -static int kvwrapDelete(KVCursor *pKVCursor){ - KVWrap *p = (KVWrap *)(pKVCursor->pStore); - KVWrapCsr *pCsr = (KVWrapCsr *)pKVCursor; - return p->pReal->pStoreVfunc->xDelete(pCsr->pReal); -} - -/* -** Return the key of the node the cursor is pointing to. -*/ -static int kvwrapKey( - KVCursor *pKVCursor, /* The cursor whose key is desired */ - const KVByteArray **paKey, /* Make this point to the key */ - KVSize *pN /* Make this point to the size of the key */ -){ - KVWrap *p = (KVWrap *)(pKVCursor->pStore); - KVWrapCsr *pCsr = (KVWrapCsr *)pKVCursor; - return p->pReal->pStoreVfunc->xKey(pCsr->pReal, paKey, pN); -} - -/* -** Return the data of the node the cursor is pointing to. -*/ -static int kvwrapData( - KVCursor *pKVCursor, /* The cursor from which to take the data */ - KVSize ofst, /* Offset into the data to begin reading */ - KVSize n, /* Number of bytes requested */ - const KVByteArray **paData, /* Pointer to the data written here */ - KVSize *pNData /* Number of bytes delivered */ -){ - KVWrap *p = (KVWrap *)(pKVCursor->pStore); - KVWrapCsr *pCsr = (KVWrapCsr *)pKVCursor; - return p->pReal->pStoreVfunc->xData(pCsr->pReal, ofst, n, paData, pNData); -} - -/* -** Destructor for the entire in-memory storage tree. -*/ -static int kvwrapClose(KVStore *pKVStore){ - int rc; - KVWrap *p = (KVWrap *)pKVStore; - rc = p->pReal->pStoreVfunc->xClose(p->pReal); - sqlite4_free(0, p); - return rc; -} - -/* -** Invoke the xControl() method of the underlying KVStore object. -*/ -static int kvwrapControl(KVStore *pKVStore, int op, void *pArg){ - KVWrap *p = (KVWrap *)pKVStore; - return p->pReal->pStoreVfunc->xControl(p->pReal, op, pArg); -} - -static int newFileStorage( - sqlite4_env *pEnv, - KVStore **ppKVStore, - const char *zName, - unsigned openFlags -){ - - /* Virtual methods for an LSM data store */ - static const KVStoreMethods kvwrapMethods = { - 1, - sizeof(KVStoreMethods), - kvwrapReplace, - kvwrapOpenCursor, - kvwrapSeek, - kvwrapNextEntry, - kvwrapPrevEntry, - kvwrapDelete, - kvwrapKey, - kvwrapData, - kvwrapReset, - kvwrapCloseCursor, - kvwrapBegin, - kvwrapCommitPhaseOne, - kvwrapCommitPhaseTwo, - kvwrapRollback, - kvwrapRevert, - kvwrapClose, - kvwrapControl - }; - - KVWrap *pNew; - int rc = SQLITE4_OK; - - pNew = (KVWrap *)sqlite4_malloc(0, sizeof(KVWrap)); - if( pNew==0 ){ - rc = SQLITE4_NOMEM; - }else{ - memset(pNew, 0, sizeof(KVWrap)); - pNew->base.pStoreVfunc = &kvwrapMethods; - rc = kvwg.xFactory(pEnv, &pNew->pReal, zName, openFlags); - if( rc!=SQLITE4_OK ){ - sqlite4_free(0, pNew); - pNew = 0; - } - } - - *ppKVStore = (KVStore*)pNew; - return rc; -} - -static int kvwrap_install_cmd(Tcl_Interp *interp, int objc, Tcl_Obj **objv){ - if( objc!=2 ){ - Tcl_WrongNumArgs(interp, 2, objv, ""); - return TCL_ERROR; - } - - if( kvwg.xFactory==0 ){ - sqlite4_env_config(0, SQLITE4_ENVCONFIG_KVSTORE_GET, "main", &kvwg.xFactory); - sqlite4_env_config(0, SQLITE4_ENVCONFIG_KVSTORE_PUSH, "main",newFileStorage); - } - return TCL_OK; -} - -static int kvwrap_seek_cmd(Tcl_Interp *interp, int objc, Tcl_Obj **objv){ - if( objc!=2 ){ - Tcl_WrongNumArgs(interp, 2, objv, ""); - return TCL_ERROR; - } - - Tcl_SetObjResult(interp, Tcl_NewIntObj(kvwg.nSeek)); - return TCL_OK; -} - -static int kvwrap_step_cmd(Tcl_Interp *interp, int objc, Tcl_Obj **objv){ - if( objc!=2 ){ - Tcl_WrongNumArgs(interp, 2, objv, ""); - return TCL_ERROR; - } - - Tcl_SetObjResult(interp, Tcl_NewIntObj(kvwg.nStep)); - return TCL_OK; -} - -static int kvwrap_reset_cmd(Tcl_Interp *interp, int objc, Tcl_Obj **objv){ - if( objc!=2 ){ - Tcl_WrongNumArgs(interp, 2, objv, ""); - return TCL_ERROR; - } - - kvwg.nStep = 0; - kvwg.nSeek = 0; - - Tcl_ResetResult(interp); - return TCL_OK; -} - - -/* -** TCLCMD: kvwrap SUB-COMMAND -*/ -static int kvwrap_command( - void * clientData, - Tcl_Interp *interp, - int objc, - Tcl_Obj *CONST objv[] -){ - struct Subcmd { - const char *zCmd; - int (*xCmd)(Tcl_Interp *, int, Tcl_Obj **); - } aSub[] = { - { "install", kvwrap_install_cmd }, - { "step", kvwrap_step_cmd }, - { "seek", kvwrap_seek_cmd }, - { "reset", kvwrap_reset_cmd }, - }; - int iSub; - int rc; - - rc = Tcl_GetIndexFromObjStruct( - interp, objv[1], aSub, sizeof(aSub[0]), "sub-command", 0, &iSub - ); - if( rc==TCL_OK ){ - rc = aSub[iSub].xCmd(interp, objc, (Tcl_Obj **)objv); - } - - return rc; -} - -/* -** Register the TCL commands defined above with the TCL interpreter. -** -** This routine should be the only externally visible symbol in this -** source code file. -*/ -int Sqliteteststorage2_Init(Tcl_Interp *interp){ - Tcl_CreateObjCommand(interp, "kvwrap", kvwrap_command, 0, 0); - return TCL_OK; -} Index: tool/mksqlite4c.tcl ================================================================== --- tool/mksqlite4c.tcl +++ tool/mksqlite4c.tcl @@ -249,11 +249,11 @@ lsm_str.c lsm_tree.c lsm_unix.c lsm_varint.c - storage.c + kv.c kvmem.c kvlsm.c rowset.c vdbemem.c