| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | #include "sqliteInt.h" |
| |
|
| | |
| | typedef struct MemJournal MemJournal; |
| | typedef struct FilePoint FilePoint; |
| | typedef struct FileChunk FileChunk; |
| |
|
| | |
| | |
| | |
| | |
| | |
| | |
| | struct FileChunk { |
| | FileChunk *pNext; |
| | u8 zChunk[8]; |
| | }; |
| |
|
| | |
| | |
| | |
| | #define MEMJOURNAL_DFLT_FILECHUNKSIZE 1024 |
| |
|
| | |
| | |
| | |
| | |
| | #define fileChunkSize(nChunkSize) (sizeof(FileChunk) + ((nChunkSize)-8)) |
| |
|
| | |
| | |
| | |
| | |
| | struct FilePoint { |
| | sqlite3_int64 iOffset; |
| | FileChunk *pChunk; |
| | }; |
| |
|
| | |
| | |
| | |
| | |
| | struct MemJournal { |
| | const sqlite3_io_methods *pMethod; |
| | int nChunkSize; |
| |
|
| | int nSpill; |
| | FileChunk *pFirst; |
| | FilePoint endpoint; |
| | FilePoint readpoint; |
| |
|
| | int flags; |
| | sqlite3_vfs *pVfs; |
| | const char *zJournal; |
| | }; |
| |
|
| | |
| | |
| | |
| | |
| | static int memjrnlRead( |
| | sqlite3_file *pJfd, |
| | void *zBuf, |
| | int iAmt, |
| | sqlite_int64 iOfst |
| | ){ |
| | MemJournal *p = (MemJournal *)pJfd; |
| | u8 *zOut = zBuf; |
| | int nRead = iAmt; |
| | int iChunkOffset; |
| | FileChunk *pChunk; |
| |
|
| | if( (iAmt+iOfst)>p->endpoint.iOffset ){ |
| | return SQLITE_IOERR_SHORT_READ; |
| | } |
| | assert( p->readpoint.iOffset==0 || p->readpoint.pChunk!=0 ); |
| | if( p->readpoint.iOffset!=iOfst || iOfst==0 ){ |
| | sqlite3_int64 iOff = 0; |
| | for(pChunk=p->pFirst; |
| | ALWAYS(pChunk) && (iOff+p->nChunkSize)<=iOfst; |
| | pChunk=pChunk->pNext |
| | ){ |
| | iOff += p->nChunkSize; |
| | } |
| | }else{ |
| | pChunk = p->readpoint.pChunk; |
| | assert( pChunk!=0 ); |
| | } |
| |
|
| | iChunkOffset = (int)(iOfst%p->nChunkSize); |
| | do { |
| | int iSpace = p->nChunkSize - iChunkOffset; |
| | int nCopy = MIN(nRead, (p->nChunkSize - iChunkOffset)); |
| | memcpy(zOut, (u8*)pChunk->zChunk + iChunkOffset, nCopy); |
| | zOut += nCopy; |
| | nRead -= iSpace; |
| | iChunkOffset = 0; |
| | } while( nRead>=0 && (pChunk=pChunk->pNext)!=0 && nRead>0 ); |
| | p->readpoint.iOffset = pChunk ? iOfst+iAmt : 0; |
| | p->readpoint.pChunk = pChunk; |
| |
|
| | return SQLITE_OK; |
| | } |
| |
|
| | |
| | |
| | |
| | static void memjrnlFreeChunks(FileChunk *pFirst){ |
| | FileChunk *pIter; |
| | FileChunk *pNext; |
| | for(pIter=pFirst; pIter; pIter=pNext){ |
| | pNext = pIter->pNext; |
| | sqlite3_free(pIter); |
| | } |
| | } |
| |
|
| | |
| | |
| | |
| | static int memjrnlCreateFile(MemJournal *p){ |
| | int rc; |
| | sqlite3_file *pReal = (sqlite3_file*)p; |
| | MemJournal copy = *p; |
| |
|
| | memset(p, 0, sizeof(MemJournal)); |
| | rc = sqlite3OsOpen(copy.pVfs, copy.zJournal, pReal, copy.flags, 0); |
| | if( rc==SQLITE_OK ){ |
| | int nChunk = copy.nChunkSize; |
| | i64 iOff = 0; |
| | FileChunk *pIter; |
| | for(pIter=copy.pFirst; pIter; pIter=pIter->pNext){ |
| | if( iOff + nChunk > copy.endpoint.iOffset ){ |
| | nChunk = copy.endpoint.iOffset - iOff; |
| | } |
| | rc = sqlite3OsWrite(pReal, (u8*)pIter->zChunk, nChunk, iOff); |
| | if( rc ) break; |
| | iOff += nChunk; |
| | } |
| | if( rc==SQLITE_OK ){ |
| | |
| | memjrnlFreeChunks(copy.pFirst); |
| | } |
| | } |
| | if( rc!=SQLITE_OK ){ |
| | |
| | |
| | |
| | |
| | sqlite3OsClose(pReal); |
| | *p = copy; |
| | } |
| | return rc; |
| | } |
| |
|
| |
|
| | |
| | static int memjrnlTruncate(sqlite3_file *pJfd, sqlite_int64 size); |
| |
|
| | |
| | |
| | |
| | static int memjrnlWrite( |
| | sqlite3_file *pJfd, |
| | const void *zBuf, |
| | int iAmt, |
| | sqlite_int64 iOfst |
| | ){ |
| | MemJournal *p = (MemJournal *)pJfd; |
| | int nWrite = iAmt; |
| | u8 *zWrite = (u8 *)zBuf; |
| |
|
| | |
| | |
| | if( p->nSpill>0 && (iAmt+iOfst)>p->nSpill ){ |
| | int rc = memjrnlCreateFile(p); |
| | if( rc==SQLITE_OK ){ |
| | rc = sqlite3OsWrite(pJfd, zBuf, iAmt, iOfst); |
| | } |
| | return rc; |
| | } |
| |
|
| | |
| | else{ |
| | |
| | |
| | |
| | |
| | |
| | assert( iOfst<=p->endpoint.iOffset ); |
| | if( iOfst>0 && iOfst!=p->endpoint.iOffset ){ |
| | memjrnlTruncate(pJfd, iOfst); |
| | } |
| | if( iOfst==0 && p->pFirst ){ |
| | assert( p->nChunkSize>iAmt ); |
| | memcpy((u8*)p->pFirst->zChunk, zBuf, iAmt); |
| | }else{ |
| | while( nWrite>0 ){ |
| | FileChunk *pChunk = p->endpoint.pChunk; |
| | int iChunkOffset = (int)(p->endpoint.iOffset%p->nChunkSize); |
| | int iSpace = MIN(nWrite, p->nChunkSize - iChunkOffset); |
| |
|
| | assert( pChunk!=0 || iChunkOffset==0 ); |
| | if( iChunkOffset==0 ){ |
| | |
| | FileChunk *pNew = sqlite3_malloc(fileChunkSize(p->nChunkSize)); |
| | if( !pNew ){ |
| | return SQLITE_IOERR_NOMEM_BKPT; |
| | } |
| | pNew->pNext = 0; |
| | if( pChunk ){ |
| | assert( p->pFirst ); |
| | pChunk->pNext = pNew; |
| | }else{ |
| | assert( !p->pFirst ); |
| | p->pFirst = pNew; |
| | } |
| | pChunk = p->endpoint.pChunk = pNew; |
| | } |
| |
|
| | assert( pChunk!=0 ); |
| | memcpy((u8*)pChunk->zChunk + iChunkOffset, zWrite, iSpace); |
| | zWrite += iSpace; |
| | nWrite -= iSpace; |
| | p->endpoint.iOffset += iSpace; |
| | } |
| | } |
| | } |
| |
|
| | return SQLITE_OK; |
| | } |
| |
|
| | |
| | |
| | |
| | static int memjrnlTruncate(sqlite3_file *pJfd, sqlite_int64 size){ |
| | MemJournal *p = (MemJournal *)pJfd; |
| | assert( p->endpoint.pChunk==0 || p->endpoint.pChunk->pNext==0 ); |
| | if( size<p->endpoint.iOffset ){ |
| | FileChunk *pIter = 0; |
| | if( size==0 ){ |
| | memjrnlFreeChunks(p->pFirst); |
| | p->pFirst = 0; |
| | }else{ |
| | i64 iOff = p->nChunkSize; |
| | for(pIter=p->pFirst; ALWAYS(pIter) && iOff<size; pIter=pIter->pNext){ |
| | iOff += p->nChunkSize; |
| | } |
| | if( ALWAYS(pIter) ){ |
| | memjrnlFreeChunks(pIter->pNext); |
| | pIter->pNext = 0; |
| | } |
| | } |
| |
|
| | p->endpoint.pChunk = pIter; |
| | p->endpoint.iOffset = size; |
| | p->readpoint.pChunk = 0; |
| | p->readpoint.iOffset = 0; |
| | } |
| | return SQLITE_OK; |
| | } |
| |
|
| | |
| | |
| | |
| | static int memjrnlClose(sqlite3_file *pJfd){ |
| | MemJournal *p = (MemJournal *)pJfd; |
| | memjrnlFreeChunks(p->pFirst); |
| | return SQLITE_OK; |
| | } |
| |
|
| | |
| | |
| | |
| | |
| | |
| | |
| | static int memjrnlSync(sqlite3_file *pJfd, int flags){ |
| | UNUSED_PARAMETER2(pJfd, flags); |
| | return SQLITE_OK; |
| | } |
| |
|
| | |
| | |
| | |
| | static int memjrnlFileSize(sqlite3_file *pJfd, sqlite_int64 *pSize){ |
| | MemJournal *p = (MemJournal *)pJfd; |
| | *pSize = (sqlite_int64) p->endpoint.iOffset; |
| | return SQLITE_OK; |
| | } |
| |
|
| | |
| | |
| | |
| | static const struct sqlite3_io_methods MemJournalMethods = { |
| | 1, |
| | memjrnlClose, |
| | memjrnlRead, |
| | memjrnlWrite, |
| | memjrnlTruncate, |
| | memjrnlSync, |
| | memjrnlFileSize, |
| | 0, |
| | 0, |
| | 0, |
| | 0, |
| | 0, |
| | 0, |
| | 0, |
| | 0, |
| | 0, |
| | 0, |
| | 0, |
| | 0 |
| | }; |
| |
|
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | int sqlite3JournalOpen( |
| | sqlite3_vfs *pVfs, |
| | const char *zName, |
| | sqlite3_file *pJfd, |
| | int flags, |
| | int nSpill |
| | ){ |
| | MemJournal *p = (MemJournal*)pJfd; |
| |
|
| | assert( zName || nSpill<0 || (flags & SQLITE_OPEN_EXCLUSIVE) ); |
| |
|
| | |
| | |
| | |
| | |
| | memset(p, 0, sizeof(MemJournal)); |
| | if( nSpill==0 ){ |
| | return sqlite3OsOpen(pVfs, zName, pJfd, flags, 0); |
| | } |
| |
|
| | if( nSpill>0 ){ |
| | p->nChunkSize = nSpill; |
| | }else{ |
| | p->nChunkSize = 8 + MEMJOURNAL_DFLT_FILECHUNKSIZE - sizeof(FileChunk); |
| | assert( MEMJOURNAL_DFLT_FILECHUNKSIZE==fileChunkSize(p->nChunkSize) ); |
| | } |
| |
|
| | pJfd->pMethods = (const sqlite3_io_methods*)&MemJournalMethods; |
| | p->nSpill = nSpill; |
| | p->flags = flags; |
| | p->zJournal = zName; |
| | p->pVfs = pVfs; |
| | return SQLITE_OK; |
| | } |
| |
|
| | |
| | |
| | |
| | void sqlite3MemJournalOpen(sqlite3_file *pJfd){ |
| | sqlite3JournalOpen(0, 0, pJfd, 0, -1); |
| | } |
| |
|
| | #if defined(SQLITE_ENABLE_ATOMIC_WRITE) \ |
| | || defined(SQLITE_ENABLE_BATCH_ATOMIC_WRITE) |
| | |
| | |
| | |
| | |
| | |
| | |
| | int sqlite3JournalCreate(sqlite3_file *pJfd){ |
| | int rc = SQLITE_OK; |
| | MemJournal *p = (MemJournal*)pJfd; |
| | if( pJfd->pMethods==&MemJournalMethods && ( |
| | #ifdef SQLITE_ENABLE_ATOMIC_WRITE |
| | p->nSpill>0 |
| | #else |
| | |
| | |
| | |
| | NEVER(p->nSpill>0) |
| | #endif |
| | #ifdef SQLITE_ENABLE_BATCH_ATOMIC_WRITE |
| | || (p->flags & SQLITE_OPEN_MAIN_JOURNAL) |
| | #endif |
| | )){ |
| | rc = memjrnlCreateFile(p); |
| | } |
| | return rc; |
| | } |
| | #endif |
| |
|
| | |
| | |
| | |
| | |
| | |
| | int sqlite3JournalIsInMemory(sqlite3_file *p){ |
| | return p->pMethods==&MemJournalMethods; |
| | } |
| |
|
| | |
| | |
| | |
| | |
| | int sqlite3JournalSize(sqlite3_vfs *pVfs){ |
| | return MAX(pVfs->szOsFile, (int)sizeof(MemJournal)); |
| | } |
| |
|