blob: 9ee6f10f00c3faeba8a7c58e57dc656f2642ee53 [file] [log] [blame]
danielk197739281b42008-10-17 19:13:04 +00001/*
drh27c3bd72008-10-28 18:12:36 +00002** 2008 October 7
danielk197739281b42008-10-17 19:13:04 +00003**
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**
drh27c3bd72008-10-28 18:12:36 +000013** 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**
danielk1977f3d3c272008-11-19 16:52:44 +000017** @(#) $Id: memjournal.c,v 1.5 2008/11/19 16:52:44 danielk1977 Exp $
danielk197739281b42008-10-17 19:13:04 +000018*/
danielk197739281b42008-10-17 19:13:04 +000019#include "sqliteInt.h"
20
drh27c3bd72008-10-28 18:12:36 +000021/* Forward references to internal structures */
danielk197739281b42008-10-17 19:13:04 +000022typedef struct MemJournal MemJournal;
23typedef struct FilePoint FilePoint;
24typedef struct FileChunk FileChunk;
25
drh27c3bd72008-10-28 18:12:36 +000026/* Space to hold the rollback journal is allocated in increments of
27** this many bytes.
28*/
danielk197739281b42008-10-17 19:13:04 +000029#define JOURNAL_CHUNKSIZE 1024
30
drh27c3bd72008-10-28 18:12:36 +000031/* Macro to find the minimum of two numeric values.
32*/
drh7ab49bf2008-11-12 15:24:27 +000033#ifndef MIN
34# define MIN(x,y) ((x)<(y)?(x):(y))
35#endif
danielk197739281b42008-10-17 19:13:04 +000036
drh27c3bd72008-10-28 18:12:36 +000037/*
38** The rollback journal is composed of a linked list of these structures.
39*/
danielk197739281b42008-10-17 19:13:04 +000040struct FileChunk {
drh27c3bd72008-10-28 18:12:36 +000041 FileChunk *pNext; /* Next chunk in the journal */
42 u8 zChunk[JOURNAL_CHUNKSIZE]; /* Content of this chunk */
danielk197739281b42008-10-17 19:13:04 +000043};
44
drh27c3bd72008-10-28 18:12:36 +000045/*
46** An instance of this object serves as a cursor into the rollback journal.
47** The cursor can be either for reading or writing.
48*/
danielk197739281b42008-10-17 19:13:04 +000049struct FilePoint {
drh27c3bd72008-10-28 18:12:36 +000050 sqlite3_int64 iOffset; /* Offset from the beginning of the file */
51 FileChunk *pChunk; /* Specific chunk into which cursor points */
danielk197739281b42008-10-17 19:13:04 +000052};
53
drh27c3bd72008-10-28 18:12:36 +000054/*
55** This subclass is a subclass of sqlite3_file. Each open memory-journal
56** is an instance of this class.
57*/
danielk197739281b42008-10-17 19:13:04 +000058struct MemJournal {
drh27c3bd72008-10-28 18:12:36 +000059 sqlite3_io_methods *pMethod; /* Parent class. MUST BE FIRST */
danielk197739281b42008-10-17 19:13:04 +000060 FileChunk *pFirst; /* Head of in-memory chunk-list */
61 FilePoint endpoint; /* Pointer to the end of the file */
62 FilePoint readpoint; /* Pointer to the end of the last xRead() */
63};
64
65/*
66** Read data from the file.
67*/
68static int memjrnlRead(
69 sqlite3_file *pJfd, /* The journal file from which to read */
70 void *zBuf, /* Put the results here */
71 int iAmt, /* Number of bytes to read */
72 sqlite_int64 iOfst /* Begin reading at this offset */
73){
74 MemJournal *p = (MemJournal *)pJfd;
75 u8 *zOut = zBuf;
76 int nRead = iAmt;
77 int iChunkOffset;
78 FileChunk *pChunk;
79
80 assert( iOfst+iAmt<=p->endpoint.iOffset );
81
82 if( p->readpoint.iOffset!=iOfst || iOfst==0 ){
83 sqlite3_int64 iOff = 0;
84 for(pChunk=p->pFirst;
85 pChunk && (iOff+JOURNAL_CHUNKSIZE)<=iOfst;
86 pChunk=pChunk->pNext
87 ){
88 iOff += JOURNAL_CHUNKSIZE;
89 }
90 }else{
91 pChunk = p->readpoint.pChunk;
92 }
93
94 iChunkOffset = (iOfst%JOURNAL_CHUNKSIZE);
95 do {
96 int iSpace = JOURNAL_CHUNKSIZE - iChunkOffset;
97 int nCopy = MIN(nRead, (JOURNAL_CHUNKSIZE - iChunkOffset));
98 memcpy(zOut, &pChunk->zChunk[iChunkOffset], nCopy);
99 zOut += nCopy;
100 nRead -= iSpace;
101 iChunkOffset = 0;
102 } while( nRead>=0 && (pChunk=pChunk->pNext) && nRead>0 );
103 p->readpoint.iOffset = iOfst+iAmt;
104 p->readpoint.pChunk = pChunk;
105
106 return SQLITE_OK;
107}
108
109/*
110** Write data to the file.
111*/
112static int memjrnlWrite(
113 sqlite3_file *pJfd, /* The journal file into which to write */
114 const void *zBuf, /* Take data to be written from here */
115 int iAmt, /* Number of bytes to write */
116 sqlite_int64 iOfst /* Begin writing at this offset into the file */
117){
118 MemJournal *p = (MemJournal *)pJfd;
119 int nWrite = iAmt;
120 u8 *zWrite = (u8 *)zBuf;
121
122 /* An in-memory journal file should only ever be appended to. Random
123 ** access writes are not required by sqlite.
124 */
125 assert(iOfst==p->endpoint.iOffset);
danielk1977f3d3c272008-11-19 16:52:44 +0000126 UNUSED_PARAMETER(iOfst);
danielk197739281b42008-10-17 19:13:04 +0000127
128 while( nWrite>0 ){
129 FileChunk *pChunk = p->endpoint.pChunk;
130 int iChunkOffset = p->endpoint.iOffset%JOURNAL_CHUNKSIZE;
131 int iSpace = MIN(nWrite, JOURNAL_CHUNKSIZE - iChunkOffset);
132
133 if( iChunkOffset==0 ){
134 /* New chunk is required to extend the file. */
135 FileChunk *pNew = sqlite3_malloc(sizeof(FileChunk));
136 if( !pNew ){
137 return SQLITE_IOERR_NOMEM;
138 }
139 pNew->pNext = 0;
140 if( pChunk ){
141 assert( p->pFirst );
142 pChunk->pNext = pNew;
143 }else{
144 assert( !p->pFirst );
145 p->pFirst = pNew;
146 }
147 p->endpoint.pChunk = pNew;
148 }
149
150 memcpy(&p->endpoint.pChunk->zChunk[iChunkOffset], zWrite, iSpace);
151 zWrite += iSpace;
152 nWrite -= iSpace;
153 p->endpoint.iOffset += iSpace;
154 }
155
156 return SQLITE_OK;
157}
158
159/*
160** Truncate the file.
161*/
162static int memjrnlTruncate(sqlite3_file *pJfd, sqlite_int64 size){
163 MemJournal *p = (MemJournal *)pJfd;
164 FileChunk *pChunk;
165 assert(size==0);
danielk1977f3d3c272008-11-19 16:52:44 +0000166 UNUSED_PARAMETER(size);
danielk197739281b42008-10-17 19:13:04 +0000167 pChunk = p->pFirst;
168 while( pChunk ){
169 FileChunk *pTmp = pChunk;
170 pChunk = pChunk->pNext;
171 sqlite3_free(pTmp);
172 }
173 sqlite3MemJournalOpen(pJfd);
174 return SQLITE_OK;
175}
176
177/*
178** Close the file.
179*/
180static int memjrnlClose(sqlite3_file *pJfd){
181 memjrnlTruncate(pJfd, 0);
182 return SQLITE_OK;
183}
184
185
186/*
187** Sync the file.
188*/
danielk197762c14b32008-11-19 09:05:26 +0000189static int memjrnlSync(sqlite3_file *NotUsed, int NotUsed2){
190 UNUSED_PARAMETER2(NotUsed, NotUsed2);
danielk197739281b42008-10-17 19:13:04 +0000191 return SQLITE_OK;
192}
193
194/*
195** Query the size of the file in bytes.
196*/
197static int memjrnlFileSize(sqlite3_file *pJfd, sqlite_int64 *pSize){
198 MemJournal *p = (MemJournal *)pJfd;
199 *pSize = (sqlite_int64) p->endpoint.iOffset;
200 return SQLITE_OK;
201}
202
203/*
204** Table of methods for MemJournal sqlite3_file object.
205*/
206static struct sqlite3_io_methods MemJournalMethods = {
207 1, /* iVersion */
208 memjrnlClose, /* xClose */
209 memjrnlRead, /* xRead */
210 memjrnlWrite, /* xWrite */
211 memjrnlTruncate, /* xTruncate */
212 memjrnlSync, /* xSync */
213 memjrnlFileSize, /* xFileSize */
214 0, /* xLock */
215 0, /* xUnlock */
216 0, /* xCheckReservedLock */
217 0, /* xFileControl */
218 0, /* xSectorSize */
219 0 /* xDeviceCharacteristics */
220};
221
222/*
223** Open a journal file.
224*/
225void sqlite3MemJournalOpen(sqlite3_file *pJfd){
226 MemJournal *p = (MemJournal *)pJfd;
227 memset(p, 0, sqlite3MemJournalSize());
228 p->pMethod = &MemJournalMethods;
229}
230
231/*
232** Return true if the file-handle passed as an argument is
233** an in-memory journal
234*/
235int sqlite3IsMemJournal(sqlite3_file *pJfd){
236 return pJfd->pMethods==&MemJournalMethods;
237}
238
239/*
240** Return the number of bytes required to store a MemJournal that uses vfs
241** pVfs to create the underlying on-disk files.
242*/
243int sqlite3MemJournalSize(){
244 return sizeof(MemJournal);
245}