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