From: dan Date: Sat, 5 Dec 2015 20:51:54 +0000 (+0000) Subject: Add untested implementations of experimental APIs sqlite3_snapshot_get(), _open(... X-Git-Tag: version-3.10.0~44^2~15 X-Git-Url: http://git.ipfire.org/?a=commitdiff_plain;h=fc1acf33b88a23a7475aaa14287a72044674fcda;p=thirdparty%2Fsqlite.git Add untested implementations of experimental APIs sqlite3_snapshot_get(), _open() and _free(). FossilOrigin-Name: 0715eb00aa8891400cd50a15509d3d7b13789626 --- diff --git a/manifest b/manifest index 00191f9fd0..2447acbb5a 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Remove\sthe\sdependence\son\s"exec\sls\s-U"\sfrom\sthe\svtabH.test\smodule,\sas\sthe\s-U\noption\sto\s"ls"\sis\snot\suniversally\savailable. -D 2015-12-04T13:44:07.797 +C Add\suntested\simplementations\sof\sexperimental\sAPIs\ssqlite3_snapshot_get(),\s_open()\sand\s_free(). +D 2015-12-05T20:51:54.748 F Makefile.in 28bcd6149e050dff35d4dcfd97e890cd387a499d F Makefile.linux-gcc 7bc79876b875010e8c8f9502eb935ca92aa3c434 F Makefile.msc e8fdca1cb89a1b58b5f4d3a130ea9a3d28cb314d @@ -304,7 +304,7 @@ F src/insert.c e1d20ae8979e25519c2670233718676bedcfedc9 F src/journal.c b4124532212b6952f42eb2c12fa3c25701d8ba8d F src/legacy.c ba1863ea58c4c840335a84ec276fc2b25e22bc4e F src/loadext.c 84996d7d70a605597d79c1f1d7b2012a5fd34f2b -F src/main.c a950e48920e8c0f0ff82b2b2ccfe11aa89ca11d4 +F src/main.c 3dc84d9bd722fb16c196a867d39acf86b8f72b70 F src/malloc.c 337bbe9c7d436ef9b7d06b5dd10bbfc8f3025972 F src/mem0.c 6a55ebe57c46ca1a7d98da93aaa07f99f1059645 F src/mem1.c 6919bcf12f221868ea066eec27e579fed95ce98b @@ -326,8 +326,8 @@ F src/os_setup.h c9d4553b5aaa6f73391448b265b89bed0b890faa F src/os_unix.c 2563734669b06432cea640cbb4f7e9d543f227b9 F src/os_win.c 386fba30419e8458b13209781c2af5590eab2811 F src/os_win.h eb7a47aa17b26b77eb97e4823f20a00b8bda12ca -F src/pager.c f92aacd5216d8815136c9e0190041783c602641a -F src/pager.h 9153c71a89dc82a5a77e485f3929792116c70aae +F src/pager.c 58d2593612acb6b542de6715b4af397ea1fa0a35 +F src/pager.h bf25005b4656cd805af43487c3139fca9678d0cc F src/parse.y 23737e649c26ce327603799e57f5c2ff50e5e6ba F src/pcache.c 73895411fa6b7bd6f0091212feabbe833b358d23 F src/pcache.h 1ff11adce609ba7de139b6abfabaf9a2bac947b5 @@ -341,7 +341,7 @@ F src/resolve.c a83b41104e6ff69855d03cd0aaa09e93927ec39f F src/rowset.c eccf6af6d620aaa4579bd3b72c1b6395d9e9fa1e F src/select.c f8fded11fc443a9f5a73cc5db069d06b34460e2f F src/shell.c 2796237990d42e6a5a7beafee65ef70cc8767d21 -F src/sqlite.h.in 1248a78548024bdc8ef5893faa0ff9552b4cceb4 +F src/sqlite.h.in fc8a2875a318df1b9dabd82cb00b1ac98081423a F src/sqlite3.rc 992c9f5fb8285ae285d6be28240a7e8d3a7f2bad F src/sqlite3ext.h dfbe62ffd95b99afe2140d8c35b180d11924072d F src/sqliteInt.h 64256d193a16a147d9f6317cc4e095fdd3e0a2e9 @@ -349,7 +349,7 @@ F src/sqliteLimit.h 216557999cb45f2e3578ed53ebefe228d779cb46 F src/status.c 70912d7be68e9e2dbc4010c93d344af61d4c59ba F src/table.c 51b46b2a62d1b3a959633d593b89bab5e2c9155e F src/tclsqlite.c d9439b6a910985b7fff43ba6756bcef00de22649 -F src/test1.c 4004bcc1b3b361a9137acd1d875599ecbdd6f961 +F src/test1.c de18fc36e0830039058e9829ac201bb203d31718 F src/test2.c 5586f43fcd9a1be0830793cf9d354082c261b25b F src/test3.c a8887dabbbee3059af338f20d290084a63ed1b0f F src/test4.c d168f83cc78d02e8d35567bb5630e40dcd85ac1e @@ -415,8 +415,8 @@ F src/vdbesort.c a7ec02da4494c59dfd071126dd3726be5a11459d F src/vdbetrace.c 8befe829faff6d9e6f6e4dee5a7d3f85cc85f1a0 F src/vtab.c 2a8b44aa372c33f6154208e7a7f6c44254549806 F src/vxworks.h c18586c8edc1bddbc15c004fa16aeb1e1342b4fb -F src/wal.c 1569802364cd192bbd5c4a8ea3fd6de593edecbd -F src/wal.h df01efe09c5cb8c8e391ff1715cca294f89668a4 +F src/wal.c b9b1d5a1dd6e9b4f14f62326f34d719d14b33f08 +F src/wal.h 907943dfdef10b583e81906679a347e0ec6f1b1b F src/walker.c 2e14d17f592d176b6dc879c33fbdec4fbccaa2ba F src/where.c b18edbb9e5afabb77f4f27550c471c5c824e0fe7 F src/whereInt.h e20801d89e34de1912bb6a3babb30c390da27add @@ -1408,7 +1408,10 @@ F tool/vdbe_profile.tcl 246d0da094856d72d2c12efec03250d71639d19f F tool/warnings-clang.sh f6aa929dc20ef1f856af04a730772f59283631d4 F tool/warnings.sh 48bd54594752d5be3337f12c72f28d2080cb630b F tool/win/sqlite.vsix deb315d026cc8400325c5863eef847784a219a2f -P 042738ad3b769ad70fd7603f928d5b94a952267d -R d4a001dfc820878042f471ef37d7e668 -U drh -Z 2a98a6e0179300d3f1024c7c0c5f05bd +P 4ecbc75b465533cf80e166a9d0879b9afd3fe2be +R 89117c604019561442a571d3627293e1 +T *branch * snapshot-get +T *sym-snapshot-get * +T -sym-trunk * +U dan +Z 6ebcf0f2bc9e753be007db23f7cb64c3 diff --git a/manifest.uuid b/manifest.uuid index b3054dd7aa..f4219011d6 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -4ecbc75b465533cf80e166a9d0879b9afd3fe2be \ No newline at end of file +0715eb00aa8891400cd50a15509d3d7b13789626 \ No newline at end of file diff --git a/src/main.c b/src/main.c index d552f7fbc8..902954b60d 100644 --- a/src/main.c +++ b/src/main.c @@ -3866,3 +3866,85 @@ int sqlite3_db_readonly(sqlite3 *db, const char *zDbName){ pBt = sqlite3DbNameToBtree(db, zDbName); return pBt ? sqlite3BtreeIsReadonly(pBt) : -1; } + +#ifdef SQLITE_ENABLE_SNAPSHOT +/* +** Obtain a snapshot handle for the snapshot of database zDb currently +** being read by handle db. +*/ +int sqlite3_snapshot_get( + sqlite3 *db, + const char *zDb, + sqlite3_snapshot **ppSnapshot +){ + int rc = SQLITE_ERROR; +#ifndef SQLITE_OMIT_WAL + int iDb; + +#ifdef SQLITE_ENABLE_API_ARMOR + if( !sqlite3SafetyCheckOk(db) ){ + return SQLITE_MISUSE_BKPT; + } +#endif + sqlite3_mutex_enter(db->mutex); + + iDb = sqlite3FindDbName(db, zDb); + if( iDb==0 || iDb>1 ){ + Btree *pBt = db->aDb[iDb].pBt; + if( 0!=sqlite3BtreeIsInReadTrans(pBt) + && 0==sqlite3BtreeIsInTrans(pBt) + ){ + rc = sqlite3PagerSnapshotGet(sqlite3BtreePager(pBt), ppSnapshot); + } + } + + sqlite3_mutex_leave(db->mutex); +#endif /* SQLITE_OMIT_WAL */ + return rc; +} + +/* +** Open a read-transaction on the snapshot idendified by pSnapshot. +*/ +int sqlite3_snapshot_open( + sqlite3 *db, + const char *zDb, + sqlite3_snapshot *pSnapshot +){ + int rc = SQLITE_ERROR; +#ifndef SQLITE_OMIT_WAL + +#ifdef SQLITE_ENABLE_API_ARMOR + if( !sqlite3SafetyCheckOk(db) ){ + return SQLITE_MISUSE_BKPT; + } +#endif + sqlite3_mutex_enter(db->mutex); + if( db->autoCommit==0 ){ + int iDb; + iDb = sqlite3FindDbName(db, zDb); + if( iDb==0 || iDb>1 ){ + Btree *pBt = db->aDb[iDb].pBt; + if( 0==sqlite3BtreeIsInReadTrans(pBt) ){ + rc = sqlite3PagerSnapshotOpen(sqlite3BtreePager(pBt), pSnapshot); + if( rc==SQLITE_OK ){ + rc = sqlite3BtreeBeginTrans(pBt, 0); + sqlite3PagerSnapshotOpen(sqlite3BtreePager(pBt), 0); + } + } + } + } + + sqlite3_mutex_leave(db->mutex); +#endif /* SQLITE_OMIT_WAL */ + return rc; +} + +/* +** Free a snapshot handle obtained from sqlite3_snapshot_get(). +*/ +void sqlite3_snapshot_free(sqlite3_snapshot *pSnapshot){ + sqlite3_free(pSnapshot); +} +#endif /* SQLITE_ENABLE_SNAPSHOT */ + diff --git a/src/pager.c b/src/pager.c index bf74eac549..2c8dceb750 100644 --- a/src/pager.c +++ b/src/pager.c @@ -7301,6 +7301,34 @@ int sqlite3PagerCloseWal(Pager *pPager){ return rc; } +#ifdef SQLITE_ENABLE_SNAPSHOT +/* +** If this is a WAL database, obtain a snapshot handle for the snapshot +** currently open. Otherwise, return an error. +*/ +int sqlite3PagerSnapshotGet(Pager *pPager, sqlite3_snapshot **ppSnapshot){ + int rc = SQLITE_ERROR; + if( pPager->pWal ){ + rc = sqlite3WalSnapshotGet(pPager->pWal, ppSnapshot); + } + return rc; +} + +/* +** If this is a WAL database, store a pointer to pSnapshot. Next time a +** read transaction is opened, attempt to read from the snapshot it +** identifies. If this is not a WAL database, return an error. +*/ +int sqlite3PagerSnapshotOpen(Pager *pPager, sqlite3_snapshot *pSnapshot){ + int rc = SQLITE_OK; + if( pPager->pWal ){ + sqlite3WalSnapshotOpen(pPager->pWal, pSnapshot); + }else{ + rc = SQLITE_ERROR; + } + return rc; +} +#endif /* SQLITE_ENABLE_SNAPSHOT */ #endif /* !SQLITE_OMIT_WAL */ #ifdef SQLITE_ENABLE_ZIPVFS diff --git a/src/pager.h b/src/pager.h index cf9cda625d..ba4eec438d 100644 --- a/src/pager.h +++ b/src/pager.h @@ -168,6 +168,10 @@ int sqlite3PagerSharedLock(Pager *pPager); int sqlite3PagerWalCallback(Pager *pPager); int sqlite3PagerOpenWal(Pager *pPager, int *pisOpen); int sqlite3PagerCloseWal(Pager *pPager); +# ifdef SQLITE_ENABLE_SNAPSHOT + int sqlite3PagerSnapshotGet(Pager *pPager, sqlite3_snapshot **ppSnapshot); + int sqlite3PagerSnapshotOpen(Pager *pPager, sqlite3_snapshot *pSnapshot); +# endif #endif #ifdef SQLITE_ENABLE_ZIPVFS diff --git a/src/sqlite.h.in b/src/sqlite.h.in index e797571062..e8940b5e08 100644 --- a/src/sqlite.h.in +++ b/src/sqlite.h.in @@ -7878,6 +7878,29 @@ void sqlite3_stmt_scanstatus_reset(sqlite3_stmt*); */ int sqlite3_db_cacheflush(sqlite3*); +/* +** CAPI3REF: Open old database snapshots. +** +** The second argument passed to sqlite3_snapshot_get() must be the name +** of a database file attached to the database handle passed as the first. +** The database handle must have an open read transaction on the named +** database, which must be in wal mode. +** +** If successful, sqlite3_snapshot_get() sets *ppSnapshot to point to a new +** snapshot handle that may be used with sqlite3_snapshot_open() and returns +** SQLITE_OK. +** +** If the specified database does not exist, or is not a wal mode database, +** or the database handle does not have an open read transaction on it, +** SQLITE_ERROR is returned. If any other error occurs, for example an IO +** error or an OOM condition, the corresponding SQLite error code is +** returned. +*/ +typedef struct sqlite3_snapshot sqlite3_snapshot; +int sqlite3_snapshot_get(sqlite3*, const char*, sqlite3_snapshot **ppSnapshot); +int sqlite3_snapshot_open(sqlite3*, const char*, sqlite3_snapshot*); +void sqlite3_snapshot_free(sqlite3_snapshot*); + /* ** Undo the hack that converts floating point types to integer for ** builds on processors without floating point support. diff --git a/src/test1.c b/src/test1.c index 186e4e4684..31f506ea4f 100644 --- a/src/test1.c +++ b/src/test1.c @@ -2269,6 +2269,88 @@ static int vfsCurrentTimeInt64( return TCL_OK; } +/* +** Usage: sqlite3_snapshot_get DB DBNAME +*/ +static int test_snapshot_get( + void * clientData, + Tcl_Interp *interp, + int objc, + Tcl_Obj *CONST objv[] +){ + int rc; + sqlite3 *db; + char *zName; + sqlite3_snapshot *pSnapshot = 0; + + if( objc!=3 ){ + Tcl_WrongNumArgs(interp, 1, objv, "DB DBNAME"); + return TCL_ERROR; + } + if( getDbPointer(interp, Tcl_GetString(objv[1]), &db) ) return TCL_ERROR; + zName = Tcl_GetString(objv[2]); + + rc = sqlite3_snapshot_get(db, zName, &pSnapshot); + if( rc!=SQLITE_OK ){ + Tcl_SetObjResult(interp, Tcl_NewStringObj(sqlite3ErrName(rc), -1)); + return TCL_ERROR; + }else{ + char zBuf[100]; + if( sqlite3TestMakePointerStr(interp, zBuf, pSnapshot) ) return TCL_ERROR; + Tcl_SetObjResult(interp, Tcl_NewStringObj(zBuf, -1)); + } + return TCL_OK; +} + +/* +** Usage: sqlite3_snapshot_open DB DBNAME SNAPSHOT +*/ +static int test_snapshot_open( + void * clientData, + Tcl_Interp *interp, + int objc, + Tcl_Obj *CONST objv[] +){ + int rc; + sqlite3 *db; + char *zName; + sqlite3_snapshot *pSnapshot; + + if( objc!=4 ){ + Tcl_WrongNumArgs(interp, 1, objv, "DB DBNAME SNAPSHOT"); + return TCL_ERROR; + } + if( getDbPointer(interp, Tcl_GetString(objv[1]), &db) ) return TCL_ERROR; + zName = Tcl_GetString(objv[2]); + pSnapshot = (sqlite3_snapshot*)sqlite3TestTextToPtr(Tcl_GetString(objv[3])); + + rc = sqlite3_snapshot_open(db, zName, pSnapshot); + if( rc!=SQLITE_OK ){ + Tcl_SetObjResult(interp, Tcl_NewStringObj(sqlite3ErrName(rc), -1)); + return TCL_ERROR; + } + return TCL_OK; +} + +/* +** Usage: sqlite3_snapshot_free SNAPSHOT +*/ +static int test_snapshot_free( + void * clientData, + Tcl_Interp *interp, + int objc, + Tcl_Obj *CONST objv[] +){ + sqlite3_snapshot *pSnapshot; + if( objc!=2 ){ + Tcl_WrongNumArgs(interp, 1, objv, "SNAPSHOT"); + return TCL_ERROR; + } + pSnapshot = (sqlite3_snapshot*)sqlite3TestTextToPtr(Tcl_GetString(objv[1])); + sqlite3_snapshot_free(pSnapshot); + return TCL_OK; +} + /* ** Usage: sqlite3_next_stmt DB STMT ** @@ -7083,6 +7165,11 @@ int Sqlitetest1_Init(Tcl_Interp *interp){ { "sqlite3_config_sqllog", test_config_sqllog, 0 }, #endif { "vfs_current_time_int64", vfsCurrentTimeInt64, 0 }, +#ifdef SQLITE_ENABLE_SNAPSHOT + { "sqlite3_snapshot_get", test_snapshot_get, 0 }, + { "sqlite3_snapshot_open", test_snapshot_open, 0 }, + { "sqlite3_snapshot_free", test_snapshot_free, 0 }, +#endif }; static int bitmask_size = sizeof(Bitmask)*8; static int longdouble_size = sizeof(LONGDOUBLE_TYPE); diff --git a/src/wal.c b/src/wal.c index 144db27a30..c4823c7cd2 100644 --- a/src/wal.c +++ b/src/wal.c @@ -434,6 +434,9 @@ struct Wal { #ifdef SQLITE_DEBUG u8 lockError; /* True if a locking error has occurred */ #endif +#ifdef SQLITE_ENABLE_SNAPSHOT + WalIndexHdr *pSnapshot; +#endif }; /* @@ -2147,6 +2150,7 @@ static int walTryBeginRead(Wal *pWal, int *pChanged, int useWal, int cnt){ int mxI; /* Index of largest aReadMark[] value */ int i; /* Loop counter */ int rc = SQLITE_OK; /* Return code */ + int mxFrame; /* Wal frame to lock to */ assert( pWal->readLock<0 ); /* Not currently locked */ @@ -2210,7 +2214,12 @@ static int walTryBeginRead(Wal *pWal, int *pChanged, int useWal, int cnt){ } pInfo = walCkptInfo(pWal); - if( !useWal && pInfo->nBackfill==pWal->hdr.mxFrame ){ + if( !useWal && pInfo->nBackfill==pWal->hdr.mxFrame +#ifdef SQLITE_ENABLE_SNAPSHOT + && (pWal->pSnapshot==0 || pWal->hdr.mxFrame==0 + || 0==memcmp(&pWal->hdr, pWal->pSnapshot, sizeof(WalIndexHdr))) +#endif + ){ /* The WAL has been completely backfilled (or it is empty). ** and can be safely ignored. */ @@ -2248,9 +2257,13 @@ static int walTryBeginRead(Wal *pWal, int *pChanged, int useWal, int cnt){ */ mxReadMark = 0; mxI = 0; + mxFrame = pWal->hdr.mxFrame; +#ifdef SQLITE_ENABLE_SNAPSHOT + if( pWal->pSnapshot ) mxFrame = pWal->pSnapshot->mxFrame; +#endif for(i=1; iaReadMark[i]; - if( mxReadMark<=thisMark && thisMark<=pWal->hdr.mxFrame ){ + if( mxReadMark<=thisMark && thisMark<=mxFrame ){ assert( thisMark!=READMARK_NOT_USED ); mxReadMark = thisMark; mxI = i; @@ -2259,12 +2272,12 @@ static int walTryBeginRead(Wal *pWal, int *pChanged, int useWal, int cnt){ /* There was once an "if" here. The extra "{" is to preserve indentation. */ { if( (pWal->readOnly & WAL_SHM_RDONLY)==0 - && (mxReadMarkhdr.mxFrame || mxI==0) + && (mxReadMarkaReadMark[i] = pWal->hdr.mxFrame; + mxReadMark = pInfo->aReadMark[i] = mxFrame; mxI = i; walUnlockExclusive(pWal, WAL_READ_LOCK(i), 1); break; @@ -2349,6 +2362,14 @@ int sqlite3WalBeginReadTransaction(Wal *pWal, int *pChanged){ int rc; /* Return code */ int cnt = 0; /* Number of TryBeginRead attempts */ +#ifdef SQLITE_ENABLE_SNAPSHOT + int bChanged = 0; + WalIndexHdr *pSnapshot = pWal->pSnapshot; + if( pSnapshot && memcmp(pSnapshot, &pWal->hdr, sizeof(WalIndexHdr))){ + bChanged = 1; + } +#endif + do{ rc = walTryBeginRead(pWal, pChanged, 0, ++cnt); }while( rc==WAL_RETRY ); @@ -2356,6 +2377,32 @@ int sqlite3WalBeginReadTransaction(Wal *pWal, int *pChanged){ testcase( (rc&0xff)==SQLITE_IOERR ); testcase( rc==SQLITE_PROTOCOL ); testcase( rc==SQLITE_OK ); + +#ifdef SQLITE_ENABLE_SNAPSHOT + if( rc==SQLITE_OK ){ + if( pSnapshot && memcmp(pSnapshot, &pWal->hdr, sizeof(WalIndexHdr)) ){ + volatile WalCkptInfo *pInfo = walCkptInfo(pWal); + rc = walLockShared(pWal, WAL_READ_LOCK(0)); + if( rc==SQLITE_OK ){ + if( pInfo->nBackfill<=pSnapshot->mxFrame + && pSnapshot->aSalt[0]==pWal->hdr.aSalt[0] + && pSnapshot->aSalt[1]==pWal->hdr.aSalt[1] + ){ + assert( pWal->readLock>0 ); + assert( pInfo->aReadMark[pWal->readLock]<=pSnapshot->mxFrame ); + memcpy(&pWal->hdr, pSnapshot, sizeof(WalIndexHdr)); + *pChanged = bChanged; + }else{ + rc = SQLITE_BUSY_SNAPSHOT; + } + walUnlockShared(pWal, WAL_READ_LOCK(0)); + } + if( rc!=SQLITE_OK ){ + sqlite3WalEndReadTransaction(pWal); + } + } + } +#endif return rc; } @@ -3165,6 +3212,29 @@ int sqlite3WalHeapMemory(Wal *pWal){ return (pWal && pWal->exclusiveMode==WAL_HEAPMEMORY_MODE ); } +#ifdef SQLITE_ENABLE_SNAPSHOT +int sqlite3WalSnapshotGet(Wal *pWal, sqlite3_snapshot **ppSnapshot){ + int rc = SQLITE_OK; + WalIndexHdr *pRet; + + assert( pWal->readLock>=0 && pWal->writeLock==0 ); + + pRet = (WalIndexHdr*)sqlite3_malloc(sizeof(WalIndexHdr)); + if( pRet==0 ){ + rc = SQLITE_NOMEM; + }else{ + memcpy(pRet, &pWal->hdr, sizeof(WalIndexHdr)); + *ppSnapshot = (sqlite3_snapshot*)pRet; + } + + return rc; +} + +void sqlite3WalSnapshotOpen(Wal *pWal, sqlite3_snapshot *pSnapshot){ + pWal->pSnapshot = (WalIndexHdr*)pSnapshot; +} +#endif /* SQLITE_ENABLE_SNAPSHOT */ + #ifdef SQLITE_ENABLE_ZIPVFS /* ** If the argument is not NULL, it points to a Wal object that holds a diff --git a/src/wal.h b/src/wal.h index 092546354b..94a049493e 100644 --- a/src/wal.h +++ b/src/wal.h @@ -126,6 +126,11 @@ int sqlite3WalExclusiveMode(Wal *pWal, int op); */ int sqlite3WalHeapMemory(Wal *pWal); +#ifdef SQLITE_ENABLE_SNAPSHOT +int sqlite3WalSnapshotGet(Wal *pWal, sqlite3_snapshot **ppSnapshot); +void sqlite3WalSnapshotOpen(Wal *pWal, sqlite3_snapshot *pSnapshot); +#endif + #ifdef SQLITE_ENABLE_ZIPVFS /* If the WAL file is not empty, return the number of bytes of content ** stored in each frame (i.e. the db page-size when the WAL was created).