GCC Code Coverage Report | |||||||||||||||||||||
|
|||||||||||||||||||||
Line | Branch | Exec | Source |
1 |
/** |
||
2 |
* This file is part of the CernVM File System. |
||
3 |
* |
||
4 |
* An optimized virtual file system layer for the client only. It expects to |
||
5 |
* operate on immutable, valid SQlite files. Hence it can do a few |
||
6 |
* optimiziations. Most notably it doesn't need to know about the path of |
||
7 |
* the SQlite file once opened. It works purely on the file descriptor. |
||
8 |
*/ |
||
9 |
|||
10 |
#ifndef __STDC_FORMAT_MACROS |
||
11 |
#define __STDC_FORMAT_MACROS |
||
12 |
#endif |
||
13 |
|||
14 |
#include "cvmfs_config.h" |
||
15 |
#include "sqlitevfs.h" |
||
16 |
|||
17 |
#include <dlfcn.h> |
||
18 |
#include <errno.h> |
||
19 |
#include <fcntl.h> |
||
20 |
#include <inttypes.h> |
||
21 |
#include <stdint.h> |
||
22 |
#include <sys/select.h> |
||
23 |
#include <sys/time.h> |
||
24 |
#include <sys/types.h> |
||
25 |
#include <sys/uio.h> |
||
26 |
#include <unistd.h> |
||
27 |
|||
28 |
#include <cassert> |
||
29 |
#include <climits> |
||
30 |
#include <cstring> |
||
31 |
#include <ctime> |
||
32 |
|||
33 |
#include "cache.h" |
||
34 |
#include "duplex_sqlite3.h" |
||
35 |
#include "logging.h" |
||
36 |
#include "platform.h" |
||
37 |
#include "smalloc.h" |
||
38 |
#include "statistics.h" |
||
39 |
#include "util/string.h" |
||
40 |
|||
41 |
using namespace std; // NOLINT |
||
42 |
|||
43 |
namespace sqlite { |
||
44 |
|||
45 |
namespace { |
||
46 |
|||
47 |
const char *kVfsName = "cvmfs-readonly"; |
||
48 |
|||
49 |
/** |
||
50 |
* The private user data attached to the sqlite_vfs object. |
||
51 |
*/ |
||
52 |
struct VfsRdOnly { |
||
53 |
107 |
VfsRdOnly() |
|
54 |
: cache_mgr(NULL) |
||
55 |
, n_access(NULL) |
||
56 |
, no_open(NULL) |
||
57 |
, n_rand(NULL) |
||
58 |
, sz_rand(NULL) |
||
59 |
, n_read(NULL) |
||
60 |
, sz_read(NULL) |
||
61 |
, n_sleep(NULL) |
||
62 |
, sz_sleep(NULL) |
||
63 |
107 |
, n_time(NULL) |
|
64 |
107 |
{ } |
|
65 |
CacheManager *cache_mgr; |
||
66 |
perf::Counter *n_access; |
||
67 |
perf::Counter *no_open; |
||
68 |
perf::Counter *n_rand; |
||
69 |
perf::Counter *sz_rand; |
||
70 |
perf::Counter *n_read; |
||
71 |
perf::Counter *sz_read; |
||
72 |
perf::Counter *n_sleep; |
||
73 |
perf::Counter *sz_sleep; |
||
74 |
perf::Counter *n_time; |
||
75 |
}; |
||
76 |
|||
77 |
/** |
||
78 |
* This is passed to all operations once a file is opened. |
||
79 |
*/ |
||
80 |
struct VfsRdOnlyFile { |
||
81 |
sqlite3_file base; // Base class. Must be first. |
||
82 |
VfsRdOnly *vfs_rdonly; |
||
83 |
int fd; |
||
84 |
uint64_t size; |
||
85 |
}; |
||
86 |
|||
87 |
} // anonymous namespace |
||
88 |
|||
89 |
|||
90 |
52 |
static int VfsRdOnlyClose(sqlite3_file *pFile) { |
|
91 |
52 |
VfsRdOnlyFile *p = reinterpret_cast<VfsRdOnlyFile *>(pFile); |
|
92 |
52 |
int retval = p->vfs_rdonly->cache_mgr->Close(p->fd); |
|
93 |
✓✗ | 52 |
if (retval == 0) { |
94 |
52 |
perf::Dec(p->vfs_rdonly->no_open); |
|
95 |
52 |
return SQLITE_OK; |
|
96 |
} |
||
97 |
return SQLITE_IOERR_CLOSE; |
||
98 |
} |
||
99 |
|||
100 |
|||
101 |
/** |
||
102 |
* On a short read, the remaining bytes must be zero'ed. |
||
103 |
* TODO(jblomer): the reads seem to be rather small. Investigate buffered read. |
||
104 |
*/ |
||
105 |
483 |
static int VfsRdOnlyRead( |
|
106 |
sqlite3_file *pFile, |
||
107 |
void *zBuf, |
||
108 |
int iAmt, |
||
109 |
sqlite_int64 iOfst |
||
110 |
) { |
||
111 |
483 |
VfsRdOnlyFile *p = reinterpret_cast<VfsRdOnlyFile *>(pFile); |
|
112 |
483 |
ssize_t got = p->vfs_rdonly->cache_mgr->Pread(p->fd, zBuf, iAmt, iOfst); |
|
113 |
483 |
perf::Inc(p->vfs_rdonly->n_read); |
|
114 |
✓✗ | 483 |
if (got == iAmt) { |
115 |
483 |
perf::Xadd(p->vfs_rdonly->sz_read, iAmt); |
|
116 |
483 |
return SQLITE_OK; |
|
117 |
} else if (got < 0) { |
||
118 |
return SQLITE_IOERR_READ; |
||
119 |
} else { |
||
120 |
perf::Xadd(p->vfs_rdonly->sz_read, got); |
||
121 |
memset(reinterpret_cast<char *>(zBuf) + got, 0, iAmt - got); |
||
122 |
return SQLITE_IOERR_SHORT_READ; |
||
123 |
} |
||
124 |
} |
||
125 |
|||
126 |
|||
127 |
static int VfsRdOnlyWrite( |
||
128 |
sqlite3_file *pFile __attribute__((unused)), |
||
129 |
const void *zBuf __attribute__((unused)), |
||
130 |
int iAmt __attribute__((unused)), |
||
131 |
sqlite_int64 iOfst __attribute__((unused))) |
||
132 |
{ |
||
133 |
return SQLITE_READONLY; |
||
134 |
} |
||
135 |
|||
136 |
|||
137 |
static int VfsRdOnlyTruncate( |
||
138 |
sqlite3_file *pFile __attribute__((unused)), |
||
139 |
sqlite_int64 size __attribute__((unused))) |
||
140 |
{ |
||
141 |
return SQLITE_READONLY; |
||
142 |
} |
||
143 |
|||
144 |
|||
145 |
static int VfsRdOnlySync( |
||
146 |
sqlite3_file *pFile __attribute__((unused)), |
||
147 |
int flags __attribute__((unused))) |
||
148 |
{ |
||
149 |
return SQLITE_OK; |
||
150 |
} |
||
151 |
|||
152 |
|||
153 |
104 |
static int VfsRdOnlyFileSize(sqlite3_file *pFile, sqlite_int64 *pSize) { |
|
154 |
104 |
VfsRdOnlyFile *p = reinterpret_cast<VfsRdOnlyFile *>(pFile); |
|
155 |
104 |
*pSize = p->size; |
|
156 |
104 |
return SQLITE_OK; |
|
157 |
} |
||
158 |
|||
159 |
|||
160 |
52 |
static int VfsRdOnlyLock( |
|
161 |
sqlite3_file *p __attribute__((unused)), |
||
162 |
int level __attribute__((unused)) |
||
163 |
) { |
||
164 |
52 |
return SQLITE_OK; |
|
165 |
} |
||
166 |
|||
167 |
|||
168 |
52 |
static int VfsRdOnlyUnlock( |
|
169 |
sqlite3_file *p __attribute__((unused)), |
||
170 |
int level __attribute__((unused)) |
||
171 |
) { |
||
172 |
52 |
return SQLITE_OK; |
|
173 |
} |
||
174 |
|||
175 |
|||
176 |
static int VfsRdOnlyCheckReservedLock( |
||
177 |
sqlite3_file *p __attribute__((unused)), |
||
178 |
int *pResOut |
||
179 |
) { |
||
180 |
*pResOut = 0; |
||
181 |
return SQLITE_OK; |
||
182 |
} |
||
183 |
|||
184 |
|||
185 |
/** |
||
186 |
* No xFileControl() verbs are implemented by this VFS. |
||
187 |
*/ |
||
188 |
208 |
static int VfsRdOnlyFileControl( |
|
189 |
sqlite3_file *p __attribute__((unused)), |
||
190 |
int op __attribute__((unused)), |
||
191 |
void *pArg __attribute__((unused)) |
||
192 |
) { |
||
193 |
208 |
return SQLITE_NOTFOUND; |
|
194 |
} |
||
195 |
|||
196 |
|||
197 |
/** |
||
198 |
* A good unit of bytes to read at once. But probably only used for writes. |
||
199 |
*/ |
||
200 |
52 |
static int VfsRdOnlySectorSize(sqlite3_file *p __attribute__((unused))) { |
|
201 |
52 |
return 4096; |
|
202 |
} |
||
203 |
|||
204 |
|||
205 |
/** |
||
206 |
* Only relevant for writing. |
||
207 |
*/ |
||
208 |
156 |
static int VfsRdOnlyDeviceCharacteristics( |
|
209 |
sqlite3_file *p __attribute__((unused))) |
||
210 |
{ |
||
211 |
156 |
return 0; |
|
212 |
} |
||
213 |
|||
214 |
|||
215 |
/** |
||
216 |
* Supports only read-only opens. The "file name" has to be in the form of |
||
217 |
* '@<file descriptor>', where file descriptor is usable by the cache manager. |
||
218 |
*/ |
||
219 |
52 |
static int VfsRdOnlyOpen( |
|
220 |
sqlite3_vfs *vfs, |
||
221 |
const char *zName, |
||
222 |
sqlite3_file *pFile, |
||
223 |
int flags, |
||
224 |
int *pOutFlags) |
||
225 |
{ |
||
226 |
static const sqlite3_io_methods io_methods = { |
||
227 |
1, // iVersion |
||
228 |
VfsRdOnlyClose, |
||
229 |
VfsRdOnlyRead, |
||
230 |
VfsRdOnlyWrite, |
||
231 |
VfsRdOnlyTruncate, |
||
232 |
VfsRdOnlySync, |
||
233 |
VfsRdOnlyFileSize, |
||
234 |
VfsRdOnlyLock, |
||
235 |
VfsRdOnlyUnlock, |
||
236 |
VfsRdOnlyCheckReservedLock, |
||
237 |
VfsRdOnlyFileControl, |
||
238 |
VfsRdOnlySectorSize, |
||
239 |
VfsRdOnlyDeviceCharacteristics |
||
240 |
}; |
||
241 |
|||
242 |
52 |
VfsRdOnlyFile *p = reinterpret_cast<VfsRdOnlyFile *>(pFile); |
|
243 |
CacheManager *cache_mgr = |
||
244 |
52 |
reinterpret_cast<VfsRdOnly *>(vfs->pAppData)->cache_mgr; |
|
245 |
// Prevent xClose from being called in case of errors |
||
246 |
52 |
p->base.pMethods = NULL; |
|
247 |
|||
248 |
✗✓ | 52 |
if (flags & SQLITE_OPEN_READWRITE) |
249 |
return SQLITE_IOERR; |
||
250 |
✗✓ | 52 |
if (flags & SQLITE_OPEN_DELETEONCLOSE) |
251 |
return SQLITE_IOERR; |
||
252 |
✗✓ | 52 |
if (flags & SQLITE_OPEN_EXCLUSIVE) |
253 |
return SQLITE_IOERR; |
||
254 |
|||
255 |
✓✗✗✓ |
52 |
assert(zName && (zName[0] == '@')); |
256 |
52 |
p->fd = String2Int64(string(&zName[1])); |
|
257 |
✗✓ | 52 |
if (p->fd < 0) |
258 |
return SQLITE_IOERR; |
||
259 |
52 |
int64_t size = cache_mgr->GetSize(p->fd); |
|
260 |
✗✓ | 52 |
if (size < 0) { |
261 |
cache_mgr->Close(p->fd); |
||
262 |
p->fd = -1; |
||
263 |
return SQLITE_IOERR_FSTAT; |
||
264 |
} |
||
265 |
✗✓ | 52 |
if (cache_mgr->Readahead(p->fd) != 0) { |
266 |
cache_mgr->Close(p->fd); |
||
267 |
p->fd = -1; |
||
268 |
return SQLITE_IOERR; |
||
269 |
} |
||
270 |
52 |
p->size = static_cast<uint64_t>(size); |
|
271 |
✓✗ | 52 |
if (pOutFlags) |
272 |
52 |
*pOutFlags = flags; |
|
273 |
52 |
p->vfs_rdonly = reinterpret_cast<VfsRdOnly *>(vfs->pAppData); |
|
274 |
52 |
p->base.pMethods = &io_methods; |
|
275 |
52 |
perf::Inc(p->vfs_rdonly->no_open); |
|
276 |
LogCvmfs(kLogSql, kLogDebug, "open sqlite3 catalog on fd %d, size %" PRIu64, |
||
277 |
52 |
p->fd, p->size); |
|
278 |
52 |
return SQLITE_OK; |
|
279 |
} |
||
280 |
|||
281 |
|||
282 |
static int VfsRdOnlyDelete( |
||
283 |
sqlite3_vfs* __attribute__((unused)), |
||
284 |
const char *zName __attribute__((unused)), |
||
285 |
int syncDir __attribute__((unused))) |
||
286 |
{ |
||
287 |
return SQLITE_IOERR_DELETE; |
||
288 |
} |
||
289 |
|||
290 |
|||
291 |
/** |
||
292 |
* Cvmfs r/o file catalogs cannot have a write-ahead log or a journal. |
||
293 |
*/ |
||
294 |
104 |
static int VfsRdOnlyAccess( |
|
295 |
sqlite3_vfs *vfs, |
||
296 |
const char *zPath, |
||
297 |
int flags, |
||
298 |
int *pResOut) |
||
299 |
{ |
||
300 |
✗✓ | 104 |
if (flags == SQLITE_ACCESS_READWRITE) { |
301 |
*pResOut = 0; |
||
302 |
return SQLITE_OK; |
||
303 |
} |
||
304 |
✓✓✓✗ ✓✓✗✗ ✗✗✓✓ ✗✗✓✓ ✗✗✓✓ ✗✗✓✗ ✗✗✓✗ ✗✗✓✗ ✗✗✓✗ ✓✗ |
104 |
if (HasSuffix(zPath, "-wal", false) || HasSuffix(zPath, "-journal", false)) { |
305 |
104 |
*pResOut = 0; |
|
306 |
104 |
return SQLITE_OK; |
|
307 |
} |
||
308 |
|||
309 |
/*int amode = 0; |
||
310 |
switch (flags) { |
||
311 |
case SQLITE_ACCESS_EXISTS: |
||
312 |
amode = F_OK; |
||
313 |
break; |
||
314 |
case SQLITE_ACCESS_READ: |
||
315 |
amode = R_OK; |
||
316 |
break; |
||
317 |
default: |
||
318 |
assert(false); |
||
319 |
} |
||
320 |
*pResOut = (access(zPath, amode) == 0);*/ |
||
321 |
// This VFS deals with file descriptors, we know the files are there |
||
322 |
*pResOut = 0; |
||
323 |
perf::Inc(reinterpret_cast<VfsRdOnly *>(vfs->pAppData)->n_access); |
||
324 |
return SQLITE_OK; |
||
325 |
} |
||
326 |
|||
327 |
|||
328 |
/** |
||
329 |
* Since the path is never stored, there is no need to produce a full path. |
||
330 |
*/ |
||
331 |
52 |
int VfsRdOnlyFullPathname( |
|
332 |
sqlite3_vfs *vfs __attribute__((unused)), |
||
333 |
const char *zPath, |
||
334 |
int nOut, |
||
335 |
char *zOut) |
||
336 |
{ |
||
337 |
52 |
zOut[nOut-1] = '\0'; |
|
338 |
52 |
sqlite3_snprintf(nOut, zOut, "%s", zPath); |
|
339 |
52 |
return SQLITE_OK; |
|
340 |
} |
||
341 |
|||
342 |
|||
343 |
/** |
||
344 |
* Taken from unixRandomness |
||
345 |
*/ |
||
346 |
static int VfsRdOnlyRandomness( |
||
347 |
sqlite3_vfs *vfs, |
||
348 |
int nBuf, |
||
349 |
char *zBuf) |
||
350 |
{ |
||
351 |
assert((size_t)nBuf >= (sizeof(time_t) + sizeof(int))); |
||
352 |
perf::Inc(reinterpret_cast<VfsRdOnly *>(vfs->pAppData)->n_rand); |
||
353 |
memset(zBuf, 0, nBuf); |
||
354 |
pid_t randomnessPid = getpid(); |
||
355 |
int fd, got; |
||
356 |
fd = open("/dev/urandom", O_RDONLY, 0); |
||
357 |
if (fd < 0) { |
||
358 |
time_t t; |
||
359 |
time(&t); |
||
360 |
memcpy(zBuf, &t, sizeof(t)); |
||
361 |
memcpy(&zBuf[sizeof(t)], &randomnessPid, sizeof(randomnessPid)); |
||
362 |
assert(sizeof(t) + sizeof(randomnessPid) <= (size_t)nBuf); |
||
363 |
nBuf = sizeof(t) + sizeof(randomnessPid); |
||
364 |
} else { |
||
365 |
do { |
||
366 |
got = read(fd, zBuf, nBuf); |
||
367 |
} while (got < 0 && errno == EINTR); |
||
368 |
close(fd); |
||
369 |
} |
||
370 |
perf::Xadd(reinterpret_cast<VfsRdOnly *>(vfs->pAppData)->sz_rand, nBuf); |
||
371 |
return nBuf; |
||
372 |
} |
||
373 |
|||
374 |
|||
375 |
/** |
||
376 |
* Like SafeSleepMs, avoid conflict with the ALARM signal. |
||
377 |
*/ |
||
378 |
static int VfsRdOnlySleep( |
||
379 |
sqlite3_vfs *vfs, |
||
380 |
int microseconds) |
||
381 |
{ |
||
382 |
struct timeval wait_for; |
||
383 |
wait_for.tv_sec = microseconds / (1000*1000); |
||
384 |
wait_for.tv_usec = microseconds % (1000 * 1000); |
||
385 |
select(0, NULL, NULL, NULL, &wait_for); |
||
386 |
perf::Inc(reinterpret_cast<VfsRdOnly *>(vfs->pAppData)->n_sleep); |
||
387 |
perf::Xadd(reinterpret_cast<VfsRdOnly *>(vfs->pAppData)->sz_sleep, |
||
388 |
microseconds); |
||
389 |
return microseconds; |
||
390 |
} |
||
391 |
|||
392 |
|||
393 |
/** |
||
394 |
* Taken from unixCurrentTimeInt64() |
||
395 |
*/ |
||
396 |
static int VfsRdOnlyCurrentTimeInt64( |
||
397 |
sqlite3_vfs *vfs, |
||
398 |
sqlite3_int64 *piNow) |
||
399 |
{ |
||
400 |
static const sqlite3_int64 unixEpoch = 24405875*(sqlite3_int64)8640000; |
||
401 |
int rc = SQLITE_OK; |
||
402 |
struct timeval sNow; |
||
403 |
if (gettimeofday(&sNow, 0) == 0) { |
||
404 |
*piNow = unixEpoch + 1000*(sqlite3_int64)sNow.tv_sec + sNow.tv_usec/1000; |
||
405 |
perf::Inc(reinterpret_cast<VfsRdOnly *>(vfs->pAppData)->n_time); |
||
406 |
} else { |
||
407 |
rc = SQLITE_ERROR; |
||
408 |
} |
||
409 |
return rc; |
||
410 |
} |
||
411 |
|||
412 |
|||
413 |
/** |
||
414 |
* Taken from unixCurrentTime |
||
415 |
*/ |
||
416 |
static int VfsRdOnlyCurrentTime( |
||
417 |
sqlite3_vfs *vfs, |
||
418 |
double *prNow) |
||
419 |
{ |
||
420 |
sqlite3_int64 i = 0; |
||
421 |
int rc = VfsRdOnlyCurrentTimeInt64(vfs, &i); |
||
422 |
*prNow = i/86400000.0; |
||
423 |
return rc; |
||
424 |
} |
||
425 |
|||
426 |
|||
427 |
/** |
||
428 |
* So far unused by sqlite. |
||
429 |
*/ |
||
430 |
static int VfsRdOnlyGetLastError( |
||
431 |
sqlite3_vfs *vfs __attribute__((unused)), |
||
432 |
int not_used1 __attribute__((unused)), |
||
433 |
char *not_used2 __attribute__((unused))) |
||
434 |
{ |
||
435 |
return 0; |
||
436 |
} |
||
437 |
|||
438 |
|||
439 |
/** |
||
440 |
* Can only be registered once. |
||
441 |
*/ |
||
442 |
107 |
bool RegisterVfsRdOnly( |
|
443 |
CacheManager *cache_mgr, |
||
444 |
perf::Statistics *statistics, |
||
445 |
const VfsOptions options) |
||
446 |
{ |
||
447 |
sqlite3_vfs *vfs = reinterpret_cast<sqlite3_vfs *>( |
||
448 |
107 |
smalloc(sizeof(sqlite3_vfs))); |
|
449 |
107 |
memset(vfs, 0, sizeof(sqlite3_vfs)); |
|
450 |
107 |
VfsRdOnly *vfs_rdonly = new VfsRdOnly(); |
|
451 |
|||
452 |
107 |
vfs->iVersion = 2; |
|
453 |
107 |
vfs->szOsFile = sizeof(VfsRdOnlyFile); |
|
454 |
107 |
vfs->mxPathname = PATH_MAX; |
|
455 |
107 |
vfs->zName = kVfsName; |
|
456 |
107 |
vfs->pAppData = vfs_rdonly; |
|
457 |
107 |
vfs->xOpen = VfsRdOnlyOpen; |
|
458 |
107 |
vfs->xDelete = VfsRdOnlyDelete; |
|
459 |
107 |
vfs->xAccess = VfsRdOnlyAccess; |
|
460 |
107 |
vfs->xFullPathname = VfsRdOnlyFullPathname; |
|
461 |
107 |
vfs->xDlOpen = NULL; |
|
462 |
107 |
vfs->xDlError = NULL; |
|
463 |
107 |
vfs->xDlSym = NULL; |
|
464 |
107 |
vfs->xDlClose = NULL; |
|
465 |
107 |
vfs->xRandomness = VfsRdOnlyRandomness; |
|
466 |
107 |
vfs->xSleep = VfsRdOnlySleep; |
|
467 |
107 |
vfs->xCurrentTime = VfsRdOnlyCurrentTime; |
|
468 |
107 |
vfs->xGetLastError = VfsRdOnlyGetLastError; |
|
469 |
107 |
vfs->xCurrentTimeInt64 = VfsRdOnlyCurrentTimeInt64; |
|
470 |
✗✓ | 107 |
assert(vfs->zName); |
471 |
|||
472 |
107 |
int retval = sqlite3_vfs_register(vfs, options == kVfsOptDefault); |
|
473 |
✗✓ | 107 |
if (retval != SQLITE_OK) { |
474 |
free(const_cast<char *>(vfs->zName)); |
||
475 |
delete vfs_rdonly; |
||
476 |
free(vfs); |
||
477 |
return false; |
||
478 |
} |
||
479 |
|||
480 |
107 |
vfs_rdonly->cache_mgr = cache_mgr; |
|
481 |
vfs_rdonly->n_access = |
||
482 |
107 |
statistics->Register("sqlite.n_access", "overall number of access() calls"); |
|
483 |
vfs_rdonly->no_open = |
||
484 |
107 |
statistics->Register("sqlite.no_open", "currently open sqlite files"); |
|
485 |
vfs_rdonly->n_rand = |
||
486 |
107 |
statistics->Register("sqlite.n_rand", "overall number of random() calls"); |
|
487 |
vfs_rdonly->sz_rand = |
||
488 |
107 |
statistics->Register("sqlite.sz_rand", "overall number of random bytes"); |
|
489 |
vfs_rdonly->n_read = |
||
490 |
107 |
statistics->Register("sqlite.n_read", "overall number of read() calls"); |
|
491 |
vfs_rdonly->sz_read = |
||
492 |
107 |
statistics->Register("sqlite.sz_read", "overall bytes read()"); |
|
493 |
vfs_rdonly->n_sleep = |
||
494 |
107 |
statistics->Register("sqlite.n_sleep", "overall number of sleep() calls"); |
|
495 |
vfs_rdonly->sz_sleep = |
||
496 |
107 |
statistics->Register("sqlite.sz_sleep", "overall microseconds slept"); |
|
497 |
vfs_rdonly->n_time = |
||
498 |
107 |
statistics->Register("sqlite.n_time", "overall number of time() calls"); |
|
499 |
|||
500 |
107 |
return true; |
|
501 |
} |
||
502 |
|||
503 |
|||
504 |
/** |
||
505 |
* If the file system was the default VFS, another default VFS is selected by |
||
506 |
* SQlite randomly. |
||
507 |
*/ |
||
508 |
104 |
bool UnregisterVfsRdOnly() { |
|
509 |
104 |
sqlite3_vfs *vfs = sqlite3_vfs_find(kVfsName); |
|
510 |
✗✓ | 104 |
if (vfs == NULL) |
511 |
return false; |
||
512 |
104 |
int retval = sqlite3_vfs_unregister(vfs); |
|
513 |
✗✓ | 104 |
if (retval != SQLITE_OK) |
514 |
return false; |
||
515 |
|||
516 |
104 |
delete reinterpret_cast<VfsRdOnly *>(vfs->pAppData); |
|
517 |
104 |
free(vfs); |
|
518 |
104 |
return true; |
|
519 |
} |
||
520 |
|||
521 |
} // namespace sqlite |
Generated by: GCOVR (Version 4.1) |