/* ** 2012 April 19 ** ** 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. ** ************************************************************************* ** */ #include "sqliteInt.h" static struct KVWrapGlobal { sqlite4_kvfactory xFactory; int nStep; /* Total number of successful next/prev */ int nSeek; /* Total number of calls to xSeek */ } kvwg = {0}; typedef struct KVWrap KVWrap; typedef struct KVWrapCsr KVWrapCsr; struct KVWrap { KVStore base; /* Base class, must be first */ KVStore *pReal; /* "Real" KVStore object */ }; struct KVWrapCsr { KVCursor base; /* Base class. Must be first */ KVCursor *pReal; /* "Real" Cursor obecjt */ }; static int kvwrapBegin(KVStore *pKVStore, int iLevel){ int rc; KVWrap *p = (KVWrap *)pKVStore; rc = p->pReal->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 kvwrapGetMeta(KVStore *pKVStore, unsigned int *piVal){ KVWrap *p = (KVWrap *)pKVStore; return p->pReal->pStoreVfunc->xGetMeta(p->pReal, piVal); } static int kvwrapPutMeta(KVStore *pKVStore, unsigned int iVal){ KVWrap *p = (KVWrap *)pKVStore; return p->pReal->pStoreVfunc->xPutMeta(p->pReal, iVal); } static int kvwrapGetMethod( sqlite4_kvstore *pKVStore, const char *zMethod, void **ppArg, void (**pxFunc)(sqlite4_context *, int, sqlite4_value **), void (**pxDestroy)(void *) ){ KVWrap *p = (KVWrap *)pKVStore; return p->pReal->pStoreVfunc->xGetMethod( p->pReal, zMethod, ppArg, pxFunc, pxDestroy ); } static int newFileStorage( sqlite4_env *pEnv, KVStore **ppKVStore, const char *zName, unsigned openFlags ){ /* Virtual methods for the new factory */ static const KVStoreMethods kvwrapMethods = { 1, sizeof(KVStoreMethods), kvwrapReplace, kvwrapOpenCursor, kvwrapSeek, kvwrapNextEntry, kvwrapPrevEntry, kvwrapDelete, kvwrapKey, kvwrapData, kvwrapReset, kvwrapCloseCursor, kvwrapBegin, kvwrapCommitPhaseOne, kvwrapCommitPhaseTwo, kvwrapRollback, kvwrapRevert, kvwrapClose, kvwrapControl, kvwrapGetMeta, kvwrapPutMeta, kvwrapGetMethod }; 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){ const char *z = "main"; if( objc!=2 && objc!=3 ){ Tcl_WrongNumArgs(interp, 2, objv, "?FACTORY?"); return TCL_ERROR; } if( objc==3 ){ z = Tcl_GetString(objv[2]); } if( kvwg.xFactory==0 ){ sqlite4_env_config(0, SQLITE4_ENVCONFIG_KVSTORE_GET,z,&kvwg.xFactory); if( kvwg.xFactory==0 ){ Tcl_AppendResult(interp, "no such factory: ", z, 0); return TCL_ERROR; } sqlite4_env_config(0, SQLITE4_ENVCONFIG_KVSTORE_PUSH,"main",newFileStorage); } return TCL_OK; } static int kvwrap_uninstall_cmd(Tcl_Interp *interp, int objc, Tcl_Obj **objv){ if( objc!=2 ){ Tcl_WrongNumArgs(interp, 2, objv, ""); return TCL_ERROR; } if( kvwg.xFactory ){ sqlite4_env_config(0, SQLITE4_ENVCONFIG_KVSTORE_POP,"main", &kvwg.xFactory); kvwg.xFactory = 0; } 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 }, { "uninstall", kvwrap_uninstall_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; } /* ** TCLCMD: kv_default DEFAULT */ static int kv_default_cmd( void * clientData, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[] ){ sqlite4_kvfactory factory = 0; const char *zDflt; if( objc!=2 ){ Tcl_WrongNumArgs(interp, 1, objv, "DEFAULT"); return TCL_ERROR; } zDflt = Tcl_GetString(objv[1]); sqlite4_env_config(0, SQLITE4_ENVCONFIG_KVSTORE_GET, zDflt, &factory); if( factory==0 ){ Tcl_ResetResult(interp); Tcl_AppendResult(interp, "no such factory: ", zDflt, 0); return TCL_ERROR; } sqlite4_env_config(0, SQLITE4_ENVCONFIG_KVSTORE_PUSH, "main", factory); 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 Sqliteteststorage2_Init(Tcl_Interp *interp){ Tcl_CreateObjCommand(interp, "kvwrap", kvwrap_command, 0, 0); Tcl_CreateObjCommand(interp, "kv_default", kv_default_cmd, 0, 0); return TCL_OK; }