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