danielk1977 | c7b6017 | 2007-08-22 11:22:03 +0000 | [diff] [blame] | 1 | /* |
| 2 | ** 2007 August 22 |
| 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 | ** |
danielk1977 | c7b6017 | 2007-08-22 11:22:03 +0000 | [diff] [blame] | 13 | ** This file implements a special kind of sqlite3_file object used |
| 14 | ** by SQLite to create journal files if the atomic-write optimization |
| 15 | ** is enabled. |
| 16 | ** |
| 17 | ** The distinctive characteristic of this sqlite3_file is that the |
| 18 | ** actual on disk file is created lazily. When the file is created, |
| 19 | ** the caller specifies a buffer size for an in-memory buffer to |
| 20 | ** be used to service read() and write() requests. The actual file |
| 21 | ** on disk is not created or populated until either: |
| 22 | ** |
| 23 | ** 1) The in-memory representation grows too large for the allocated |
| 24 | ** buffer, or |
danielk1977 | bea2a94 | 2009-01-20 17:06:27 +0000 | [diff] [blame] | 25 | ** 2) The sqlite3JournalCreate() function is called. |
danielk1977 | c7b6017 | 2007-08-22 11:22:03 +0000 | [diff] [blame] | 26 | */ |
drh | c81c11f | 2009-11-10 01:30:52 +0000 | [diff] [blame] | 27 | #ifdef SQLITE_ENABLE_ATOMIC_WRITE |
danielk1977 | c7b6017 | 2007-08-22 11:22:03 +0000 | [diff] [blame] | 28 | #include "sqliteInt.h" |
| 29 | |
drh | 24e824c | 2007-09-06 13:49:37 +0000 | [diff] [blame] | 30 | |
| 31 | /* |
| 32 | ** A JournalFile object is a subclass of sqlite3_file used by |
| 33 | ** as an open file handle for journal files. |
| 34 | */ |
danielk1977 | c7b6017 | 2007-08-22 11:22:03 +0000 | [diff] [blame] | 35 | struct JournalFile { |
drh | 24e824c | 2007-09-06 13:49:37 +0000 | [diff] [blame] | 36 | sqlite3_io_methods *pMethod; /* I/O methods on journal files */ |
| 37 | int nBuf; /* Size of zBuf[] in bytes */ |
| 38 | char *zBuf; /* Space to buffer journal writes */ |
| 39 | int iSize; /* Amount of zBuf[] currently used */ |
| 40 | int flags; /* xOpen flags */ |
| 41 | sqlite3_vfs *pVfs; /* The "real" underlying VFS */ |
| 42 | sqlite3_file *pReal; /* The "real" underlying file descriptor */ |
| 43 | const char *zJournal; /* Name of the journal file */ |
danielk1977 | c7b6017 | 2007-08-22 11:22:03 +0000 | [diff] [blame] | 44 | }; |
| 45 | typedef struct JournalFile JournalFile; |
| 46 | |
| 47 | /* |
| 48 | ** If it does not already exists, create and populate the on-disk file |
| 49 | ** for JournalFile p. |
| 50 | */ |
| 51 | static int createFile(JournalFile *p){ |
| 52 | int rc = SQLITE_OK; |
| 53 | if( !p->pReal ){ |
danielk1977 | 2ca0f86 | 2007-08-23 08:06:44 +0000 | [diff] [blame] | 54 | sqlite3_file *pReal = (sqlite3_file *)&p[1]; |
| 55 | rc = sqlite3OsOpen(p->pVfs, p->zJournal, pReal, p->flags, 0); |
| 56 | if( rc==SQLITE_OK ){ |
| 57 | p->pReal = pReal; |
| 58 | if( p->iSize>0 ){ |
| 59 | assert(p->iSize<=p->nBuf); |
| 60 | rc = sqlite3OsWrite(p->pReal, p->zBuf, p->iSize, 0); |
| 61 | } |
dan | 985cd59 | 2012-12-18 11:59:39 +0000 | [diff] [blame] | 62 | if( rc!=SQLITE_OK ){ |
| 63 | /* If an error occurred while writing to the file, close it before |
| 64 | ** returning. This way, SQLite uses the in-memory journal data to |
| 65 | ** roll back changes made to the internal page-cache before this |
| 66 | ** function was called. */ |
| 67 | sqlite3OsClose(pReal); |
| 68 | p->pReal = 0; |
| 69 | } |
danielk1977 | c7b6017 | 2007-08-22 11:22:03 +0000 | [diff] [blame] | 70 | } |
| 71 | } |
| 72 | return rc; |
| 73 | } |
| 74 | |
| 75 | /* |
| 76 | ** Close the file. |
| 77 | */ |
| 78 | static int jrnlClose(sqlite3_file *pJfd){ |
| 79 | JournalFile *p = (JournalFile *)pJfd; |
| 80 | if( p->pReal ){ |
| 81 | sqlite3OsClose(p->pReal); |
| 82 | } |
| 83 | sqlite3_free(p->zBuf); |
| 84 | return SQLITE_OK; |
| 85 | } |
| 86 | |
| 87 | /* |
| 88 | ** Read data from the file. |
| 89 | */ |
| 90 | static int jrnlRead( |
drh | 24e824c | 2007-09-06 13:49:37 +0000 | [diff] [blame] | 91 | sqlite3_file *pJfd, /* The journal file from which to read */ |
| 92 | void *zBuf, /* Put the results here */ |
| 93 | int iAmt, /* Number of bytes to read */ |
| 94 | sqlite_int64 iOfst /* Begin reading at this offset */ |
danielk1977 | c7b6017 | 2007-08-22 11:22:03 +0000 | [diff] [blame] | 95 | ){ |
| 96 | int rc = SQLITE_OK; |
| 97 | JournalFile *p = (JournalFile *)pJfd; |
| 98 | if( p->pReal ){ |
| 99 | rc = sqlite3OsRead(p->pReal, zBuf, iAmt, iOfst); |
danielk1977 | bea2a94 | 2009-01-20 17:06:27 +0000 | [diff] [blame] | 100 | }else if( (iAmt+iOfst)>p->iSize ){ |
| 101 | rc = SQLITE_IOERR_SHORT_READ; |
danielk1977 | c7b6017 | 2007-08-22 11:22:03 +0000 | [diff] [blame] | 102 | }else{ |
danielk1977 | 880c15b | 2007-09-01 18:24:55 +0000 | [diff] [blame] | 103 | memcpy(zBuf, &p->zBuf[iOfst], iAmt); |
danielk1977 | c7b6017 | 2007-08-22 11:22:03 +0000 | [diff] [blame] | 104 | } |
| 105 | return rc; |
| 106 | } |
| 107 | |
| 108 | /* |
| 109 | ** Write data to the file. |
| 110 | */ |
| 111 | static int jrnlWrite( |
drh | 24e824c | 2007-09-06 13:49:37 +0000 | [diff] [blame] | 112 | sqlite3_file *pJfd, /* The journal file into which to write */ |
| 113 | const void *zBuf, /* Take data to be written from here */ |
| 114 | int iAmt, /* Number of bytes to write */ |
| 115 | sqlite_int64 iOfst /* Begin writing at this offset into the file */ |
danielk1977 | c7b6017 | 2007-08-22 11:22:03 +0000 | [diff] [blame] | 116 | ){ |
| 117 | int rc = SQLITE_OK; |
| 118 | JournalFile *p = (JournalFile *)pJfd; |
| 119 | if( !p->pReal && (iOfst+iAmt)>p->nBuf ){ |
| 120 | rc = createFile(p); |
| 121 | } |
| 122 | if( rc==SQLITE_OK ){ |
| 123 | if( p->pReal ){ |
| 124 | rc = sqlite3OsWrite(p->pReal, zBuf, iAmt, iOfst); |
| 125 | }else{ |
| 126 | memcpy(&p->zBuf[iOfst], zBuf, iAmt); |
| 127 | if( p->iSize<(iOfst+iAmt) ){ |
| 128 | p->iSize = (iOfst+iAmt); |
| 129 | } |
| 130 | } |
| 131 | } |
| 132 | return rc; |
| 133 | } |
| 134 | |
| 135 | /* |
| 136 | ** Truncate the file. |
| 137 | */ |
| 138 | static int jrnlTruncate(sqlite3_file *pJfd, sqlite_int64 size){ |
| 139 | int rc = SQLITE_OK; |
| 140 | JournalFile *p = (JournalFile *)pJfd; |
| 141 | if( p->pReal ){ |
| 142 | rc = sqlite3OsTruncate(p->pReal, size); |
danielk1977 | 880c15b | 2007-09-01 18:24:55 +0000 | [diff] [blame] | 143 | }else if( size<p->iSize ){ |
danielk1977 | c7b6017 | 2007-08-22 11:22:03 +0000 | [diff] [blame] | 144 | p->iSize = size; |
| 145 | } |
| 146 | return rc; |
| 147 | } |
| 148 | |
| 149 | /* |
| 150 | ** Sync the file. |
| 151 | */ |
| 152 | static int jrnlSync(sqlite3_file *pJfd, int flags){ |
| 153 | int rc; |
| 154 | JournalFile *p = (JournalFile *)pJfd; |
drh | b7d6362 | 2008-05-01 18:01:46 +0000 | [diff] [blame] | 155 | if( p->pReal ){ |
danielk1977 | c7b6017 | 2007-08-22 11:22:03 +0000 | [diff] [blame] | 156 | rc = sqlite3OsSync(p->pReal, flags); |
drh | b7d6362 | 2008-05-01 18:01:46 +0000 | [diff] [blame] | 157 | }else{ |
| 158 | rc = SQLITE_OK; |
danielk1977 | c7b6017 | 2007-08-22 11:22:03 +0000 | [diff] [blame] | 159 | } |
| 160 | return rc; |
| 161 | } |
| 162 | |
| 163 | /* |
| 164 | ** Query the size of the file in bytes. |
| 165 | */ |
| 166 | static int jrnlFileSize(sqlite3_file *pJfd, sqlite_int64 *pSize){ |
| 167 | int rc = SQLITE_OK; |
| 168 | JournalFile *p = (JournalFile *)pJfd; |
| 169 | if( p->pReal ){ |
| 170 | rc = sqlite3OsFileSize(p->pReal, pSize); |
| 171 | }else{ |
| 172 | *pSize = (sqlite_int64) p->iSize; |
| 173 | } |
| 174 | return rc; |
| 175 | } |
| 176 | |
| 177 | /* |
| 178 | ** Table of methods for JournalFile sqlite3_file object. |
| 179 | */ |
| 180 | static struct sqlite3_io_methods JournalFileMethods = { |
| 181 | 1, /* iVersion */ |
| 182 | jrnlClose, /* xClose */ |
| 183 | jrnlRead, /* xRead */ |
| 184 | jrnlWrite, /* xWrite */ |
| 185 | jrnlTruncate, /* xTruncate */ |
| 186 | jrnlSync, /* xSync */ |
| 187 | jrnlFileSize, /* xFileSize */ |
| 188 | 0, /* xLock */ |
| 189 | 0, /* xUnlock */ |
| 190 | 0, /* xCheckReservedLock */ |
drh | 9e33c2c | 2007-08-31 18:34:59 +0000 | [diff] [blame] | 191 | 0, /* xFileControl */ |
danielk1977 | c7b6017 | 2007-08-22 11:22:03 +0000 | [diff] [blame] | 192 | 0, /* xSectorSize */ |
dan | 230fd98 | 2010-07-02 16:36:18 +0000 | [diff] [blame] | 193 | 0, /* xDeviceCharacteristics */ |
dan | 230fd98 | 2010-07-02 16:36:18 +0000 | [diff] [blame] | 194 | 0, /* xShmMap */ |
drh | 6e1f482 | 2010-07-13 23:41:40 +0000 | [diff] [blame] | 195 | 0, /* xShmLock */ |
dan | 230fd98 | 2010-07-02 16:36:18 +0000 | [diff] [blame] | 196 | 0, /* xShmBarrier */ |
drh | 6e1f482 | 2010-07-13 23:41:40 +0000 | [diff] [blame] | 197 | 0 /* xShmUnmap */ |
danielk1977 | c7b6017 | 2007-08-22 11:22:03 +0000 | [diff] [blame] | 198 | }; |
| 199 | |
| 200 | /* |
| 201 | ** Open a journal file. |
| 202 | */ |
| 203 | int sqlite3JournalOpen( |
drh | 24e824c | 2007-09-06 13:49:37 +0000 | [diff] [blame] | 204 | sqlite3_vfs *pVfs, /* The VFS to use for actual file I/O */ |
| 205 | const char *zName, /* Name of the journal file */ |
| 206 | sqlite3_file *pJfd, /* Preallocated, blank file handle */ |
| 207 | int flags, /* Opening flags */ |
| 208 | int nBuf /* Bytes buffered before opening the file */ |
danielk1977 | c7b6017 | 2007-08-22 11:22:03 +0000 | [diff] [blame] | 209 | ){ |
| 210 | JournalFile *p = (JournalFile *)pJfd; |
| 211 | memset(p, 0, sqlite3JournalSize(pVfs)); |
| 212 | if( nBuf>0 ){ |
| 213 | p->zBuf = sqlite3MallocZero(nBuf); |
| 214 | if( !p->zBuf ){ |
| 215 | return SQLITE_NOMEM; |
| 216 | } |
| 217 | }else{ |
| 218 | return sqlite3OsOpen(pVfs, zName, pJfd, flags, 0); |
| 219 | } |
| 220 | p->pMethod = &JournalFileMethods; |
| 221 | p->nBuf = nBuf; |
| 222 | p->flags = flags; |
| 223 | p->zJournal = zName; |
| 224 | p->pVfs = pVfs; |
| 225 | return SQLITE_OK; |
| 226 | } |
| 227 | |
danielk1977 | f55b899 | 2007-08-24 08:15:53 +0000 | [diff] [blame] | 228 | /* |
| 229 | ** If the argument p points to a JournalFile structure, and the underlying |
| 230 | ** file has not yet been created, create it now. |
| 231 | */ |
| 232 | int sqlite3JournalCreate(sqlite3_file *p){ |
| 233 | if( p->pMethods!=&JournalFileMethods ){ |
| 234 | return SQLITE_OK; |
| 235 | } |
| 236 | return createFile((JournalFile *)p); |
| 237 | } |
| 238 | |
dan | 3de0f18 | 2012-12-05 10:01:35 +0000 | [diff] [blame] | 239 | /* |
| 240 | ** The file-handle passed as the only argument is guaranteed to be an open |
| 241 | ** file. It may or may not be of class JournalFile. If the file is a |
| 242 | ** JournalFile, and the underlying file on disk has not yet been opened, |
| 243 | ** return 0. Otherwise, return 1. |
| 244 | */ |
| 245 | int sqlite3JournalExists(sqlite3_file *p){ |
| 246 | return (p->pMethods!=&JournalFileMethods || ((JournalFile *)p)->pReal!=0); |
| 247 | } |
| 248 | |
danielk1977 | c7b6017 | 2007-08-22 11:22:03 +0000 | [diff] [blame] | 249 | /* |
| 250 | ** Return the number of bytes required to store a JournalFile that uses vfs |
| 251 | ** pVfs to create the underlying on-disk files. |
| 252 | */ |
| 253 | int sqlite3JournalSize(sqlite3_vfs *pVfs){ |
| 254 | return (pVfs->szOsFile+sizeof(JournalFile)); |
| 255 | } |
| 256 | #endif |