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