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