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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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