| OLD | NEW | 
 | (Empty) | 
|    1 /* |  | 
|    2 ** 2008 October 7 |  | 
|    3 ** |  | 
|    4 ** The author disclaims copyright to this source code.  In place of |  | 
|    5 ** a legal notice, here is a blessing: |  | 
|    6 ** |  | 
|    7 **    May you do good and not evil. |  | 
|    8 **    May you find forgiveness for yourself and forgive others. |  | 
|    9 **    May you share freely, never taking more than you give. |  | 
|   10 ** |  | 
|   11 ************************************************************************* |  | 
|   12 ** |  | 
|   13 ** This file contains code use to implement an in-memory rollback journal. |  | 
|   14 ** The in-memory rollback journal is used to journal transactions for |  | 
|   15 ** ":memory:" databases and when the journal_mode=MEMORY pragma is used. |  | 
|   16 ** |  | 
|   17 ** @(#) $Id: memjournal.c,v 1.12 2009/05/04 11:42:30 danielk1977 Exp $ |  | 
|   18 */ |  | 
|   19 #include "sqliteInt.h" |  | 
|   20  |  | 
|   21 /* Forward references to internal structures */ |  | 
|   22 typedef struct MemJournal MemJournal; |  | 
|   23 typedef struct FilePoint FilePoint; |  | 
|   24 typedef struct FileChunk FileChunk; |  | 
|   25  |  | 
|   26 /* Space to hold the rollback journal is allocated in increments of |  | 
|   27 ** this many bytes. |  | 
|   28 ** |  | 
|   29 ** The size chosen is a little less than a power of two.  That way, |  | 
|   30 ** the FileChunk object will have a size that almost exactly fills |  | 
|   31 ** a power-of-two allocation.  This mimimizes wasted space in power-of-two |  | 
|   32 ** memory allocators. |  | 
|   33 */ |  | 
|   34 #define JOURNAL_CHUNKSIZE ((int)(1024-sizeof(FileChunk*))) |  | 
|   35  |  | 
|   36 /* Macro to find the minimum of two numeric values. |  | 
|   37 */ |  | 
|   38 #ifndef MIN |  | 
|   39 # define MIN(x,y) ((x)<(y)?(x):(y)) |  | 
|   40 #endif |  | 
|   41  |  | 
|   42 /* |  | 
|   43 ** The rollback journal is composed of a linked list of these structures. |  | 
|   44 */ |  | 
|   45 struct FileChunk { |  | 
|   46   FileChunk *pNext;               /* Next chunk in the journal */ |  | 
|   47   u8 zChunk[JOURNAL_CHUNKSIZE];   /* Content of this chunk */ |  | 
|   48 }; |  | 
|   49  |  | 
|   50 /* |  | 
|   51 ** An instance of this object serves as a cursor into the rollback journal. |  | 
|   52 ** The cursor can be either for reading or writing. |  | 
|   53 */ |  | 
|   54 struct FilePoint { |  | 
|   55   sqlite3_int64 iOffset;          /* Offset from the beginning of the file */ |  | 
|   56   FileChunk *pChunk;              /* Specific chunk into which cursor points */ |  | 
|   57 }; |  | 
|   58  |  | 
|   59 /* |  | 
|   60 ** This subclass is a subclass of sqlite3_file.  Each open memory-journal |  | 
|   61 ** is an instance of this class. |  | 
|   62 */ |  | 
|   63 struct MemJournal { |  | 
|   64   sqlite3_io_methods *pMethod;    /* Parent class. MUST BE FIRST */ |  | 
|   65   FileChunk *pFirst;              /* Head of in-memory chunk-list */ |  | 
|   66   FilePoint endpoint;             /* Pointer to the end of the file */ |  | 
|   67   FilePoint readpoint;            /* Pointer to the end of the last xRead() */ |  | 
|   68 }; |  | 
|   69  |  | 
|   70 /* |  | 
|   71 ** Read data from the in-memory journal file.  This is the implementation |  | 
|   72 ** of the sqlite3_vfs.xRead method. |  | 
|   73 */ |  | 
|   74 static int memjrnlRead( |  | 
|   75   sqlite3_file *pJfd,    /* The journal file from which to read */ |  | 
|   76   void *zBuf,            /* Put the results here */ |  | 
|   77   int iAmt,              /* Number of bytes to read */ |  | 
|   78   sqlite_int64 iOfst     /* Begin reading at this offset */ |  | 
|   79 ){ |  | 
|   80   MemJournal *p = (MemJournal *)pJfd; |  | 
|   81   u8 *zOut = zBuf; |  | 
|   82   int nRead = iAmt; |  | 
|   83   int iChunkOffset; |  | 
|   84   FileChunk *pChunk; |  | 
|   85  |  | 
|   86   /* SQLite never tries to read past the end of a rollback journal file */ |  | 
|   87   assert( iOfst+iAmt<=p->endpoint.iOffset ); |  | 
|   88  |  | 
|   89   if( p->readpoint.iOffset!=iOfst || iOfst==0 ){ |  | 
|   90     sqlite3_int64 iOff = 0; |  | 
|   91     for(pChunk=p->pFirst;  |  | 
|   92         ALWAYS(pChunk) && (iOff+JOURNAL_CHUNKSIZE)<=iOfst; |  | 
|   93         pChunk=pChunk->pNext |  | 
|   94     ){ |  | 
|   95       iOff += JOURNAL_CHUNKSIZE; |  | 
|   96     } |  | 
|   97   }else{ |  | 
|   98     pChunk = p->readpoint.pChunk; |  | 
|   99   } |  | 
|  100  |  | 
|  101   iChunkOffset = (int)(iOfst%JOURNAL_CHUNKSIZE); |  | 
|  102   do { |  | 
|  103     int iSpace = JOURNAL_CHUNKSIZE - iChunkOffset; |  | 
|  104     int nCopy = MIN(nRead, (JOURNAL_CHUNKSIZE - iChunkOffset)); |  | 
|  105     memcpy(zOut, &pChunk->zChunk[iChunkOffset], nCopy); |  | 
|  106     zOut += nCopy; |  | 
|  107     nRead -= iSpace; |  | 
|  108     iChunkOffset = 0; |  | 
|  109   } while( nRead>=0 && (pChunk=pChunk->pNext)!=0 && nRead>0 ); |  | 
|  110   p->readpoint.iOffset = iOfst+iAmt; |  | 
|  111   p->readpoint.pChunk = pChunk; |  | 
|  112  |  | 
|  113   return SQLITE_OK; |  | 
|  114 } |  | 
|  115  |  | 
|  116 /* |  | 
|  117 ** Write data to the file. |  | 
|  118 */ |  | 
|  119 static int memjrnlWrite( |  | 
|  120   sqlite3_file *pJfd,    /* The journal file into which to write */ |  | 
|  121   const void *zBuf,      /* Take data to be written from here */ |  | 
|  122   int iAmt,              /* Number of bytes to write */ |  | 
|  123   sqlite_int64 iOfst     /* Begin writing at this offset into the file */ |  | 
|  124 ){ |  | 
|  125   MemJournal *p = (MemJournal *)pJfd; |  | 
|  126   int nWrite = iAmt; |  | 
|  127   u8 *zWrite = (u8 *)zBuf; |  | 
|  128  |  | 
|  129   /* An in-memory journal file should only ever be appended to. Random |  | 
|  130   ** access writes are not required by sqlite. |  | 
|  131   */ |  | 
|  132   assert( iOfst==p->endpoint.iOffset ); |  | 
|  133   UNUSED_PARAMETER(iOfst); |  | 
|  134  |  | 
|  135   while( nWrite>0 ){ |  | 
|  136     FileChunk *pChunk = p->endpoint.pChunk; |  | 
|  137     int iChunkOffset = (int)(p->endpoint.iOffset%JOURNAL_CHUNKSIZE); |  | 
|  138     int iSpace = MIN(nWrite, JOURNAL_CHUNKSIZE - iChunkOffset); |  | 
|  139  |  | 
|  140     if( iChunkOffset==0 ){ |  | 
|  141       /* New chunk is required to extend the file. */ |  | 
|  142       FileChunk *pNew = sqlite3_malloc(sizeof(FileChunk)); |  | 
|  143       if( !pNew ){ |  | 
|  144         return SQLITE_IOERR_NOMEM; |  | 
|  145       } |  | 
|  146       pNew->pNext = 0; |  | 
|  147       if( pChunk ){ |  | 
|  148         assert( p->pFirst ); |  | 
|  149         pChunk->pNext = pNew; |  | 
|  150       }else{ |  | 
|  151         assert( !p->pFirst ); |  | 
|  152         p->pFirst = pNew; |  | 
|  153       } |  | 
|  154       p->endpoint.pChunk = pNew; |  | 
|  155     } |  | 
|  156  |  | 
|  157     memcpy(&p->endpoint.pChunk->zChunk[iChunkOffset], zWrite, iSpace); |  | 
|  158     zWrite += iSpace; |  | 
|  159     nWrite -= iSpace; |  | 
|  160     p->endpoint.iOffset += iSpace; |  | 
|  161   } |  | 
|  162  |  | 
|  163   return SQLITE_OK; |  | 
|  164 } |  | 
|  165  |  | 
|  166 /* |  | 
|  167 ** Truncate the file. |  | 
|  168 */ |  | 
|  169 static int memjrnlTruncate(sqlite3_file *pJfd, sqlite_int64 size){ |  | 
|  170   MemJournal *p = (MemJournal *)pJfd; |  | 
|  171   FileChunk *pChunk; |  | 
|  172   assert(size==0); |  | 
|  173   UNUSED_PARAMETER(size); |  | 
|  174   pChunk = p->pFirst; |  | 
|  175   while( pChunk ){ |  | 
|  176     FileChunk *pTmp = pChunk; |  | 
|  177     pChunk = pChunk->pNext; |  | 
|  178     sqlite3_free(pTmp); |  | 
|  179   } |  | 
|  180   sqlite3MemJournalOpen(pJfd); |  | 
|  181   return SQLITE_OK; |  | 
|  182 } |  | 
|  183  |  | 
|  184 /* |  | 
|  185 ** Close the file. |  | 
|  186 */ |  | 
|  187 static int memjrnlClose(sqlite3_file *pJfd){ |  | 
|  188   memjrnlTruncate(pJfd, 0); |  | 
|  189   return SQLITE_OK; |  | 
|  190 } |  | 
|  191  |  | 
|  192  |  | 
|  193 /* |  | 
|  194 ** Sync the file. |  | 
|  195 ** |  | 
|  196 ** Syncing an in-memory journal is a no-op.  And, in fact, this routine |  | 
|  197 ** is never called in a working implementation.  This implementation |  | 
|  198 ** exists purely as a contingency, in case some malfunction in some other |  | 
|  199 ** part of SQLite causes Sync to be called by mistake. |  | 
|  200 */ |  | 
|  201 static int memjrnlSync(sqlite3_file *NotUsed, int NotUsed2){   /*NO_TEST*/ |  | 
|  202   UNUSED_PARAMETER2(NotUsed, NotUsed2);                        /*NO_TEST*/ |  | 
|  203   assert( 0 );                                                 /*NO_TEST*/ |  | 
|  204   return SQLITE_OK;                                            /*NO_TEST*/ |  | 
|  205 }                                                              /*NO_TEST*/ |  | 
|  206  |  | 
|  207 /* |  | 
|  208 ** Query the size of the file in bytes. |  | 
|  209 */ |  | 
|  210 static int memjrnlFileSize(sqlite3_file *pJfd, sqlite_int64 *pSize){ |  | 
|  211   MemJournal *p = (MemJournal *)pJfd; |  | 
|  212   *pSize = (sqlite_int64) p->endpoint.iOffset; |  | 
|  213   return SQLITE_OK; |  | 
|  214 } |  | 
|  215  |  | 
|  216 /* |  | 
|  217 ** Table of methods for MemJournal sqlite3_file object. |  | 
|  218 */ |  | 
|  219 static struct sqlite3_io_methods MemJournalMethods = { |  | 
|  220   1,                /* iVersion */ |  | 
|  221   memjrnlClose,     /* xClose */ |  | 
|  222   memjrnlRead,      /* xRead */ |  | 
|  223   memjrnlWrite,     /* xWrite */ |  | 
|  224   memjrnlTruncate,  /* xTruncate */ |  | 
|  225   memjrnlSync,      /* xSync */ |  | 
|  226   memjrnlFileSize,  /* xFileSize */ |  | 
|  227   0,                /* xLock */ |  | 
|  228   0,                /* xUnlock */ |  | 
|  229   0,                /* xCheckReservedLock */ |  | 
|  230   0,                /* xFileControl */ |  | 
|  231   0,                /* xSectorSize */ |  | 
|  232   0                 /* xDeviceCharacteristics */ |  | 
|  233 }; |  | 
|  234  |  | 
|  235 /*  |  | 
|  236 ** Open a journal file. |  | 
|  237 */ |  | 
|  238 void sqlite3MemJournalOpen(sqlite3_file *pJfd){ |  | 
|  239   MemJournal *p = (MemJournal *)pJfd; |  | 
|  240   assert( EIGHT_BYTE_ALIGNMENT(p) ); |  | 
|  241   memset(p, 0, sqlite3MemJournalSize()); |  | 
|  242   p->pMethod = &MemJournalMethods; |  | 
|  243 } |  | 
|  244  |  | 
|  245 /* |  | 
|  246 ** Return true if the file-handle passed as an argument is  |  | 
|  247 ** an in-memory journal  |  | 
|  248 */ |  | 
|  249 int sqlite3IsMemJournal(sqlite3_file *pJfd){ |  | 
|  250   return pJfd->pMethods==&MemJournalMethods; |  | 
|  251 } |  | 
|  252  |  | 
|  253 /*  |  | 
|  254 ** Return the number of bytes required to store a MemJournal that uses vfs |  | 
|  255 ** pVfs to create the underlying on-disk files. |  | 
|  256 */ |  | 
|  257 int sqlite3MemJournalSize(void){ |  | 
|  258   return sizeof(MemJournal); |  | 
|  259 } |  | 
| OLD | NEW |