1
2 #pragma ident "%Z%%M% %I% %E% SMI"
3
4 /*
5 ** 2001 September 15
6 **
7 ** The author disclaims copyright to this source code. In place of
8 ** a legal notice, here is a blessing:
9 **
10 ** May you do good and not evil.
11 ** May you find forgiveness for yourself and forgive others.
12 ** May you share freely, never taking more than you give.
13 **
14 *************************************************************************
15 ** Code for testing the pager.c module in SQLite. This code
16 ** is not included in the SQLite library. It is used for automated
17 ** testing of the SQLite library.
18 **
19 ** $Id: test2.c,v 1.16 2004/02/10 01:54:28 drh Exp $
20 */
21 #include "os.h"
22 #include "sqliteInt.h"
23 #include "pager.h"
24 #include "tcl.h"
25 #include <stdlib.h>
26 #include <string.h>
27
28 /*
29 ** Interpret an SQLite error number
30 */
errorName(int rc)31 static char *errorName(int rc){
32 char *zName;
33 switch( rc ){
34 case SQLITE_OK: zName = "SQLITE_OK"; break;
35 case SQLITE_ERROR: zName = "SQLITE_ERROR"; break;
36 case SQLITE_INTERNAL: zName = "SQLITE_INTERNAL"; break;
37 case SQLITE_PERM: zName = "SQLITE_PERM"; break;
38 case SQLITE_ABORT: zName = "SQLITE_ABORT"; break;
39 case SQLITE_BUSY: zName = "SQLITE_BUSY"; break;
40 case SQLITE_NOMEM: zName = "SQLITE_NOMEM"; break;
41 case SQLITE_READONLY: zName = "SQLITE_READONLY"; break;
42 case SQLITE_INTERRUPT: zName = "SQLITE_INTERRUPT"; break;
43 case SQLITE_IOERR: zName = "SQLITE_IOERR"; break;
44 case SQLITE_CORRUPT: zName = "SQLITE_CORRUPT"; break;
45 case SQLITE_NOTFOUND: zName = "SQLITE_NOTFOUND"; break;
46 case SQLITE_FULL: zName = "SQLITE_FULL"; break;
47 case SQLITE_CANTOPEN: zName = "SQLITE_CANTOPEN"; break;
48 case SQLITE_PROTOCOL: zName = "SQLITE_PROTOCOL"; break;
49 case SQLITE_EMPTY: zName = "SQLITE_EMPTY"; break;
50 case SQLITE_SCHEMA: zName = "SQLITE_SCHEMA"; break;
51 case SQLITE_TOOBIG: zName = "SQLITE_TOOBIG"; break;
52 case SQLITE_CONSTRAINT: zName = "SQLITE_CONSTRAINT"; break;
53 case SQLITE_MISMATCH: zName = "SQLITE_MISMATCH"; break;
54 case SQLITE_MISUSE: zName = "SQLITE_MISUSE"; break;
55 case SQLITE_NOLFS: zName = "SQLITE_NOLFS"; break;
56 default: zName = "SQLITE_Unknown"; break;
57 }
58 return zName;
59 }
60
61 /*
62 ** Usage: pager_open FILENAME N-PAGE
63 **
64 ** Open a new pager
65 */
pager_open(void * NotUsed,Tcl_Interp * interp,int argc,const char ** argv)66 static int pager_open(
67 void *NotUsed,
68 Tcl_Interp *interp, /* The TCL interpreter that invoked this command */
69 int argc, /* Number of arguments */
70 const char **argv /* Text of each argument */
71 ){
72 Pager *pPager;
73 int nPage;
74 int rc;
75 char zBuf[100];
76 if( argc!=3 ){
77 Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
78 " FILENAME N-PAGE\"", 0);
79 return TCL_ERROR;
80 }
81 if( Tcl_GetInt(interp, argv[2], &nPage) ) return TCL_ERROR;
82 rc = sqlitepager_open(&pPager, argv[1], nPage, 0, 1);
83 if( rc!=SQLITE_OK ){
84 Tcl_AppendResult(interp, errorName(rc), 0);
85 return TCL_ERROR;
86 }
87 sprintf(zBuf,"0x%x",(int)pPager);
88 Tcl_AppendResult(interp, zBuf, 0);
89 return TCL_OK;
90 }
91
92 /*
93 ** Usage: pager_close ID
94 **
95 ** Close the given pager.
96 */
pager_close(void * NotUsed,Tcl_Interp * interp,int argc,const char ** argv)97 static int pager_close(
98 void *NotUsed,
99 Tcl_Interp *interp, /* The TCL interpreter that invoked this command */
100 int argc, /* Number of arguments */
101 const char **argv /* Text of each argument */
102 ){
103 Pager *pPager;
104 int rc;
105 if( argc!=2 ){
106 Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
107 " ID\"", 0);
108 return TCL_ERROR;
109 }
110 if( Tcl_GetInt(interp, argv[1], (int*)&pPager) ) return TCL_ERROR;
111 rc = sqlitepager_close(pPager);
112 if( rc!=SQLITE_OK ){
113 Tcl_AppendResult(interp, errorName(rc), 0);
114 return TCL_ERROR;
115 }
116 return TCL_OK;
117 }
118
119 /*
120 ** Usage: pager_rollback ID
121 **
122 ** Rollback changes
123 */
pager_rollback(void * NotUsed,Tcl_Interp * interp,int argc,const char ** argv)124 static int pager_rollback(
125 void *NotUsed,
126 Tcl_Interp *interp, /* The TCL interpreter that invoked this command */
127 int argc, /* Number of arguments */
128 const char **argv /* Text of each argument */
129 ){
130 Pager *pPager;
131 int rc;
132 if( argc!=2 ){
133 Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
134 " ID\"", 0);
135 return TCL_ERROR;
136 }
137 if( Tcl_GetInt(interp, argv[1], (int*)&pPager) ) return TCL_ERROR;
138 rc = sqlitepager_rollback(pPager);
139 if( rc!=SQLITE_OK ){
140 Tcl_AppendResult(interp, errorName(rc), 0);
141 return TCL_ERROR;
142 }
143 return TCL_OK;
144 }
145
146 /*
147 ** Usage: pager_commit ID
148 **
149 ** Commit all changes
150 */
pager_commit(void * NotUsed,Tcl_Interp * interp,int argc,const char ** argv)151 static int pager_commit(
152 void *NotUsed,
153 Tcl_Interp *interp, /* The TCL interpreter that invoked this command */
154 int argc, /* Number of arguments */
155 const char **argv /* Text of each argument */
156 ){
157 Pager *pPager;
158 int rc;
159 if( argc!=2 ){
160 Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
161 " ID\"", 0);
162 return TCL_ERROR;
163 }
164 if( Tcl_GetInt(interp, argv[1], (int*)&pPager) ) return TCL_ERROR;
165 rc = sqlitepager_commit(pPager);
166 if( rc!=SQLITE_OK ){
167 Tcl_AppendResult(interp, errorName(rc), 0);
168 return TCL_ERROR;
169 }
170 return TCL_OK;
171 }
172
173 /*
174 ** Usage: pager_ckpt_begin ID
175 **
176 ** Start a new checkpoint.
177 */
pager_ckpt_begin(void * NotUsed,Tcl_Interp * interp,int argc,const char ** argv)178 static int pager_ckpt_begin(
179 void *NotUsed,
180 Tcl_Interp *interp, /* The TCL interpreter that invoked this command */
181 int argc, /* Number of arguments */
182 const char **argv /* Text of each argument */
183 ){
184 Pager *pPager;
185 int rc;
186 if( argc!=2 ){
187 Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
188 " ID\"", 0);
189 return TCL_ERROR;
190 }
191 if( Tcl_GetInt(interp, argv[1], (int*)&pPager) ) return TCL_ERROR;
192 rc = sqlitepager_ckpt_begin(pPager);
193 if( rc!=SQLITE_OK ){
194 Tcl_AppendResult(interp, errorName(rc), 0);
195 return TCL_ERROR;
196 }
197 return TCL_OK;
198 }
199
200 /*
201 ** Usage: pager_ckpt_rollback ID
202 **
203 ** Rollback changes to a checkpoint
204 */
pager_ckpt_rollback(void * NotUsed,Tcl_Interp * interp,int argc,const char ** argv)205 static int pager_ckpt_rollback(
206 void *NotUsed,
207 Tcl_Interp *interp, /* The TCL interpreter that invoked this command */
208 int argc, /* Number of arguments */
209 const char **argv /* Text of each argument */
210 ){
211 Pager *pPager;
212 int rc;
213 if( argc!=2 ){
214 Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
215 " ID\"", 0);
216 return TCL_ERROR;
217 }
218 if( Tcl_GetInt(interp, argv[1], (int*)&pPager) ) return TCL_ERROR;
219 rc = sqlitepager_ckpt_rollback(pPager);
220 if( rc!=SQLITE_OK ){
221 Tcl_AppendResult(interp, errorName(rc), 0);
222 return TCL_ERROR;
223 }
224 return TCL_OK;
225 }
226
227 /*
228 ** Usage: pager_ckpt_commit ID
229 **
230 ** Commit changes to a checkpoint
231 */
pager_ckpt_commit(void * NotUsed,Tcl_Interp * interp,int argc,const char ** argv)232 static int pager_ckpt_commit(
233 void *NotUsed,
234 Tcl_Interp *interp, /* The TCL interpreter that invoked this command */
235 int argc, /* Number of arguments */
236 const char **argv /* Text of each argument */
237 ){
238 Pager *pPager;
239 int rc;
240 if( argc!=2 ){
241 Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
242 " ID\"", 0);
243 return TCL_ERROR;
244 }
245 if( Tcl_GetInt(interp, argv[1], (int*)&pPager) ) return TCL_ERROR;
246 rc = sqlitepager_ckpt_commit(pPager);
247 if( rc!=SQLITE_OK ){
248 Tcl_AppendResult(interp, errorName(rc), 0);
249 return TCL_ERROR;
250 }
251 return TCL_OK;
252 }
253
254 /*
255 ** Usage: pager_stats ID
256 **
257 ** Return pager statistics.
258 */
pager_stats(void * NotUsed,Tcl_Interp * interp,int argc,const char ** argv)259 static int pager_stats(
260 void *NotUsed,
261 Tcl_Interp *interp, /* The TCL interpreter that invoked this command */
262 int argc, /* Number of arguments */
263 const char **argv /* Text of each argument */
264 ){
265 Pager *pPager;
266 int i, *a;
267 if( argc!=2 ){
268 Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
269 " ID\"", 0);
270 return TCL_ERROR;
271 }
272 if( Tcl_GetInt(interp, argv[1], (int*)&pPager) ) return TCL_ERROR;
273 a = sqlitepager_stats(pPager);
274 for(i=0; i<9; i++){
275 static char *zName[] = {
276 "ref", "page", "max", "size", "state", "err",
277 "hit", "miss", "ovfl",
278 };
279 char zBuf[100];
280 Tcl_AppendElement(interp, zName[i]);
281 sprintf(zBuf,"%d",a[i]);
282 Tcl_AppendElement(interp, zBuf);
283 }
284 return TCL_OK;
285 }
286
287 /*
288 ** Usage: pager_pagecount ID
289 **
290 ** Return the size of the database file.
291 */
pager_pagecount(void * NotUsed,Tcl_Interp * interp,int argc,const char ** argv)292 static int pager_pagecount(
293 void *NotUsed,
294 Tcl_Interp *interp, /* The TCL interpreter that invoked this command */
295 int argc, /* Number of arguments */
296 const char **argv /* Text of each argument */
297 ){
298 Pager *pPager;
299 char zBuf[100];
300 if( argc!=2 ){
301 Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
302 " ID\"", 0);
303 return TCL_ERROR;
304 }
305 if( Tcl_GetInt(interp, argv[1], (int*)&pPager) ) return TCL_ERROR;
306 sprintf(zBuf,"%d",sqlitepager_pagecount(pPager));
307 Tcl_AppendResult(interp, zBuf, 0);
308 return TCL_OK;
309 }
310
311 /*
312 ** Usage: page_get ID PGNO
313 **
314 ** Return a pointer to a page from the database.
315 */
page_get(void * NotUsed,Tcl_Interp * interp,int argc,const char ** argv)316 static int page_get(
317 void *NotUsed,
318 Tcl_Interp *interp, /* The TCL interpreter that invoked this command */
319 int argc, /* Number of arguments */
320 const char **argv /* Text of each argument */
321 ){
322 Pager *pPager;
323 char zBuf[100];
324 void *pPage;
325 int pgno;
326 int rc;
327 if( argc!=3 ){
328 Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
329 " ID PGNO\"", 0);
330 return TCL_ERROR;
331 }
332 if( Tcl_GetInt(interp, argv[1], (int*)&pPager) ) return TCL_ERROR;
333 if( Tcl_GetInt(interp, argv[2], &pgno) ) return TCL_ERROR;
334 rc = sqlitepager_get(pPager, pgno, &pPage);
335 if( rc!=SQLITE_OK ){
336 Tcl_AppendResult(interp, errorName(rc), 0);
337 return TCL_ERROR;
338 }
339 sprintf(zBuf,"0x%x",(int)pPage);
340 Tcl_AppendResult(interp, zBuf, 0);
341 return TCL_OK;
342 }
343
344 /*
345 ** Usage: page_lookup ID PGNO
346 **
347 ** Return a pointer to a page if the page is already in cache.
348 ** If not in cache, return an empty string.
349 */
page_lookup(void * NotUsed,Tcl_Interp * interp,int argc,const char ** argv)350 static int page_lookup(
351 void *NotUsed,
352 Tcl_Interp *interp, /* The TCL interpreter that invoked this command */
353 int argc, /* Number of arguments */
354 const char **argv /* Text of each argument */
355 ){
356 Pager *pPager;
357 char zBuf[100];
358 void *pPage;
359 int pgno;
360 if( argc!=3 ){
361 Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
362 " ID PGNO\"", 0);
363 return TCL_ERROR;
364 }
365 if( Tcl_GetInt(interp, argv[1], (int*)&pPager) ) return TCL_ERROR;
366 if( Tcl_GetInt(interp, argv[2], &pgno) ) return TCL_ERROR;
367 pPage = sqlitepager_lookup(pPager, pgno);
368 if( pPage ){
369 sprintf(zBuf,"0x%x",(int)pPage);
370 Tcl_AppendResult(interp, zBuf, 0);
371 }
372 return TCL_OK;
373 }
374
375 /*
376 ** Usage: page_unref PAGE
377 **
378 ** Drop a pointer to a page.
379 */
page_unref(void * NotUsed,Tcl_Interp * interp,int argc,const char ** argv)380 static int page_unref(
381 void *NotUsed,
382 Tcl_Interp *interp, /* The TCL interpreter that invoked this command */
383 int argc, /* Number of arguments */
384 const char **argv /* Text of each argument */
385 ){
386 void *pPage;
387 int rc;
388 if( argc!=2 ){
389 Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
390 " PAGE\"", 0);
391 return TCL_ERROR;
392 }
393 if( Tcl_GetInt(interp, argv[1], (int*)&pPage) ) return TCL_ERROR;
394 rc = sqlitepager_unref(pPage);
395 if( rc!=SQLITE_OK ){
396 Tcl_AppendResult(interp, errorName(rc), 0);
397 return TCL_ERROR;
398 }
399 return TCL_OK;
400 }
401
402 /*
403 ** Usage: page_read PAGE
404 **
405 ** Return the content of a page
406 */
page_read(void * NotUsed,Tcl_Interp * interp,int argc,const char ** argv)407 static int page_read(
408 void *NotUsed,
409 Tcl_Interp *interp, /* The TCL interpreter that invoked this command */
410 int argc, /* Number of arguments */
411 const char **argv /* Text of each argument */
412 ){
413 char zBuf[100];
414 void *pPage;
415 if( argc!=2 ){
416 Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
417 " PAGE\"", 0);
418 return TCL_ERROR;
419 }
420 if( Tcl_GetInt(interp, argv[1], (int*)&pPage) ) return TCL_ERROR;
421 memcpy(zBuf, pPage, sizeof(zBuf));
422 Tcl_AppendResult(interp, zBuf, 0);
423 return TCL_OK;
424 }
425
426 /*
427 ** Usage: page_number PAGE
428 **
429 ** Return the page number for a page.
430 */
page_number(void * NotUsed,Tcl_Interp * interp,int argc,const char ** argv)431 static int page_number(
432 void *NotUsed,
433 Tcl_Interp *interp, /* The TCL interpreter that invoked this command */
434 int argc, /* Number of arguments */
435 const char **argv /* Text of each argument */
436 ){
437 char zBuf[100];
438 void *pPage;
439 if( argc!=2 ){
440 Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
441 " PAGE\"", 0);
442 return TCL_ERROR;
443 }
444 if( Tcl_GetInt(interp, argv[1], (int*)&pPage) ) return TCL_ERROR;
445 sprintf(zBuf, "%d", sqlitepager_pagenumber(pPage));
446 Tcl_AppendResult(interp, zBuf, 0);
447 return TCL_OK;
448 }
449
450 /*
451 ** Usage: page_write PAGE DATA
452 **
453 ** Write something into a page.
454 */
page_write(void * NotUsed,Tcl_Interp * interp,int argc,const char ** argv)455 static int page_write(
456 void *NotUsed,
457 Tcl_Interp *interp, /* The TCL interpreter that invoked this command */
458 int argc, /* Number of arguments */
459 const char **argv /* Text of each argument */
460 ){
461 void *pPage;
462 int rc;
463 if( argc!=3 ){
464 Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
465 " PAGE DATA\"", 0);
466 return TCL_ERROR;
467 }
468 if( Tcl_GetInt(interp, argv[1], (int*)&pPage) ) return TCL_ERROR;
469 rc = sqlitepager_write(pPage);
470 if( rc!=SQLITE_OK ){
471 Tcl_AppendResult(interp, errorName(rc), 0);
472 return TCL_ERROR;
473 }
474 strncpy((char*)pPage, argv[2], SQLITE_USABLE_SIZE-1);
475 ((char*)pPage)[SQLITE_USABLE_SIZE-1] = 0;
476 return TCL_OK;
477 }
478
479 /*
480 ** Usage: fake_big_file N FILENAME
481 **
482 ** Write a few bytes at the N megabyte point of FILENAME. This will
483 ** create a large file. If the file was a valid SQLite database, then
484 ** the next time the database is opened, SQLite will begin allocating
485 ** new pages after N. If N is 2096 or bigger, this will test the
486 ** ability of SQLite to write to large files.
487 */
fake_big_file(void * NotUsed,Tcl_Interp * interp,int argc,const char ** argv)488 static int fake_big_file(
489 void *NotUsed,
490 Tcl_Interp *interp, /* The TCL interpreter that invoked this command */
491 int argc, /* Number of arguments */
492 const char **argv /* Text of each argument */
493 ){
494 int rc;
495 int n;
496 off_t offset;
497 OsFile fd;
498 int readOnly = 0;
499 if( argc!=3 ){
500 Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
501 " N-MEGABYTES FILE\"", 0);
502 return TCL_ERROR;
503 }
504 if( Tcl_GetInt(interp, argv[1], &n) ) return TCL_ERROR;
505 rc = sqliteOsOpenReadWrite(argv[2], &fd, &readOnly);
506 if( rc ){
507 Tcl_AppendResult(interp, "open failed: ", errorName(rc), 0);
508 return TCL_ERROR;
509 }
510 offset = n;
511 offset *= 1024*1024;
512 rc = sqliteOsSeek(&fd, offset);
513 if( rc ){
514 Tcl_AppendResult(interp, "seek failed: ", errorName(rc), 0);
515 return TCL_ERROR;
516 }
517 rc = sqliteOsWrite(&fd, "Hello, World!", 14);
518 sqliteOsClose(&fd);
519 if( rc ){
520 Tcl_AppendResult(interp, "write failed: ", errorName(rc), 0);
521 return TCL_ERROR;
522 }
523 return TCL_OK;
524 }
525
526 /*
527 ** Register commands with the TCL interpreter.
528 */
Sqlitetest2_Init(Tcl_Interp * interp)529 int Sqlitetest2_Init(Tcl_Interp *interp){
530 extern int sqlite_io_error_pending;
531 char zBuf[100];
532 static struct {
533 char *zName;
534 Tcl_CmdProc *xProc;
535 } aCmd[] = {
536 { "pager_open", (Tcl_CmdProc*)pager_open },
537 { "pager_close", (Tcl_CmdProc*)pager_close },
538 { "pager_commit", (Tcl_CmdProc*)pager_commit },
539 { "pager_rollback", (Tcl_CmdProc*)pager_rollback },
540 { "pager_ckpt_begin", (Tcl_CmdProc*)pager_ckpt_begin },
541 { "pager_ckpt_commit", (Tcl_CmdProc*)pager_ckpt_commit },
542 { "pager_ckpt_rollback", (Tcl_CmdProc*)pager_ckpt_rollback },
543 { "pager_stats", (Tcl_CmdProc*)pager_stats },
544 { "pager_pagecount", (Tcl_CmdProc*)pager_pagecount },
545 { "page_get", (Tcl_CmdProc*)page_get },
546 { "page_lookup", (Tcl_CmdProc*)page_lookup },
547 { "page_unref", (Tcl_CmdProc*)page_unref },
548 { "page_read", (Tcl_CmdProc*)page_read },
549 { "page_write", (Tcl_CmdProc*)page_write },
550 { "page_number", (Tcl_CmdProc*)page_number },
551 { "fake_big_file", (Tcl_CmdProc*)fake_big_file },
552 };
553 int i;
554 for(i=0; i<sizeof(aCmd)/sizeof(aCmd[0]); i++){
555 Tcl_CreateCommand(interp, aCmd[i].zName, aCmd[i].xProc, 0, 0);
556 }
557 Tcl_LinkVar(interp, "sqlite_io_error_pending",
558 (char*)&sqlite_io_error_pending, TCL_LINK_INT);
559 #ifdef SQLITE_TEST
560 Tcl_LinkVar(interp, "journal_format",
561 (char*)&journal_format, TCL_LINK_INT);
562 #endif
563 sprintf(zBuf, "%d", SQLITE_PAGE_SIZE);
564 Tcl_SetVar(interp, "SQLITE_PAGE_SIZE", zBuf, TCL_GLOBAL_ONLY);
565 sprintf(zBuf, "%d", SQLITE_PAGE_RESERVE);
566 Tcl_SetVar(interp, "SQLITE_PAGE_RESERVE", zBuf, TCL_GLOBAL_ONLY);
567 sprintf(zBuf, "%d", SQLITE_USABLE_SIZE);
568 Tcl_SetVar(interp, "SQLITE_USABLE_SIZE", zBuf, TCL_GLOBAL_ONLY);
569 return TCL_OK;
570 }
571