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