1 /*
2 ** 2001 September 15
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 ** Code for testing the pager.c module in SQLite.  This code
13 ** is not included in the SQLite library.  It is used for automated
14 ** testing of the SQLite library.
15 */
16 #include "sqliteInt.h"
17 #if defined(INCLUDE_SQLITE_TCL_H)
18 #  include "sqlite_tcl.h"
19 #else
20 #  include "tcl.h"
21 #endif
22 #include <stdlib.h>
23 #include <string.h>
24 #include <ctype.h>
25 
26 extern const char *sqlite3ErrName(int);
27 
28 /*
29 ** Page size and reserved size used for testing.
30 */
31 static int test_pagesize = 1024;
32 
33 /*
34 ** Dummy page reinitializer
35 */
pager_test_reiniter(DbPage * pNotUsed)36 static void pager_test_reiniter(DbPage *pNotUsed){
37   return;
38 }
39 
40 /*
41 ** Usage:   pager_open FILENAME N-PAGE
42 **
43 ** Open a new pager
44 */
pager_open(void * NotUsed,Tcl_Interp * interp,int argc,const char ** argv)45 static int SQLITE_TCLAPI pager_open(
46   void *NotUsed,
47   Tcl_Interp *interp,    /* The TCL interpreter that invoked this command */
48   int argc,              /* Number of arguments */
49   const char **argv      /* Text of each argument */
50 ){
51   u32 pageSize;
52   Pager *pPager;
53   int nPage;
54   int rc;
55   char zBuf[100];
56   if( argc!=3 ){
57     Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
58        " FILENAME N-PAGE\"", 0);
59     return TCL_ERROR;
60   }
61   if( Tcl_GetInt(interp, argv[2], &nPage) ) return TCL_ERROR;
62   rc = sqlite3PagerOpen(sqlite3_vfs_find(0), &pPager, argv[1], 0, 0,
63       SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE | SQLITE_OPEN_MAIN_DB,
64       pager_test_reiniter);
65   if( rc!=SQLITE_OK ){
66     Tcl_AppendResult(interp, sqlite3ErrName(rc), 0);
67     return TCL_ERROR;
68   }
69   sqlite3PagerSetCachesize(pPager, nPage);
70   pageSize = test_pagesize;
71   sqlite3PagerSetPagesize(pPager, &pageSize, -1);
72   sqlite3_snprintf(sizeof(zBuf),zBuf,"%p",pPager);
73   Tcl_AppendResult(interp, zBuf, 0);
74   return TCL_OK;
75 }
76 
77 /*
78 ** Usage:   pager_close ID
79 **
80 ** Close the given pager.
81 */
pager_close(void * NotUsed,Tcl_Interp * interp,int argc,const char ** argv)82 static int SQLITE_TCLAPI pager_close(
83   void *NotUsed,
84   Tcl_Interp *interp,    /* The TCL interpreter that invoked this command */
85   int argc,              /* Number of arguments */
86   const char **argv      /* Text of each argument */
87 ){
88   Pager *pPager;
89   int rc;
90   if( argc!=2 ){
91     Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
92        " ID\"", 0);
93     return TCL_ERROR;
94   }
95   pPager = sqlite3TestTextToPtr(argv[1]);
96   rc = sqlite3PagerClose(pPager, 0);
97   if( rc!=SQLITE_OK ){
98     Tcl_AppendResult(interp, sqlite3ErrName(rc), 0);
99     return TCL_ERROR;
100   }
101   return TCL_OK;
102 }
103 
104 /*
105 ** Usage:   pager_rollback ID
106 **
107 ** Rollback changes
108 */
pager_rollback(void * NotUsed,Tcl_Interp * interp,int argc,const char ** argv)109 static int SQLITE_TCLAPI pager_rollback(
110   void *NotUsed,
111   Tcl_Interp *interp,    /* The TCL interpreter that invoked this command */
112   int argc,              /* Number of arguments */
113   const char **argv      /* Text of each argument */
114 ){
115   Pager *pPager;
116   int rc;
117   if( argc!=2 ){
118     Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
119        " ID\"", 0);
120     return TCL_ERROR;
121   }
122   pPager = sqlite3TestTextToPtr(argv[1]);
123   rc = sqlite3PagerRollback(pPager);
124   if( rc!=SQLITE_OK ){
125     Tcl_AppendResult(interp, sqlite3ErrName(rc), 0);
126     return TCL_ERROR;
127   }
128   return TCL_OK;
129 }
130 
131 /*
132 ** Usage:   pager_commit ID
133 **
134 ** Commit all changes
135 */
pager_commit(void * NotUsed,Tcl_Interp * interp,int argc,const char ** argv)136 static int SQLITE_TCLAPI pager_commit(
137   void *NotUsed,
138   Tcl_Interp *interp,    /* The TCL interpreter that invoked this command */
139   int argc,              /* Number of arguments */
140   const char **argv      /* Text of each argument */
141 ){
142   Pager *pPager;
143   int rc;
144   if( argc!=2 ){
145     Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
146        " ID\"", 0);
147     return TCL_ERROR;
148   }
149   pPager = sqlite3TestTextToPtr(argv[1]);
150   rc = sqlite3PagerCommitPhaseOne(pPager, 0, 0);
151   if( rc!=SQLITE_OK ){
152     Tcl_AppendResult(interp, sqlite3ErrName(rc), 0);
153     return TCL_ERROR;
154   }
155   rc = sqlite3PagerCommitPhaseTwo(pPager);
156   if( rc!=SQLITE_OK ){
157     Tcl_AppendResult(interp, sqlite3ErrName(rc), 0);
158     return TCL_ERROR;
159   }
160   return TCL_OK;
161 }
162 
163 /*
164 ** Usage:   pager_stmt_begin ID
165 **
166 ** Start a new checkpoint.
167 */
pager_stmt_begin(void * NotUsed,Tcl_Interp * interp,int argc,const char ** argv)168 static int SQLITE_TCLAPI pager_stmt_begin(
169   void *NotUsed,
170   Tcl_Interp *interp,    /* The TCL interpreter that invoked this command */
171   int argc,              /* Number of arguments */
172   const char **argv      /* Text of each argument */
173 ){
174   Pager *pPager;
175   int rc;
176   if( argc!=2 ){
177     Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
178        " ID\"", 0);
179     return TCL_ERROR;
180   }
181   pPager = sqlite3TestTextToPtr(argv[1]);
182   rc = sqlite3PagerOpenSavepoint(pPager, 1);
183   if( rc!=SQLITE_OK ){
184     Tcl_AppendResult(interp, sqlite3ErrName(rc), 0);
185     return TCL_ERROR;
186   }
187   return TCL_OK;
188 }
189 
190 /*
191 ** Usage:   pager_stmt_rollback ID
192 **
193 ** Rollback changes to a checkpoint
194 */
pager_stmt_rollback(void * NotUsed,Tcl_Interp * interp,int argc,const char ** argv)195 static int SQLITE_TCLAPI pager_stmt_rollback(
196   void *NotUsed,
197   Tcl_Interp *interp,    /* The TCL interpreter that invoked this command */
198   int argc,              /* Number of arguments */
199   const char **argv      /* Text of each argument */
200 ){
201   Pager *pPager;
202   int rc;
203   if( argc!=2 ){
204     Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
205        " ID\"", 0);
206     return TCL_ERROR;
207   }
208   pPager = sqlite3TestTextToPtr(argv[1]);
209   rc = sqlite3PagerSavepoint(pPager, SAVEPOINT_ROLLBACK, 0);
210   sqlite3PagerSavepoint(pPager, SAVEPOINT_RELEASE, 0);
211   if( rc!=SQLITE_OK ){
212     Tcl_AppendResult(interp, sqlite3ErrName(rc), 0);
213     return TCL_ERROR;
214   }
215   return TCL_OK;
216 }
217 
218 /*
219 ** Usage:   pager_stmt_commit ID
220 **
221 ** Commit changes to a checkpoint
222 */
pager_stmt_commit(void * NotUsed,Tcl_Interp * interp,int argc,const char ** argv)223 static int SQLITE_TCLAPI pager_stmt_commit(
224   void *NotUsed,
225   Tcl_Interp *interp,    /* The TCL interpreter that invoked this command */
226   int argc,              /* Number of arguments */
227   const char **argv      /* Text of each argument */
228 ){
229   Pager *pPager;
230   int rc;
231   if( argc!=2 ){
232     Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
233        " ID\"", 0);
234     return TCL_ERROR;
235   }
236   pPager = sqlite3TestTextToPtr(argv[1]);
237   rc = sqlite3PagerSavepoint(pPager, SAVEPOINT_RELEASE, 0);
238   if( rc!=SQLITE_OK ){
239     Tcl_AppendResult(interp, sqlite3ErrName(rc), 0);
240     return TCL_ERROR;
241   }
242   return TCL_OK;
243 }
244 
245 /*
246 ** Usage:   pager_stats ID
247 **
248 ** Return pager statistics.
249 */
pager_stats(void * NotUsed,Tcl_Interp * interp,int argc,const char ** argv)250 static int SQLITE_TCLAPI pager_stats(
251   void *NotUsed,
252   Tcl_Interp *interp,    /* The TCL interpreter that invoked this command */
253   int argc,              /* Number of arguments */
254   const char **argv      /* Text of each argument */
255 ){
256   Pager *pPager;
257   int i, *a;
258   if( argc!=2 ){
259     Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
260        " ID\"", 0);
261     return TCL_ERROR;
262   }
263   pPager = sqlite3TestTextToPtr(argv[1]);
264   a = sqlite3PagerStats(pPager);
265   for(i=0; i<9; i++){
266     static char *zName[] = {
267       "ref", "page", "max", "size", "state", "err",
268       "hit", "miss", "ovfl",
269     };
270     char zBuf[100];
271     Tcl_AppendElement(interp, zName[i]);
272     sqlite3_snprintf(sizeof(zBuf),zBuf,"%d",a[i]);
273     Tcl_AppendElement(interp, zBuf);
274   }
275   return TCL_OK;
276 }
277 
278 /*
279 ** Usage:   pager_pagecount ID
280 **
281 ** Return the size of the database file.
282 */
pager_pagecount(void * NotUsed,Tcl_Interp * interp,int argc,const char ** argv)283 static int SQLITE_TCLAPI pager_pagecount(
284   void *NotUsed,
285   Tcl_Interp *interp,    /* The TCL interpreter that invoked this command */
286   int argc,              /* Number of arguments */
287   const char **argv      /* Text of each argument */
288 ){
289   Pager *pPager;
290   char zBuf[100];
291   int nPage;
292   if( argc!=2 ){
293     Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
294        " ID\"", 0);
295     return TCL_ERROR;
296   }
297   pPager = sqlite3TestTextToPtr(argv[1]);
298   sqlite3PagerPagecount(pPager, &nPage);
299   sqlite3_snprintf(sizeof(zBuf), zBuf, "%d", nPage);
300   Tcl_AppendResult(interp, zBuf, 0);
301   return TCL_OK;
302 }
303 
304 /*
305 ** Usage:   page_get ID PGNO
306 **
307 ** Return a pointer to a page from the database.
308 */
page_get(void * NotUsed,Tcl_Interp * interp,int argc,const char ** argv)309 static int SQLITE_TCLAPI page_get(
310   void *NotUsed,
311   Tcl_Interp *interp,    /* The TCL interpreter that invoked this command */
312   int argc,              /* Number of arguments */
313   const char **argv      /* Text of each argument */
314 ){
315   Pager *pPager;
316   char zBuf[100];
317   DbPage *pPage = 0;
318   int pgno;
319   int rc;
320   if( argc!=3 ){
321     Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
322        " ID PGNO\"", 0);
323     return TCL_ERROR;
324   }
325   pPager = sqlite3TestTextToPtr(argv[1]);
326   if( Tcl_GetInt(interp, argv[2], &pgno) ) return TCL_ERROR;
327   rc = sqlite3PagerSharedLock(pPager);
328   if( rc==SQLITE_OK ){
329     rc = sqlite3PagerGet(pPager, pgno, &pPage, 0);
330   }
331   if( rc!=SQLITE_OK ){
332     Tcl_AppendResult(interp, sqlite3ErrName(rc), 0);
333     return TCL_ERROR;
334   }
335   sqlite3_snprintf(sizeof(zBuf),zBuf,"%p",pPage);
336   Tcl_AppendResult(interp, zBuf, 0);
337   return TCL_OK;
338 }
339 
340 /*
341 ** Usage:   page_lookup ID PGNO
342 **
343 ** Return a pointer to a page if the page is already in cache.
344 ** If not in cache, return an empty string.
345 */
page_lookup(void * NotUsed,Tcl_Interp * interp,int argc,const char ** argv)346 static int SQLITE_TCLAPI page_lookup(
347   void *NotUsed,
348   Tcl_Interp *interp,    /* The TCL interpreter that invoked this command */
349   int argc,              /* Number of arguments */
350   const char **argv      /* Text of each argument */
351 ){
352   Pager *pPager;
353   char zBuf[100];
354   DbPage *pPage;
355   int pgno;
356   if( argc!=3 ){
357     Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
358        " ID PGNO\"", 0);
359     return TCL_ERROR;
360   }
361   pPager = sqlite3TestTextToPtr(argv[1]);
362   if( Tcl_GetInt(interp, argv[2], &pgno) ) return TCL_ERROR;
363   pPage = sqlite3PagerLookup(pPager, pgno);
364   if( pPage ){
365     sqlite3_snprintf(sizeof(zBuf),zBuf,"%p",pPage);
366     Tcl_AppendResult(interp, zBuf, 0);
367   }
368   return TCL_OK;
369 }
370 
371 /*
372 ** Usage:   pager_truncate ID PGNO
373 */
pager_truncate(void * NotUsed,Tcl_Interp * interp,int argc,const char ** argv)374 static int SQLITE_TCLAPI pager_truncate(
375   void *NotUsed,
376   Tcl_Interp *interp,    /* The TCL interpreter that invoked this command */
377   int argc,              /* Number of arguments */
378   const char **argv      /* Text of each argument */
379 ){
380   Pager *pPager;
381   int pgno;
382   if( argc!=3 ){
383     Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
384        " ID PGNO\"", 0);
385     return TCL_ERROR;
386   }
387   pPager = sqlite3TestTextToPtr(argv[1]);
388   if( Tcl_GetInt(interp, argv[2], &pgno) ) return TCL_ERROR;
389   sqlite3PagerTruncateImage(pPager, pgno);
390   return TCL_OK;
391 }
392 
393 
394 /*
395 ** Usage:   page_unref PAGE
396 **
397 ** Drop a pointer to a page.
398 */
page_unref(void * NotUsed,Tcl_Interp * interp,int argc,const char ** argv)399 static int SQLITE_TCLAPI page_unref(
400   void *NotUsed,
401   Tcl_Interp *interp,    /* The TCL interpreter that invoked this command */
402   int argc,              /* Number of arguments */
403   const char **argv      /* Text of each argument */
404 ){
405   DbPage *pPage;
406   if( argc!=2 ){
407     Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
408        " PAGE\"", 0);
409     return TCL_ERROR;
410   }
411   pPage = (DbPage *)sqlite3TestTextToPtr(argv[1]);
412   sqlite3PagerUnref(pPage);
413   return TCL_OK;
414 }
415 
416 /*
417 ** Usage:   page_read PAGE
418 **
419 ** Return the content of a page
420 */
page_read(void * NotUsed,Tcl_Interp * interp,int argc,const char ** argv)421 static int SQLITE_TCLAPI page_read(
422   void *NotUsed,
423   Tcl_Interp *interp,    /* The TCL interpreter that invoked this command */
424   int argc,              /* Number of arguments */
425   const char **argv      /* Text of each argument */
426 ){
427   char zBuf[100];
428   DbPage *pPage;
429   if( argc!=2 ){
430     Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
431        " PAGE\"", 0);
432     return TCL_ERROR;
433   }
434   pPage = sqlite3TestTextToPtr(argv[1]);
435   memcpy(zBuf, sqlite3PagerGetData(pPage), sizeof(zBuf));
436   Tcl_AppendResult(interp, zBuf, 0);
437   return TCL_OK;
438 }
439 
440 /*
441 ** Usage:   page_number PAGE
442 **
443 ** Return the page number for a page.
444 */
page_number(void * NotUsed,Tcl_Interp * interp,int argc,const char ** argv)445 static int SQLITE_TCLAPI page_number(
446   void *NotUsed,
447   Tcl_Interp *interp,    /* The TCL interpreter that invoked this command */
448   int argc,              /* Number of arguments */
449   const char **argv      /* Text of each argument */
450 ){
451   char zBuf[100];
452   DbPage *pPage;
453   if( argc!=2 ){
454     Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
455        " PAGE\"", 0);
456     return TCL_ERROR;
457   }
458   pPage = (DbPage *)sqlite3TestTextToPtr(argv[1]);
459   sqlite3_snprintf(sizeof(zBuf), zBuf, "%d", sqlite3PagerPagenumber(pPage));
460   Tcl_AppendResult(interp, zBuf, 0);
461   return TCL_OK;
462 }
463 
464 /*
465 ** Usage:   page_write PAGE DATA
466 **
467 ** Write something into a page.
468 */
page_write(void * NotUsed,Tcl_Interp * interp,int argc,const char ** argv)469 static int SQLITE_TCLAPI page_write(
470   void *NotUsed,
471   Tcl_Interp *interp,    /* The TCL interpreter that invoked this command */
472   int argc,              /* Number of arguments */
473   const char **argv      /* Text of each argument */
474 ){
475   DbPage *pPage;
476   char *pData;
477   int rc;
478   if( argc!=3 ){
479     Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
480        " PAGE DATA\"", 0);
481     return TCL_ERROR;
482   }
483   pPage = (DbPage *)sqlite3TestTextToPtr(argv[1]);
484   rc = sqlite3PagerWrite(pPage);
485   if( rc!=SQLITE_OK ){
486     Tcl_AppendResult(interp, sqlite3ErrName(rc), 0);
487     return TCL_ERROR;
488   }
489   pData = sqlite3PagerGetData(pPage);
490   strncpy(pData, argv[2], test_pagesize-1);
491   pData[test_pagesize-1] = 0;
492   return TCL_OK;
493 }
494 
495 #ifndef SQLITE_OMIT_DISKIO
496 /*
497 ** Usage:   fake_big_file  N  FILENAME
498 **
499 ** Write a few bytes at the N megabyte point of FILENAME.  This will
500 ** create a large file.  If the file was a valid SQLite database, then
501 ** the next time the database is opened, SQLite will begin allocating
502 ** new pages after N.  If N is 2096 or bigger, this will test the
503 ** ability of SQLite to write to large files.
504 */
fake_big_file(void * NotUsed,Tcl_Interp * interp,int argc,const char ** argv)505 static int SQLITE_TCLAPI fake_big_file(
506   void *NotUsed,
507   Tcl_Interp *interp,    /* The TCL interpreter that invoked this command */
508   int argc,              /* Number of arguments */
509   const char **argv      /* Text of each argument */
510 ){
511   sqlite3_vfs *pVfs;
512   sqlite3_file *fd = 0;
513   int rc;
514   int n;
515   i64 offset;
516   char *zFile;
517   int nFile;
518   if( argc!=3 ){
519     Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
520        " N-MEGABYTES FILE\"", 0);
521     return TCL_ERROR;
522   }
523   if( Tcl_GetInt(interp, argv[1], &n) ) return TCL_ERROR;
524 
525   pVfs = sqlite3_vfs_find(0);
526   nFile = (int)strlen(argv[2]);
527   zFile = sqlite3_malloc( nFile+2 );
528   if( zFile==0 ) return TCL_ERROR;
529   memcpy(zFile, argv[2], nFile+1);
530   zFile[nFile+1] = 0;
531   rc = sqlite3OsOpenMalloc(pVfs, zFile, &fd,
532       (SQLITE_OPEN_CREATE|SQLITE_OPEN_READWRITE|SQLITE_OPEN_MAIN_DB), 0
533   );
534   if( rc ){
535     Tcl_AppendResult(interp, "open failed: ", sqlite3ErrName(rc), 0);
536     sqlite3_free(zFile);
537     return TCL_ERROR;
538   }
539   offset = n;
540   offset *= 1024*1024;
541   rc = sqlite3OsWrite(fd, "Hello, World!", 14, offset);
542   sqlite3OsCloseFree(fd);
543   sqlite3_free(zFile);
544   if( rc ){
545     Tcl_AppendResult(interp, "write failed: ", sqlite3ErrName(rc), 0);
546     return TCL_ERROR;
547   }
548   return TCL_OK;
549 }
550 #endif
551 
552 
553 /*
554 ** test_control_pending_byte  PENDING_BYTE
555 **
556 ** Set the PENDING_BYTE using the sqlite3_test_control() interface.
557 */
testPendingByte(void * NotUsed,Tcl_Interp * interp,int argc,const char ** argv)558 static int SQLITE_TCLAPI testPendingByte(
559   void *NotUsed,
560   Tcl_Interp *interp,    /* The TCL interpreter that invoked this command */
561   int argc,              /* Number of arguments */
562   const char **argv      /* Text of each argument */
563 ){
564   int pbyte;
565   int rc;
566   if( argc!=2 ){
567     Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
568                      " PENDING-BYTE\"", (void*)0);
569     return TCL_ERROR;
570   }
571   if( Tcl_GetInt(interp, argv[1], &pbyte) ) return TCL_ERROR;
572   rc = sqlite3_test_control(SQLITE_TESTCTRL_PENDING_BYTE, pbyte);
573   Tcl_SetObjResult(interp, Tcl_NewIntObj(rc));
574   return TCL_OK;
575 }
576 
577 /*
578 ** The sqlite3FaultSim() callback:
579 */
580 static Tcl_Interp *faultSimInterp = 0;
581 static int faultSimScriptSize = 0;
582 static char *faultSimScript;
faultSimCallback(int x)583 static int faultSimCallback(int x){
584   char zInt[30];
585   int i;
586   int isNeg;
587   int rc;
588   if( x==0 ){
589     memcpy(faultSimScript+faultSimScriptSize, "0", 2);
590   }else{
591     /* Convert x to text without using any sqlite3 routines */
592     if( x<0 ){
593       isNeg = 1;
594       x = -x;
595     }else{
596       isNeg = 0;
597     }
598     zInt[sizeof(zInt)-1] = 0;
599     for(i=sizeof(zInt)-2; i>0 && x>0; i--, x /= 10){
600       zInt[i] = (x%10) + '0';
601     }
602     if( isNeg ) zInt[i--] = '-';
603     memcpy(faultSimScript+faultSimScriptSize, zInt+i+1, sizeof(zInt)-i);
604   }
605   rc = Tcl_Eval(faultSimInterp, faultSimScript);
606   if( rc ){
607     fprintf(stderr, "fault simulator script failed: [%s]", faultSimScript);
608     rc = SQLITE_ERROR;
609   }else{
610     rc = atoi(Tcl_GetStringResult(faultSimInterp));
611   }
612   Tcl_ResetResult(faultSimInterp);
613   return rc;
614 }
615 
616 /*
617 ** sqlite3_test_control_fault_install SCRIPT
618 **
619 ** Arrange to invoke SCRIPT with the integer argument to sqlite3FaultSim()
620 ** appended, whenever sqlite3FaultSim() is called.  Or, if SCRIPT is the
621 ** empty string, cancel the sqlite3FaultSim() callback.
622 */
faultInstallCmd(void * NotUsed,Tcl_Interp * interp,int argc,const char ** argv)623 static int SQLITE_TCLAPI faultInstallCmd(
624   void *NotUsed,
625   Tcl_Interp *interp,    /* The TCL interpreter that invoked this command */
626   int argc,              /* Number of arguments */
627   const char **argv      /* Text of each argument */
628 ){
629   const char *zScript;
630   int nScript;
631   int rc;
632   if( argc!=1 && argc!=2 ){
633     Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
634                      " SCRIPT\"", (void*)0);
635   }
636   zScript = argc==2 ? argv[1] : "";
637   nScript = (int)strlen(zScript);
638   if( faultSimScript ){
639     free(faultSimScript);
640     faultSimScript = 0;
641   }
642   if( nScript==0 ){
643     rc = sqlite3_test_control(SQLITE_TESTCTRL_FAULT_INSTALL, 0);
644   }else{
645     faultSimScript = malloc( nScript+100 );
646     if( faultSimScript==0 ){
647       Tcl_AppendResult(interp, "out of memory", (void*)0);
648       return SQLITE_ERROR;
649     }
650     memcpy(faultSimScript, zScript, nScript);
651     faultSimScript[nScript] = ' ';
652     faultSimScriptSize = nScript+1;
653     faultSimInterp = interp;
654     rc = sqlite3_test_control(SQLITE_TESTCTRL_FAULT_INSTALL, faultSimCallback);
655   }
656   Tcl_SetObjResult(interp, Tcl_NewIntObj(rc));
657   return SQLITE_OK;
658 }
659 
660 /*
661 ** sqlite3BitvecBuiltinTest SIZE PROGRAM
662 **
663 ** Invoke the SQLITE_TESTCTRL_BITVEC_TEST operator on test_control.
664 ** See comments on sqlite3BitvecBuiltinTest() for additional information.
665 */
testBitvecBuiltinTest(void * NotUsed,Tcl_Interp * interp,int argc,const char ** argv)666 static int SQLITE_TCLAPI testBitvecBuiltinTest(
667   void *NotUsed,
668   Tcl_Interp *interp,    /* The TCL interpreter that invoked this command */
669   int argc,              /* Number of arguments */
670   const char **argv      /* Text of each argument */
671 ){
672   int sz, rc;
673   int nProg = 0;
674   int aProg[100];
675   const char *z;
676   if( argc!=3 ){
677     Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
678                      " SIZE PROGRAM\"", (void*)0);
679   }
680   if( Tcl_GetInt(interp, argv[1], &sz) ) return TCL_ERROR;
681   z = argv[2];
682   while( nProg<99 && *z ){
683     while( *z && !sqlite3Isdigit(*z) ){ z++; }
684     if( *z==0 ) break;
685     aProg[nProg++] = atoi(z);
686     while( sqlite3Isdigit(*z) ){ z++; }
687   }
688   aProg[nProg] = 0;
689   rc = sqlite3_test_control(SQLITE_TESTCTRL_BITVEC_TEST, sz, aProg);
690   Tcl_SetObjResult(interp, Tcl_NewIntObj(rc));
691   return TCL_OK;
692 }
693 
694 /*
695 ** Register commands with the TCL interpreter.
696 */
Sqlitetest2_Init(Tcl_Interp * interp)697 int Sqlitetest2_Init(Tcl_Interp *interp){
698   extern int sqlite3_io_error_persist;
699   extern int sqlite3_io_error_pending;
700   extern int sqlite3_io_error_hit;
701   extern int sqlite3_io_error_hardhit;
702   extern int sqlite3_diskfull_pending;
703   extern int sqlite3_diskfull;
704   static struct {
705     char *zName;
706     Tcl_CmdProc *xProc;
707   } aCmd[] = {
708     { "pager_open",              (Tcl_CmdProc*)pager_open          },
709     { "pager_close",             (Tcl_CmdProc*)pager_close         },
710     { "pager_commit",            (Tcl_CmdProc*)pager_commit        },
711     { "pager_rollback",          (Tcl_CmdProc*)pager_rollback      },
712     { "pager_stmt_begin",        (Tcl_CmdProc*)pager_stmt_begin    },
713     { "pager_stmt_commit",       (Tcl_CmdProc*)pager_stmt_commit   },
714     { "pager_stmt_rollback",     (Tcl_CmdProc*)pager_stmt_rollback },
715     { "pager_stats",             (Tcl_CmdProc*)pager_stats         },
716     { "pager_pagecount",         (Tcl_CmdProc*)pager_pagecount     },
717     { "page_get",                (Tcl_CmdProc*)page_get            },
718     { "page_lookup",             (Tcl_CmdProc*)page_lookup         },
719     { "page_unref",              (Tcl_CmdProc*)page_unref          },
720     { "page_read",               (Tcl_CmdProc*)page_read           },
721     { "page_write",              (Tcl_CmdProc*)page_write          },
722     { "page_number",             (Tcl_CmdProc*)page_number         },
723     { "pager_truncate",          (Tcl_CmdProc*)pager_truncate      },
724 #ifndef SQLITE_OMIT_DISKIO
725     { "fake_big_file",           (Tcl_CmdProc*)fake_big_file       },
726 #endif
727     { "sqlite3BitvecBuiltinTest",(Tcl_CmdProc*)testBitvecBuiltinTest     },
728     { "sqlite3_test_control_pending_byte",  (Tcl_CmdProc*)testPendingByte },
729     { "sqlite3_test_control_fault_install", (Tcl_CmdProc*)faultInstallCmd },
730   };
731   int i;
732   for(i=0; i<sizeof(aCmd)/sizeof(aCmd[0]); i++){
733     Tcl_CreateCommand(interp, aCmd[i].zName, aCmd[i].xProc, 0, 0);
734   }
735   Tcl_LinkVar(interp, "sqlite_io_error_pending",
736      (char*)&sqlite3_io_error_pending, TCL_LINK_INT);
737   Tcl_LinkVar(interp, "sqlite_io_error_persist",
738      (char*)&sqlite3_io_error_persist, TCL_LINK_INT);
739   Tcl_LinkVar(interp, "sqlite_io_error_hit",
740      (char*)&sqlite3_io_error_hit, TCL_LINK_INT);
741   Tcl_LinkVar(interp, "sqlite_io_error_hardhit",
742      (char*)&sqlite3_io_error_hardhit, TCL_LINK_INT);
743   Tcl_LinkVar(interp, "sqlite_diskfull_pending",
744      (char*)&sqlite3_diskfull_pending, TCL_LINK_INT);
745   Tcl_LinkVar(interp, "sqlite_diskfull",
746      (char*)&sqlite3_diskfull, TCL_LINK_INT);
747 #ifndef SQLITE_OMIT_WSD
748   Tcl_LinkVar(interp, "sqlite_pending_byte",
749      (char*)&sqlite3PendingByte, TCL_LINK_INT | TCL_LINK_READ_ONLY);
750 #endif
751   return TCL_OK;
752 }
753