| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| #include "sqlite3ext.h" |
| SQLITE_EXTENSION_INIT1 |
| #include <string.h> |
| #include <assert.h> |
|
|
| |
| |
| |
| |
| |
| |
| |
| |
| #define APND_MARK_PREFIX "Start-Of-SQLite3-" |
| #define APND_MARK_PREFIX_SZ 17 |
| #define APND_MARK_FOS_SZ 8 |
| #define APND_MARK_SIZE (APND_MARK_PREFIX_SZ+APND_MARK_FOS_SZ) |
|
|
| |
| |
| |
| |
| #define APND_MAX_SIZE (0x40000000) |
|
|
| |
| |
| |
| #ifndef APND_ROUNDUP |
| #define APND_ROUNDUP 4096 |
| #endif |
| #define APND_ALIGN_MASK ((sqlite3_int64)(APND_ROUNDUP-1)) |
| #define APND_START_ROUNDUP(fsz) (((fsz)+APND_ALIGN_MASK) & ~APND_ALIGN_MASK) |
|
|
| |
| |
| |
| typedef struct sqlite3_vfs ApndVfs; |
| typedef struct ApndFile ApndFile; |
|
|
| |
| |
| |
| #define ORIGVFS(p) ((sqlite3_vfs*)((p)->pAppData)) |
| #define ORIGFILE(p) ((sqlite3_file*)(((ApndFile*)(p))+1)) |
|
|
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| struct ApndFile { |
| sqlite3_file base; |
| sqlite3_int64 iPgOne; |
| sqlite3_int64 iMark; |
| |
| }; |
|
|
| |
| |
| |
| static int apndClose(sqlite3_file*); |
| static int apndRead(sqlite3_file*, void*, int iAmt, sqlite3_int64 iOfst); |
| static int apndWrite(sqlite3_file*,const void*,int iAmt, sqlite3_int64 iOfst); |
| static int apndTruncate(sqlite3_file*, sqlite3_int64 size); |
| static int apndSync(sqlite3_file*, int flags); |
| static int apndFileSize(sqlite3_file*, sqlite3_int64 *pSize); |
| static int apndLock(sqlite3_file*, int); |
| static int apndUnlock(sqlite3_file*, int); |
| static int apndCheckReservedLock(sqlite3_file*, int *pResOut); |
| static int apndFileControl(sqlite3_file*, int op, void *pArg); |
| static int apndSectorSize(sqlite3_file*); |
| static int apndDeviceCharacteristics(sqlite3_file*); |
| static int apndShmMap(sqlite3_file*, int iPg, int pgsz, int, void volatile**); |
| static int apndShmLock(sqlite3_file*, int offset, int n, int flags); |
| static void apndShmBarrier(sqlite3_file*); |
| static int apndShmUnmap(sqlite3_file*, int deleteFlag); |
| static int apndFetch(sqlite3_file*, sqlite3_int64 iOfst, int iAmt, void **pp); |
| static int apndUnfetch(sqlite3_file*, sqlite3_int64 iOfst, void *p); |
|
|
| |
| |
| |
| static int apndOpen(sqlite3_vfs*, const char *, sqlite3_file*, int , int *); |
| static int apndDelete(sqlite3_vfs*, const char *zName, int syncDir); |
| static int apndAccess(sqlite3_vfs*, const char *zName, int flags, int *); |
| static int apndFullPathname(sqlite3_vfs*, const char *zName, int, char *zOut); |
| static void *apndDlOpen(sqlite3_vfs*, const char *zFilename); |
| static void apndDlError(sqlite3_vfs*, int nByte, char *zErrMsg); |
| static void (*apndDlSym(sqlite3_vfs *pVfs, void *p, const char*zSym))(void); |
| static void apndDlClose(sqlite3_vfs*, void*); |
| static int apndRandomness(sqlite3_vfs*, int nByte, char *zOut); |
| static int apndSleep(sqlite3_vfs*, int microseconds); |
| static int apndCurrentTime(sqlite3_vfs*, double*); |
| static int apndGetLastError(sqlite3_vfs*, int, char *); |
| static int apndCurrentTimeInt64(sqlite3_vfs*, sqlite3_int64*); |
| static int apndSetSystemCall(sqlite3_vfs*, const char*,sqlite3_syscall_ptr); |
| static sqlite3_syscall_ptr apndGetSystemCall(sqlite3_vfs*, const char *z); |
| static const char *apndNextSystemCall(sqlite3_vfs*, const char *zName); |
|
|
| static sqlite3_vfs apnd_vfs = { |
| 3, |
| 0, |
| 1024, |
| 0, |
| "apndvfs", |
| 0, |
| apndOpen, |
| apndDelete, |
| apndAccess, |
| apndFullPathname, |
| apndDlOpen, |
| apndDlError, |
| apndDlSym, |
| apndDlClose, |
| apndRandomness, |
| apndSleep, |
| apndCurrentTime, |
| apndGetLastError, |
| apndCurrentTimeInt64, |
| apndSetSystemCall, |
| apndGetSystemCall, |
| apndNextSystemCall |
| }; |
|
|
| static const sqlite3_io_methods apnd_io_methods = { |
| 3, |
| apndClose, |
| apndRead, |
| apndWrite, |
| apndTruncate, |
| apndSync, |
| apndFileSize, |
| apndLock, |
| apndUnlock, |
| apndCheckReservedLock, |
| apndFileControl, |
| apndSectorSize, |
| apndDeviceCharacteristics, |
| apndShmMap, |
| apndShmLock, |
| apndShmBarrier, |
| apndShmUnmap, |
| apndFetch, |
| apndUnfetch |
| }; |
|
|
| |
| |
| |
| static int apndClose(sqlite3_file *pFile){ |
| pFile = ORIGFILE(pFile); |
| return pFile->pMethods->xClose(pFile); |
| } |
|
|
| |
| |
| |
| static int apndRead( |
| sqlite3_file *pFile, |
| void *zBuf, |
| int iAmt, |
| sqlite_int64 iOfst |
| ){ |
| ApndFile *paf = (ApndFile *)pFile; |
| pFile = ORIGFILE(pFile); |
| return pFile->pMethods->xRead(pFile, zBuf, iAmt, paf->iPgOne+iOfst); |
| } |
|
|
| |
| |
| |
| |
| |
| static int apndWriteMark( |
| ApndFile *paf, |
| sqlite3_file *pFile, |
| sqlite_int64 iWriteEnd |
| ){ |
| sqlite_int64 iPgOne = paf->iPgOne; |
| unsigned char a[APND_MARK_SIZE]; |
| int i = APND_MARK_FOS_SZ; |
| int rc; |
| assert(pFile == ORIGFILE(paf)); |
| memcpy(a, APND_MARK_PREFIX, APND_MARK_PREFIX_SZ); |
| while( --i >= 0 ){ |
| a[APND_MARK_PREFIX_SZ+i] = (unsigned char)(iPgOne & 0xff); |
| iPgOne >>= 8; |
| } |
| iWriteEnd += paf->iPgOne; |
| if( SQLITE_OK==(rc = pFile->pMethods->xWrite |
| (pFile, a, APND_MARK_SIZE, iWriteEnd)) ){ |
| paf->iMark = iWriteEnd; |
| } |
| return rc; |
| } |
|
|
| |
| |
| |
| static int apndWrite( |
| sqlite3_file *pFile, |
| const void *zBuf, |
| int iAmt, |
| sqlite_int64 iOfst |
| ){ |
| ApndFile *paf = (ApndFile *)pFile; |
| sqlite_int64 iWriteEnd = iOfst + iAmt; |
| if( iWriteEnd>=APND_MAX_SIZE ) return SQLITE_FULL; |
| pFile = ORIGFILE(pFile); |
| |
| if( paf->iMark < 0 || paf->iPgOne + iWriteEnd > paf->iMark ){ |
| int rc = apndWriteMark(paf, pFile, iWriteEnd); |
| if( SQLITE_OK!=rc ) return rc; |
| } |
| return pFile->pMethods->xWrite(pFile, zBuf, iAmt, paf->iPgOne+iOfst); |
| } |
|
|
| |
| |
| |
| static int apndTruncate(sqlite3_file *pFile, sqlite_int64 size){ |
| ApndFile *paf = (ApndFile *)pFile; |
| pFile = ORIGFILE(pFile); |
| |
| if( SQLITE_OK!=apndWriteMark(paf, pFile, size) ) return SQLITE_IOERR; |
| |
| return pFile->pMethods->xTruncate(pFile, paf->iMark+APND_MARK_SIZE); |
| } |
|
|
| |
| |
| |
| static int apndSync(sqlite3_file *pFile, int flags){ |
| pFile = ORIGFILE(pFile); |
| return pFile->pMethods->xSync(pFile, flags); |
| } |
|
|
| |
| |
| |
| |
| static int apndFileSize(sqlite3_file *pFile, sqlite_int64 *pSize){ |
| ApndFile *paf = (ApndFile *)pFile; |
| *pSize = ( paf->iMark >= 0 )? (paf->iMark - paf->iPgOne) : 0; |
| return SQLITE_OK; |
| } |
|
|
| |
| |
| |
| static int apndLock(sqlite3_file *pFile, int eLock){ |
| pFile = ORIGFILE(pFile); |
| return pFile->pMethods->xLock(pFile, eLock); |
| } |
|
|
| |
| |
| |
| static int apndUnlock(sqlite3_file *pFile, int eLock){ |
| pFile = ORIGFILE(pFile); |
| return pFile->pMethods->xUnlock(pFile, eLock); |
| } |
|
|
| |
| |
| |
| static int apndCheckReservedLock(sqlite3_file *pFile, int *pResOut){ |
| pFile = ORIGFILE(pFile); |
| return pFile->pMethods->xCheckReservedLock(pFile, pResOut); |
| } |
|
|
| |
| |
| |
| static int apndFileControl(sqlite3_file *pFile, int op, void *pArg){ |
| ApndFile *paf = (ApndFile *)pFile; |
| int rc; |
| pFile = ORIGFILE(pFile); |
| if( op==SQLITE_FCNTL_SIZE_HINT ) *(sqlite3_int64*)pArg += paf->iPgOne; |
| rc = pFile->pMethods->xFileControl(pFile, op, pArg); |
| if( rc==SQLITE_OK && op==SQLITE_FCNTL_VFSNAME ){ |
| *(char**)pArg = sqlite3_mprintf("apnd(%lld)/%z", paf->iPgOne,*(char**)pArg); |
| } |
| return rc; |
| } |
|
|
| |
| |
| |
| static int apndSectorSize(sqlite3_file *pFile){ |
| pFile = ORIGFILE(pFile); |
| return pFile->pMethods->xSectorSize(pFile); |
| } |
|
|
| |
| |
| |
| static int apndDeviceCharacteristics(sqlite3_file *pFile){ |
| pFile = ORIGFILE(pFile); |
| return pFile->pMethods->xDeviceCharacteristics(pFile); |
| } |
|
|
| |
| static int apndShmMap( |
| sqlite3_file *pFile, |
| int iPg, |
| int pgsz, |
| int bExtend, |
| void volatile **pp |
| ){ |
| pFile = ORIGFILE(pFile); |
| return pFile->pMethods->xShmMap(pFile,iPg,pgsz,bExtend,pp); |
| } |
|
|
| |
| static int apndShmLock(sqlite3_file *pFile, int offset, int n, int flags){ |
| pFile = ORIGFILE(pFile); |
| return pFile->pMethods->xShmLock(pFile,offset,n,flags); |
| } |
|
|
| |
| static void apndShmBarrier(sqlite3_file *pFile){ |
| pFile = ORIGFILE(pFile); |
| pFile->pMethods->xShmBarrier(pFile); |
| } |
|
|
| |
| static int apndShmUnmap(sqlite3_file *pFile, int deleteFlag){ |
| pFile = ORIGFILE(pFile); |
| return pFile->pMethods->xShmUnmap(pFile,deleteFlag); |
| } |
|
|
| |
| static int apndFetch( |
| sqlite3_file *pFile, |
| sqlite3_int64 iOfst, |
| int iAmt, |
| void **pp |
| ){ |
| ApndFile *p = (ApndFile *)pFile; |
| if( p->iMark < 0 || iOfst+iAmt > p->iMark ){ |
| return SQLITE_IOERR; |
| } |
| pFile = ORIGFILE(pFile); |
| return pFile->pMethods->xFetch(pFile, iOfst+p->iPgOne, iAmt, pp); |
| } |
|
|
| |
| static int apndUnfetch(sqlite3_file *pFile, sqlite3_int64 iOfst, void *pPage){ |
| ApndFile *p = (ApndFile *)pFile; |
| pFile = ORIGFILE(pFile); |
| return pFile->pMethods->xUnfetch(pFile, iOfst+p->iPgOne, pPage); |
| } |
|
|
| |
| |
| |
| |
| |
| |
| |
| |
| |
| static sqlite3_int64 apndReadMark(sqlite3_int64 sz, sqlite3_file *pFile){ |
| int rc, i; |
| sqlite3_int64 iMark; |
| int msbs = 8 * (APND_MARK_FOS_SZ-1); |
| unsigned char a[APND_MARK_SIZE]; |
|
|
| if( APND_MARK_SIZE!=(sz & 0x1ff) ) return -1; |
| rc = pFile->pMethods->xRead(pFile, a, APND_MARK_SIZE, sz-APND_MARK_SIZE); |
| if( rc ) return -1; |
| if( memcmp(a, APND_MARK_PREFIX, APND_MARK_PREFIX_SZ)!=0 ) return -1; |
| iMark = ((sqlite3_int64)(a[APND_MARK_PREFIX_SZ] & 0x7f)) << msbs; |
| for(i=1; i<8; i++){ |
| msbs -= 8; |
| iMark |= (sqlite3_int64)a[APND_MARK_PREFIX_SZ+i]<<msbs; |
| } |
| if( iMark > (sz - APND_MARK_SIZE - 512) ) return -1; |
| if( iMark & 0x1ff ) return -1; |
| return iMark; |
| } |
|
|
| static const char apvfsSqliteHdr[] = "SQLite format 3"; |
| |
| |
| |
| |
| static int apndIsAppendvfsDatabase(sqlite3_int64 sz, sqlite3_file *pFile){ |
| int rc; |
| char zHdr[16]; |
| sqlite3_int64 iMark = apndReadMark(sz, pFile); |
| if( iMark>=0 ){ |
| |
| |
| |
| |
| rc = pFile->pMethods->xRead(pFile, zHdr, sizeof(zHdr), iMark); |
| if( SQLITE_OK==rc |
| && memcmp(zHdr, apvfsSqliteHdr, sizeof(zHdr))==0 |
| && (sz & 0x1ff) == APND_MARK_SIZE |
| && sz>=512+APND_MARK_SIZE |
| ){ |
| return 1; |
| } |
| } |
| return 0; |
| } |
|
|
| |
| |
| |
| |
| static int apndIsOrdinaryDatabaseFile(sqlite3_int64 sz, sqlite3_file *pFile){ |
| char zHdr[16]; |
| if( apndIsAppendvfsDatabase(sz, pFile) |
| || (sz & 0x1ff) != 0 |
| || SQLITE_OK!=pFile->pMethods->xRead(pFile, zHdr, sizeof(zHdr), 0) |
| || memcmp(zHdr, apvfsSqliteHdr, sizeof(zHdr))!=0 |
| ){ |
| return 0; |
| }else{ |
| return 1; |
| } |
| } |
|
|
| |
| |
| |
| static int apndOpen( |
| sqlite3_vfs *pApndVfs, |
| const char *zName, |
| sqlite3_file *pFile, |
| int flags, |
| int *pOutFlags |
| ){ |
| ApndFile *pApndFile = (ApndFile*)pFile; |
| sqlite3_file *pBaseFile = ORIGFILE(pFile); |
| sqlite3_vfs *pBaseVfs = ORIGVFS(pApndVfs); |
| int rc; |
| sqlite3_int64 sz = 0; |
| if( (flags & SQLITE_OPEN_MAIN_DB)==0 ){ |
| |
| |
| |
| |
| return pBaseVfs->xOpen(pBaseVfs, zName, pFile, flags, pOutFlags); |
| } |
| memset(pApndFile, 0, sizeof(ApndFile)); |
| pFile->pMethods = &apnd_io_methods; |
| pApndFile->iMark = -1; |
|
|
| rc = pBaseVfs->xOpen(pBaseVfs, zName, pBaseFile, flags, pOutFlags); |
| if( rc==SQLITE_OK ){ |
| rc = pBaseFile->pMethods->xFileSize(pBaseFile, &sz); |
| if( rc ){ |
| pBaseFile->pMethods->xClose(pBaseFile); |
| } |
| } |
| if( rc ){ |
| pFile->pMethods = 0; |
| return rc; |
| } |
| if( apndIsOrdinaryDatabaseFile(sz, pBaseFile) ){ |
| |
| |
| |
| memmove(pApndFile, pBaseFile, pBaseVfs->szOsFile); |
| return SQLITE_OK; |
| } |
| pApndFile->iPgOne = apndReadMark(sz, pFile); |
| if( pApndFile->iPgOne>=0 ){ |
| pApndFile->iMark = sz - APND_MARK_SIZE; |
| return SQLITE_OK; |
| } |
| if( (flags & SQLITE_OPEN_CREATE)==0 ){ |
| pBaseFile->pMethods->xClose(pBaseFile); |
| rc = SQLITE_CANTOPEN; |
| pFile->pMethods = 0; |
| }else{ |
| |
| |
| |
| |
| |
| pApndFile->iPgOne = APND_START_ROUNDUP(sz); |
| } |
| return rc; |
| } |
|
|
| |
| |
| |
| |
| |
| |
| static int apndDelete(sqlite3_vfs *pVfs, const char *zPath, int dirSync){ |
| return ORIGVFS(pVfs)->xDelete(ORIGVFS(pVfs), zPath, dirSync); |
| } |
|
|
| |
| |
| |
| static int apndAccess( |
| sqlite3_vfs *pVfs, |
| const char *zPath, |
| int flags, |
| int *pResOut |
| ){ |
| return ORIGVFS(pVfs)->xAccess(ORIGVFS(pVfs), zPath, flags, pResOut); |
| } |
| static int apndFullPathname( |
| sqlite3_vfs *pVfs, |
| const char *zPath, |
| int nOut, |
| char *zOut |
| ){ |
| return ORIGVFS(pVfs)->xFullPathname(ORIGVFS(pVfs),zPath,nOut,zOut); |
| } |
| static void *apndDlOpen(sqlite3_vfs *pVfs, const char *zPath){ |
| return ORIGVFS(pVfs)->xDlOpen(ORIGVFS(pVfs), zPath); |
| } |
| static void apndDlError(sqlite3_vfs *pVfs, int nByte, char *zErrMsg){ |
| ORIGVFS(pVfs)->xDlError(ORIGVFS(pVfs), nByte, zErrMsg); |
| } |
| static void (*apndDlSym(sqlite3_vfs *pVfs, void *p, const char *zSym))(void){ |
| return ORIGVFS(pVfs)->xDlSym(ORIGVFS(pVfs), p, zSym); |
| } |
| static void apndDlClose(sqlite3_vfs *pVfs, void *pHandle){ |
| ORIGVFS(pVfs)->xDlClose(ORIGVFS(pVfs), pHandle); |
| } |
| static int apndRandomness(sqlite3_vfs *pVfs, int nByte, char *zBufOut){ |
| return ORIGVFS(pVfs)->xRandomness(ORIGVFS(pVfs), nByte, zBufOut); |
| } |
| static int apndSleep(sqlite3_vfs *pVfs, int nMicro){ |
| return ORIGVFS(pVfs)->xSleep(ORIGVFS(pVfs), nMicro); |
| } |
| static int apndCurrentTime(sqlite3_vfs *pVfs, double *pTimeOut){ |
| return ORIGVFS(pVfs)->xCurrentTime(ORIGVFS(pVfs), pTimeOut); |
| } |
| static int apndGetLastError(sqlite3_vfs *pVfs, int a, char *b){ |
| return ORIGVFS(pVfs)->xGetLastError(ORIGVFS(pVfs), a, b); |
| } |
| static int apndCurrentTimeInt64(sqlite3_vfs *pVfs, sqlite3_int64 *p){ |
| return ORIGVFS(pVfs)->xCurrentTimeInt64(ORIGVFS(pVfs), p); |
| } |
| static int apndSetSystemCall( |
| sqlite3_vfs *pVfs, |
| const char *zName, |
| sqlite3_syscall_ptr pCall |
| ){ |
| return ORIGVFS(pVfs)->xSetSystemCall(ORIGVFS(pVfs),zName,pCall); |
| } |
| static sqlite3_syscall_ptr apndGetSystemCall( |
| sqlite3_vfs *pVfs, |
| const char *zName |
| ){ |
| return ORIGVFS(pVfs)->xGetSystemCall(ORIGVFS(pVfs),zName); |
| } |
| static const char *apndNextSystemCall(sqlite3_vfs *pVfs, const char *zName){ |
| return ORIGVFS(pVfs)->xNextSystemCall(ORIGVFS(pVfs), zName); |
| } |
|
|
| |
| #ifdef _WIN32 |
| __declspec(dllexport) |
| #endif |
| |
| |
| |
| |
| int sqlite3_appendvfs_init( |
| sqlite3 *db, |
| char **pzErrMsg, |
| const sqlite3_api_routines *pApi |
| ){ |
| int rc = SQLITE_OK; |
| sqlite3_vfs *pOrig; |
| SQLITE_EXTENSION_INIT2(pApi); |
| (void)pzErrMsg; |
| (void)db; |
| pOrig = sqlite3_vfs_find(0); |
| if( pOrig==0 ) return SQLITE_ERROR; |
| apnd_vfs.iVersion = pOrig->iVersion; |
| apnd_vfs.pAppData = pOrig; |
| apnd_vfs.szOsFile = pOrig->szOsFile + sizeof(ApndFile); |
| rc = sqlite3_vfs_register(&apnd_vfs, 0); |
| #ifdef APPENDVFS_TEST |
| if( rc==SQLITE_OK ){ |
| rc = sqlite3_auto_extension((void(*)(void))apndvfsRegister); |
| } |
| #endif |
| if( rc==SQLITE_OK ) rc = SQLITE_OK_LOAD_PERMANENTLY; |
| return rc; |
| } |
|
|