Many hyperlinks are disabled.
Use anonymous login
to enable hyperlinks.
Overview
Comment: | Faster :memory: database COMMITs. Ticket #1790. (CVS 3178) |
---|---|
Downloads: | Tarball | ZIP archive |
Timelines: | family | ancestors | descendants | both | trunk |
Files: | files | file ages | folders |
SHA1: |
8f820e435272c0a4861421508c7e6f29 |
User & Date: | drh 2006-05-03 23:34:06.000 |
Context
2006-05-03
| ||
23:39 | Fix a typo on a webpage. Ticket #1792. (CVS 3179) (check-in: 2702205277 user: drh tags: trunk) | |
23:34 | Faster :memory: database COMMITs. Ticket #1790. (CVS 3178) (check-in: 8f820e4352 user: drh tags: trunk) | |
2006-04-26
| ||
17:39 | Get LIMIT 0 working on subqueries. Ticket #1784. (CVS 3177) (check-in: 7f3ef7ddba user: drh tags: trunk) | |
Changes
Changes to src/pager.c.
︙ | ︙ | |||
14 15 16 17 18 19 20 | ** The pager is used to access a database disk file. It implements ** atomic commit and rollback through the use of a journal file that ** is separate from the database file. The pager also implements file ** locking to prevent two processes from writing the same database ** file simultaneously, or one process from reading the database while ** another is writing. ** | | | 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 | ** The pager is used to access a database disk file. It implements ** atomic commit and rollback through the use of a journal file that ** is separate from the database file. The pager also implements file ** locking to prevent two processes from writing the same database ** file simultaneously, or one process from reading the database while ** another is writing. ** ** @(#) $Id: pager.c,v 1.267 2006/05/03 23:34:06 drh Exp $ */ #ifndef SQLITE_OMIT_DISKIO #include "sqliteInt.h" #include "os.h" #include "pager.h" #include <assert.h> #include <string.h> |
︙ | ︙ | |||
157 158 159 160 161 162 163 | PgHdr *pNextStmt, *pPrevStmt; /* List of pages in the statement journal */ u8 inJournal; /* TRUE if has been written to journal */ u8 inStmt; /* TRUE if in the statement subjournal */ u8 dirty; /* TRUE if we need to write back changes */ u8 needSync; /* Sync journal before writing this page */ u8 alwaysRollback; /* Disable dont_rollback() for this page */ short int nRef; /* Number of users of this page */ | | > | 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 | PgHdr *pNextStmt, *pPrevStmt; /* List of pages in the statement journal */ u8 inJournal; /* TRUE if has been written to journal */ u8 inStmt; /* TRUE if in the statement subjournal */ u8 dirty; /* TRUE if we need to write back changes */ u8 needSync; /* Sync journal before writing this page */ u8 alwaysRollback; /* Disable dont_rollback() for this page */ short int nRef; /* Number of users of this page */ PgHdr *pDirty, *pPrevDirty; /* Dirty pages sorted by PgHdr.pgno */ u32 notUsed; /* Buffer space */ #ifdef SQLITE_CHECK_PAGES u32 pageHash; #endif /* pPager->pageSize bytes of page data follow this header */ /* Pager.nExtra bytes of local data follow the page data */ }; |
︙ | ︙ | |||
276 277 278 279 280 281 282 283 284 285 286 287 288 289 | OsFile *fd, *jfd; /* File descriptors for database and journal */ OsFile *stfd; /* File descriptor for the statement subjournal*/ BusyHandler *pBusyHandler; /* Pointer to sqlite.busyHandler */ PgHdr *pFirst, *pLast; /* List of free pages */ PgHdr *pFirstSynced; /* First free page with PgHdr.needSync==0 */ PgHdr *pAll; /* List of all pages */ PgHdr *pStmt; /* List of pages in the statement subjournal */ i64 journalOff; /* Current byte offset in the journal file */ i64 journalHdr; /* Byte offset to previous journal header */ i64 stmtHdrOff; /* First journal header written this statement */ i64 stmtCksum; /* cksumInit when statement was started */ i64 stmtJSize; /* Size of journal at stmt_begin() */ int sectorSize; /* Assumed sector size during rollback */ #ifdef SQLITE_TEST | > | 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 | OsFile *fd, *jfd; /* File descriptors for database and journal */ OsFile *stfd; /* File descriptor for the statement subjournal*/ BusyHandler *pBusyHandler; /* Pointer to sqlite.busyHandler */ PgHdr *pFirst, *pLast; /* List of free pages */ PgHdr *pFirstSynced; /* First free page with PgHdr.needSync==0 */ PgHdr *pAll; /* List of all pages */ PgHdr *pStmt; /* List of pages in the statement subjournal */ PgHdr *pDirty; /* List of all dirty pages */ i64 journalOff; /* Current byte offset in the journal file */ i64 journalHdr; /* Byte offset to previous journal header */ i64 stmtHdrOff; /* First journal header written this statement */ i64 stmtCksum; /* cksumInit when statement was started */ i64 stmtJSize; /* Size of journal at stmt_begin() */ int sectorSize; /* Assumed sector size during rollback */ #ifdef SQLITE_TEST |
︙ | ︙ | |||
888 889 890 891 892 893 894 895 896 897 898 899 900 901 | pPg->inJournal = 0; pPg->dirty = 0; pPg->needSync = 0; #ifdef SQLITE_CHECK_PAGES pPg->pageHash = pager_pagehash(pPg); #endif } pPager->dirtyCache = 0; pPager->nRec = 0; }else{ assert( pPager->aInJournal==0 ); assert( pPager->dirtyCache==0 || pPager->useJournal==0 ); } rc = sqlite3OsUnlock(pPager->fd, SHARED_LOCK); | > | 890 891 892 893 894 895 896 897 898 899 900 901 902 903 904 | pPg->inJournal = 0; pPg->dirty = 0; pPg->needSync = 0; #ifdef SQLITE_CHECK_PAGES pPg->pageHash = pager_pagehash(pPg); #endif } pPager->pDirty = 0; pPager->dirtyCache = 0; pPager->nRec = 0; }else{ assert( pPager->aInJournal==0 ); assert( pPager->dirtyCache==0 || pPager->useJournal==0 ); } rc = sqlite3OsUnlock(pPager->fd, SHARED_LOCK); |
︙ | ︙ | |||
933 934 935 936 937 938 939 940 941 942 943 944 945 946 | while( i>0 ){ cksum += aData[i]; i -= 200; } return cksum; } /* ** Read a single page from the journal file opened on file descriptor ** jfd. Playback this one page. ** ** If useCksum==0 it means this journal does not use checksums. Checksums ** are not used in statement journals because statement journals do not ** need to survive power failures. | > > > | 936 937 938 939 940 941 942 943 944 945 946 947 948 949 950 951 952 | while( i>0 ){ cksum += aData[i]; i -= 200; } return cksum; } /* Forward declaration */ static void makeClean(PgHdr*); /* ** Read a single page from the journal file opened on file descriptor ** jfd. Playback this one page. ** ** If useCksum==0 it means this journal does not use checksums. Checksums ** are not used in statement journals because statement journals do not ** need to survive power failures. |
︙ | ︙ | |||
1010 1011 1012 1013 1014 1015 1016 | assert( pPager->state>=PAGER_EXCLUSIVE || pPg!=0 ); TRACE3("PLAYBACK %d page %d\n", PAGERID(pPager), pgno); if( pPager->state>=PAGER_EXCLUSIVE && (pPg==0 || pPg->needSync==0) ){ rc = sqlite3OsSeek(pPager->fd, (pgno-1)*(i64)pPager->pageSize); if( rc==SQLITE_OK ){ rc = sqlite3OsWrite(pPager->fd, aData, pPager->pageSize); } | | > > | 1016 1017 1018 1019 1020 1021 1022 1023 1024 1025 1026 1027 1028 1029 1030 1031 1032 | assert( pPager->state>=PAGER_EXCLUSIVE || pPg!=0 ); TRACE3("PLAYBACK %d page %d\n", PAGERID(pPager), pgno); if( pPager->state>=PAGER_EXCLUSIVE && (pPg==0 || pPg->needSync==0) ){ rc = sqlite3OsSeek(pPager->fd, (pgno-1)*(i64)pPager->pageSize); if( rc==SQLITE_OK ){ rc = sqlite3OsWrite(pPager->fd, aData, pPager->pageSize); } if( pPg ){ makeClean(pPg); } } if( pPg ){ /* No page should ever be explicitly rolled back that is in use, except ** for page 1 which is held in use in order to keep the lock on the ** database active. However such a page may be rolled back as a result ** of an internal error resulting in an automatic call to ** sqlite3pager_rollback(). |
︙ | ︙ | |||
1160 1161 1162 1163 1164 1165 1166 1167 1168 1169 1170 1171 1172 1173 | } pPg->needSync = 0; pPg->dirty = 0; #ifdef SQLITE_CHECK_PAGES pPg->pageHash = pager_pagehash(pPg); #endif } return rc; } /* ** Truncate the main file of the given pager to the number of pages ** indicated. */ | > | 1168 1169 1170 1171 1172 1173 1174 1175 1176 1177 1178 1179 1180 1181 1182 | } pPg->needSync = 0; pPg->dirty = 0; #ifdef SQLITE_CHECK_PAGES pPg->pageHash = pager_pagehash(pPg); #endif } pPager->pDirty = 0; return rc; } /* ** Truncate the main file of the given pager to the number of pages ** indicated. */ |
︙ | ︙ | |||
1904 1905 1906 1907 1908 1909 1910 1911 1912 1913 1914 1915 1916 1917 | ppPg = &pPg->pNextAll; }else if( pPg->nRef>0 ){ memset(PGHDR_TO_DATA(pPg), 0, pPager->pageSize); ppPg = &pPg->pNextAll; }else{ *ppPg = pPg->pNextAll; unlinkPage(pPg); sqliteFree(pPg); pPager->nPage--; } } } #else #define memoryTruncate(p) | > | 1913 1914 1915 1916 1917 1918 1919 1920 1921 1922 1923 1924 1925 1926 1927 | ppPg = &pPg->pNextAll; }else if( pPg->nRef>0 ){ memset(PGHDR_TO_DATA(pPg), 0, pPager->pageSize); ppPg = &pPg->pNextAll; }else{ *ppPg = pPg->pNextAll; unlinkPage(pPg); makeClean(pPg); sqliteFree(pPg); pPager->nPage--; } } } #else #define memoryTruncate(p) |
︙ | ︙ | |||
2301 2302 2303 2304 2305 2306 2307 | /* ** Collect every dirty page into a dirty list and ** return a pointer to the head of that list. All pages are ** collected even if they are still in use. */ static PgHdr *pager_get_all_dirty_pages(Pager *pPager){ | < < < < < < < < | | 2311 2312 2313 2314 2315 2316 2317 2318 2319 2320 2321 2322 2323 2324 2325 | /* ** Collect every dirty page into a dirty list and ** return a pointer to the head of that list. All pages are ** collected even if they are still in use. */ static PgHdr *pager_get_all_dirty_pages(Pager *pPager){ return pPager->pDirty; } /* ** Return TRUE if there is a hot journal on the given pager. ** A hot journal is one that needs to be played back. ** ** If the current size of the database file is 0 but a journal file |
︙ | ︙ | |||
2384 2385 2386 2387 2388 2389 2390 2391 2392 2393 2394 2395 2396 2397 | assert( pPg->nRef==0 ); /* Write the page to the database file if it is dirty. */ if( pPg->dirty ){ int rc; assert( pPg->needSync==0 ); pPg->pDirty = 0; rc = pager_write_pagelist( pPg ); if( rc!=SQLITE_OK ){ return rc; } } assert( pPg->dirty==0 ); | > > | 2386 2387 2388 2389 2390 2391 2392 2393 2394 2395 2396 2397 2398 2399 2400 2401 | assert( pPg->nRef==0 ); /* Write the page to the database file if it is dirty. */ if( pPg->dirty ){ int rc; assert( pPg->needSync==0 ); makeClean(pPg); pPg->dirty = 1; pPg->pDirty = 0; rc = pager_write_pagelist( pPg ); if( rc!=SQLITE_OK ){ return rc; } } assert( pPg->dirty==0 ); |
︙ | ︙ | |||
2655 2656 2657 2658 2659 2660 2661 | } if( pPager->aInStmt && (int)pgno<=pPager->stmtSize && (pPager->aInStmt[pgno/8] & (1<<(pgno&7)))!=0 ){ page_add_to_stmt_list(pPg); }else{ page_remove_from_stmt_list(pPg); } | | | 2659 2660 2661 2662 2663 2664 2665 2666 2667 2668 2669 2670 2671 2672 2673 | } if( pPager->aInStmt && (int)pgno<=pPager->stmtSize && (pPager->aInStmt[pgno/8] & (1<<(pgno&7)))!=0 ){ page_add_to_stmt_list(pPg); }else{ page_remove_from_stmt_list(pPg); } makeClean(pPg); pPg->nRef = 1; REFINFO(pPg); pPager->nRef++; if( pPager->nExtra>0 ){ memset(PGHDR_TO_EXTRA(pPg, pPager), 0, pPager->nExtra); } |
︙ | ︙ | |||
2930 2931 2932 2933 2934 2935 2936 2937 2938 2939 2940 2941 2942 2943 | if( pPager->useJournal && !pPager->tempFile ){ rc = pager_open_journal(pPager); } } } return rc; } /* ** 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 | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 2934 2935 2936 2937 2938 2939 2940 2941 2942 2943 2944 2945 2946 2947 2948 2949 2950 2951 2952 2953 2954 2955 2956 2957 2958 2959 2960 2961 2962 2963 2964 2965 2966 2967 2968 2969 2970 2971 2972 2973 2974 2975 2976 2977 2978 2979 2980 2981 2982 2983 | if( pPager->useJournal && !pPager->tempFile ){ rc = pager_open_journal(pPager); } } } return rc; } /* ** Make a page dirty. Set its dirty flag and add it to the dirty ** page list. */ static void makeDirty(PgHdr *pPg){ if( pPg->dirty==0 ){ Pager *pPager = pPg->pPager; pPg->dirty = 1; pPg->pDirty = pPager->pDirty; if( pPager->pDirty ){ pPager->pDirty->pPrevDirty = pPg; } pPg->pPrevDirty = 0; pPager->pDirty = pPg; } } /* ** Make a page clean. Clear its dirty bit and remove it from the ** dirty page list. */ static void makeClean(PgHdr *pPg){ if( pPg->dirty ){ pPg->dirty = 0; if( pPg->pDirty ){ pPg->pDirty->pPrevDirty = pPg->pPrevDirty; } if( pPg->pPrevDirty ){ pPg->pPrevDirty->pDirty = pPg->pDirty; }else{ pPg->pPager->pDirty = pPg->pDirty; } } } /* ** 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 |
︙ | ︙ | |||
2969 2970 2971 2972 2973 2974 2975 | assert( !pPager->setMaster ); CHECK_PAGE(pPg); /* Mark the page as dirty. If the page has already been written ** to the journal then we can return right away. */ | | | 3009 3010 3011 3012 3013 3014 3015 3016 3017 3018 3019 3020 3021 3022 3023 | assert( !pPager->setMaster ); CHECK_PAGE(pPg); /* Mark the page as dirty. If the page has already been written ** to the journal then we can return right away. */ makeDirty(pPg); if( pPg->inJournal && (pPg->inStmt || pPager->stmtInUse==0) ){ pPager->dirtyCache = 1; }else{ /* If we get this far, it means that the page needs to be ** written to the transaction journal or the ckeckpoint journal ** or both. |
︙ | ︙ | |||
3176 3177 3178 3179 3180 3181 3182 | ** gets written at least once so that the disk file will be the correct ** size. If you do not write this page and the size of the file ** on the disk ends up being too small, that can lead to database ** corruption during the next transaction. */ }else{ TRACE3("DONT_WRITE page %d of %d\n", pgno, PAGERID(pPager)); | | | 3216 3217 3218 3219 3220 3221 3222 3223 3224 3225 3226 3227 3228 3229 3230 | ** gets written at least once so that the disk file will be the correct ** size. If you do not write this page and the size of the file ** on the disk ends up being too small, that can lead to database ** corruption during the next transaction. */ }else{ TRACE3("DONT_WRITE page %d of %d\n", pgno, PAGERID(pPager)); makeClean(pPg); #ifdef SQLITE_CHECK_PAGES pPg->pageHash = pager_pagehash(pPg); #endif } } } |
︙ | ︙ | |||
3244 3245 3246 3247 3248 3249 3250 3251 3252 3253 3254 3255 3256 3257 | pPg->dirty = 0; pPg->inJournal = 0; pPg->inStmt = 0; pPg->needSync = 0; pPg->pPrevStmt = pPg->pNextStmt = 0; pPg = pPg->pDirty; } #ifndef NDEBUG for(pPg=pPager->pAll; pPg; pPg=pPg->pNextAll){ PgHistory *pHist = PGHDR_TO_HIST(pPg, pPager); assert( !pPg->alwaysRollback ); assert( !pHist->pOrig ); assert( !pHist->pStmt ); } | > | 3284 3285 3286 3287 3288 3289 3290 3291 3292 3293 3294 3295 3296 3297 3298 | pPg->dirty = 0; pPg->inJournal = 0; pPg->inStmt = 0; pPg->needSync = 0; pPg->pPrevStmt = pPg->pNextStmt = 0; pPg = pPg->pDirty; } pPager->pDirty = 0; #ifndef NDEBUG for(pPg=pPager->pAll; pPg; pPg=pPg->pNextAll){ PgHistory *pHist = PGHDR_TO_HIST(pPg, pPager); assert( !pPg->alwaysRollback ); assert( !pHist->pOrig ); assert( !pHist->pStmt ); } |
︙ | ︙ | |||
3311 3312 3313 3314 3315 3316 3317 | TRACE3("PAGE %d is clean on %d\n", p->pgno, PAGERID(pPager)); } clearHistory(pHist); p->dirty = 0; p->inJournal = 0; p->inStmt = 0; p->pPrevStmt = p->pNextStmt = 0; | < | < > | 3352 3353 3354 3355 3356 3357 3358 3359 3360 3361 3362 3363 3364 3365 3366 3367 3368 3369 3370 | TRACE3("PAGE %d is clean on %d\n", p->pgno, PAGERID(pPager)); } clearHistory(pHist); p->dirty = 0; p->inJournal = 0; p->inStmt = 0; p->pPrevStmt = p->pNextStmt = 0; if( pPager->xReiniter ){ pPager->xReiniter(PGHDR_TO_DATA(p), pPager->pageSize); } } pPager->pDirty = 0; pPager->pStmt = 0; pPager->dbSize = pPager->origDbSize; memoryTruncate(pPager); pPager->stmtInUse = 0; pPager->state = PAGER_SHARED; return SQLITE_OK; } |
︙ | ︙ | |||
3711 3712 3713 3714 3715 3716 3717 | ** page pgno before the 'move' operation, it needs to be retained ** for the page moved there. */ pPgOld = pager_lookup(pPager, pgno); if( pPgOld ){ assert( pPgOld->nRef==0 ); unlinkHashChain(pPager, pPgOld); | | | | | 3751 3752 3753 3754 3755 3756 3757 3758 3759 3760 3761 3762 3763 3764 3765 3766 3767 3768 3769 3770 3771 3772 3773 3774 3775 3776 3777 3778 3779 3780 3781 3782 3783 3784 3785 3786 3787 3788 3789 3790 3791 3792 3793 3794 3795 3796 3797 3798 3799 3800 3801 3802 3803 3804 3805 3806 | ** page pgno before the 'move' operation, it needs to be retained ** for the page moved there. */ pPgOld = pager_lookup(pPager, pgno); if( pPgOld ){ assert( pPgOld->nRef==0 ); unlinkHashChain(pPager, pPgOld); makeClean(pPgOld); if( pPgOld->needSync ){ assert( pPgOld->inJournal ); pPg->inJournal = 1; pPg->needSync = 1; assert( pPager->needSync ); } } /* Change the page number for pPg and insert it into the new hash-chain. */ pPg->pgno = pgno; h = pager_hash(pgno); if( pPager->aHash[h] ){ assert( pPager->aHash[h]->pPrevHash==0 ); pPager->aHash[h]->pPrevHash = pPg; } pPg->pNextHash = pPager->aHash[h]; pPager->aHash[h] = pPg; pPg->pPrevHash = 0; makeDirty(pPg); pPager->dirtyCache = 1; if( needSyncPgno ){ /* If needSyncPgno is non-zero, then the journal file needs to be ** sync()ed before any data is written to database file page needSyncPgno. ** Currently, no such page exists in the page-cache and the ** Pager.aInJournal bit has been set. This needs to be remedied by loading ** the page into the pager-cache and setting the PgHdr.needSync flag. ** ** The sqlite3pager_get() call may cause the journal to sync. So make ** sure the Pager.needSync flag is set too. */ int rc; void *pNeedSync; assert( pPager->needSync ); rc = sqlite3pager_get(pPager, needSyncPgno, &pNeedSync); if( rc!=SQLITE_OK ) return rc; pPager->needSync = 1; DATA_TO_PGHDR(pNeedSync)->needSync = 1; DATA_TO_PGHDR(pNeedSync)->inJournal = 1; makeDirty(DATA_TO_PGHDR(pNeedSync)); sqlite3pager_unref(pNeedSync); } return SQLITE_OK; } #endif |
︙ | ︙ |
Changes to test/aggerror.test.
︙ | ︙ | |||
9 10 11 12 13 14 15 | # #*********************************************************************** # This file implements regression tests for SQLite library. # # This file implements tests for calling sqlite3_result_error() # from within an aggregate function implementation. # | | | | | | | | | 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 | # #*********************************************************************** # This file implements regression tests for SQLite library. # # This file implements tests for calling sqlite3_result_error() # from within an aggregate function implementation. # # $Id: aggerror.test,v 1.3 2006/05/03 23:34:06 drh Exp $ set testdir [file dirname $argv0] source $testdir/tester.tcl # Add the x_count aggregate function to the database handle. # x_count will error out if its input is 40 or 41 or if its # final results is 42. Make sure that such errors are handled # appropriately. # do_test aggerror-1.1 { set DB [sqlite3_connection_pointer db] sqlite3_create_aggregate $DB execsql { CREATE TABLE t1(a); INSERT INTO t1 VALUES(1); INSERT INTO t1 VALUES(2); INSERT INTO t1 SELECT a+2 FROM t1; INSERT INTO t1 SELECT a+4 FROM t1; INSERT INTO t1 SELECT a+8 FROM t1; INSERT INTO t1 SELECT a+16 FROM t1; INSERT INTO t1 SELECT a+32 FROM t1 ORDER BY a LIMIT 7; SELECT x_count(*) FROM t1; } } {39} do_test aggerror-1.2 { execsql { INSERT INTO t1 VALUES(40); SELECT x_count(*) FROM t1; } } {40} do_test aggerror-1.3 { catchsql { SELECT x_count(a) FROM t1; } } {1 {value of 40 handed to x_count}} ifcapable utf16 { do_test aggerror-1.4 { execsql { UPDATE t1 SET a=41 WHERE a=40 } catchsql { SELECT x_count(a) FROM t1; } } {1 abc} } do_test aggerror-1.5 { execsql { SELECT x_count(*) FROM t1 } } 40 do_test aggerror-1.6 { execsql { INSERT INTO t1 VALUES(40); INSERT INTO t1 VALUES(42); } catchsql { SELECT x_count(*) FROM t1; } } {1 {x_count totals to 42}} finish_test |