|
1 /* |
|
2 ** 2008 April 10 |
|
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 the implementation of an SQLite vfs wrapper that |
|
14 ** adds instrumentation to all vfs and file methods. C and Tcl interfaces |
|
15 ** are provided to control the instrumentation. |
|
16 ** |
|
17 ** $Id: test_osinst.c,v 1.18 2008/07/25 13:32:45 drh Exp $ |
|
18 */ |
|
19 |
|
20 #ifdef SQLITE_ENABLE_INSTVFS |
|
21 /* |
|
22 ** C interface: |
|
23 ** |
|
24 ** sqlite3_instvfs_create() |
|
25 ** sqlite3_instvfs_destroy() |
|
26 ** sqlite3_instvfs_configure() |
|
27 ** |
|
28 ** sqlite3_instvfs_reset() |
|
29 ** sqlite3_instvfs_get() |
|
30 ** |
|
31 ** sqlite3_instvfs_binarylog |
|
32 ** sqlite3_instvfs_binarylog_marker |
|
33 ** |
|
34 ** Tcl interface (omitted if SQLITE_TEST is not set): |
|
35 ** |
|
36 ** sqlite3_instvfs create NAME ?PARENT? |
|
37 ** |
|
38 ** Create and register new vfs called $NAME, which is a wrapper around |
|
39 ** the existing vfs $PARENT. If the PARENT argument is omitted, the |
|
40 ** new vfs is a wrapper around the current default vfs. |
|
41 ** |
|
42 ** sqlite3_instvfs destroy NAME |
|
43 ** |
|
44 ** Deregister and destroy the vfs named $NAME, which must have been |
|
45 ** created by an earlier invocation of [sqlite3_instvfs create]. |
|
46 ** |
|
47 ** sqlite3_instvfs configure NAME SCRIPT |
|
48 ** |
|
49 ** Configure the callback script for the vfs $NAME, which much have |
|
50 ** been created by an earlier invocation of [sqlite3_instvfs create]. |
|
51 ** After a callback script has been configured, it is invoked each |
|
52 ** time a vfs or file method is called by SQLite. Before invoking |
|
53 ** the callback script, five arguments are appended to it: |
|
54 ** |
|
55 ** * The name of the invoked method - i.e. "xRead". |
|
56 ** |
|
57 ** * The time consumed by the method call as measured by |
|
58 ** sqlite3Hwtime() (an integer value) |
|
59 ** |
|
60 ** * A string value with a different meaning for different calls. |
|
61 ** For file methods, the name of the file being operated on. For |
|
62 ** other methods it is the filename argument, if any. |
|
63 ** |
|
64 ** * A 32-bit integer value with a call-specific meaning. |
|
65 ** |
|
66 ** * A 64-bit integer value. For xRead() and xWrite() calls this |
|
67 ** is the file offset being written to or read from. Unused by |
|
68 ** all other calls. |
|
69 ** |
|
70 ** sqlite3_instvfs reset NAME |
|
71 ** |
|
72 ** Zero the internal event counters associated with vfs $NAME, |
|
73 ** which must have been created by an earlier invocation of |
|
74 ** [sqlite3_instvfs create]. |
|
75 ** |
|
76 ** sqlite3_instvfs report NAME |
|
77 ** |
|
78 ** Return the values of the internal event counters associated |
|
79 ** with vfs $NAME. The report format is a list with one element |
|
80 ** for each method call (xWrite, xRead etc.). Each element is |
|
81 ** itself a list with three elements: |
|
82 ** |
|
83 ** * The name of the method call - i.e. "xWrite", |
|
84 ** * The total number of calls to the method (an integer). |
|
85 ** * The aggregate time consumed by all calls to the method as |
|
86 ** measured by sqlite3Hwtime() (an integer). |
|
87 */ |
|
88 |
|
89 #include "sqlite3.h" |
|
90 #include <string.h> |
|
91 #include <assert.h> |
|
92 |
|
93 /* |
|
94 ** Maximum pathname length supported by the inst backend. |
|
95 */ |
|
96 #define INST_MAX_PATHNAME 512 |
|
97 |
|
98 |
|
99 /* File methods */ |
|
100 /* Vfs methods */ |
|
101 #define OS_ACCESS 1 |
|
102 #define OS_CHECKRESERVEDLOCK 2 |
|
103 #define OS_CLOSE 3 |
|
104 #define OS_CURRENTTIME 4 |
|
105 #define OS_DELETE 5 |
|
106 #define OS_DEVCHAR 6 |
|
107 #define OS_FILECONTROL 7 |
|
108 #define OS_FILESIZE 8 |
|
109 #define OS_FULLPATHNAME 9 |
|
110 #define OS_LOCK 11 |
|
111 #define OS_OPEN 12 |
|
112 #define OS_RANDOMNESS 13 |
|
113 #define OS_READ 14 |
|
114 #define OS_SECTORSIZE 15 |
|
115 #define OS_SLEEP 16 |
|
116 #define OS_SYNC 17 |
|
117 #define OS_TRUNCATE 18 |
|
118 #define OS_UNLOCK 19 |
|
119 #define OS_WRITE 20 |
|
120 |
|
121 #define OS_NUMEVENTS 21 |
|
122 |
|
123 #define BINARYLOG_STRING 30 |
|
124 #define BINARYLOG_MARKER 31 |
|
125 |
|
126 #define BINARYLOG_PREPARE_V2 64 |
|
127 #define BINARYLOG_STEP 65 |
|
128 #define BINARYLOG_FINALIZE 66 |
|
129 |
|
130 struct InstVfs { |
|
131 sqlite3_vfs base; |
|
132 sqlite3_vfs *pVfs; |
|
133 |
|
134 void *pClient; |
|
135 void (*xDel)(void *); |
|
136 void (*xCall)(void *, int, int, sqlite3_int64, int, const char *, int, int, sqlite3_int64); |
|
137 |
|
138 /* Counters */ |
|
139 sqlite3_int64 aTime[OS_NUMEVENTS]; |
|
140 int aCount[OS_NUMEVENTS]; |
|
141 |
|
142 int iNextFileId; |
|
143 }; |
|
144 typedef struct InstVfs InstVfs; |
|
145 |
|
146 #define REALVFS(p) (((InstVfs *)(p))->pVfs) |
|
147 |
|
148 typedef struct inst_file inst_file; |
|
149 struct inst_file { |
|
150 sqlite3_file base; |
|
151 sqlite3_file *pReal; |
|
152 InstVfs *pInstVfs; |
|
153 const char *zName; |
|
154 int iFileId; /* File id number */ |
|
155 int flags; |
|
156 }; |
|
157 |
|
158 /* |
|
159 ** Method declarations for inst_file. |
|
160 */ |
|
161 static int instClose(sqlite3_file*); |
|
162 static int instRead(sqlite3_file*, void*, int iAmt, sqlite3_int64 iOfst); |
|
163 static int instWrite(sqlite3_file*,const void*,int iAmt, sqlite3_int64 iOfst); |
|
164 static int instTruncate(sqlite3_file*, sqlite3_int64 size); |
|
165 static int instSync(sqlite3_file*, int flags); |
|
166 static int instFileSize(sqlite3_file*, sqlite3_int64 *pSize); |
|
167 static int instLock(sqlite3_file*, int); |
|
168 static int instUnlock(sqlite3_file*, int); |
|
169 static int instCheckReservedLock(sqlite3_file*, int *pResOut); |
|
170 static int instFileControl(sqlite3_file*, int op, void *pArg); |
|
171 static int instSectorSize(sqlite3_file*); |
|
172 static int instDeviceCharacteristics(sqlite3_file*); |
|
173 |
|
174 /* |
|
175 ** Method declarations for inst_vfs. |
|
176 */ |
|
177 static int instOpen(sqlite3_vfs*, const char *, sqlite3_file*, int , int *); |
|
178 static int instDelete(sqlite3_vfs*, const char *zName, int syncDir); |
|
179 static int instAccess(sqlite3_vfs*, const char *zName, int flags, int *); |
|
180 static int instFullPathname(sqlite3_vfs*, const char *zName, int, char *zOut); |
|
181 static void *instDlOpen(sqlite3_vfs*, const char *zFilename); |
|
182 static void instDlError(sqlite3_vfs*, int nByte, char *zErrMsg); |
|
183 static void *instDlSym(sqlite3_vfs*,void*, const char *zSymbol); |
|
184 static void instDlClose(sqlite3_vfs*, void*); |
|
185 static int instRandomness(sqlite3_vfs*, int nByte, char *zOut); |
|
186 static int instSleep(sqlite3_vfs*, int microseconds); |
|
187 static int instCurrentTime(sqlite3_vfs*, double*); |
|
188 |
|
189 static void binarylog_blob(sqlite3_vfs *, const char *, int, int); |
|
190 |
|
191 static sqlite3_vfs inst_vfs = { |
|
192 1, /* iVersion */ |
|
193 sizeof(inst_file), /* szOsFile */ |
|
194 INST_MAX_PATHNAME, /* mxPathname */ |
|
195 0, /* pNext */ |
|
196 0, /* zName */ |
|
197 0, /* pAppData */ |
|
198 instOpen, /* xOpen */ |
|
199 instDelete, /* xDelete */ |
|
200 instAccess, /* xAccess */ |
|
201 instFullPathname, /* xFullPathname */ |
|
202 instDlOpen, /* xDlOpen */ |
|
203 instDlError, /* xDlError */ |
|
204 instDlSym, /* xDlSym */ |
|
205 instDlClose, /* xDlClose */ |
|
206 instRandomness, /* xRandomness */ |
|
207 instSleep, /* xSleep */ |
|
208 instCurrentTime /* xCurrentTime */ |
|
209 }; |
|
210 |
|
211 static sqlite3_io_methods inst_io_methods = { |
|
212 1, /* iVersion */ |
|
213 instClose, /* xClose */ |
|
214 instRead, /* xRead */ |
|
215 instWrite, /* xWrite */ |
|
216 instTruncate, /* xTruncate */ |
|
217 instSync, /* xSync */ |
|
218 instFileSize, /* xFileSize */ |
|
219 instLock, /* xLock */ |
|
220 instUnlock, /* xUnlock */ |
|
221 instCheckReservedLock, /* xCheckReservedLock */ |
|
222 instFileControl, /* xFileControl */ |
|
223 instSectorSize, /* xSectorSize */ |
|
224 instDeviceCharacteristics /* xDeviceCharacteristics */ |
|
225 }; |
|
226 |
|
227 /* |
|
228 ** hwtime.h contains inline assembler code for implementing |
|
229 ** high-performance timing routines. |
|
230 */ |
|
231 #include "hwtime.h" |
|
232 |
|
233 #define OS_TIME_IO(eEvent, A, B, Call) { \ |
|
234 inst_file *p = (inst_file *)pFile; \ |
|
235 InstVfs *pInstVfs = p->pInstVfs; \ |
|
236 int rc; \ |
|
237 sqlite_uint64 t = sqlite3Hwtime(); \ |
|
238 rc = Call; \ |
|
239 t = sqlite3Hwtime() - t; \ |
|
240 pInstVfs->aTime[eEvent] += t; \ |
|
241 pInstVfs->aCount[eEvent] += 1; \ |
|
242 if( pInstVfs->xCall ){ \ |
|
243 pInstVfs->xCall( \ |
|
244 pInstVfs->pClient,eEvent,p->iFileId,t,rc,p->zName,p->flags,A,B \ |
|
245 ); \ |
|
246 } \ |
|
247 return rc; \ |
|
248 } |
|
249 |
|
250 #define OS_TIME_VFS(eEvent, Z, flags, A, B, Call) { \ |
|
251 InstVfs *pInstVfs = (InstVfs *)pVfs; \ |
|
252 int rc; \ |
|
253 sqlite_uint64 t = sqlite3Hwtime(); \ |
|
254 rc = Call; \ |
|
255 t = sqlite3Hwtime() - t; \ |
|
256 pInstVfs->aTime[eEvent] += t; \ |
|
257 pInstVfs->aCount[eEvent] += 1; \ |
|
258 if( pInstVfs->xCall ){ \ |
|
259 pInstVfs->xCall(pInstVfs->pClient,eEvent,0, t, rc, Z, flags, A, B); \ |
|
260 } \ |
|
261 return rc; \ |
|
262 } |
|
263 |
|
264 /* |
|
265 ** Close an inst-file. |
|
266 */ |
|
267 static int instClose(sqlite3_file *pFile){ |
|
268 OS_TIME_IO(OS_CLOSE, 0, 0, |
|
269 (p->pReal->pMethods ? p->pReal->pMethods->xClose(p->pReal) : SQLITE_OK) |
|
270 ); |
|
271 } |
|
272 |
|
273 /* |
|
274 ** Read data from an inst-file. |
|
275 */ |
|
276 static int instRead( |
|
277 sqlite3_file *pFile, |
|
278 void *zBuf, |
|
279 int iAmt, |
|
280 sqlite_int64 iOfst |
|
281 ){ |
|
282 sqlite3_vfs *pVfs = (sqlite3_vfs *)(((inst_file *)pFile)->pInstVfs); |
|
283 OS_TIME_IO(OS_READ, iAmt, (binarylog_blob(pVfs, zBuf, iAmt, 1), iOfst), |
|
284 p->pReal->pMethods->xRead(p->pReal, zBuf, iAmt, iOfst) |
|
285 ); |
|
286 } |
|
287 |
|
288 /* |
|
289 ** Write data to an inst-file. |
|
290 */ |
|
291 static int instWrite( |
|
292 sqlite3_file *pFile, |
|
293 const void *z, |
|
294 int iAmt, |
|
295 sqlite_int64 iOfst |
|
296 ){ |
|
297 sqlite3_vfs *pVfs = (sqlite3_vfs *)(((inst_file *)pFile)->pInstVfs); |
|
298 binarylog_blob(pVfs, z, iAmt, 1); |
|
299 OS_TIME_IO(OS_WRITE, iAmt, iOfst, |
|
300 p->pReal->pMethods->xWrite(p->pReal, z, iAmt, iOfst) |
|
301 ); |
|
302 } |
|
303 |
|
304 /* |
|
305 ** Truncate an inst-file. |
|
306 */ |
|
307 static int instTruncate(sqlite3_file *pFile, sqlite_int64 size){ |
|
308 OS_TIME_IO(OS_TRUNCATE, 0, (int)size, |
|
309 p->pReal->pMethods->xTruncate(p->pReal, size) |
|
310 ); |
|
311 } |
|
312 |
|
313 /* |
|
314 ** Sync an inst-file. |
|
315 */ |
|
316 static int instSync(sqlite3_file *pFile, int flags){ |
|
317 OS_TIME_IO(OS_SYNC, flags, 0, p->pReal->pMethods->xSync(p->pReal, flags)); |
|
318 } |
|
319 |
|
320 /* |
|
321 ** Return the current file-size of an inst-file. |
|
322 */ |
|
323 static int instFileSize(sqlite3_file *pFile, sqlite_int64 *pSize){ |
|
324 OS_TIME_IO(OS_FILESIZE, (int)(*pSize), 0, |
|
325 p->pReal->pMethods->xFileSize(p->pReal, pSize) |
|
326 ); |
|
327 } |
|
328 |
|
329 /* |
|
330 ** Lock an inst-file. |
|
331 */ |
|
332 static int instLock(sqlite3_file *pFile, int eLock){ |
|
333 OS_TIME_IO(OS_LOCK, eLock, 0, p->pReal->pMethods->xLock(p->pReal, eLock)); |
|
334 } |
|
335 |
|
336 /* |
|
337 ** Unlock an inst-file. |
|
338 */ |
|
339 static int instUnlock(sqlite3_file *pFile, int eLock){ |
|
340 OS_TIME_IO(OS_UNLOCK, eLock, 0, p->pReal->pMethods->xUnlock(p->pReal, eLock)); |
|
341 } |
|
342 |
|
343 /* |
|
344 ** Check if another file-handle holds a RESERVED lock on an inst-file. |
|
345 */ |
|
346 static int instCheckReservedLock(sqlite3_file *pFile, int *pResOut){ |
|
347 OS_TIME_IO(OS_CHECKRESERVEDLOCK, 0, 0, |
|
348 p->pReal->pMethods->xCheckReservedLock(p->pReal, pResOut) |
|
349 ); |
|
350 } |
|
351 |
|
352 /* |
|
353 ** File control method. For custom operations on an inst-file. |
|
354 */ |
|
355 static int instFileControl(sqlite3_file *pFile, int op, void *pArg){ |
|
356 OS_TIME_IO(OS_FILECONTROL, 0, 0, p->pReal->pMethods->xFileControl(p->pReal, op, pArg)); |
|
357 } |
|
358 |
|
359 /* |
|
360 ** Return the sector-size in bytes for an inst-file. |
|
361 */ |
|
362 static int instSectorSize(sqlite3_file *pFile){ |
|
363 OS_TIME_IO(OS_SECTORSIZE, 0, 0, p->pReal->pMethods->xSectorSize(p->pReal)); |
|
364 } |
|
365 |
|
366 /* |
|
367 ** Return the device characteristic flags supported by an inst-file. |
|
368 */ |
|
369 static int instDeviceCharacteristics(sqlite3_file *pFile){ |
|
370 OS_TIME_IO(OS_DEVCHAR, 0, 0, p->pReal->pMethods->xDeviceCharacteristics(p->pReal)); |
|
371 } |
|
372 |
|
373 /* |
|
374 ** Open an inst file handle. |
|
375 */ |
|
376 static int instOpen( |
|
377 sqlite3_vfs *pVfs, |
|
378 const char *zName, |
|
379 sqlite3_file *pFile, |
|
380 int flags, |
|
381 int *pOutFlags |
|
382 ){ |
|
383 inst_file *p = (inst_file *)pFile; |
|
384 pFile->pMethods = &inst_io_methods; |
|
385 p->pReal = (sqlite3_file *)&p[1]; |
|
386 p->pInstVfs = (InstVfs *)pVfs; |
|
387 p->zName = zName; |
|
388 p->flags = flags; |
|
389 p->iFileId = ++p->pInstVfs->iNextFileId; |
|
390 |
|
391 binarylog_blob(pVfs, zName, -1, 0); |
|
392 OS_TIME_VFS(OS_OPEN, zName, flags, p->iFileId, 0, |
|
393 REALVFS(pVfs)->xOpen(REALVFS(pVfs), zName, p->pReal, flags, pOutFlags) |
|
394 ); |
|
395 } |
|
396 |
|
397 /* |
|
398 ** Delete the file located at zPath. If the dirSync argument is true, |
|
399 ** ensure the file-system modifications are synced to disk before |
|
400 ** returning. |
|
401 */ |
|
402 static int instDelete(sqlite3_vfs *pVfs, const char *zPath, int dirSync){ |
|
403 binarylog_blob(pVfs, zPath, -1, 0); |
|
404 OS_TIME_VFS(OS_DELETE, zPath, 0, dirSync, 0, |
|
405 REALVFS(pVfs)->xDelete(REALVFS(pVfs), zPath, dirSync) |
|
406 ); |
|
407 } |
|
408 |
|
409 /* |
|
410 ** Test for access permissions. Return true if the requested permission |
|
411 ** is available, or false otherwise. |
|
412 */ |
|
413 static int instAccess( |
|
414 sqlite3_vfs *pVfs, |
|
415 const char *zPath, |
|
416 int flags, |
|
417 int *pResOut |
|
418 ){ |
|
419 binarylog_blob(pVfs, zPath, -1, 0); |
|
420 OS_TIME_VFS(OS_ACCESS, zPath, 0, flags, *pResOut, |
|
421 REALVFS(pVfs)->xAccess(REALVFS(pVfs), zPath, flags, pResOut) |
|
422 ); |
|
423 } |
|
424 |
|
425 /* |
|
426 ** Populate buffer zOut with the full canonical pathname corresponding |
|
427 ** to the pathname in zPath. zOut is guaranteed to point to a buffer |
|
428 ** of at least (INST_MAX_PATHNAME+1) bytes. |
|
429 */ |
|
430 static int instFullPathname( |
|
431 sqlite3_vfs *pVfs, |
|
432 const char *zPath, |
|
433 int nOut, |
|
434 char *zOut |
|
435 ){ |
|
436 OS_TIME_VFS( OS_FULLPATHNAME, zPath, 0, 0, 0, |
|
437 REALVFS(pVfs)->xFullPathname(REALVFS(pVfs), zPath, nOut, zOut); |
|
438 ); |
|
439 } |
|
440 |
|
441 /* |
|
442 ** Open the dynamic library located at zPath and return a handle. |
|
443 */ |
|
444 static void *instDlOpen(sqlite3_vfs *pVfs, const char *zPath){ |
|
445 return REALVFS(pVfs)->xDlOpen(REALVFS(pVfs), zPath); |
|
446 } |
|
447 |
|
448 /* |
|
449 ** Populate the buffer zErrMsg (size nByte bytes) with a human readable |
|
450 ** utf-8 string describing the most recent error encountered associated |
|
451 ** with dynamic libraries. |
|
452 */ |
|
453 static void instDlError(sqlite3_vfs *pVfs, int nByte, char *zErrMsg){ |
|
454 REALVFS(pVfs)->xDlError(REALVFS(pVfs), nByte, zErrMsg); |
|
455 } |
|
456 |
|
457 /* |
|
458 ** Return a pointer to the symbol zSymbol in the dynamic library pHandle. |
|
459 */ |
|
460 static void *instDlSym(sqlite3_vfs *pVfs, void *pHandle, const char *zSymbol){ |
|
461 return REALVFS(pVfs)->xDlSym(REALVFS(pVfs), pHandle, zSymbol); |
|
462 } |
|
463 |
|
464 /* |
|
465 ** Close the dynamic library handle pHandle. |
|
466 */ |
|
467 static void instDlClose(sqlite3_vfs *pVfs, void *pHandle){ |
|
468 REALVFS(pVfs)->xDlClose(REALVFS(pVfs), pHandle); |
|
469 } |
|
470 |
|
471 /* |
|
472 ** Populate the buffer pointed to by zBufOut with nByte bytes of |
|
473 ** random data. |
|
474 */ |
|
475 static int instRandomness(sqlite3_vfs *pVfs, int nByte, char *zBufOut){ |
|
476 OS_TIME_VFS( OS_RANDOMNESS, 0, 0, nByte, 0, |
|
477 REALVFS(pVfs)->xRandomness(REALVFS(pVfs), nByte, zBufOut); |
|
478 ); |
|
479 } |
|
480 |
|
481 /* |
|
482 ** Sleep for nMicro microseconds. Return the number of microseconds |
|
483 ** actually slept. |
|
484 */ |
|
485 static int instSleep(sqlite3_vfs *pVfs, int nMicro){ |
|
486 OS_TIME_VFS( OS_SLEEP, 0, 0, nMicro, 0, |
|
487 REALVFS(pVfs)->xSleep(REALVFS(pVfs), nMicro) |
|
488 ); |
|
489 } |
|
490 |
|
491 /* |
|
492 ** Return the current time as a Julian Day number in *pTimeOut. |
|
493 */ |
|
494 static int instCurrentTime(sqlite3_vfs *pVfs, double *pTimeOut){ |
|
495 OS_TIME_VFS( OS_CURRENTTIME, 0, 0, 0, 0, |
|
496 REALVFS(pVfs)->xCurrentTime(REALVFS(pVfs), pTimeOut) |
|
497 ); |
|
498 } |
|
499 |
|
500 sqlite3_vfs *sqlite3_instvfs_create(const char *zName, const char *zParent){ |
|
501 int nByte; |
|
502 InstVfs *p; |
|
503 sqlite3_vfs *pParent; |
|
504 |
|
505 pParent = sqlite3_vfs_find(zParent); |
|
506 if( !pParent ){ |
|
507 return 0; |
|
508 } |
|
509 |
|
510 nByte = strlen(zName) + 1 + sizeof(InstVfs); |
|
511 p = (InstVfs *)sqlite3_malloc(nByte); |
|
512 if( p ){ |
|
513 char *zCopy = (char *)&p[1]; |
|
514 memset(p, 0, nByte); |
|
515 memcpy(p, &inst_vfs, sizeof(sqlite3_vfs)); |
|
516 p->pVfs = pParent; |
|
517 memcpy(zCopy, zName, strlen(zName)); |
|
518 p->base.zName = (const char *)zCopy; |
|
519 p->base.szOsFile += pParent->szOsFile; |
|
520 sqlite3_vfs_register((sqlite3_vfs *)p, 0); |
|
521 } |
|
522 |
|
523 return (sqlite3_vfs *)p; |
|
524 } |
|
525 |
|
526 void sqlite3_instvfs_configure( |
|
527 sqlite3_vfs *pVfs, |
|
528 void (*xCall)( |
|
529 void*, |
|
530 int, /* File id */ |
|
531 int, /* Event code */ |
|
532 sqlite3_int64, |
|
533 int, /* Return code */ |
|
534 const char*, /* File name */ |
|
535 int, |
|
536 int, |
|
537 sqlite3_int64 |
|
538 ), |
|
539 void *pClient, |
|
540 void (*xDel)(void *) |
|
541 ){ |
|
542 InstVfs *p = (InstVfs *)pVfs; |
|
543 assert( pVfs->xOpen==instOpen ); |
|
544 if( p->xDel ){ |
|
545 p->xDel(p->pClient); |
|
546 } |
|
547 p->xCall = xCall; |
|
548 p->xDel = xDel; |
|
549 p->pClient = pClient; |
|
550 } |
|
551 |
|
552 void sqlite3_instvfs_destroy(sqlite3_vfs *pVfs){ |
|
553 if( pVfs ){ |
|
554 sqlite3_vfs_unregister(pVfs); |
|
555 sqlite3_instvfs_configure(pVfs, 0, 0, 0); |
|
556 sqlite3_free(pVfs); |
|
557 } |
|
558 } |
|
559 |
|
560 void sqlite3_instvfs_reset(sqlite3_vfs *pVfs){ |
|
561 InstVfs *p = (InstVfs *)pVfs; |
|
562 assert( pVfs->xOpen==instOpen ); |
|
563 memset(p->aTime, 0, sizeof(sqlite3_int64)*OS_NUMEVENTS); |
|
564 memset(p->aCount, 0, sizeof(int)*OS_NUMEVENTS); |
|
565 } |
|
566 |
|
567 const char *sqlite3_instvfs_name(int eEvent){ |
|
568 const char *zEvent = 0; |
|
569 |
|
570 switch( eEvent ){ |
|
571 case OS_CLOSE: zEvent = "xClose"; break; |
|
572 case OS_READ: zEvent = "xRead"; break; |
|
573 case OS_WRITE: zEvent = "xWrite"; break; |
|
574 case OS_TRUNCATE: zEvent = "xTruncate"; break; |
|
575 case OS_SYNC: zEvent = "xSync"; break; |
|
576 case OS_FILESIZE: zEvent = "xFilesize"; break; |
|
577 case OS_LOCK: zEvent = "xLock"; break; |
|
578 case OS_UNLOCK: zEvent = "xUnlock"; break; |
|
579 case OS_CHECKRESERVEDLOCK: zEvent = "xCheckReservedLock"; break; |
|
580 case OS_FILECONTROL: zEvent = "xFileControl"; break; |
|
581 case OS_SECTORSIZE: zEvent = "xSectorSize"; break; |
|
582 case OS_DEVCHAR: zEvent = "xDeviceCharacteristics"; break; |
|
583 case OS_OPEN: zEvent = "xOpen"; break; |
|
584 case OS_DELETE: zEvent = "xDelete"; break; |
|
585 case OS_ACCESS: zEvent = "xAccess"; break; |
|
586 case OS_FULLPATHNAME: zEvent = "xFullPathname"; break; |
|
587 case OS_RANDOMNESS: zEvent = "xRandomness"; break; |
|
588 case OS_SLEEP: zEvent = "xSleep"; break; |
|
589 case OS_CURRENTTIME: zEvent = "xCurrentTime"; break; |
|
590 } |
|
591 |
|
592 return zEvent; |
|
593 } |
|
594 |
|
595 void sqlite3_instvfs_get( |
|
596 sqlite3_vfs *pVfs, |
|
597 int eEvent, |
|
598 const char **pzEvent, |
|
599 sqlite3_int64 *pnClick, |
|
600 int *pnCall |
|
601 ){ |
|
602 InstVfs *p = (InstVfs *)pVfs; |
|
603 assert( pVfs->xOpen==instOpen ); |
|
604 if( eEvent<1 || eEvent>=OS_NUMEVENTS ){ |
|
605 *pzEvent = 0; |
|
606 *pnClick = 0; |
|
607 *pnCall = 0; |
|
608 return; |
|
609 } |
|
610 |
|
611 *pzEvent = sqlite3_instvfs_name(eEvent); |
|
612 *pnClick = p->aTime[eEvent]; |
|
613 *pnCall = p->aCount[eEvent]; |
|
614 } |
|
615 |
|
616 #define BINARYLOG_BUFFERSIZE 8192 |
|
617 |
|
618 struct InstVfsBinaryLog { |
|
619 int nBuf; |
|
620 char *zBuf; |
|
621 sqlite3_int64 iOffset; |
|
622 int log_data; |
|
623 sqlite3_file *pOut; |
|
624 char *zOut; /* Log file name */ |
|
625 }; |
|
626 typedef struct InstVfsBinaryLog InstVfsBinaryLog; |
|
627 |
|
628 static void put32bits(unsigned char *p, unsigned int v){ |
|
629 p[0] = v>>24; |
|
630 p[1] = v>>16; |
|
631 p[2] = v>>8; |
|
632 p[3] = v; |
|
633 } |
|
634 |
|
635 static void binarylog_flush(InstVfsBinaryLog *pLog){ |
|
636 sqlite3_file *pFile = pLog->pOut; |
|
637 |
|
638 #ifdef SQLITE_TEST |
|
639 extern int sqlite3_io_error_pending; |
|
640 extern int sqlite3_io_error_persist; |
|
641 extern int sqlite3_diskfull_pending; |
|
642 |
|
643 int pending = sqlite3_io_error_pending; |
|
644 int persist = sqlite3_io_error_persist; |
|
645 int diskfull = sqlite3_diskfull_pending; |
|
646 |
|
647 sqlite3_io_error_pending = 0; |
|
648 sqlite3_io_error_persist = 0; |
|
649 sqlite3_diskfull_pending = 0; |
|
650 #endif |
|
651 |
|
652 pFile->pMethods->xWrite(pFile, pLog->zBuf, pLog->nBuf, pLog->iOffset); |
|
653 pLog->iOffset += pLog->nBuf; |
|
654 pLog->nBuf = 0; |
|
655 |
|
656 #ifdef SQLITE_TEST |
|
657 sqlite3_io_error_pending = pending; |
|
658 sqlite3_io_error_persist = persist; |
|
659 sqlite3_diskfull_pending = diskfull; |
|
660 #endif |
|
661 } |
|
662 |
|
663 static void binarylog_xcall( |
|
664 void *p, |
|
665 int eEvent, |
|
666 int iFileId, |
|
667 sqlite3_int64 nClick, |
|
668 int return_code, |
|
669 const char *zName, |
|
670 int flags, |
|
671 int nByte, |
|
672 sqlite3_int64 iOffset |
|
673 ){ |
|
674 InstVfsBinaryLog *pLog = (InstVfsBinaryLog *)p; |
|
675 unsigned char *zRec; |
|
676 if( (28+pLog->nBuf)>BINARYLOG_BUFFERSIZE ){ |
|
677 binarylog_flush(pLog); |
|
678 } |
|
679 zRec = (unsigned char *)&pLog->zBuf[pLog->nBuf]; |
|
680 put32bits(&zRec[0], eEvent); |
|
681 put32bits(&zRec[4], (int)iFileId); |
|
682 put32bits(&zRec[8], (int)nClick); |
|
683 put32bits(&zRec[12], return_code); |
|
684 put32bits(&zRec[16], flags); |
|
685 put32bits(&zRec[20], nByte); |
|
686 put32bits(&zRec[24], (int)iOffset); |
|
687 pLog->nBuf += 28; |
|
688 } |
|
689 |
|
690 static void binarylog_xdel(void *p){ |
|
691 /* Close the log file and free the memory allocated for the |
|
692 ** InstVfsBinaryLog structure. |
|
693 */ |
|
694 InstVfsBinaryLog *pLog = (InstVfsBinaryLog *)p; |
|
695 sqlite3_file *pFile = pLog->pOut; |
|
696 if( pLog->nBuf ){ |
|
697 binarylog_flush(pLog); |
|
698 } |
|
699 pFile->pMethods->xClose(pFile); |
|
700 sqlite3_free(pLog->pOut); |
|
701 sqlite3_free(pLog->zBuf); |
|
702 sqlite3_free(pLog); |
|
703 } |
|
704 |
|
705 static void binarylog_blob( |
|
706 sqlite3_vfs *pVfs, |
|
707 const char *zBlob, |
|
708 int nBlob, |
|
709 int isBinary |
|
710 ){ |
|
711 InstVfsBinaryLog *pLog; |
|
712 InstVfs *pInstVfs = (InstVfs *)pVfs; |
|
713 |
|
714 if( pVfs->xOpen!=instOpen || pInstVfs->xCall!=binarylog_xcall ){ |
|
715 return; |
|
716 } |
|
717 pLog = (InstVfsBinaryLog *)pInstVfs->pClient; |
|
718 if( zBlob && (!isBinary || pLog->log_data) ){ |
|
719 unsigned char *zRec; |
|
720 int nWrite; |
|
721 |
|
722 if( nBlob<0 ){ |
|
723 nBlob = strlen(zBlob); |
|
724 } |
|
725 nWrite = nBlob + 28; |
|
726 |
|
727 if( (nWrite+pLog->nBuf)>BINARYLOG_BUFFERSIZE ){ |
|
728 binarylog_flush(pLog); |
|
729 } |
|
730 |
|
731 zRec = (unsigned char *)&pLog->zBuf[pLog->nBuf]; |
|
732 memset(zRec, 0, nWrite); |
|
733 put32bits(&zRec[0], BINARYLOG_STRING); |
|
734 put32bits(&zRec[4], (int)nBlob); |
|
735 put32bits(&zRec[8], (int)isBinary); |
|
736 memcpy(&zRec[28], zBlob, nBlob); |
|
737 pLog->nBuf += nWrite; |
|
738 } |
|
739 } |
|
740 |
|
741 void sqlite3_instvfs_binarylog_call( |
|
742 sqlite3_vfs *pVfs, |
|
743 int eEvent, |
|
744 sqlite3_int64 nClick, |
|
745 int return_code, |
|
746 const char *zString |
|
747 ){ |
|
748 InstVfs *pInstVfs = (InstVfs *)pVfs; |
|
749 InstVfsBinaryLog *pLog = (InstVfsBinaryLog *)pInstVfs->pClient; |
|
750 |
|
751 if( zString ){ |
|
752 binarylog_blob(pVfs, zString, -1, 0); |
|
753 } |
|
754 binarylog_xcall(pLog, eEvent, 0, nClick, return_code, 0, 0, 0, 0); |
|
755 } |
|
756 |
|
757 void sqlite3_instvfs_binarylog_marker( |
|
758 sqlite3_vfs *pVfs, |
|
759 const char *zMarker |
|
760 ){ |
|
761 InstVfs *pInstVfs = (InstVfs *)pVfs; |
|
762 InstVfsBinaryLog *pLog = (InstVfsBinaryLog *)pInstVfs->pClient; |
|
763 binarylog_blob(pVfs, zMarker, -1, 0); |
|
764 binarylog_xcall(pLog, BINARYLOG_MARKER, 0, 0, 0, 0, 0, 0, 0); |
|
765 } |
|
766 |
|
767 sqlite3_vfs *sqlite3_instvfs_binarylog( |
|
768 const char *zVfs, |
|
769 const char *zParentVfs, |
|
770 const char *zLog, |
|
771 int log_data |
|
772 ){ |
|
773 InstVfsBinaryLog *p; |
|
774 sqlite3_vfs *pVfs; |
|
775 sqlite3_vfs *pParent; |
|
776 int nByte; |
|
777 int flags; |
|
778 int rc; |
|
779 |
|
780 pParent = sqlite3_vfs_find(zParentVfs); |
|
781 if( !pParent ){ |
|
782 return 0; |
|
783 } |
|
784 |
|
785 nByte = sizeof(InstVfsBinaryLog) + pParent->mxPathname+1; |
|
786 p = (InstVfsBinaryLog *)sqlite3_malloc(nByte); |
|
787 memset(p, 0, nByte); |
|
788 p->zBuf = sqlite3_malloc(BINARYLOG_BUFFERSIZE); |
|
789 p->zOut = (char *)&p[1]; |
|
790 p->pOut = (sqlite3_file *)sqlite3_malloc(pParent->szOsFile); |
|
791 p->log_data = log_data; |
|
792 pParent->xFullPathname(pParent, zLog, pParent->mxPathname, p->zOut); |
|
793 flags = SQLITE_OPEN_READWRITE|SQLITE_OPEN_CREATE|SQLITE_OPEN_MASTER_JOURNAL; |
|
794 pParent->xDelete(pParent, p->zOut, 0); |
|
795 rc = pParent->xOpen(pParent, p->zOut, p->pOut, flags, &flags); |
|
796 if( rc==SQLITE_OK ){ |
|
797 memcpy(p->zBuf, "sqlite_ostrace1.....", 20); |
|
798 p->iOffset = 0; |
|
799 p->nBuf = 20; |
|
800 } |
|
801 if( rc ){ |
|
802 binarylog_xdel(p); |
|
803 return 0; |
|
804 } |
|
805 |
|
806 pVfs = sqlite3_instvfs_create(zVfs, zParentVfs); |
|
807 if( pVfs ){ |
|
808 sqlite3_instvfs_configure(pVfs, binarylog_xcall, p, binarylog_xdel); |
|
809 } |
|
810 |
|
811 return pVfs; |
|
812 } |
|
813 #endif /* SQLITE_ENABLE_INSTVFS */ |
|
814 |
|
815 /************************************************************************** |
|
816 *************************************************************************** |
|
817 ** Tcl interface starts here. |
|
818 */ |
|
819 #if SQLITE_TEST |
|
820 |
|
821 #include "tcl.h" |
|
822 |
|
823 #ifdef SQLITE_ENABLE_INSTVFS |
|
824 struct InstVfsCall { |
|
825 Tcl_Interp *interp; |
|
826 Tcl_Obj *pScript; |
|
827 }; |
|
828 typedef struct InstVfsCall InstVfsCall; |
|
829 |
|
830 static void test_instvfs_xcall( |
|
831 void *p, |
|
832 int eEvent, |
|
833 int iFileId, |
|
834 sqlite3_int64 nClick, |
|
835 int return_code, |
|
836 const char *zName, |
|
837 int flags, |
|
838 int nByte, |
|
839 sqlite3_int64 iOffset |
|
840 ){ |
|
841 int rc; |
|
842 InstVfsCall *pCall = (InstVfsCall *)p; |
|
843 Tcl_Obj *pObj = Tcl_DuplicateObj( pCall->pScript); |
|
844 const char *zEvent = sqlite3_instvfs_name(eEvent); |
|
845 |
|
846 Tcl_IncrRefCount(pObj); |
|
847 Tcl_ListObjAppendElement(0, pObj, Tcl_NewStringObj(zEvent, -1)); |
|
848 Tcl_ListObjAppendElement(0, pObj, Tcl_NewWideIntObj(nClick)); |
|
849 Tcl_ListObjAppendElement(0, pObj, Tcl_NewStringObj(zName, -1)); |
|
850 Tcl_ListObjAppendElement(0, pObj, Tcl_NewIntObj(nByte)); |
|
851 Tcl_ListObjAppendElement(0, pObj, Tcl_NewWideIntObj(iOffset)); |
|
852 |
|
853 rc = Tcl_EvalObjEx(pCall->interp, pObj, TCL_EVAL_GLOBAL|TCL_EVAL_DIRECT); |
|
854 if( rc ){ |
|
855 Tcl_BackgroundError(pCall->interp); |
|
856 } |
|
857 Tcl_DecrRefCount(pObj); |
|
858 } |
|
859 |
|
860 static void test_instvfs_xdel(void *p){ |
|
861 InstVfsCall *pCall = (InstVfsCall *)p; |
|
862 Tcl_DecrRefCount(pCall->pScript); |
|
863 sqlite3_free(pCall); |
|
864 } |
|
865 |
|
866 static int test_sqlite3_instvfs( |
|
867 void * clientData, |
|
868 Tcl_Interp *interp, |
|
869 int objc, |
|
870 Tcl_Obj *CONST objv[] |
|
871 ){ |
|
872 static const char *IV_strs[] = |
|
873 { "create", "destroy", "reset", "report", "configure", "binarylog", "marker", 0 }; |
|
874 enum IV_enum { IV_CREATE, IV_DESTROY, IV_RESET, IV_REPORT, IV_CONFIGURE, IV_BINARYLOG, IV_MARKER }; |
|
875 int iSub; |
|
876 |
|
877 if( objc<2 ){ |
|
878 Tcl_WrongNumArgs(interp, 1, objv, "SUB-COMMAND ..."); |
|
879 } |
|
880 if( Tcl_GetIndexFromObj(interp, objv[1], IV_strs, "sub-command", 0, &iSub) ){ |
|
881 return TCL_ERROR; |
|
882 } |
|
883 |
|
884 switch( (enum IV_enum)iSub ){ |
|
885 case IV_CREATE: { |
|
886 char *zParent = 0; |
|
887 sqlite3_vfs *p; |
|
888 int isDefault = 0; |
|
889 if( objc>2 && 0==strcmp("-default", Tcl_GetString(objv[2])) ){ |
|
890 isDefault = 1; |
|
891 } |
|
892 if( (objc-isDefault)!=4 && (objc-isDefault)!=3 ){ |
|
893 Tcl_WrongNumArgs(interp, 2, objv, "?-default? NAME ?PARENT-VFS?"); |
|
894 return TCL_ERROR; |
|
895 } |
|
896 if( objc==(4+isDefault) ){ |
|
897 zParent = Tcl_GetString(objv[3+isDefault]); |
|
898 } |
|
899 p = sqlite3_instvfs_create(Tcl_GetString(objv[2+isDefault]), zParent); |
|
900 if( !p ){ |
|
901 Tcl_AppendResult(interp, "error creating vfs ", 0); |
|
902 return TCL_ERROR; |
|
903 } |
|
904 if( isDefault ){ |
|
905 sqlite3_vfs_register(p, 1); |
|
906 } |
|
907 Tcl_SetObjResult(interp, objv[2]); |
|
908 break; |
|
909 } |
|
910 case IV_BINARYLOG: { |
|
911 char *zName = 0; |
|
912 char *zLog = 0; |
|
913 char *zParent = 0; |
|
914 sqlite3_vfs *p; |
|
915 int isDefault = 0; |
|
916 int isLogdata = 0; |
|
917 int argbase = 2; |
|
918 |
|
919 for(argbase=2; argbase<(objc-2); argbase++){ |
|
920 if( 0==strcmp("-default", Tcl_GetString(objv[argbase])) ){ |
|
921 isDefault = 1; |
|
922 } |
|
923 else if( 0==strcmp("-parent", Tcl_GetString(objv[argbase])) ){ |
|
924 argbase++; |
|
925 zParent = Tcl_GetString(objv[argbase]); |
|
926 } |
|
927 else if( 0==strcmp("-logdata", Tcl_GetString(objv[argbase])) ){ |
|
928 isLogdata = 1; |
|
929 }else{ |
|
930 break; |
|
931 } |
|
932 } |
|
933 |
|
934 if( (objc-argbase)!=2 ){ |
|
935 Tcl_WrongNumArgs( |
|
936 interp, 2, objv, "?-default? ?-parent VFS? ?-logdata? NAME LOGFILE" |
|
937 ); |
|
938 return TCL_ERROR; |
|
939 } |
|
940 zName = Tcl_GetString(objv[argbase]); |
|
941 zLog = Tcl_GetString(objv[argbase+1]); |
|
942 p = sqlite3_instvfs_binarylog(zName, zParent, zLog, isLogdata); |
|
943 if( !p ){ |
|
944 Tcl_AppendResult(interp, "error creating vfs ", 0); |
|
945 return TCL_ERROR; |
|
946 } |
|
947 if( isDefault ){ |
|
948 sqlite3_vfs_register(p, 1); |
|
949 } |
|
950 Tcl_SetObjResult(interp, objv[2]); |
|
951 break; |
|
952 } |
|
953 |
|
954 case IV_MARKER: { |
|
955 sqlite3_vfs *p; |
|
956 if( objc!=4 ){ |
|
957 Tcl_WrongNumArgs(interp, 2, objv, "VFS MARKER"); |
|
958 return TCL_ERROR; |
|
959 } |
|
960 p = sqlite3_vfs_find(Tcl_GetString(objv[2])); |
|
961 if( !p || p->xOpen!=instOpen ){ |
|
962 Tcl_AppendResult(interp, "no such vfs: ", Tcl_GetString(objv[2]), 0); |
|
963 return TCL_ERROR; |
|
964 } |
|
965 sqlite3_instvfs_binarylog_marker(p, Tcl_GetString(objv[3])); |
|
966 Tcl_ResetResult(interp); |
|
967 break; |
|
968 } |
|
969 |
|
970 case IV_CONFIGURE: { |
|
971 InstVfsCall *pCall; |
|
972 |
|
973 sqlite3_vfs *p; |
|
974 if( objc!=4 ){ |
|
975 Tcl_WrongNumArgs(interp, 2, objv, "NAME SCRIPT"); |
|
976 return TCL_ERROR; |
|
977 } |
|
978 p = sqlite3_vfs_find(Tcl_GetString(objv[2])); |
|
979 if( !p || p->xOpen!=instOpen ){ |
|
980 Tcl_AppendResult(interp, "no such vfs: ", Tcl_GetString(objv[2]), 0); |
|
981 return TCL_ERROR; |
|
982 } |
|
983 |
|
984 if( strlen(Tcl_GetString(objv[3])) ){ |
|
985 pCall = (InstVfsCall *)sqlite3_malloc(sizeof(InstVfsCall)); |
|
986 pCall->interp = interp; |
|
987 pCall->pScript = Tcl_DuplicateObj(objv[3]); |
|
988 Tcl_IncrRefCount(pCall->pScript); |
|
989 sqlite3_instvfs_configure(p, |
|
990 test_instvfs_xcall, (void *)pCall, test_instvfs_xdel |
|
991 ); |
|
992 }else{ |
|
993 sqlite3_instvfs_configure(p, 0, 0, 0); |
|
994 } |
|
995 break; |
|
996 } |
|
997 |
|
998 case IV_REPORT: |
|
999 case IV_DESTROY: |
|
1000 case IV_RESET: { |
|
1001 sqlite3_vfs *p; |
|
1002 if( objc!=3 ){ |
|
1003 Tcl_WrongNumArgs(interp, 2, objv, "NAME"); |
|
1004 return TCL_ERROR; |
|
1005 } |
|
1006 p = sqlite3_vfs_find(Tcl_GetString(objv[2])); |
|
1007 if( !p || p->xOpen!=instOpen ){ |
|
1008 Tcl_AppendResult(interp, "no such vfs: ", Tcl_GetString(objv[2]), 0); |
|
1009 return TCL_ERROR; |
|
1010 } |
|
1011 |
|
1012 if( ((enum IV_enum)iSub)==IV_DESTROY ){ |
|
1013 sqlite3_instvfs_destroy(p); |
|
1014 } |
|
1015 if( ((enum IV_enum)iSub)==IV_RESET ){ |
|
1016 sqlite3_instvfs_reset(p); |
|
1017 } |
|
1018 if( ((enum IV_enum)iSub)==IV_REPORT ){ |
|
1019 int ii; |
|
1020 Tcl_Obj *pRet = Tcl_NewObj(); |
|
1021 |
|
1022 const char *zName = (char *)1; |
|
1023 sqlite3_int64 nClick; |
|
1024 int nCall; |
|
1025 for(ii=1; zName; ii++){ |
|
1026 sqlite3_instvfs_get(p, ii, &zName, &nClick, &nCall); |
|
1027 if( zName ){ |
|
1028 Tcl_Obj *pElem = Tcl_NewObj(); |
|
1029 Tcl_ListObjAppendElement(0, pElem, Tcl_NewStringObj(zName, -1)); |
|
1030 Tcl_ListObjAppendElement(0, pElem, Tcl_NewIntObj(nCall)); |
|
1031 Tcl_ListObjAppendElement(0, pElem, Tcl_NewWideIntObj(nClick)); |
|
1032 Tcl_ListObjAppendElement(0, pRet, pElem); |
|
1033 } |
|
1034 } |
|
1035 |
|
1036 Tcl_SetObjResult(interp, pRet); |
|
1037 } |
|
1038 |
|
1039 break; |
|
1040 } |
|
1041 } |
|
1042 |
|
1043 return TCL_OK; |
|
1044 } |
|
1045 #endif /* SQLITE_ENABLE_INSTVFS */ |
|
1046 |
|
1047 /* Alternative implementation of sqlite3_instvfs when the real |
|
1048 ** implementation is unavailable. |
|
1049 */ |
|
1050 #ifndef SQLITE_ENABLE_INSTVFS |
|
1051 static int test_sqlite3_instvfs( |
|
1052 void * clientData, |
|
1053 Tcl_Interp *interp, |
|
1054 int objc, |
|
1055 Tcl_Obj *CONST objv[] |
|
1056 ){ |
|
1057 Tcl_AppendResult(interp, |
|
1058 "not compiled with -DSQLITE_ENABLE_INSTVFS; sqlite3_instvfs is " |
|
1059 "unavailable", (char*)0); |
|
1060 return TCL_ERROR; |
|
1061 } |
|
1062 #endif /* !defined(SQLITE_ENABLE_INSTVFS) */ |
|
1063 |
|
1064 int SqlitetestOsinst_Init(Tcl_Interp *interp){ |
|
1065 Tcl_CreateObjCommand(interp, "sqlite3_instvfs", test_sqlite3_instvfs, 0, 0); |
|
1066 return TCL_OK; |
|
1067 } |
|
1068 |
|
1069 #endif /* SQLITE_TEST */ |