diff options
Diffstat (limited to 'ext/pdo_sqlite/sqlite/src/os_test.c')
| -rw-r--r-- | ext/pdo_sqlite/sqlite/src/os_test.c | 461 |
1 files changed, 461 insertions, 0 deletions
diff --git a/ext/pdo_sqlite/sqlite/src/os_test.c b/ext/pdo_sqlite/sqlite/src/os_test.c new file mode 100644 index 0000000000..0e292bc428 --- /dev/null +++ b/ext/pdo_sqlite/sqlite/src/os_test.c @@ -0,0 +1,461 @@ +/* +** 2004 May 22 +** +** 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. +** +****************************************************************************** +** +** This file contains code that is specific to Unix systems. It is used +** for testing SQLite only. +*/ +#if OS_TEST /* This file is used for the test backend only */ +#include "sqliteInt.h" +#include "os.h" /* Must be first to enable large file support */ + +#define sqlite3OsOpenReadWrite sqlite3RealOpenReadWrite +#define sqlite3OsOpenExclusive sqlite3RealOpenExclusive +#define sqlite3OsOpenReadOnly sqlite3RealOpenReadOnly +#define sqlite3OsOpenDirectory sqlite3RealOpenDirectory +#define sqlite3OsClose sqlite3RealClose +#define sqlite3OsRead sqlite3RealRead +#define sqlite3OsWrite sqlite3RealWrite +#define sqlite3OsSeek sqlite3RealSeek +#define sqlite3OsSync sqlite3RealSync +#define sqlite3OsTruncate sqlite3RealTruncate +#define sqlite3OsFileSize sqlite3RealFileSize +#define sqlite3OsLock sqlite3RealLock +#define sqlite3OsUnlock sqlite3RealUnlock +#define sqlite3OsCheckReservedLock sqlite3RealCheckReservedLock + +#define OsFile OsRealFile +#define OS_UNIX 1 +#include "os_unix.c" +#undef OS_UNIX +#undef OsFile + +#undef sqlite3OsOpenReadWrite +#undef sqlite3OsOpenExclusive +#undef sqlite3OsOpenReadOnly +#undef sqlite3OsOpenDirectory +#undef sqlite3OsClose +#undef sqlite3OsRead +#undef sqlite3OsWrite +#undef sqlite3OsSeek +#undef sqlite3OsSync +#undef sqlite3OsTruncate +#undef sqlite3OsFileSize +#undef sqlite3OsLock +#undef sqlite3OsUnlock +#undef sqlite3OsCheckReservedLock + +#define BLOCKSIZE 512 +#define BLOCK_OFFSET(x) ((x) * BLOCKSIZE) + + +/* +** The following variables control when a simulated crash occurs. +** +** If iCrashDelay is non-zero, then zCrashFile contains (full path) name of +** a file that SQLite will call sqlite3OsSync() on. Each time this happens +** iCrashDelay is decremented. If iCrashDelay is zero after being +** decremented, a "crash" occurs during the sync() operation. +** +** In other words, a crash occurs the iCrashDelay'th time zCrashFile is +** synced. +*/ +static int iCrashDelay = 0; +char zCrashFile[256]; + +/* +** Set the value of the two crash parameters. +*/ +void sqlite3SetCrashParams(int iDelay, char const *zFile){ + sqlite3OsEnterMutex(); + assert( strlen(zFile)<256 ); + strcpy(zCrashFile, zFile); + iCrashDelay = iDelay; + sqlite3OsLeaveMutex(); +} + +/* +** File zPath is being sync()ed. Return non-zero if this should +** cause a crash. +*/ +static int crashRequired(char const *zPath){ + int r; + int n; + sqlite3OsEnterMutex(); + n = strlen(zCrashFile); + if( zCrashFile[n-1]=='*' ){ + n--; + }else if( strlen(zPath)>n ){ + n = strlen(zPath); + } + r = 0; + if( iCrashDelay>0 && strncmp(zPath, zCrashFile, n)==0 ){ + iCrashDelay--; + if( iCrashDelay<=0 ){ + r = 1; + } + } + sqlite3OsLeaveMutex(); + return r; +} + + +static OsTestFile *pAllFiles = 0; + +/* +** Initialise the os_test.c specific fields of pFile. +*/ +static void initFile(OsFile *id, char const *zName){ + OsTestFile *pFile = (OsTestFile *) + sqliteMalloc(sizeof(OsTestFile) + strlen(zName)+1); + pFile->nMaxWrite = 0; + pFile->nBlk = 0; + pFile->apBlk = 0; + pFile->zName = (char *)(&pFile[1]); + strcpy(pFile->zName, zName); + *id = pFile; + pFile->pNext = pAllFiles; + pAllFiles = pFile; +} + +/* +** Undo the work done by initFile. Delete the OsTestFile structure +** and unlink the structure from the pAllFiles list. +*/ +static void closeFile(OsFile *id){ + OsTestFile *pFile = *id; + if( pFile==pAllFiles ){ + pAllFiles = pFile->pNext; + }else{ + OsTestFile *p; + for(p=pAllFiles; p->pNext!=pFile; p=p->pNext ){ + assert( p ); + } + p->pNext = pFile->pNext; + } + sqliteFree(pFile); + *id = 0; +} + +/* +** Return the current seek offset from the start of the file. This +** is unix-only code. +*/ +static i64 osTell(OsTestFile *pFile){ + return lseek(pFile->fd.h, 0, SEEK_CUR); +} + +/* +** Load block 'blk' into the cache of pFile. +*/ +static int cacheBlock(OsTestFile *pFile, int blk){ + if( blk>=pFile->nBlk ){ + int n = ((pFile->nBlk * 2) + 100 + blk); + /* if( pFile->nBlk==0 ){ printf("DIRTY %s\n", pFile->zName); } */ + pFile->apBlk = (u8 **)sqliteRealloc(pFile->apBlk, n * sizeof(u8*)); + if( !pFile->apBlk ) return SQLITE_NOMEM; + memset(&pFile->apBlk[pFile->nBlk], 0, (n - pFile->nBlk)*sizeof(u8*)); + pFile->nBlk = n; + } + + if( !pFile->apBlk[blk] ){ + i64 filesize; + int rc; + + u8 *p = sqliteMalloc(BLOCKSIZE); + if( !p ) return SQLITE_NOMEM; + pFile->apBlk[blk] = p; + + rc = sqlite3RealFileSize(&pFile->fd, &filesize); + if( rc!=SQLITE_OK ) return rc; + + if( BLOCK_OFFSET(blk)<filesize ){ + int len = BLOCKSIZE; + rc = sqlite3RealSeek(&pFile->fd, blk*BLOCKSIZE); + if( BLOCK_OFFSET(blk+1)>filesize ){ + len = filesize - BLOCK_OFFSET(blk); + } + if( rc!=SQLITE_OK ) return rc; + rc = sqlite3RealRead(&pFile->fd, p, len); + if( rc!=SQLITE_OK ) return rc; + } + } + + return SQLITE_OK; +} + +/* #define TRACE_WRITECACHE */ + +/* +** Write the cache of pFile to disk. If crash is non-zero, randomly +** skip blocks when writing. The cache is deleted before returning. +*/ +static int writeCache2(OsTestFile *pFile, int crash){ + int i; + int nMax = pFile->nMaxWrite; + i64 offset; + int rc = SQLITE_OK; + + offset = osTell(pFile); + for(i=0; i<pFile->nBlk; i++){ + u8 *p = pFile->apBlk[i]; + if( p ){ + int skip = 0; + int trash = 0; + if( crash ){ + char random; + sqlite3Randomness(1, &random); + if( random & 0x01 ){ + if( random & 0x02 ){ + trash = 1; +#ifdef TRACE_WRITECACHE +printf("Trashing block %d of %s\n", i, pFile->zName); +#endif + }else{ + skip = 1; +#ifdef TRACE_WRITECACHE +printf("Skiping block %d of %s\n", i, pFile->zName); +#endif + } + }else{ +#ifdef TRACE_WRITECACHE +printf("Writing block %d of %s\n", i, pFile->zName); +#endif + } + } + if( rc==SQLITE_OK ){ + rc = sqlite3RealSeek(&pFile->fd, BLOCK_OFFSET(i)); + } + if( rc==SQLITE_OK && !skip ){ + int len = BLOCKSIZE; + if( BLOCK_OFFSET(i+1)>nMax ){ + len = nMax-BLOCK_OFFSET(i); + } + if( trash ){ + sqlite3Randomness(len, p); + } + rc = sqlite3RealWrite(&pFile->fd, p, len); + } + sqliteFree(p); + } + } + sqliteFree(pFile->apBlk); + pFile->nBlk = 0; + pFile->apBlk = 0; + pFile->nMaxWrite = 0; + + if( rc==SQLITE_OK ){ + rc = sqlite3RealSeek(&pFile->fd, offset); + } + return rc; +} + +/* +** Write the cache to disk. +*/ +static int writeCache(OsTestFile *pFile){ + if( pFile->apBlk ){ + int c = crashRequired(pFile->zName); + if( c ){ + OsTestFile *p; +#ifdef TRACE_WRITECACHE + printf("\nCrash during sync of %s\n", pFile->zName); +#endif + for(p=pAllFiles; p; p=p->pNext){ + writeCache2(p, 1); + } + exit(-1); + }else{ + return writeCache2(pFile, 0); + } + } + return SQLITE_OK; +} + +/* +** Close the file. +*/ +int sqlite3OsClose(OsFile *id){ + if( !(*id) ) return SQLITE_OK; + if( (*id)->fd.isOpen ){ + /* printf("CLOSE %s (%d blocks)\n", (*id)->zName, (*id)->nBlk); */ + writeCache(*id); + sqlite3RealClose(&(*id)->fd); + } + closeFile(id); + return SQLITE_OK; +} + +int sqlite3OsRead(OsFile *id, void *pBuf, int amt){ + i64 offset; /* The current offset from the start of the file */ + i64 end; /* The byte just past the last byte read */ + int blk; /* Block number the read starts on */ + int i; + u8 *zCsr; + int rc = SQLITE_OK; + OsTestFile *pFile = *id; + + offset = osTell(pFile); + end = offset+amt; + blk = (offset/BLOCKSIZE); + + zCsr = (u8 *)pBuf; + for(i=blk; i*BLOCKSIZE<end; i++){ + int off = 0; + int len = 0; + + + if( BLOCK_OFFSET(i) < offset ){ + off = offset-BLOCK_OFFSET(i); + } + len = BLOCKSIZE - off; + if( BLOCK_OFFSET(i+1) > end ){ + len = len - (BLOCK_OFFSET(i+1)-end); + } + + if( i<pFile->nBlk && pFile->apBlk[i]){ + u8 *pBlk = pFile->apBlk[i]; + memcpy(zCsr, &pBlk[off], len); + }else{ + rc = sqlite3RealSeek(&pFile->fd, BLOCK_OFFSET(i) + off); + if( rc!=SQLITE_OK ) return rc; + rc = sqlite3RealRead(&pFile->fd, zCsr, len); + if( rc!=SQLITE_OK ) return rc; + } + + zCsr += len; + } + assert( zCsr==&((u8 *)pBuf)[amt] ); + + rc = sqlite3RealSeek(&pFile->fd, end); + return rc; +} + +int sqlite3OsWrite(OsFile *id, const void *pBuf, int amt){ + i64 offset; /* The current offset from the start of the file */ + i64 end; /* The byte just past the last byte written */ + int blk; /* Block number the write starts on */ + int i; + const u8 *zCsr; + int rc = SQLITE_OK; + OsTestFile *pFile = *id; + + offset = osTell(pFile); + end = offset+amt; + blk = (offset/BLOCKSIZE); + + zCsr = (u8 *)pBuf; + for(i=blk; i*BLOCKSIZE<end; i++){ + u8 *pBlk; + int off = 0; + int len = 0; + + /* Make sure the block is in the cache */ + rc = cacheBlock(pFile, i); + if( rc!=SQLITE_OK ) return rc; + + /* Write into the cache */ + pBlk = pFile->apBlk[i]; + assert( pBlk ); + + if( BLOCK_OFFSET(i) < offset ){ + off = offset-BLOCK_OFFSET(i); + } + len = BLOCKSIZE - off; + if( BLOCK_OFFSET(i+1) > end ){ + len = len - (BLOCK_OFFSET(i+1)-end); + } + memcpy(&pBlk[off], zCsr, len); + zCsr += len; + } + if( pFile->nMaxWrite<end ){ + pFile->nMaxWrite = end; + } + assert( zCsr==&((u8 *)pBuf)[amt] ); + + rc = sqlite3RealSeek(&pFile->fd, end); + return rc; +} + +/* +** Sync the file. First flush the write-cache to disk, then call the +** real sync() function. +*/ +int sqlite3OsSync(OsFile *id){ + int rc; + /* printf("SYNC %s (%d blocks)\n", (*id)->zName, (*id)->nBlk); */ + rc = writeCache(*id); + if( rc!=SQLITE_OK ) return rc; + rc = sqlite3RealSync(&(*id)->fd); + return rc; +} + +/* +** Truncate the file. Set the internal OsFile.nMaxWrite variable to the new +** file size to ensure that nothing in the write-cache past this point +** is written to disk. +*/ +int sqlite3OsTruncate(OsFile *id, i64 nByte){ + (*id)->nMaxWrite = nByte; + return sqlite3RealTruncate(&(*id)->fd, nByte); +} + +/* +** Return the size of the file. If the cache contains a write that extended +** the file, then return this size instead of the on-disk size. +*/ +int sqlite3OsFileSize(OsFile *id, i64 *pSize){ + int rc = sqlite3RealFileSize(&(*id)->fd, pSize); + if( rc==SQLITE_OK && pSize && *pSize<(*id)->nMaxWrite ){ + *pSize = (*id)->nMaxWrite; + } + return rc; +} + +/* +** The three functions used to open files. All that is required is to +** initialise the os_test.c specific fields and then call the corresponding +** os_unix.c function to really open the file. +*/ +int sqlite3OsOpenReadWrite(const char *zFilename, OsFile *id, int *pReadonly){ + initFile(id, zFilename); + return sqlite3RealOpenReadWrite(zFilename, &(*id)->fd, pReadonly); +} +int sqlite3OsOpenExclusive(const char *zFilename, OsFile *id, int delFlag){ + initFile(id, zFilename); + return sqlite3RealOpenExclusive(zFilename, &(*id)->fd, delFlag); +} +int sqlite3OsOpenReadOnly(const char *zFilename, OsFile *id){ + initFile(id, zFilename); + return sqlite3RealOpenReadOnly(zFilename, &(*id)->fd); +} + +/* +** These six function calls are passed straight through to the os_unix.c +** backend. +*/ +int sqlite3OsSeek(OsFile *id, i64 offset){ + return sqlite3RealSeek(&(*id)->fd, offset); +} +int sqlite3OsCheckReservedLock(OsFile *id){ + return sqlite3RealCheckReservedLock(&(*id)->fd); +} +int sqlite3OsLock(OsFile *id, int locktype){ + return sqlite3RealLock(&(*id)->fd, locktype); +} +int sqlite3OsUnlock(OsFile *id, int locktype){ + return sqlite3RealUnlock(&(*id)->fd, locktype); +} +int sqlite3OsOpenDirectory(const char *zDirname, OsFile *id){ + return sqlite3RealOpenDirectory(zDirname, &(*id)->fd); +} + +#endif /* OS_TEST */ |
