| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | #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; |
| | } |
| |
|