Index: src/kv.c ================================================================== --- src/kv.c +++ src/kv.c @@ -371,10 +371,27 @@ } sqlite4KVCursorClose(pCur); } return rc; } + +/* +** Store schema cookie value iVal. +*/ +int sqlite4KVStorePutSchema(KVStore *p, unsigned int iVal){ + kvTrace(p, "xPutMeta(%d,%d) -> %s", p->kvId, (int)iVal); + return p->pStoreVfunc->xPutMeta(p, iVal); +} + +/* +** Read the schema cookie value into *piVal. +*/ +int sqlite4KVStoreGetSchema(KVStore *p, unsigned int *piVal){ + int rc = p->pStoreVfunc->xGetMeta(p, piVal); + kvTrace(p, "xGetMeta(%d) -> %d", p->kvId, (int)*piVal); + return rc; +} /* ** Write nMeta unsigned 32-bit integers beginning with iStart. */ int sqlite4KVStorePutMeta( Index: src/kv.h ================================================================== --- src/kv.h +++ src/kv.h @@ -172,8 +172,12 @@ int sqlite4KVStoreRevert(KVStore *p, int iLevel); int sqlite4KVStoreClose(KVStore *p); int sqlite4KVStoreGetMeta(KVStore *p, int, int, unsigned int*); int sqlite4KVStorePutMeta(sqlite4*, KVStore *p, int, int, unsigned int*); + +int sqlite4KVStorePutSchema(KVStore *p, unsigned int iVal); +int sqlite4KVStoreGetSchema(KVStore *p, unsigned int *piVal); + #ifdef SQLITE4_DEBUG void sqlite4KVStoreDump(KVStore *p); #endif Index: src/kvlsm.c ================================================================== --- src/kvlsm.c +++ src/kvlsm.c @@ -406,10 +406,20 @@ break; } return rc; } + +static int kvlsmGetMeta(KVStore *pKVStore, unsigned int *piVal){ + KVLsm *p = (KVLsm *)pKVStore; + return lsm_get_user_version(p->pDb, piVal); +} + +static int kvlsmPutMeta(KVStore *pKVStore, unsigned int iVal){ + KVLsm *p = (KVLsm *)pKVStore; + return lsm_set_user_version(p->pDb, iVal); +} /* ** Create a new in-memory storage engine and return a pointer to it. */ int sqlite4KVStoreOpenLsm( @@ -419,29 +429,31 @@ unsigned openFlags /* Flags */ ){ /* Virtual methods for an LSM data store */ static const KVStoreMethods kvlsmMethods = { - 1, /* iVersion */ - sizeof(KVStoreMethods), /* szSelf */ - kvlsmReplace, /* xReplace */ - kvlsmOpenCursor, /* xOpenCursor */ - kvlsmSeek, /* xSeek */ - kvlsmNextEntry, /* xNext */ - kvlsmPrevEntry, /* xPrev */ - kvlsmDelete, /* xDelete */ - kvlsmKey, /* xKey */ - kvlsmData, /* xData */ - kvlsmReset, /* xReset */ - kvlsmCloseCursor, /* xCloseCursor */ - kvlsmBegin, /* xBegin */ - kvlsmCommitPhaseOne, /* xCommitPhaseOne */ - kvlsmCommitPhaseTwo, /* xCommitPhaseTwo */ - kvlsmRollback, /* xRollback */ - kvlsmRevert, /* xRevert */ - kvlsmClose, /* xClose */ - kvlsmControl /* xControl */ + 1, /* iVersion */ + sizeof(KVStoreMethods), /* szSelf */ + kvlsmReplace, /* xReplace */ + kvlsmOpenCursor, /* xOpenCursor */ + kvlsmSeek, /* xSeek */ + kvlsmNextEntry, /* xNext */ + kvlsmPrevEntry, /* xPrev */ + kvlsmDelete, /* xDelete */ + kvlsmKey, /* xKey */ + kvlsmData, /* xData */ + kvlsmReset, /* xReset */ + kvlsmCloseCursor, /* xCloseCursor */ + kvlsmBegin, /* xBegin */ + kvlsmCommitPhaseOne, /* xCommitPhaseOne */ + kvlsmCommitPhaseTwo, /* xCommitPhaseTwo */ + kvlsmRollback, /* xRollback */ + kvlsmRevert, /* xRevert */ + kvlsmClose, /* xClose */ + kvlsmControl, /* xControl */ + kvlsmGetMeta, /* xGetMeta */ + kvlsmPutMeta /* xPutMeta */ }; KVLsm *pNew; int rc = SQLITE4_OK; Index: src/kvmem.c ================================================================== --- src/kvmem.c +++ src/kvmem.c @@ -68,10 +68,11 @@ KVMemNode *pRoot; /* Root of the tree of content */ unsigned openFlags; /* Flags used at open */ KVMemChng **apLog; /* Array of transaction logs */ int nCursor; /* Number of outstanding cursors */ int iMagicKVMemBase; /* Magic number of sanity */ + unsigned int iMeta; /* Schema cookie value */ }; #define SQLITE4_KVMEMBASE_MAGIC 0xbfcd47d0 /* ** A cursor used for scanning through the tree @@ -913,10 +914,22 @@ } static int kvmemControl(KVStore *pKVStore, int op, void *pArg){ return SQLITE4_NOTFOUND; } + +static int kvmemGetMeta(KVStore *pKVStore, unsigned int *piVal){ + KVMem *p = (KVMem*)pKVStore; + *piVal = p->iMeta; + return SQLITE4_OK; +} + +static int kvmemPutMeta(KVStore *pKVStore, unsigned int iVal){ + KVMem *p = (KVMem*)pKVStore; + p->iMeta = iVal; + return SQLITE4_OK; +} /* Virtual methods for the in-memory storage engine */ static const KVStoreMethods kvmemMethods = { 1, /* iVersion */ sizeof(KVStoreMethods), /* szSelf */ @@ -934,11 +947,13 @@ kvmemCommitPhaseOne, /* xCommitPhaseOne */ kvmemCommitPhaseTwo, /* xCommitPhaseTwo */ kvmemRollback, /* xRollback */ kvmemRevert, /* xRevert */ kvmemClose, /* xClose */ - kvmemControl /* xControl */ + kvmemControl, /* xControl */ + kvmemGetMeta, /* xGetMeta */ + kvmemPutMeta /* xPutMeta */ }; /* ** Create a new in-memory storage engine and return a pointer to it. */ Index: src/lsm.h ================================================================== --- src/lsm.h +++ src/lsm.h @@ -331,10 +331,13 @@ ** CAPI: Querying a Connection For Operational Data ** ** Query a database connection for operational statistics or data. */ int lsm_info(lsm_db *, int, ...); + +int lsm_get_user_version(lsm_db *, unsigned int *); +int lsm_set_user_version(lsm_db *, unsigned int); /* ** The following values may be passed as the second argument to lsm_info(). ** ** LSM_INFO_NWRITE: Index: src/lsmInt.h ================================================================== --- src/lsmInt.h +++ src/lsmInt.h @@ -279,10 +279,11 @@ u32 nChunk; /* Number of chunks in shared-memory file */ TreeRoot root; /* Root and height of current tree */ u32 iWrite; /* Write offset in shm file */ TreeRoot oldroot; /* Root and height of the previous tree */ u32 iOldShmid; /* Last shm-id used by previous tree */ + u32 iUsrVersion; /* get/set_user_version() value */ i64 iOldLog; /* Log offset associated with old tree */ u32 oldcksum0; u32 oldcksum1; DbLog log; /* Current layout of log file */ u32 aCksum[2]; /* Checksums 1 and 2. */ Index: src/lsm_file.c ================================================================== --- src/lsm_file.c +++ src/lsm_file.c @@ -891,27 +891,29 @@ /* ** Purge the page cache of all entries with nRef==0. */ void lsmFsPurgeCache(FileSystem *pFS){ - Page *pPg; - - pPg = pFS->pLruFirst; - while( pPg ){ - Page *pNext = pPg->pLruNext; - fsPageRemoveFromHash(pFS, pPg); - if( pPg->flags & PAGE_FREE ){ - lsmFree(pFS->pEnv, pPg->aData); - } - lsmFree(pFS->pEnv, pPg); - pPg = pNext; - pFS->nCacheAlloc--; - } - pFS->pLruFirst = 0; - pFS->pLruLast = 0; - - assert( pFS->nCacheAlloc<=pFS->nOut && pFS->nCacheAlloc>=0 ); + if( pFS->bUseMmap==0 ){ + Page *pPg; + + pPg = pFS->pLruFirst; + while( pPg ){ + Page *pNext = pPg->pLruNext; + fsPageRemoveFromHash(pFS, pPg); + if( pPg->flags & PAGE_FREE ){ + lsmFree(pFS->pEnv, pPg->aData); + } + lsmFree(pFS->pEnv, pPg); + pPg = pNext; + pFS->nCacheAlloc--; + } + pFS->pLruFirst = 0; + pFS->pLruLast = 0; + + assert( pFS->nCacheAlloc<=pFS->nOut && pFS->nCacheAlloc>=0 ); + } } /* ** Search the hash-table for page iPg. If an entry is round, return a pointer ** to it. Otherwise, return NULL. Index: src/lsm_main.c ================================================================== --- src/lsm_main.c +++ src/lsm_main.c @@ -954,10 +954,58 @@ if( pDb->nTransOpen==0 ){ lsmFinishWriteTrans(pDb, 0); } dbReleaseClientSnapshot(pDb); } + + return rc; +} + +int lsm_get_user_version(lsm_db *pDb, unsigned int *piUsr){ + int rc = LSM_OK; /* Return code */ + + /* Open a read transaction if one is not already open. */ + assert_db_state(pDb); + if( pDb->pShmhdr==0 ){ + assert( pDb->bReadonly ); + rc = lsmBeginRoTrans(pDb); + }else if( pDb->iReader<0 ){ + rc = lsmBeginReadTrans(pDb); + } + + /* Allocate the multi-cursor. */ + if( rc==LSM_OK ){ + *piUsr = pDb->treehdr.iUsrVersion; + } + + dbReleaseClientSnapshot(pDb); + assert_db_state(pDb); + return rc; +} + +int lsm_set_user_version(lsm_db *pDb, unsigned int iUsr){ + int rc = LSM_OK; /* Return code */ + int bCommit = 0; /* True to commit before returning */ + + if( pDb->nTransOpen==0 ){ + bCommit = 1; + rc = lsm_begin(pDb, 1); + } + + if( rc==LSM_OK ){ + pDb->treehdr.iUsrVersion = iUsr; + } + + /* If a transaction was opened at the start of this function, commit it. + ** Or, if an error has occurred, roll it back. */ + if( bCommit ){ + if( rc==LSM_OK ){ + rc = lsm_commit(pDb, 0); + }else{ + lsm_rollback(pDb, 0); + } + } return rc; } Index: src/prepare.c ================================================================== --- src/prepare.c +++ src/prepare.c @@ -134,11 +134,10 @@ static int sqlite4InitOne(sqlite4 *db, int iDb, char **pzErrMsg){ int rc; Table *pTab; Db *pDb; char const *azArg[4]; - unsigned int meta[5]; InitData initData; char const *zMasterSchema; char const *zMasterName; int openedTransaction = 0; @@ -222,29 +221,13 @@ goto initone_error_out; } openedTransaction = 1; } - /* Get the database meta information. - ** - ** Meta values are as follows: - ** meta[0] Schema cookie. Changes with each schema change. - ** meta[1] unused - ** meta[2] unused - ** meta[3] unused - ** meta[4] unused - ** meta[5] unused - ** meta[6] unused - ** meta[7] unused - ** meta[8] unused - ** meta[9] unused - ** - ** Note: The #defined SQLITE4_UTF* symbols in sqliteInt.h correspond to - ** the possible values of meta[4]. + /* Get the database schema version. */ - sqlite4KVStoreGetMeta(pDb->pKV, 0, ArraySize(meta), meta); - pDb->pSchema->schema_cookie = meta[0]; + sqlite4KVStoreGetSchema(pDb->pKV, (u32 *)&pDb->pSchema->schema_cookie); /* Read the schema information out of the schema tables */ assert( db->init.busy ); { @@ -400,11 +383,11 @@ } /* Read the schema cookie from the database. If it does not match the ** value stored as part of the in-memory schema representation, ** set Parse.rc to SQLITE4_SCHEMA. */ - sqlite4KVStoreGetMeta(pKV, 0, 1, (u32 *)&cookie); + sqlite4KVStoreGetSchema(pKV, (u32 *)&cookie); if( cookie!=db->aDb[iDb].pSchema->schema_cookie ){ sqlite4ResetInternalSchema(db, iDb); pParse->rc = SQLITE4_SCHEMA; } Index: src/sqlite.h.in ================================================================== --- src/sqlite.h.in +++ src/sqlite.h.in @@ -4229,10 +4229,12 @@ int (*xCommitPhaseTwo)(sqlite4_kvstore*, int); int (*xRollback)(sqlite4_kvstore*, int); int (*xRevert)(sqlite4_kvstore*, int); int (*xClose)(sqlite4_kvstore*); int (*xControl)(sqlite4_kvstore*, int, void*); + int (*xGetMeta)(sqlite4_kvstore*, unsigned int *); + int (*xPutMeta)(sqlite4_kvstore*, unsigned int); }; typedef struct sqlite4_kv_methods sqlite4_kv_methods; /* ** CAPI4REF: Key-value storage engine open flags Index: src/vdbe.c ================================================================== --- src/vdbe.c +++ src/vdbe.c @@ -2557,11 +2557,11 @@ assert( pOp->p1>=0 && pOp->p1nDb ); pDb = &db->aDb[pOp->p1]; pIn3 = &aMem[pOp->p3]; sqlite4VdbeMemIntegerify(pIn3); v = (u32)pIn3->u.i; - rc = sqlite4KVStorePutMeta(db, pDb->pKV, 0, 1, &v); + rc = sqlite4KVStorePutSchema(pDb->pKV, v); pDb->pSchema->schema_cookie = (int)pIn3->u.i; db->flags |= SQLITE4_InternChanges; if( pOp->p1==1 ){ /* Invalidate all prepared statements whenever the TEMP database ** schema is changed. Ticket #1644 */ @@ -2595,11 +2595,11 @@ KVStore *pKV; assert( pOp->p1>=0 && pOp->p1nDb ); pKV = db->aDb[pOp->p1].pKV; if( pKV ){ - rc = sqlite4KVStoreGetMeta(pKV, 0, 1, &iMeta); + rc = sqlite4KVStoreGetSchema(pKV, &iMeta); if( rc ) break; iGen = db->aDb[pOp->p1].pSchema->iGeneration; }else{ iGen = iMeta = 0; } Index: test/test_kv2.c ================================================================== --- test/test_kv2.c +++ test/test_kv2.c @@ -231,10 +231,20 @@ */ 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 newFileStorage( sqlite4_env *pEnv, KVStore **ppKVStore, const char *zName, @@ -259,11 +269,13 @@ kvwrapCommitPhaseOne, kvwrapCommitPhaseTwo, kvwrapRollback, kvwrapRevert, kvwrapClose, - kvwrapControl + kvwrapControl, + kvwrapGetMeta, + kvwrapPutMeta }; KVWrap *pNew; int rc = SQLITE4_OK;