Many hyperlinks are disabled.
Use anonymous login
to enable hyperlinks.
Overview
Comment: | Pager is working, mostly. (CVS 211) |
---|---|
Downloads: | Tarball | ZIP archive |
Timelines: | family | ancestors | descendants | both | trunk |
Files: | files | file ages | folders |
SHA1: |
f82fa7070ae281804c019e6b05cd767d |
User & Date: | drh 2001-04-15 00:37:09.000 |
Context
2001-04-15
| ||
02:27 | Working on the pager (CVS 212) (check-in: 1f07abe46e user: drh tags: trunk) | |
00:37 | Pager is working, mostly. (CVS 211) (check-in: f82fa7070a user: drh tags: trunk) | |
2001-04-14
| ||
16:38 | Getting ready to redo the journal file format. (CVS 210) (check-in: 42c2f3fe68 user: drh tags: trunk) | |
Changes
Changes to Makefile.in.
︙ | ︙ | |||
44 45 46 47 48 49 50 | # The library that programs using readline() must link against. # LIBREADLINE = @TARGET_READLINE_LIBS@ # Object files for the SQLite library. # LIBOBJ = build.o dbbe.o dbbegdbm.o dbbemem.o delete.o expr.o insert.o \ | | | > > > > > > > | 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 | # The library that programs using readline() must link against. # LIBREADLINE = @TARGET_READLINE_LIBS@ # Object files for the SQLite library. # LIBOBJ = build.o dbbe.o dbbegdbm.o dbbemem.o delete.o expr.o insert.o \ main.o pager.o parse.o printf.o random.o select.o table.o \ tokenize.o update.o util.o vdbe.o where.o tclsqlite.o # All of the source code files. # SRC = \ $(TOP)/src/build.c \ $(TOP)/src/dbbe.c \ $(TOP)/src/dbbe.h \ $(TOP)/src/dbbegdbm.c \ $(TOP)/src/dbbemem.c \ $(TOP)/src/delete.c \ $(TOP)/src/expr.c \ $(TOP)/src/insert.c \ $(TOP)/src/main.c \ $(TOP)/src/pager.c \ $(TOP)/src/parse.y \ $(TOP)/src/printf.c \ $(TOP)/src/random.c \ $(TOP)/src/select.c \ $(TOP)/src/shell.c \ $(TOP)/src/sqlite.h.in \ $(TOP)/src/sqliteInt.h \ $(TOP)/src/table.c \ $(TOP)/src/tclsqlite.c \ $(TOP)/src/tokenize.c \ $(TOP)/src/update.c \ $(TOP)/src/util.c \ $(TOP)/src/vdbe.c \ $(TOP)/src/vdbe.h \ $(TOP)/src/where.c # Source code to the test files. # TESTSRC = \ $(TOP)/src/test1.c \ $(TOP)/src/test2.c # This is the default Makefile target. The objects listed here # are what get build when you type just "make" with no arguments. # all: sqlite.h libsqlite.a sqlite # Generate the file "last_change" which contains the date of change |
︙ | ︙ | |||
178 179 180 181 182 183 184 185 186 187 188 189 190 191 | tclsqlite.o: $(TOP)/src/tclsqlite.c $(HDR) $(TCC) $(GDBM_FLAGS) $(TCL_FLAGS) -c $(TOP)/src/tclsqlite.c printf.o: $(TOP)/src/printf.c $(HDR) $(TCC) $(GDBM_FLAGS) $(TCL_FLAGS) -c $(TOP)/src/printf.c gdbmdump: $(TOP)/tool/gdbmdump.c $(TCC) $(GDBM_FLAGS) -o gdbmdump $(TOP)/tool/gdbmdump.c $(LIBGDBM) tclsqlite: $(TOP)/src/tclsqlite.c libsqlite.a $(TCC) $(TCL_FLAGS) -DTCLSH=1 -o sqlite_tester \ $(TOP)/src/tclsqlite.c libsqlite.a $(LIBGDBM) $(LIBTCL) | > > > | | | | 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 | tclsqlite.o: $(TOP)/src/tclsqlite.c $(HDR) $(TCC) $(GDBM_FLAGS) $(TCL_FLAGS) -c $(TOP)/src/tclsqlite.c printf.o: $(TOP)/src/printf.c $(HDR) $(TCC) $(GDBM_FLAGS) $(TCL_FLAGS) -c $(TOP)/src/printf.c pager.o: $(TOP)/src/pager.c $(HDR) $(TCC) $(GDBM_FLAGS) $(TCL_FLAGS) -c $(TOP)/src/pager.c gdbmdump: $(TOP)/tool/gdbmdump.c $(TCC) $(GDBM_FLAGS) -o gdbmdump $(TOP)/tool/gdbmdump.c $(LIBGDBM) tclsqlite: $(TOP)/src/tclsqlite.c libsqlite.a $(TCC) $(TCL_FLAGS) -DTCLSH=1 -o sqlite_tester \ $(TOP)/src/tclsqlite.c libsqlite.a $(LIBGDBM) $(LIBTCL) sqlite_tester: $(TOP)/src/tclsqlite.c libsqlite.a $(TESTSRC) $(TCC) $(TCL_FLAGS) -DTCLSH=1 -DSQLITE_TEST=1 -o sqlite_tester \ $(TESTSRC) $(TOP)/src/tclsqlite.c \ libsqlite.a $(LIBGDBM) $(LIBTCL) test: sqlite_tester sqlite ./sqlite_tester $(TOP)/test/all.test sqlite.tar.gz: pwd=`pwd`; cd $(TOP)/..; tar czf $$pwd/sqlite.tar.gz sqlite |
︙ | ︙ |
Changes to src/build.c.
︙ | ︙ | |||
29 30 31 32 33 34 35 | ** DROP TABLE ** CREATE INDEX ** DROP INDEX ** creating expressions and ID lists ** COPY ** VACUUM ** | | | 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 | ** DROP TABLE ** CREATE INDEX ** DROP INDEX ** creating expressions and ID lists ** COPY ** VACUUM ** ** $Id: build.c,v 1.28 2001/04/15 00:37:09 drh Exp $ */ #include "sqliteInt.h" /* ** This routine is called after a single SQL statement has been ** parsed and we want to execute the VDBE code to implement ** that statement. Prior action routines should have already |
︙ | ︙ | |||
192 193 194 195 196 197 198 | } /* ** Remove the memory data structures associated with the given ** Table. No changes are made to disk by this routine. ** ** This routine just deletes the data structure. It does not unlink | | | 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 | } /* ** Remove the memory data structures associated with the given ** Table. No changes are made to disk by this routine. ** ** This routine just deletes the data structure. It does not unlink ** the table data structure from the hash table. But it does destroy ** memory structures of the indices associated with the table. ** ** Indices associated with the table are unlinked from the "db" ** data structure if db!=NULL. If db==NULL, indices attached to ** the table are deleted, but it is assumed they have already been ** unlinked. */ |
︙ | ︙ | |||
232 233 234 235 236 237 238 | sqliteDequote(zName); return zName; } /* ** Begin constructing a new table representation in memory. This is ** the first of several action routines that get called in response | | > > > > > > | 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 | sqliteDequote(zName); return zName; } /* ** Begin constructing a new table representation in memory. This is ** the first of several action routines that get called in response ** to a CREATE TABLE statement. In particular, this routine is called ** after seeing tokens "CREATE" and "TABLE" and the table name. The ** pStart token is the CREATE and pName is the table name. ** ** The new table is constructed in files of the pParse structure. As ** more of the CREATE TABLE statement is parsed, additional action ** routines are called to build up more of the table. */ void sqliteStartTable(Parse *pParse, Token *pStart, Token *pName){ Table *pTable; char *zName; pParse->sFirstToken = *pStart; zName = sqliteTableNameFromToken(pName); |
︙ | ︙ | |||
269 270 271 272 273 274 275 276 277 278 279 280 281 282 | pTable->pIndex = 0; if( pParse->pNewTable ) sqliteDeleteTable(pParse->db, pParse->pNewTable); pParse->pNewTable = pTable; } /* ** Add a new column to the table currently being constructed. */ void sqliteAddColumn(Parse *pParse, Token *pName){ Table *p; char **pz; if( (p = pParse->pNewTable)==0 ) return; if( (p->nCol & 0x7)==0 ){ p->aCol = sqliteRealloc( p->aCol, (p->nCol+8)*sizeof(p->aCol[0])); | > > > > > | 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 | pTable->pIndex = 0; if( pParse->pNewTable ) sqliteDeleteTable(pParse->db, pParse->pNewTable); pParse->pNewTable = pTable; } /* ** Add a new column to the table currently being constructed. ** ** The parser calls this routine once for each column declaration ** in a CREATE TABLE statement. sqliteStartTable() gets called ** first to get things going. Then this routine is called for each ** column. */ void sqliteAddColumn(Parse *pParse, Token *pName){ Table *p; char **pz; if( (p = pParse->pNewTable)==0 ) return; if( (p->nCol & 0x7)==0 ){ p->aCol = sqliteRealloc( p->aCol, (p->nCol+8)*sizeof(p->aCol[0])); |
︙ | ︙ | |||
291 292 293 294 295 296 297 298 299 300 301 302 303 304 | sqliteDequote(*pz); } /* ** The given token is the default value for the last column added to ** the table currently under construction. If "minusFlag" is true, it ** means the value token was preceded by a minus sign. */ void sqliteAddDefaultValue(Parse *pParse, Token *pVal, int minusFlag){ Table *p; int i; char **pz; if( (p = pParse->pNewTable)==0 ) return; i = p->nCol-1; | > > > | 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 | sqliteDequote(*pz); } /* ** The given token is the default value for the last column added to ** the table currently under construction. If "minusFlag" is true, it ** means the value token was preceded by a minus sign. ** ** This routine is called by the parser while in the middle of ** parsing a CREATE TABLE statement. */ void sqliteAddDefaultValue(Parse *pParse, Token *pVal, int minusFlag){ Table *p; int i; char **pz; if( (p = pParse->pNewTable)==0 ) return; i = p->nCol-1; |
︙ | ︙ | |||
399 400 401 402 403 404 405 406 407 408 409 410 411 412 | pParse->nErr++; } return pTab; } /* ** This routine is called to do the work of a DROP TABLE statement. */ void sqliteDropTable(Parse *pParse, Token *pName){ Table *pTable; int h; Vdbe *v; int base; | > | 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 | pParse->nErr++; } return pTab; } /* ** This routine is called to do the work of a DROP TABLE statement. ** pName is the name of the table to be dropped. */ void sqliteDropTable(Parse *pParse, Token *pName){ Table *pTable; int h; Vdbe *v; int base; |
︙ | ︙ |
Changes to src/pager.c.
︙ | ︙ | |||
23 24 25 26 27 28 29 | ************************************************************************* ** This is the implementation of the page cache subsystem. ** ** The page cache is used to access a database file. The pager journals ** all writes in order to support rollback. Locking is used to limit ** access to one or more reader or on writer. ** | | > > | 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 | ************************************************************************* ** This is the implementation of the page cache subsystem. ** ** The page cache is used to access a database file. The pager journals ** all writes in order to support rollback. Locking is used to limit ** access to one or more reader or on writer. ** ** @(#) $Id: pager.c,v 1.3 2001/04/15 00:37:09 drh Exp $ */ #include "sqliteInt.h" #include "pager.h" #include <fcntl.h> #include <sys/stat.h> #include <unistd.h> #include <assert.h> #include <string.h> /* ** The page cache as a whole is always in one of the following ** states: ** ** SQLITE_UNLOCK The page cache is not currently reading or ** writing the database file. There is no |
︙ | ︙ | |||
61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 | ** that sqlite_page_write() is called, the state transitions to ** PCS_WRITELOCK. The sqlite_page_rollback() and sqlite_page_commit() ** functions transition the state back to PCS_READLOCK. */ #define SQLITE_UNLOCK 0 #define SQLITE_READLOCK 1 #define SQLITE_WRITELOCK 2 /* ** Each in-memory image of a page begins with the following header. */ struct PgHdr { Pager *pPager; /* The pager to which this page belongs */ Pgno pgno; /* The page number for this page */ PgHdr *pNextHash, *pPrevHash; /* Hash collision chain for PgHdr.pgno */ int nRef; /* Number of users of this page */ | > > | > < < < < < | < < < > > | | > > | > > > > > > > > > > > > > > > > > > > > > > > > | | > | > > | > | > > > | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | | | | | > | | | > | > | | > | | | > > > > < > > < | | | | < | < > | < < < | < < | > | | < < < < < > > | < < < < < < | | | | | | > | < | | < > | > > | > > | > > | | > > > | | < > | > > | | < < | < < | < < < | | | | < < < < < < | < < | < < | > > | < < < | < | < < < | < < < | | < < < | < < < | > > > | < | < < < < | | | | > > > > | | > > > > | > | | < | | | | < < | | | < | | > > > > > > | > > > > > > > > > | > | | | | > > | < | > | > > | > > | > > > > > > > > > > > > | > > > > > > > > > > > > > > > > > > > > | | > > | > > > | | | > > | | | > > | | | | | | > | | > > > > > > > | | | | | > > > | | | | | > > > > > > > > | > > > > | | | | | | < > > | | < < | < | | | | > | | > | < | | > | | | > | > > > > > > | | | | > > > | > > > | > > | < > | | | | | > > | | > > > | > > > > > > > > > > | > > > | < | < < > | | > > > > > > > > > > > > > > > > > > > | 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716 717 718 719 720 721 722 723 724 725 726 727 728 729 730 731 732 733 734 735 736 737 738 739 740 741 742 743 744 745 746 747 748 749 750 751 752 753 754 755 756 757 758 759 760 761 762 763 764 765 766 767 768 769 770 771 772 773 774 775 776 777 778 779 780 781 782 783 784 785 786 787 788 789 790 791 792 793 794 795 796 797 798 799 800 801 802 803 804 805 806 807 808 809 810 811 812 813 814 815 816 817 818 819 820 821 822 823 824 825 826 827 828 829 830 831 832 833 834 835 836 837 838 839 840 841 842 843 844 845 846 847 848 849 850 851 852 853 854 855 856 857 858 859 860 861 862 863 864 865 866 867 868 869 870 871 872 873 874 875 876 877 878 879 880 881 882 883 884 885 886 887 888 889 890 891 892 893 894 895 896 897 898 899 900 901 902 903 904 905 906 907 908 909 910 911 912 913 914 915 916 917 918 919 920 921 922 923 924 925 926 927 928 929 930 | ** that sqlite_page_write() is called, the state transitions to ** PCS_WRITELOCK. The sqlite_page_rollback() and sqlite_page_commit() ** functions transition the state back to PCS_READLOCK. */ #define SQLITE_UNLOCK 0 #define SQLITE_READLOCK 1 #define SQLITE_WRITELOCK 2 /* ** Each in-memory image of a page begins with the following header. */ typedef struct PgHdr PgHdr; struct PgHdr { Pager *pPager; /* The pager to which this page belongs */ Pgno pgno; /* The page number for this page */ PgHdr *pNextHash, *pPrevHash; /* Hash collision chain for PgHdr.pgno */ int nRef; /* Number of users of this page */ PgHdr *pNextFree, *pPrevFree; /* Freelist of pages where nRef==0 */ PgHdr *pNextAll, *pPrevAll; /* A list of all pages */ char inJournal; /* TRUE if has been written to journal */ char dirty; /* TRUE if we need to write back changes */ /* SQLITE_PAGE_SIZE bytes of page data follow this header */ }; /* ** Convert a pointer to a PgHdr into a pointer to its data ** and back again. */ #define PGHDR_TO_DATA(P) ((void*)(&(P)[1])) #define DATA_TO_PGHDR(D) (&((PgHdr*)(D))[-1]) /* ** How big to make the hash table used for locating in-memory pages ** by page number. */ #define N_PG_HASH 101 /* ** A open page cache is an instance of the following structure. */ struct Pager { char *zFilename; /* Name of the database file */ char *zJournal; /* Name of the journal file */ int fd, jfd; /* File descriptors for database and journal */ int dbSize; /* Number of pages in the file */ int origDbSize; /* dbSize before the current change */ int nPage; /* Total number of in-memory pages */ int nRef; /* Number of in-memory pages with PgHdr.nRef>0 */ int mxPage; /* Maximum number of pages to hold in cache */ int nHit, nMiss, nOvfl; /* Cache hits, missing, and LRU overflows */ unsigned char state; /* SQLITE_UNLOCK, _READLOCK or _WRITELOCK */ unsigned char errMask; /* One of several kinds of errors */ PgHdr *pFirst, *pLast; /* List of free pages */ PgHdr *pAll; /* List of all pages */ PgHdr *aHash[N_PG_HASH]; /* Hash table to map page number of PgHdr */ }; /* ** These are bits that can be set in Pager.errMask. */ #define PAGER_ERR_FULL 0x01 /* a write() failed */ #define PAGER_ERR_MEM 0x02 /* malloc() failed */ #define PAGER_ERR_LOCK 0x04 /* error in the locking protocol */ #define PAGER_ERR_CORRUPT 0x08 /* database or journal corruption */ /* ** The journal file contains page records in the following ** format. */ typedef struct PageRecord PageRecord; struct PageRecord { Pgno pgno; /* The page number */ char aData[SQLITE_PAGE_SIZE]; /* Original data for page pgno */ }; /* ** Journal files begin with the following magic string. This data ** is completely random. It is used only as a sanity check. */ static const unsigned char aJournalMagic[] = { 0xd9, 0xd5, 0x05, 0xf9, 0x20, 0xa1, 0x63, 0xd4, }; /* ** Hash a page number */ #define pager_hash(PN) ((PN)%N_PG_HASH) /* ** Attempt to acquire a read lock (if wrlock==0) or a write lock (if wrlock==1) ** on the database file. Return 0 on success and non-zero if the lock ** could not be acquired. */ static int pager_lock(int fd, int wrlock){ int rc; struct flock lock; lock.l_type = wrlock ? F_WRLCK : F_RDLCK; lock.l_whence = SEEK_SET; lock.l_start = lock.l_len = 0L; rc = fcntl(fd, F_SETLK, &lock); return rc!=0; } /* ** Unlock the database file. */ static int pager_unlock(fd){ int rc; struct flock lock; lock.l_type = F_UNLCK; lock.l_whence = SEEK_SET; lock.l_start = lock.l_len = 0L; rc = fcntl(fd, F_SETLK, &lock); return rc!=0; } /* ** Move the cursor for file descriptor fd to the point whereto from ** the beginning of the file. */ static int pager_seek(int fd, off_t whereto){ lseek(fd, whereto, SEEK_SET); return SQLITE_OK; } /* ** Truncate the given file so that it contains exactly mxPg pages ** of data. */ static int pager_truncate(int fd, Pgno mxPg){ int rc; rc = ftruncate(fd, mxPg*SQLITE_PAGE_SIZE); return rc!=0 ? SQLITE_IOERR : SQLITE_OK; } /* ** Read nBytes of data from fd into pBuf. If the data cannot be ** read or only a partial read occurs, then the unread parts of ** pBuf are filled with zeros and this routine returns SQLITE_IOERR. ** If the read is completely successful, return SQLITE_OK. */ static int pager_read(int fd, void *pBuf, int nByte){ int rc; rc = read(fd, pBuf, nByte); if( rc<0 ){ memset(pBuf, 0, nByte); return SQLITE_IOERR; } if( rc<nByte ){ memset(&((char*)pBuf)[rc], 0, nByte - rc); rc = SQLITE_IOERR; }else{ rc = SQLITE_OK; } return rc; } /* ** Write nBytes of data into fd. If any problem occurs or if the ** write is incomplete, SQLITE_IOERR is returned. SQLITE_OK is ** returned upon complete success. */ static int pager_write(int fd, const void *pBuf, int nByte){ int rc; rc = write(fd, pBuf, nByte); if( rc<nByte ){ return SQLITE_FULL; }else{ return SQLITE_OK; } } /* ** Convert the bits in the pPager->errMask into an approprate ** return code. */ static int pager_errcode(Pager *pPager){ int rc = SQLITE_OK; if( pPager->errMask & PAGER_ERR_LOCK ) rc = SQLITE_PROTOCOL; if( pPager->errMask & PAGER_ERR_FULL ) rc = SQLITE_FULL; if( pPager->errMask & PAGER_ERR_MEM ) rc = SQLITE_NOMEM; if( pPager->errMask & PAGER_ERR_CORRUPT ) rc = SQLITE_CORRUPT; return rc; } /* ** Find a page in the hash table given its page number. Return ** a pointer to the page or NULL if not found. */ static PgHdr *pager_lookup(Pager *pPager, Pgno pgno){ PgHdr *p = pPager->aHash[pgno % N_PG_HASH]; while( p && p->pgno!=pgno ){ p = p->pNextHash; } return p; } /* ** Unlock the database and clear the in-memory cache. This routine ** sets the state of the pager back to what it was when it was first ** opened. Any outstanding pages are invalidated and subsequent attempts ** to access those pages will likely result in a coredump. */ static void pager_reset(Pager *pPager){ PgHdr *pPg, *pNext; for(pPg=pPager->pAll; pPg; pPg=pNext){ pNext = pPg->pNextAll; sqliteFree(pPg); } pPager->pFirst = 0; pPager->pLast = 0; pPager->pAll = 0; memset(pPager->aHash, 0, sizeof(pPager->aHash)); pPager->nPage = 0; if( pPager->state==SQLITE_WRITELOCK ){ sqlitepager_rollback(pPager); } pager_unlock(pPager->fd); pPager->state = SQLITE_UNLOCK; pPager->dbSize = -1; pPager->nRef = 0; } /* ** When this routine is called, the pager has the journal file open and ** a write lock on the database. This routine releases the database ** write lock and acquires a read lock in its place. The journal file ** is deleted and closed. ** ** We have to release the write lock before acquiring the read lock, ** so there is a race condition where another process can get the lock ** while we are not holding it. But, no other process should do this ** because we are also holding a lock on the journal, and no process ** should get a write lock on the database without first getting a lock ** on the journal. So this routine should never fail. But it can fail ** if another process is not playing by the rules. If it does fail, ** all in-memory cache pages are invalidated, the PAGER_ERR_LOCK bit ** is set in pPager->errMask, and this routine returns SQLITE_PROTOCOL. ** SQLITE_OK is returned on success. */ static int pager_unwritelock(Pager *pPager){ int rc; PgHdr *pPg; if( pPager->state!=SQLITE_WRITELOCK ) return SQLITE_OK; pager_unlock(pPager->fd); rc = pager_lock(pPager->fd, 0); unlink(pPager->zJournal); close(pPager->jfd); pPager->jfd = -1; for(pPg=pPager->pAll; pPg; pPg=pPg->pNextAll){ pPg->inJournal = 0; pPg->dirty = 0; } if( rc!=SQLITE_OK ){ pPager->state = SQLITE_UNLOCK; rc = SQLITE_PROTOCOL; pPager->errMask |= PAGER_ERR_LOCK; }else{ rc = SQLITE_OK; pPager->state = SQLITE_READLOCK; } return rc; } /* ** Playback the journal and thus restore the database file to ** the state it was in before we started making changes. ** ** The journal file format is as follows: There is an initial ** file-type string for sanity checking. Then there is a single ** Pgno number which is the number of pages in the database before ** changes were made. The database is truncated to this size. ** Next come zero or more page records which each page record ** consists of a Pgno, SQLITE_PAGE_SIZE bytes of data. ** ** For playback, the pages have to be read from the journal in ** reverse order and put back into the original database file. ** ** If the file opened as the journal file is not a well-formed ** journal file (as determined by looking at the magic number ** at the beginning) then this routine returns SQLITE_PROTOCOL. ** If any other errors occur during playback, the database will ** likely be corrupted, so the PAGER_ERR_CORRUPT bit is set in ** pPager->errMask and SQLITE_CORRUPT is returned. If it all ** works, then this routine returns SQLITE_OK. */ static int pager_playback(Pager *pPager){ int nRec; /* Number of Records */ int i; /* Loop counter */ Pgno mxPg = 0; /* Size of the original file in pages */ struct stat statbuf; /* Used to size the journal */ PgHdr *pPg; /* An existing page in the cache */ PageRecord pgRec; unsigned char aMagic[sizeof(aJournalMagic)]; int rc; /* Read the beginning of the journal and truncate the ** database file back to its original size. */ assert( pPager->jfd>=0 ); pager_seek(pPager->jfd, 0); rc = pager_read(pPager->jfd, aMagic, sizeof(aMagic)); if( rc!=SQLITE_OK || memcmp(aMagic,aJournalMagic,sizeof(aMagic))!=0 ){ return SQLITE_PROTOCOL; } rc = pager_read(pPager->jfd, &mxPg, sizeof(mxPg)); if( rc!=SQLITE_OK ){ return SQLITE_PROTOCOL; } pager_truncate(pPager->fd, mxPg); pPager->dbSize = mxPg; /* Begin reading the journal beginning at the end and moving ** toward the beginning. */ if( fstat(pPager->jfd, &statbuf)!=0 ){ return SQLITE_OK; } nRec = (statbuf.st_size - (sizeof(aMagic)+sizeof(Pgno))) / sizeof(PageRecord); /* Process segments beginning with the last and working backwards ** to the first. */ for(i=nRec-1; i>=0; i--){ /* Seek to the beginning of the segment */ off_t ofst; ofst = i*sizeof(PageRecord) + sizeof(aMagic) + sizeof(Pgno); rc = pager_seek(pPager->jfd, ofst); if( rc!=SQLITE_OK ) break; rc = pager_read(pPager->jfd, &pgRec, sizeof(pgRec)); if( rc!=SQLITE_OK ) break; /* Sanity checking on the page */ if( pgRec.pgno>mxPg || pgRec.pgno==0 ){ rc = SQLITE_CORRUPT; break; } /* Playback the page. Update the in-memory copy of the page ** at the same time, if there is one. */ pPg = pager_lookup(pPager, pgRec.pgno); if( pPg ){ memcpy(PGHDR_TO_DATA(pPg), pgRec.aData, SQLITE_PAGE_SIZE); } rc = pager_seek(pPager->fd, (pgRec.pgno-1)*SQLITE_PAGE_SIZE); if( rc!=SQLITE_OK ) break; rc = pager_write(pPager->fd, pgRec.aData, SQLITE_PAGE_SIZE); if( rc!=SQLITE_OK ) break; } if( rc!=SQLITE_OK ){ pager_unwritelock(pPager); pPager->errMask |= PAGER_ERR_CORRUPT; rc = SQLITE_CORRUPT; }else{ rc = pager_unwritelock(pPager); } return rc; } /* ** Create a new page cache and put a pointer to the page cache in *ppPager. ** The file to be cached need not exist. The file is not opened until ** the first call to sqlitepager_get() and is only held open until the ** last page is released using sqlitepager_unref(). */ int sqlitepager_open(Pager **ppPager, const char *zFilename, int mxPage){ Pager *pPager; int nameLen; int fd; *ppPager = 0; if( sqlite_malloc_failed ){ return SQLITE_NOMEM; } fd = open(zFilename, O_RDWR|O_CREAT, 0644); if( fd<0 ){ return SQLITE_CANTOPEN; } nameLen = strlen(zFilename); pPager = sqliteMalloc( sizeof(*pPager) + nameLen*2 + 30 ); if( pPager==0 ){ close(fd); return SQLITE_NOMEM; } pPager->zFilename = (char*)&pPager[1]; pPager->zJournal = &pPager->zFilename[nameLen+1]; strcpy(pPager->zFilename, zFilename); strcpy(pPager->zJournal, zFilename); strcpy(&pPager->zJournal[nameLen], "-journal"); pPager->fd = fd; pPager->jfd = -1; pPager->nRef = 0; pPager->dbSize = -1; pPager->nPage = 0; pPager->mxPage = mxPage>10 ? mxPage : 10; pPager->state = SQLITE_UNLOCK; pPager->errMask = 0; pPager->pFirst = 0; pPager->pLast = 0; memset(pPager->aHash, 0, sizeof(pPager->aHash)); *ppPager = pPager; return SQLITE_OK; } /* ** Return the total number of pages in the file opened by pPager. */ int sqlitepager_pagecount(Pager *pPager){ int n; struct stat statbuf; assert( pPager!=0 ); if( pPager->dbSize>=0 ){ return pPager->dbSize; } if( fstat(pPager->fd, &statbuf)!=0 ){ n = 0; }else{ n = statbuf.st_size/SQLITE_PAGE_SIZE; } if( pPager->state!=SQLITE_UNLOCK ){ pPager->dbSize = n; } return n; } /* ** Shutdown the page cache. Free all memory and close all files. ** ** If a transaction was in progress when this routine is called, that ** transaction is rolled back. All outstanding pages are invalidated ** and their memory is freed. Any attempt to use a page associated ** with this page cache after this function returns will likely ** result in a coredump. */ int sqlitepager_close(Pager *pPager){ PgHdr *pPg, *pNext; switch( pPager->state ){ case SQLITE_WRITELOCK: { sqlitepager_rollback(pPager); pager_unlock(pPager->fd); break; } case SQLITE_READLOCK: { pager_unlock(pPager->fd); break; } default: { /* Do nothing */ break; } } for(pPg=pPager->pAll; pPg; pPg=pNext){ pNext = pPg->pNextAll; sqliteFree(pPg); } if( pPager->fd>=0 ) close(pPager->fd); assert( pPager->jfd<0 ); sqliteFree(pPager); return SQLITE_OK; } /* ** Return the page number for the given page data */ Pgno sqlitepager_pagenumber(void *pData){ PgHdr *p = DATA_TO_PGHDR(pData); return p->pgno; } /* ** Acquire a page. ** ** A read lock is obtained for the first page acquired. The lock ** is dropped when the last page is released. ** ** The acquisition might fail for several reasons. In all cases, ** an appropriate error code is returned and *ppPage is set to NULL. */ int sqlitepager_get(Pager *pPager, Pgno pgno, void **ppPage){ PgHdr *pPg; /* Make sure we have not hit any critical errors. */ if( pPager==0 || pgno==0 ){ return SQLITE_ERROR; } if( pPager->errMask & ~(PAGER_ERR_FULL) ){ return pager_errcode(pPager); } /* If this is the first page accessed, then get a read lock ** on the database file. */ if( pPager->nRef==0 ){ if( pager_lock(pPager->fd, 0)!=0 ){ *ppPage = 0; return SQLITE_BUSY; } pPager->state = SQLITE_READLOCK; /* If a journal file exists, try to play it back. */ if( access(pPager->zJournal,0)==0 ){ int rc; /* Open the journal for exclusive access. Return SQLITE_BUSY if ** we cannot get exclusive access to the journal file */ pPager->jfd = open(pPager->zJournal, O_RDONLY, 0); if( pPager->jfd<0 || pager_lock(pPager->jfd, 1)!=0 ){ if( pPager->jfd>=0 ){ close(pPager->jfd); pPager->jfd = -1; } pager_unlock(pPager->fd); *ppPage = 0; return SQLITE_BUSY; } /* Get a write lock on the database */ pager_unlock(pPager->fd); if( pager_lock(pPager->fd, 1)!=0 ){ close(pPager->jfd); pPager->jfd = -1; *ppPage = 0; return SQLITE_PROTOCOL; } /* Playback and delete the journal. Drop the database write ** lock and reacquire the read lock. */ rc = pager_playback(pPager); if( rc!=SQLITE_OK ){ return rc; } } pPg = 0; pPager->nMiss++; }else{ /* Search for page in cache */ pPg = pager_lookup(pPager, pgno); pPager->nHit++; } if( pPg==0 ){ /* The requested page is not in the page cache. */ int h; if( pPager->nPage<pPager->mxPage || pPager->pFirst==0 ){ /* Create a new page */ pPg = sqliteMalloc( sizeof(*pPg) + SQLITE_PAGE_SIZE ); if( pPg==0 ){ *ppPage = 0; pager_unwritelock(pPager); pPager->errMask |= PAGER_ERR_MEM; return SQLITE_NOMEM; } pPg->pPager = pPager; pPg->pNextAll = pPager->pAll; if( pPager->pAll ){ pPager->pAll->pPrevAll = pPg; } pPg->pPrevAll = 0; pPager->nPage++; }else{ /* Recycle an older page. First locate the page to be recycled. ** Try to find one that is not dirty and is near the head of ** of the free list */ int cnt = 4; pPg = pPager->pFirst; while( pPg->dirty && 0<cnt-- ){ pPg = pPg->pNextFree; } if( pPg==0 || pPg->dirty ) pPg = pPager->pFirst; assert( pPg->nRef==0 ); /* If the page to be recycled is dirty, sync the journal and write ** the old page into the database. */ if( pPg->dirty ){ int rc; assert( pPg->inJournal==1 ); assert( pPager->state==SQLITE_WRITELOCK ); rc = fsync(pPager->jfd); if( rc!=0 ){ rc = sqlitepager_rollback(pPager); *ppPage = 0; if( rc==SQLITE_OK ) rc = SQLITE_IOERR; return rc; } pager_seek(pPager->fd, (pPg->pgno-1)*SQLITE_PAGE_SIZE); rc = pager_write(pPager->fd, PGHDR_TO_DATA(pPg), SQLITE_PAGE_SIZE); if( rc!=SQLITE_OK ){ rc = sqlitepager_rollback(pPager); *ppPage = 0; if( rc==SQLITE_OK ) rc = SQLITE_FULL; return rc; } } /* Unlink the old page from the free list and the hash table */ pPager->pFirst = pPg->pNextFree; if( pPager->pFirst ){ pPager->pFirst->pPrevFree = 0; }else{ pPager->pLast = 0; } if( pPg->pNextHash ){ pPg->pNextHash->pPrevHash = pPg->pPrevHash; } if( pPg->pPrevHash ){ pPg->pPrevHash->pNextHash = pPg->pNextHash; }else{ h = pager_hash(pPg->pgno); assert( pPager->aHash[h]==pPg ); pPager->aHash[h] = pPg->pNextHash; } pPager->nOvfl++; } pPg->pgno = pgno; pPg->inJournal = 0; pPg->dirty = 0; pPg->nRef = 1; pPager->nRef++; h = pager_hash(pgno); pPg->pNextHash = pPager->aHash[h]; pPager->aHash[h] = pPg; if( pPg->pNextHash ){ assert( pPg->pNextHash->pPrevHash==0 ); pPg->pNextHash->pPrevHash = pPg; } pager_seek(pPager->fd, (pgno-1)*SQLITE_PAGE_SIZE); pager_read(pPager->fd, PGHDR_TO_DATA(pPg), SQLITE_PAGE_SIZE); }else{ /* The requested page is in the page cache. */ if( pPg->nRef==0 ){ /* The page is currently on the freelist. Remove it. */ if( pPg->pPrevFree ){ pPg->pPrevFree->pNextFree = pPg->pNextFree; }else{ pPager->pFirst = pPg->pNextFree; } if( pPg->pNextFree ){ pPg->pNextFree->pPrevFree = pPg->pPrevFree; }else{ pPager->pLast = pPg->pPrevFree; } pPager->nRef++; } pPg->nRef++; } *ppPage = PGHDR_TO_DATA(pPg); return SQLITE_OK; } /* ** Release a page. ** ** If the number of references to the page drop to zero, then the ** page is added to the LRU list. When all references to all pages ** are released, a rollback occurs and the lock on the database is ** removed. */ int sqlitepager_unref(void *pData){ Pager *pPager; PgHdr *pPg; /* Decrement the reference count for this page */ pPg = DATA_TO_PGHDR(pData); assert( pPg->nRef>0 ); pPager = pPg->pPager; pPg->nRef--; /* When the number of references to a page reach 0, add the ** page to the freelist. */ if( pPg->nRef==0 ){ pPg->pNextFree = 0; pPg->pPrevFree = pPager->pLast; pPager->pLast = pPg; if( pPg->pPrevFree ){ pPg->pPrevFree->pNextFree = pPg; }else{ pPager->pFirst = pPg; } /* When all pages reach the freelist, drop the read lock from ** the database file. */ pPager->nRef--; assert( pPager->nRef>=0 ); if( pPager->nRef==0 ){ pager_reset(pPager); } } return SQLITE_OK; } /* ** Mark a data page as writeable. The page is written into the journal ** if it is not there already. This routine must be called before making ** changes to a page. ** ** The first time this routine is called, the pager creates a new ** journal and acquires a write lock on the database. If the write ** lock could not be acquired, this routine returns SQLITE_BUSY. The ** calling routine must check for that routine and be careful not to ** change any page data until this routine returns SQLITE_OK. ** ** If the journal file could not be written because the disk is full, ** then this routine returns SQLITE_FULL and does an immediate rollback. ** All subsequent write attempts also return SQLITE_FULL until there ** is a call to sqlitepager_commit() or sqlitepager_rollback() to ** reset. */ int sqlitepager_write(void *pData){ PgHdr *pPg = DATA_TO_PGHDR(pData); Pager *pPager = pPg->pPager; int rc; if( pPager->errMask ){ return pager_errcode(pPager); } pPg->dirty = 1; if( pPg->inJournal ){ return SQLITE_OK; } assert( pPager->state!=SQLITE_UNLOCK ); if( pPager->state==SQLITE_READLOCK ){ pPager->jfd = open(pPager->zJournal, O_RDWR|O_CREAT, 0644); if( pPager->jfd<0 ){ return SQLITE_CANTOPEN; } if( pager_lock(pPager->jfd, 1) ){ close(pPager->jfd); pPager->jfd = -1; return SQLITE_BUSY; } pager_unlock(pPager->fd); if( pager_lock(pPager->fd, 1) ){ close(pPager->jfd); pPager->jfd = -1; pPager->state = SQLITE_UNLOCK; pPager->errMask |= PAGER_ERR_LOCK; return SQLITE_PROTOCOL; } pPager->state = SQLITE_WRITELOCK; sqlitepager_pagecount(pPager); pPager->origDbSize = pPager->dbSize; rc = pager_write(pPager->jfd, aJournalMagic, sizeof(aJournalMagic)); if( rc==SQLITE_OK ){ rc = pager_write(pPager->jfd, &pPager->dbSize, sizeof(Pgno)); } if( rc!=SQLITE_OK ){ rc = pager_unwritelock(pPager); if( rc==SQLITE_OK ) rc = SQLITE_FULL; return rc; } } assert( pPager->state==SQLITE_WRITELOCK ); assert( pPager->jfd>=0 ); if( pPg->pgno <= pPager->origDbSize ){ rc = pager_write(pPager->jfd, &pPg->pgno, sizeof(Pgno)); if( rc==SQLITE_OK ){ rc = pager_write(pPager->jfd, pData, SQLITE_PAGE_SIZE); } if( rc!=SQLITE_OK ){ sqlitepager_rollback(pPager); pPager->errMask |= PAGER_ERR_FULL; return rc; } } pPg->inJournal = 1; return rc; } /* ** Commit all changes to the database and release the write lock. ** ** If the commit fails for any reason, a rollback attempt is made ** and an error code is returned. If the commit worked, SQLITE_OK ** is returned. */ int sqlitepager_commit(Pager *pPager){ int i, rc; PgHdr *pPg; if( pPager->errMask==PAGER_ERR_FULL ){ rc = sqlitepager_rollback(pPager); if( rc==SQLITE_OK ) rc = SQLITE_FULL; return rc; } if( pPager->errMask!=0 ){ rc = pager_errcode(pPager); return rc; } if( pPager->state!=SQLITE_WRITELOCK ){ return SQLITE_ERROR; } assert( pPager->jfd>=0 ); if( fsync(pPager->jfd) ){ goto commit_abort; } for(i=0; i<N_PG_HASH; i++){ for(pPg=pPager->aHash[i]; pPg; pPg=pPg->pNextHash){ if( pPg->dirty==0 ) continue; rc = pager_seek(pPager->fd, (pPg->pgno-1)*SQLITE_PAGE_SIZE); if( rc!=SQLITE_OK ) goto commit_abort; rc = pager_write(pPager->fd, PGHDR_TO_DATA(pPg), SQLITE_PAGE_SIZE); if( rc!=SQLITE_OK ) goto commit_abort; } } if( fsync(pPager->fd) ) goto commit_abort; rc = pager_unwritelock(pPager); pPager->dbSize = -1; return rc; /* Jump here if anything goes wrong during the commit process. */ commit_abort: rc = sqlitepager_rollback(pPager); if( rc==SQLITE_OK ){ rc = SQLITE_FULL; } return rc; } /* ** Rollback all changes. The database falls back to read-only mode. ** All in-memory cache pages revert to their original data contents. ** The journal is deleted. ** ** This routine cannot fail unless some other process is not following ** the correct locking protocol (SQLITE_PROTOCOL) or unless some other ** process is writing trash into the journal file (SQLITE_CORRUPT) or ** unless a prior malloc() failed (SQLITE_NOMEM). Appropriate error ** codes are returned for all these occasions. Otherwise, ** SQLITE_OK is returned. */ int sqlitepager_rollback(Pager *pPager){ int rc; if( pPager->errMask!=0 && pPager->errMask!=PAGER_ERR_FULL ){ return pager_errcode(pPager); } if( pPager->state!=SQLITE_WRITELOCK ){ return SQLITE_OK; } rc = pager_playback(pPager); if( rc!=SQLITE_OK ){ rc = SQLITE_CORRUPT; pPager->errMask |= PAGER_ERR_CORRUPT; } pPager->dbSize = -1; return rc; }; /* ** This routine is used for testing and analysis only. */ int *sqlitepager_stats(Pager *pPager){ static int a[9]; a[0] = pPager->nRef; a[1] = pPager->nPage; a[2] = pPager->mxPage; a[3] = pPager->dbSize; a[4] = pPager->state; a[5] = pPager->errMask; a[6] = pPager->nHit; a[7] = pPager->nMiss; a[8] = pPager->nOvfl; return a; } |
Changes to src/pager.h.
︙ | ︙ | |||
21 22 23 24 25 26 27 | ** http://www.hwaci.com/drh/ ** ************************************************************************* ** This header file defines the interface that the sqlite page cache ** subsystem. The page cache subsystem reads and writes a file a page ** at a time and provides a journal for rollback. ** | | < | | | | | | | | | > > | 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 | ** http://www.hwaci.com/drh/ ** ************************************************************************* ** This header file defines the interface that the sqlite page cache ** subsystem. The page cache subsystem reads and writes a file a page ** at a time and provides a journal for rollback. ** ** @(#) $Id: pager.h,v 1.2 2001/04/15 00:37:09 drh Exp $ */ /* ** The size of one page */ #define SQLITE_PAGE_SIZE 1024 /* ** The type used to represent a page number. The first page in a file ** is called page 1. 0 is used to represent "not a page". */ typedef unsigned int Pgno; /* ** Each open file is managed by a separate instance of the "Pager" structure. */ typedef struct Pager Pager; int sqlitepager_open(Pager **ppPager, const char *zFilename, int nPage); int sqlitepager_close(Pager *pPager); int sqlitepager_get(Pager *pPager, Pgno pgno, void **ppPage); int sqlitepager_unref(void*); Pgno sqlitepager_pagenumber(void*); int sqlitepager_write(void*); int sqlitepager_pagecount(Pager*); int sqlitepager_commit(Pager*); int sqlitepager_rollback(Pager*); int *sqlitepager_stats(Pager*); |
Changes to src/tclsqlite.c.
︙ | ︙ | |||
19 20 21 22 23 24 25 | ** Author contact information: ** drh@hwaci.com ** http://www.hwaci.com/drh/ ** ************************************************************************* ** A TCL Interface to SQLite ** | | | 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 | ** Author contact information: ** drh@hwaci.com ** http://www.hwaci.com/drh/ ** ************************************************************************* ** A TCL Interface to SQLite ** ** $Id: tclsqlite.c,v 1.18 2001/04/15 00:37:09 drh Exp $ */ #ifndef NO_TCL /* Omit this whole file if TCL is unavailable */ #include "sqlite.h" #include "tcl.h" #include <stdlib.h> #include <string.h> |
︙ | ︙ | |||
503 504 505 506 507 508 509 | #define TCLSH_MAIN main /* Needed to fake out mktclapp */ int TCLSH_MAIN(int argc, char **argv){ Tcl_Interp *interp; Tcl_FindExecutable(argv[0]); interp = Tcl_CreateInterp(); Sqlite_Init(interp); | | > > | 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 | #define TCLSH_MAIN main /* Needed to fake out mktclapp */ int TCLSH_MAIN(int argc, char **argv){ Tcl_Interp *interp; Tcl_FindExecutable(argv[0]); interp = Tcl_CreateInterp(); Sqlite_Init(interp); #ifdef SQLITE_TEST { extern int Sqlitetest1_Init(Tcl_Interp*); extern int Sqlitetest2_Init(Tcl_Interp*); Sqlitetest1_Init(interp); Sqlitetest2_Init(interp); } #endif if( argc>=2 ){ int i; Tcl_SetVar(interp,"argv0",argv[1],TCL_GLOBAL_ONLY); Tcl_SetVar(interp,"argv", "", TCL_GLOBAL_ONLY); for(i=2; i<argc; i++){ |
︙ | ︙ |
Added src/test2.c.
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 | /* ** Copyright (c) 2001 D. Richard Hipp ** ** This program is free software; you can redistribute it and/or ** modify it under the terms of the GNU General Public ** License as published by the Free Software Foundation; either ** version 2 of the License, or (at your option) any later version. ** ** This program is distributed in the hope that it will be useful, ** but WITHOUT ANY WARRANTY; without even the implied warranty of ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU ** General Public License for more details. ** ** You should have received a copy of the GNU General Public ** License along with this library; if not, write to the ** Free Software Foundation, Inc., 59 Temple Place - Suite 330, ** Boston, MA 02111-1307, USA. ** ** Author contact information: ** drh@hwaci.com ** http://www.hwaci.com/drh/ ** ************************************************************************* ** Code for testing the pager.c module in SQLite. This code ** is not included in the SQLite library. It is used for automated ** testing of the SQLite library. ** ** $Id: test2.c,v 1.1 2001/04/15 00:37:09 drh Exp $ */ #include "sqliteInt.h" #include "pager.h" #include "tcl.h" #include <stdlib.h> #include <string.h> /* ** Interpret an SQLite error number */ static char *errorName(int rc){ char *zName; switch( rc ){ case SQLITE_OK: zName = "SQLITE_OK"; break; case SQLITE_ERROR: zName = "SQLITE_ERROR"; break; case SQLITE_INTERNAL: zName = "SQLITE_INTERNAL"; break; case SQLITE_PERM: zName = "SQLITE_PERM"; break; case SQLITE_ABORT: zName = "SQLITE_ABORT"; break; case SQLITE_BUSY: zName = "SQLITE_BUSY"; break; case SQLITE_NOMEM: zName = "SQLITE_NOMEM"; break; case SQLITE_READONLY: zName = "SQLITE_READONLY"; break; case SQLITE_INTERRUPT: zName = "SQLITE_INTERRUPT"; break; case SQLITE_IOERR: zName = "SQLITE_IOERR"; break; case SQLITE_CORRUPT: zName = "SQLITE_CORRUPT"; break; case SQLITE_NOTFOUND: zName = "SQLITE_NOTFOUND"; break; case SQLITE_FULL: zName = "SQLITE_FULL"; break; case SQLITE_CANTOPEN: zName = "SQLITE_CANTOPEN"; break; case SQLITE_PROTOCOL: zName = "SQLITE_PROTOCOL"; break; default: zName = "SQLITE_Unknown"; break; } return zName; } /* ** Usage: pager_open FILENAME N-PAGE ** ** Open a new pager */ static int pager_open( void *NotUsed, Tcl_Interp *interp, /* The TCL interpreter that invoked this command */ int argc, /* Number of arguments */ char **argv /* Text of each argument */ ){ Pager *pPager; int nPage; int rc; char zBuf[100]; if( argc!=3 ){ Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0], " FILENAME N-PAGE\"", 0); return TCL_ERROR; } if( Tcl_GetInt(interp, argv[2], &nPage) ) return TCL_ERROR; rc = sqlitepager_open(&pPager, argv[1], nPage); if( rc!=SQLITE_OK ){ Tcl_AppendResult(interp, errorName(rc), 0); return TCL_ERROR; } sprintf(zBuf,"0x%x",(int)pPager); Tcl_AppendResult(interp, zBuf, 0); return TCL_OK; } /* ** Usage: pager_close ID ** ** Close the given pager. */ static int pager_close( void *NotUsed, Tcl_Interp *interp, /* The TCL interpreter that invoked this command */ int argc, /* Number of arguments */ char **argv /* Text of each argument */ ){ Pager *pPager; int rc; if( argc!=2 ){ Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0], " ID\"", 0); return TCL_ERROR; } if( Tcl_GetInt(interp, argv[1], (int*)&pPager) ) return TCL_ERROR; rc = sqlitepager_close(pPager); if( rc!=SQLITE_OK ){ Tcl_AppendResult(interp, errorName(rc), 0); return TCL_ERROR; } return TCL_OK; } /* ** Usage: pager_rollback ID ** ** Rollback changes */ static int pager_rollback( void *NotUsed, Tcl_Interp *interp, /* The TCL interpreter that invoked this command */ int argc, /* Number of arguments */ char **argv /* Text of each argument */ ){ Pager *pPager; int rc; if( argc!=2 ){ Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0], " ID\"", 0); return TCL_ERROR; } if( Tcl_GetInt(interp, argv[1], (int*)&pPager) ) return TCL_ERROR; rc = sqlitepager_rollback(pPager); if( rc!=SQLITE_OK ){ Tcl_AppendResult(interp, errorName(rc), 0); return TCL_ERROR; } return TCL_OK; } /* ** Usage: pager_commit ID ** ** Commit all changes */ static int pager_commit( void *NotUsed, Tcl_Interp *interp, /* The TCL interpreter that invoked this command */ int argc, /* Number of arguments */ char **argv /* Text of each argument */ ){ Pager *pPager; int rc; if( argc!=2 ){ Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0], " ID\"", 0); return TCL_ERROR; } if( Tcl_GetInt(interp, argv[1], (int*)&pPager) ) return TCL_ERROR; rc = sqlitepager_commit(pPager); if( rc!=SQLITE_OK ){ Tcl_AppendResult(interp, errorName(rc), 0); return TCL_ERROR; } return TCL_OK; } /* ** Usage: pager_stats ID ** ** Return pager statistics. */ static int pager_stats( void *NotUsed, Tcl_Interp *interp, /* The TCL interpreter that invoked this command */ int argc, /* Number of arguments */ char **argv /* Text of each argument */ ){ Pager *pPager; int i, *a; if( argc!=2 ){ Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0], " ID\"", 0); return TCL_ERROR; } if( Tcl_GetInt(interp, argv[1], (int*)&pPager) ) return TCL_ERROR; a = sqlitepager_stats(pPager); for(i=0; i<9; i++){ static char *zName[] = { "ref", "page", "max", "size", "state", "err", "hit", "miss", "ovfl", }; char zBuf[100]; Tcl_AppendElement(interp, zName[i]); sprintf(zBuf,"%d",a[i]); Tcl_AppendElement(interp, zBuf); } return TCL_OK; } /* ** Usage: pager_pagecount ID ** ** Return the size of the database file. */ static int pager_pagecount( void *NotUsed, Tcl_Interp *interp, /* The TCL interpreter that invoked this command */ int argc, /* Number of arguments */ char **argv /* Text of each argument */ ){ Pager *pPager; char zBuf[100]; if( argc!=2 ){ Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0], " ID\"", 0); return TCL_ERROR; } if( Tcl_GetInt(interp, argv[1], (int*)&pPager) ) return TCL_ERROR; sprintf(zBuf,"%d",sqlitepager_pagecount(pPager)); Tcl_AppendResult(interp, zBuf, 0); return TCL_OK; } /* ** Usage: page_get ID PGNO ** ** Return a pointer to a page from the database. */ static int page_get( void *NotUsed, Tcl_Interp *interp, /* The TCL interpreter that invoked this command */ int argc, /* Number of arguments */ char **argv /* Text of each argument */ ){ Pager *pPager; char zBuf[100]; void *pPage; int pgno; int rc; if( argc!=3 ){ Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0], " ID PGNO\"", 0); return TCL_ERROR; } if( Tcl_GetInt(interp, argv[1], (int*)&pPager) ) return TCL_ERROR; if( Tcl_GetInt(interp, argv[2], &pgno) ) return TCL_ERROR; rc = sqlitepager_get(pPager, pgno, &pPage); if( rc!=SQLITE_OK ){ Tcl_AppendResult(interp, errorName(rc), 0); return TCL_ERROR; } sprintf(zBuf,"0x%x",(int)pPage); Tcl_AppendResult(interp, zBuf, 0); return TCL_OK; } /* ** Usage: page_unref PAGE ** ** Drop a pointer to a page. */ static int page_unref( void *NotUsed, Tcl_Interp *interp, /* The TCL interpreter that invoked this command */ int argc, /* Number of arguments */ char **argv /* Text of each argument */ ){ void *pPage; int rc; if( argc!=2 ){ Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0], " PAGE\"", 0); return TCL_ERROR; } if( Tcl_GetInt(interp, argv[1], (int*)&pPage) ) return TCL_ERROR; rc = sqlitepager_unref(pPage); if( rc!=SQLITE_OK ){ Tcl_AppendResult(interp, errorName(rc), 0); return TCL_ERROR; } return TCL_OK; } /* ** Usage: page_read PAGE ** ** Return the content of a page */ static int page_read( void *NotUsed, Tcl_Interp *interp, /* The TCL interpreter that invoked this command */ int argc, /* Number of arguments */ char **argv /* Text of each argument */ ){ char zBuf[100]; void *pPage; if( argc!=2 ){ Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0], " PAGE\"", 0); return TCL_ERROR; } if( Tcl_GetInt(interp, argv[1], (int*)&pPage) ) return TCL_ERROR; memcpy(zBuf, pPage, sizeof(zBuf)); Tcl_AppendResult(interp, zBuf, 0); return TCL_OK; } /* ** Usage: page_number PAGE ** ** Return the page number for a page. */ static int page_number( void *NotUsed, Tcl_Interp *interp, /* The TCL interpreter that invoked this command */ int argc, /* Number of arguments */ char **argv /* Text of each argument */ ){ char zBuf[100]; void *pPage; if( argc!=2 ){ Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0], " PAGE\"", 0); return TCL_ERROR; } if( Tcl_GetInt(interp, argv[1], (int*)&pPage) ) return TCL_ERROR; sprintf(zBuf, "%d", sqlitepager_pagenumber(pPage)); Tcl_AppendResult(interp, zBuf, 0); return TCL_OK; } /* ** Usage: page_write PAGE DATA ** ** Write something into a page. */ static int page_write( void *NotUsed, Tcl_Interp *interp, /* The TCL interpreter that invoked this command */ int argc, /* Number of arguments */ char **argv /* Text of each argument */ ){ void *pPage; int rc; if( argc!=3 ){ Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0], " PAGE DATA\"", 0); return TCL_ERROR; } if( Tcl_GetInt(interp, argv[1], (int*)&pPage) ) return TCL_ERROR; rc = sqlitepager_write(pPage); if( rc!=SQLITE_OK ){ Tcl_AppendResult(interp, errorName(rc), 0); return TCL_ERROR; } strncpy((char*)pPage, argv[2], SQLITE_PAGE_SIZE-1); ((char*)pPage)[SQLITE_PAGE_SIZE-1] = 0; return TCL_OK; } /* ** Register commands with the TCL interpreter. */ int Sqlitetest2_Init(Tcl_Interp *interp){ Tcl_CreateCommand(interp, "pager_open", pager_open, 0, 0); Tcl_CreateCommand(interp, "pager_close", pager_close, 0, 0); Tcl_CreateCommand(interp, "pager_commit", pager_commit, 0, 0); Tcl_CreateCommand(interp, "pager_rollback", pager_rollback, 0, 0); Tcl_CreateCommand(interp, "pager_stats", pager_stats, 0, 0); Tcl_CreateCommand(interp, "pager_pagecount", pager_pagecount, 0, 0); Tcl_CreateCommand(interp, "page_get", page_get, 0, 0); Tcl_CreateCommand(interp, "page_unref", page_unref, 0, 0); Tcl_CreateCommand(interp, "page_read", page_read, 0, 0); Tcl_CreateCommand(interp, "page_write", page_write, 0, 0); Tcl_CreateCommand(interp, "page_number", page_number, 0, 0); return TCL_OK; } |
Added test/pager.test.
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 | # Copyright (c) 1999, 2000 D. Richard Hipp # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public # License as published by the Free Software Foundation; either # version 2 of the License, or (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU # General Public License for more details. # # You should have received a copy of the GNU General Public # License along with this library; if not, write to the # Free Software Foundation, Inc., 59 Temple Place - Suite 330, # Boston, MA 02111-1307, USA. # # Author contact information: # drh@hwaci.com # http://www.hwaci.com/drh/ # #*********************************************************************** # This file implements regression tests for SQLite library. The # focus of this script is page cache subsystem. # # $Id: pager.test,v 1.1 2001/04/15 00:37:21 drh Exp $ set testdir [file dirname $argv0] source $testdir/tester.tcl if {$dbprefix!="mem:" && [info commands pager_open]!=""} { # Basic sanity check. Open and close a pager. # do_test pager-1.0 { catch {file delete -force ptf1.db} catch {file delete -force ptf1.db-journal} set v [catch { set ::p1 [pager_open ptf1.db 10] } msg] } {0} do_test pager-1.1 { pager_stats $::p1 } {ref 0 page 0 max 10 size -1 state 0 err 0 hit 0 miss 0 ovfl 0} do_test pager-1.2 { pager_pagecount $::p1 } {0} do_test pager-1.3 { pager_stats $::p1 } {ref 0 page 0 max 10 size -1 state 0 err 0 hit 0 miss 0 ovfl 0} do_test pager-1.4 { pager_close $::p1 } {} # Try to write a few pages. # do_test pager-2.1 { set v [catch { set ::p1 [pager_open ptf1.db 10] } msg] } {0} do_test pager-2.2 { set v [catch { set ::g1 [page_get $::p1 0] } msg] lappend v $msg } {1 SQLITE_ERROR} do_test pager-2.3 { set v [catch { set ::g1 [page_get $::p1 1] } msg] if {$v} {lappend v $msg} set v } {0} do_test pager-2.4 { pager_stats $::p1 } {ref 1 page 1 max 10 size -1 state 1 err 0 hit 0 miss 1 ovfl 0} do_test pager-2.5 { pager_pagecount $::p1 } {0} do_test pager-2.6 { pager_stats $::p1 } {ref 1 page 1 max 10 size 0 state 1 err 0 hit 0 miss 1 ovfl 0} do_test pager-2.7 { page_number $::g1 } {1} do_test pager-2.8 { page_read $::g1 } {} do_test pager-2.9 { page_unref $::g1 } {} do_test pager-2.10 { pager_stats $::p1 } {ref 0 page 0 max 10 size -1 state 0 err 0 hit 0 miss 1 ovfl 0} do_test pager-2.11 { set ::g1 [page_get $::p1 1] expr {$::g1!=0} } {1} do_test pager-2.12 { page_number $::g1 } {1} do_test pager-2.13 { pager_stats $::p1 } {ref 1 page 1 max 10 size -1 state 1 err 0 hit 0 miss 2 ovfl 0} do_test pager-2.14 { set v [catch { page_write $::g1 "Page-One" } msg] lappend v $msg } {0 {}} do_test pager-2.15 { pager_stats $::p1 } {ref 1 page 1 max 10 size 0 state 2 err 0 hit 0 miss 2 ovfl 0} do_test pager-2.16 { page_read $::g1 } {Page-One} do_test pager-2.17 { set v [catch { pager_commit $::p1 } msg] lappend v $msg } {0 {}} do_test pager-2.20 { pager_stats $::p1 } {ref 1 page 1 max 10 size -1 state 1 err 0 hit 0 miss 2 ovfl 0} do_test pager-2.19 { pager_pagecount $::p1 } {1} do_test pager-2.21 { pager_stats $::p1 } {ref 1 page 1 max 10 size 1 state 1 err 0 hit 0 miss 2 ovfl 0} do_test pager-2.22 { page_unref $::g1 } {} do_test pager-2.23 { pager_stats $::p1 } {ref 0 page 0 max 10 size -1 state 0 err 0 hit 0 miss 2 ovfl 0} do_test pager-2.24 { set v [catch { page_get $::p1 1 } ::g1] if {$v} {lappend v $::g1} set v } {0} do_test pager-2.25 { page_read $::g1 } {Page-One} do_test pager-2.26 { set v [catch { page_write $::g1 {page-one} } msg] lappend v $msg } {0 {}} do_test pager-2.27 { page_read $::g1 } {page-one} do_test pager-2.28 { set v [catch { pager_rollback $::p1 } msg] lappend v $msg } {0 {}} do_test pager-2.29 { page_read $::g1 } {Page-One} do_test pager-2.99 { pager_close $::p1 } {} } ;# end if( not mem: and has pager_open command ); finish_test |
Changes to www/changes.tcl.
︙ | ︙ | |||
13 14 15 16 17 18 19 | proc chng {date desc} { puts "<DT><B>$date</B></DT>" puts "<DD><P><UL>$desc</UL></P></DD>" } | | > | 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 | proc chng {date desc} { puts "<DT><B>$date</B></DT>" puts "<DD><P><UL>$desc</UL></P></DD>" } chng {2001 Apr 14 (1.0.31)} { <li>Pager subsystem added but not yet used.</li> <li>More robust handling of out-of-memory errors.</li> <li>New tests added to the test suite.</li> } chng {2001 Apr 6 (1.0.30)} { <li>Remove the <b>sqlite_encoding</b> TCL variable that was introduced in the previous version.</li> |
︙ | ︙ |