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.30 2005/05/27 09:41:13 danielk1977 Exp $ 17 */ 18 #include "sqliteInt.h" 19 #include "os.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 */ 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 ** Page size and reserved size used for testing. 60 */ 61 static int test_pagesize = 1024; 62 63 /* 64 ** Usage: pager_open FILENAME N-PAGE 65 ** 66 ** Open a new pager 67 */ 68 static int pager_open( 69 void *NotUsed, 70 Tcl_Interp *interp, /* The TCL interpreter that invoked this command */ 71 int argc, /* Number of arguments */ 72 const char **argv /* Text of each argument */ 73 ){ 74 Pager *pPager; 75 int nPage; 76 int rc; 77 char zBuf[100]; 78 if( argc!=3 ){ 79 Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0], 80 " FILENAME N-PAGE\"", 0); 81 return TCL_ERROR; 82 } 83 if( Tcl_GetInt(interp, argv[2], &nPage) ) return TCL_ERROR; 84 rc = sqlite3pager_open(&pPager, argv[1], 0, 0); 85 if( rc!=SQLITE_OK ){ 86 Tcl_AppendResult(interp, errorName(rc), 0); 87 return TCL_ERROR; 88 } 89 sqlite3pager_set_cachesize(pPager, nPage); 90 sqlite3pager_set_pagesize(pPager, test_pagesize); 91 sqlite3_snprintf(sizeof(zBuf),zBuf,"%p",pPager); 92 Tcl_AppendResult(interp, zBuf, 0); 93 return TCL_OK; 94 } 95 96 /* 97 ** Usage: pager_close ID 98 ** 99 ** Close the given pager. 100 */ 101 static int pager_close( 102 void *NotUsed, 103 Tcl_Interp *interp, /* The TCL interpreter that invoked this command */ 104 int argc, /* Number of arguments */ 105 const char **argv /* Text of each argument */ 106 ){ 107 Pager *pPager; 108 int rc; 109 if( argc!=2 ){ 110 Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0], 111 " ID\"", 0); 112 return TCL_ERROR; 113 } 114 pPager = sqlite3TextToPtr(argv[1]); 115 rc = sqlite3pager_close(pPager); 116 if( rc!=SQLITE_OK ){ 117 Tcl_AppendResult(interp, errorName(rc), 0); 118 return TCL_ERROR; 119 } 120 return TCL_OK; 121 } 122 123 /* 124 ** Usage: pager_rollback ID 125 ** 126 ** Rollback changes 127 */ 128 static int pager_rollback( 129 void *NotUsed, 130 Tcl_Interp *interp, /* The TCL interpreter that invoked this command */ 131 int argc, /* Number of arguments */ 132 const char **argv /* Text of each argument */ 133 ){ 134 Pager *pPager; 135 int rc; 136 if( argc!=2 ){ 137 Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0], 138 " ID\"", 0); 139 return TCL_ERROR; 140 } 141 pPager = sqlite3TextToPtr(argv[1]); 142 rc = sqlite3pager_rollback(pPager); 143 if( rc!=SQLITE_OK ){ 144 Tcl_AppendResult(interp, errorName(rc), 0); 145 return TCL_ERROR; 146 } 147 return TCL_OK; 148 } 149 150 /* 151 ** Usage: pager_commit ID 152 ** 153 ** Commit all changes 154 */ 155 static int pager_commit( 156 void *NotUsed, 157 Tcl_Interp *interp, /* The TCL interpreter that invoked this command */ 158 int argc, /* Number of arguments */ 159 const char **argv /* Text of each argument */ 160 ){ 161 Pager *pPager; 162 int rc; 163 if( argc!=2 ){ 164 Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0], 165 " ID\"", 0); 166 return TCL_ERROR; 167 } 168 pPager = sqlite3TextToPtr(argv[1]); 169 rc = sqlite3pager_commit(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 = sqlite3pager_stmt_begin(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 = sqlite3pager_stmt_rollback(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 = sqlite3pager_stmt_commit(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 = sqlite3pager_stats(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",sqlite3pager_pagecount(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 void *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 = sqlite3pager_get(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 void *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 = sqlite3pager_lookup(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 = sqlite3pager_truncate(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 void *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 = sqlite3TextToPtr(argv[1]); 426 rc = sqlite3pager_unref(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 void *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, 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 void *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 = sqlite3TextToPtr(argv[1]); 477 sqlite3_snprintf(sizeof(zBuf), zBuf, "%d", sqlite3pager_pagenumber(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 void *pPage; 494 int rc; 495 if( argc!=3 ){ 496 Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0], 497 " PAGE DATA\"", 0); 498 return TCL_ERROR; 499 } 500 pPage = sqlite3TextToPtr(argv[1]); 501 rc = sqlite3pager_write(pPage); 502 if( rc!=SQLITE_OK ){ 503 Tcl_AppendResult(interp, errorName(rc), 0); 504 return TCL_ERROR; 505 } 506 strncpy((char*)pPage, argv[2], test_pagesize-1); 507 ((char*)pPage)[test_pagesize-1] = 0; 508 return TCL_OK; 509 } 510 511 #ifndef SQLITE_OMIT_DISKIO 512 /* 513 ** Usage: fake_big_file N FILENAME 514 ** 515 ** Write a few bytes at the N megabyte point of FILENAME. This will 516 ** create a large file. If the file was a valid SQLite database, then 517 ** the next time the database is opened, SQLite will begin allocating 518 ** new pages after N. If N is 2096 or bigger, this will test the 519 ** ability of SQLite to write to large files. 520 */ 521 static int fake_big_file( 522 void *NotUsed, 523 Tcl_Interp *interp, /* The TCL interpreter that invoked this command */ 524 int argc, /* Number of arguments */ 525 const char **argv /* Text of each argument */ 526 ){ 527 int rc; 528 int n; 529 i64 offset; 530 OsFile fd; 531 int readOnly = 0; 532 if( argc!=3 ){ 533 Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0], 534 " N-MEGABYTES FILE\"", 0); 535 return TCL_ERROR; 536 } 537 if( Tcl_GetInt(interp, argv[1], &n) ) return TCL_ERROR; 538 memset(&fd, 0, sizeof(fd)); 539 rc = sqlite3OsOpenReadWrite(argv[2], &fd, &readOnly); 540 if( rc ){ 541 Tcl_AppendResult(interp, "open failed: ", errorName(rc), 0); 542 return TCL_ERROR; 543 } 544 offset = n; 545 offset *= 1024*1024; 546 rc = sqlite3OsSeek(&fd, offset); 547 if( rc ){ 548 Tcl_AppendResult(interp, "seek failed: ", errorName(rc), 0); 549 return TCL_ERROR; 550 } 551 rc = sqlite3OsWrite(&fd, "Hello, World!", 14); 552 sqlite3OsClose(&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_pending; 566 extern int sqlite3_diskfull_pending; 567 static struct { 568 char *zName; 569 Tcl_CmdProc *xProc; 570 } aCmd[] = { 571 { "pager_open", (Tcl_CmdProc*)pager_open }, 572 { "pager_close", (Tcl_CmdProc*)pager_close }, 573 { "pager_commit", (Tcl_CmdProc*)pager_commit }, 574 { "pager_rollback", (Tcl_CmdProc*)pager_rollback }, 575 { "pager_stmt_begin", (Tcl_CmdProc*)pager_stmt_begin }, 576 { "pager_stmt_commit", (Tcl_CmdProc*)pager_stmt_commit }, 577 { "pager_stmt_rollback", (Tcl_CmdProc*)pager_stmt_rollback }, 578 { "pager_stats", (Tcl_CmdProc*)pager_stats }, 579 { "pager_pagecount", (Tcl_CmdProc*)pager_pagecount }, 580 { "page_get", (Tcl_CmdProc*)page_get }, 581 { "page_lookup", (Tcl_CmdProc*)page_lookup }, 582 { "page_unref", (Tcl_CmdProc*)page_unref }, 583 { "page_read", (Tcl_CmdProc*)page_read }, 584 { "page_write", (Tcl_CmdProc*)page_write }, 585 { "page_number", (Tcl_CmdProc*)page_number }, 586 { "pager_truncate", (Tcl_CmdProc*)pager_truncate }, 587 #ifndef SQLITE_OMIT_DISKIO 588 { "fake_big_file", (Tcl_CmdProc*)fake_big_file }, 589 #endif 590 }; 591 int i; 592 for(i=0; i<sizeof(aCmd)/sizeof(aCmd[0]); i++){ 593 Tcl_CreateCommand(interp, aCmd[i].zName, aCmd[i].xProc, 0, 0); 594 } 595 Tcl_LinkVar(interp, "sqlite_io_error_pending", 596 (char*)&sqlite3_io_error_pending, TCL_LINK_INT); 597 Tcl_LinkVar(interp, "sqlite_diskfull_pending", 598 (char*)&sqlite3_diskfull_pending, TCL_LINK_INT); 599 Tcl_LinkVar(interp, "pager_pagesize", 600 (char*)&test_pagesize, TCL_LINK_INT); 601 return TCL_OK; 602 } 603