Many hyperlinks are disabled.
Use anonymous login
to enable hyperlinks.
Overview
Comment: | Postpone I/O associated with TEMP files for as long as possible, with the hope that the I/O can ultimately be avoided completely. |
---|---|
Downloads: | Tarball | ZIP archive |
Timelines: | family | ancestors | descendants | both | trunk |
Files: | files | file ages | folders |
SHA1: |
9d0a5ae00273686ea35b43bc2ffaa877 |
User & Date: | drh 2016-04-29 15:39:48.423 |
Context
2016-04-29
| ||
20:30 | Fix the temporary directory search algorithm for unix so that it fails gracefully even if all candidate directories are inaccessible. This fixes a bug that was introduced by check-in [9b8fec60d8e]. (check-in: 614bb709d3 user: drh tags: trunk) | |
16:01 | Merge the latest enhancements from trunk. (check-in: 91e5c07eaf user: drh tags: begin-concurrent) | |
15:52 | Merge enhancements from trunk, and in particular the TEMP file deferred I/O enhancements. (check-in: 81b76901e6 user: drh tags: apple-osx) | |
15:39 | Postpone I/O associated with TEMP files for as long as possible, with the hope that the I/O can ultimately be avoided completely. (check-in: 9d0a5ae002 user: drh tags: trunk) | |
14:12 | Fix test script temptable2.test so that it works with the "inmemory_journal" and "journaltest" permutations. (Closed-Leaf check-in: b7bec7f2d3 user: dan tags: tempfiles-25) | |
11:33 | Modify the permutations.test script so as to set any permutation specific configuration values before running each individual test script. Fix a mostly harmless buffer overread in the sessions module. (check-in: 4cbd502454 user: dan tags: trunk) | |
Changes
Changes to src/pager.c.
︙ | ︙ | |||
868 869 870 871 872 873 874 875 876 877 878 879 880 881 | ** either SQLITE_IOERR or SQLITE_FULL during rollback or while finalizing ** a journal file. (although the in-memory journal implementation may ** return SQLITE_IOERR_NOMEM while the journal file is being written). It ** is therefore not possible for an in-memory pager to enter the ERROR ** state. */ if( MEMDB ){ assert( p->noSync ); assert( p->journalMode==PAGER_JOURNALMODE_OFF || p->journalMode==PAGER_JOURNALMODE_MEMORY ); assert( p->eState!=PAGER_ERROR && p->eState!=PAGER_OPEN ); assert( pagerUseWal(p)==0 ); } | > | 868 869 870 871 872 873 874 875 876 877 878 879 880 881 882 | ** either SQLITE_IOERR or SQLITE_FULL during rollback or while finalizing ** a journal file. (although the in-memory journal implementation may ** return SQLITE_IOERR_NOMEM while the journal file is being written). It ** is therefore not possible for an in-memory pager to enter the ERROR ** state. */ if( MEMDB ){ assert( !isOpen(p->fd) ); assert( p->noSync ); assert( p->journalMode==PAGER_JOURNALMODE_OFF || p->journalMode==PAGER_JOURNALMODE_MEMORY ); assert( p->eState!=PAGER_ERROR && p->eState!=PAGER_OPEN ); assert( pagerUseWal(p)==0 ); } |
︙ | ︙ | |||
954 955 956 957 958 959 960 | case PAGER_ERROR: /* There must be at least one outstanding reference to the pager if ** in ERROR state. Otherwise the pager should have already dropped ** back to OPEN state. */ assert( pPager->errCode!=SQLITE_OK ); | | | 955 956 957 958 959 960 961 962 963 964 965 966 967 968 969 | case PAGER_ERROR: /* There must be at least one outstanding reference to the pager if ** in ERROR state. Otherwise the pager should have already dropped ** back to OPEN state. */ assert( pPager->errCode!=SQLITE_OK ); assert( sqlite3PcacheRefCount(pPager->pPCache)>0 || pPager->tempFile ); break; } return 1; } #endif /* ifndef NDEBUG */ |
︙ | ︙ | |||
1166 1167 1168 1169 1170 1171 1172 1173 1174 1175 1176 1177 1178 1179 | if( 0==(dc&(SQLITE_IOCAP_ATOMIC|(szPage>>8)) || nSector>szPage) ){ return 0; } } return JOURNAL_HDR_SZ(pPager) + JOURNAL_PG_SZ(pPager); } #endif /* ** If SQLITE_CHECK_PAGES is defined then we do some sanity checking ** on the cache using a hash function. This is used for testing ** and debugging only. */ | > > | 1167 1168 1169 1170 1171 1172 1173 1174 1175 1176 1177 1178 1179 1180 1181 1182 | if( 0==(dc&(SQLITE_IOCAP_ATOMIC|(szPage>>8)) || nSector>szPage) ){ return 0; } } return JOURNAL_HDR_SZ(pPager) + JOURNAL_PG_SZ(pPager); } #else # define jrnlBufferSize(x) 0 #endif /* ** If SQLITE_CHECK_PAGES is defined then we do some sanity checking ** on the cache using a hash function. This is used for testing ** and debugging only. */ |
︙ | ︙ | |||
1814 1815 1816 1817 1818 1819 1820 1821 | } /* If Pager.errCode is set, the contents of the pager cache cannot be ** trusted. Now that there are no outstanding references to the pager, ** it can safely move back to PAGER_OPEN state. This happens in both ** normal and exclusive-locking mode. */ if( pPager->errCode ){ | > | | | | > | > > | 1817 1818 1819 1820 1821 1822 1823 1824 1825 1826 1827 1828 1829 1830 1831 1832 1833 1834 1835 1836 1837 1838 1839 1840 1841 | } /* If Pager.errCode is set, the contents of the pager cache cannot be ** trusted. Now that there are no outstanding references to the pager, ** it can safely move back to PAGER_OPEN state. This happens in both ** normal and exclusive-locking mode. */ assert( pPager->errCode==SQLITE_OK || !MEMDB ); if( pPager->errCode ){ if( pPager->tempFile==0 ){ pager_reset(pPager); pPager->changeCountDone = 0; pPager->eState = PAGER_OPEN; }else{ pPager->eState = (isOpen(pPager->jfd) ? PAGER_OPEN : PAGER_READER); } if( USEFETCH(pPager) ) sqlite3OsUnfetch(pPager->fd, 0, 0); pPager->errCode = SQLITE_OK; } pPager->journalOff = 0; pPager->journalHdr = 0; pPager->setMaster = 0; } |
︙ | ︙ | |||
1863 1864 1865 1866 1867 1868 1869 1870 1871 1872 1873 1874 1875 1876 | pPager->errCode = rc; pPager->eState = PAGER_ERROR; } return rc; } static int pager_truncate(Pager *pPager, Pgno nPage); /* ** This routine ends a transaction. A transaction is usually ended by ** either a COMMIT or a ROLLBACK operation. This routine may be called ** after rollback of a hot-journal, or if an error occurs while opening ** the journal file or writing the very first journal-header of a ** database transaction. | > > > > > > > > > > > > > > > > > > > | 1870 1871 1872 1873 1874 1875 1876 1877 1878 1879 1880 1881 1882 1883 1884 1885 1886 1887 1888 1889 1890 1891 1892 1893 1894 1895 1896 1897 1898 1899 1900 1901 1902 | pPager->errCode = rc; pPager->eState = PAGER_ERROR; } return rc; } static int pager_truncate(Pager *pPager, Pgno nPage); /* ** The write transaction open on the pager passed as the only argument is ** being committed. This function returns true if all dirty pages should ** be flushed to disk, or false otherwise. Pages should be flushed to disk ** unless one of the following is true: ** ** * The db is an in-memory database. ** ** * The db is a temporary database and the db file has not been opened. ** ** * The db is a temporary database and the cache contains less than ** C/4 dirty pages, where C is the configured cache-size. */ static int pagerFlushOnCommit(Pager *pPager){ if( pPager->tempFile==0 ) return 1; if( !isOpen(pPager->fd) ) return 0; return (sqlite3PCachePercentDirty(pPager->pPCache)>=25); } /* ** This routine ends a transaction. A transaction is usually ended by ** either a COMMIT or a ROLLBACK operation. This routine may be called ** after rollback of a hot-journal, or if an error occurs while opening ** the journal file or writing the very first journal-header of a ** database transaction. |
︙ | ︙ | |||
2002 2003 2004 2005 2006 2007 2008 | } } #endif sqlite3BitvecDestroy(pPager->pInJournal); pPager->pInJournal = 0; pPager->nRec = 0; | > | > > > | 2028 2029 2030 2031 2032 2033 2034 2035 2036 2037 2038 2039 2040 2041 2042 2043 2044 2045 2046 | } } #endif sqlite3BitvecDestroy(pPager->pInJournal); pPager->pInJournal = 0; pPager->nRec = 0; if( MEMDB || pagerFlushOnCommit(pPager) ){ sqlite3PcacheCleanAll(pPager->pPCache); }else{ sqlite3PcacheClearWritable(pPager->pPCache); } sqlite3PcacheTruncate(pPager->pPCache, pPager->dbSize); if( pagerUseWal(pPager) ){ /* Drop the WAL write-lock, if any. Also, if the connection was in ** locking_mode=exclusive mode but is no longer, drop the EXCLUSIVE ** lock held on the database file. */ |
︙ | ︙ | |||
2287 2288 2289 2290 2291 2292 2293 | */ if( pagerUseWal(pPager) ){ pPg = 0; }else{ pPg = sqlite3PagerLookup(pPager, pgno); } assert( pPg || !MEMDB ); | | | 2317 2318 2319 2320 2321 2322 2323 2324 2325 2326 2327 2328 2329 2330 2331 | */ if( pagerUseWal(pPager) ){ pPg = 0; }else{ pPg = sqlite3PagerLookup(pPager, pgno); } assert( pPg || !MEMDB ); assert( pPager->eState!=PAGER_OPEN || pPg==0 || pPager->tempFile ); PAGERTRACE(("PLAYBACK %d page %d hash(%08x) %s\n", PAGERID(pPager), pgno, pager_datahash(pPager->pageSize, (u8*)aData), (isMainJrnl?"main-journal":"sub-journal") )); if( isMainJrnl ){ isSynced = pPager->noSync || (*pOffset <= pPager->journalHdr); }else{ |
︙ | ︙ | |||
2370 2371 2372 2373 2374 2375 2376 2377 2378 | ** already in the journal file (recorded in Pager.pInJournal) and ** the PGHDR_NEED_SYNC flag is cleared, if the page is written to ** again within this transaction, it will be marked as dirty but ** the PGHDR_NEED_SYNC flag will not be set. It could then potentially ** be written out into the database file before its journal file ** segment is synced. If a crash occurs during or following this, ** database corruption may ensue. */ assert( !pagerUseWal(pPager) ); | > > > > | | 2400 2401 2402 2403 2404 2405 2406 2407 2408 2409 2410 2411 2412 2413 2414 2415 2416 2417 2418 2419 2420 | ** already in the journal file (recorded in Pager.pInJournal) and ** the PGHDR_NEED_SYNC flag is cleared, if the page is written to ** again within this transaction, it will be marked as dirty but ** the PGHDR_NEED_SYNC flag will not be set. It could then potentially ** be written out into the database file before its journal file ** segment is synced. If a crash occurs during or following this, ** database corruption may ensue. ** ** Update: Another exception is for temp files that are not ** in-memory databases. In this case the page may have been dirty ** at the start of the transaction. */ assert( !pagerUseWal(pPager) ); if( pPager->tempFile==0 ) sqlite3PcacheMakeClean(pPg); } pager_set_pagehash(pPg); /* If this was page 1, then restore the value of Pager.dbFileVers. ** Do this before any decoding. */ if( pgno==1 ){ memcpy(&pPager->dbFileVers, &((u8*)pData)[24],sizeof(pPager->dbFileVers)); |
︙ | ︙ | |||
3164 3165 3166 3167 3168 3169 3170 3171 3172 3173 3174 3175 3176 3177 | ** function returns zero if the WAL is not open (i.e. Pager.pWal==0), or ** if the database size is not available. The database size is not ** available from the WAL sub-system if the log file is empty or ** contains no valid committed transactions. */ assert( pPager->eState==PAGER_OPEN ); assert( pPager->eLock>=SHARED_LOCK ); nPage = sqlite3WalDbsize(pPager->pWal); /* If the number of pages in the database is not available from the ** WAL sub-system, determine the page counte based on the size of ** the database file. If the size of the database file is not an ** integer multiple of the page-size, round up the result. */ | > > | < < | | | < | 3198 3199 3200 3201 3202 3203 3204 3205 3206 3207 3208 3209 3210 3211 3212 3213 3214 3215 3216 3217 3218 3219 3220 3221 3222 3223 3224 3225 | ** function returns zero if the WAL is not open (i.e. Pager.pWal==0), or ** if the database size is not available. The database size is not ** available from the WAL sub-system if the log file is empty or ** contains no valid committed transactions. */ assert( pPager->eState==PAGER_OPEN ); assert( pPager->eLock>=SHARED_LOCK ); assert( isOpen(pPager->fd) ); assert( pPager->tempFile==0 ); nPage = sqlite3WalDbsize(pPager->pWal); /* If the number of pages in the database is not available from the ** WAL sub-system, determine the page counte based on the size of ** the database file. If the size of the database file is not an ** integer multiple of the page-size, round up the result. */ if( nPage==0 && ALWAYS(isOpen(pPager->fd)) ){ i64 n = 0; /* Size of db file in bytes */ int rc = sqlite3OsFileSize(pPager->fd, &n); if( rc!=SQLITE_OK ){ return rc; } nPage = (Pgno)((n+pPager->pageSize-1) / pPager->pageSize); } /* If the current number of pages in the file is greater than the ** configured maximum pager number, increase the allowed limit so ** that the file can be read. |
︙ | ︙ | |||
4254 4255 4256 4257 4258 4259 4260 | ** be obtained, SQLITE_BUSY is returned. */ static int pager_write_pagelist(Pager *pPager, PgHdr *pList){ int rc = SQLITE_OK; /* Return code */ /* This function is only called for rollback pagers in WRITER_DBMOD state. */ assert( !pagerUseWal(pPager) ); | | > | 4287 4288 4289 4290 4291 4292 4293 4294 4295 4296 4297 4298 4299 4300 4301 4302 4303 | ** be obtained, SQLITE_BUSY is returned. */ static int pager_write_pagelist(Pager *pPager, PgHdr *pList){ int rc = SQLITE_OK; /* Return code */ /* This function is only called for rollback pagers in WRITER_DBMOD state. */ assert( !pagerUseWal(pPager) ); assert( pPager->tempFile || pPager->eState==PAGER_WRITER_DBMOD ); assert( pPager->eLock==EXCLUSIVE_LOCK ); assert( isOpen(pPager->fd) || pList->pDirty==0 ); /* If the file is a temp-file has not yet been opened, open it now. It ** is not possible for rc to be other than SQLITE_OK if this branch ** is taken, as pager_wait_on_lock() is a no-op for temp-files. */ if( !isOpen(pPager->fd) ){ assert( pPager->tempFile && rc==SQLITE_OK ); |
︙ | ︙ | |||
4923 4924 4925 4926 4927 4928 4929 4930 4931 4932 4933 4934 4935 4936 | ** in fact there is none. This results in a false-positive which will ** be dealt with by the playback routine. Ticket #3883. */ rc = sqlite3OsCheckReservedLock(pPager->fd, &locked); if( rc==SQLITE_OK && !locked ){ Pgno nPage; /* Number of pages in database file */ rc = pagerPagecount(pPager, &nPage); if( rc==SQLITE_OK ){ /* If the database is zero pages in size, that means that either (1) the ** journal is a remnant from a prior database with the same name where ** the database file but not the journal was deleted, or (2) the initial ** transaction that populates a new database is being rolled back. ** In either case, the journal file can be deleted. However, take care | > | 4957 4958 4959 4960 4961 4962 4963 4964 4965 4966 4967 4968 4969 4970 4971 | ** in fact there is none. This results in a false-positive which will ** be dealt with by the playback routine. Ticket #3883. */ rc = sqlite3OsCheckReservedLock(pPager->fd, &locked); if( rc==SQLITE_OK && !locked ){ Pgno nPage; /* Number of pages in database file */ assert( pPager->tempFile==0 ); rc = pagerPagecount(pPager, &nPage); if( rc==SQLITE_OK ){ /* If the database is zero pages in size, that means that either (1) the ** journal is a remnant from a prior database with the same name where ** the database file but not the journal was deleted, or (2) the initial ** transaction that populates a new database is being rolled back. ** In either case, the journal file can be deleted. However, take care |
︙ | ︙ | |||
5015 5016 5017 5018 5019 5020 5021 | */ int sqlite3PagerSharedLock(Pager *pPager){ int rc = SQLITE_OK; /* Return code */ /* This routine is only called from b-tree and only when there are no ** outstanding pages. This implies that the pager state should either ** be OPEN or READER. READER is only possible if the pager is or was in | | < | > | 5050 5051 5052 5053 5054 5055 5056 5057 5058 5059 5060 5061 5062 5063 5064 5065 5066 5067 5068 5069 5070 5071 5072 5073 5074 | */ int sqlite3PagerSharedLock(Pager *pPager){ int rc = SQLITE_OK; /* Return code */ /* This routine is only called from b-tree and only when there are no ** outstanding pages. This implies that the pager state should either ** be OPEN or READER. READER is only possible if the pager is or was in ** exclusive access mode. */ assert( sqlite3PcacheRefCount(pPager->pPCache)==0 ); assert( assert_pager_state(pPager) ); assert( pPager->eState==PAGER_OPEN || pPager->eState==PAGER_READER ); assert( pPager->errCode==SQLITE_OK ); if( !pagerUseWal(pPager) && pPager->eState==PAGER_OPEN ){ int bHotJournal = 1; /* True if there exists a hot journal-file */ assert( !MEMDB ); assert( pPager->tempFile==0 || pPager->eLock==EXCLUSIVE_LOCK ); rc = pager_wait_on_lock(pPager, SHARED_LOCK); if( rc!=SQLITE_OK ){ assert( pPager->eLock==NO_LOCK || pPager->eLock==UNKNOWN_LOCK ); goto failed; } |
︙ | ︙ | |||
5111 5112 5113 5114 5115 5116 5117 | ** probably did not sync it and we are required to always sync ** the journal before playing it back. */ if( isOpen(pPager->jfd) ){ assert( rc==SQLITE_OK ); rc = pagerSyncHotJournal(pPager); if( rc==SQLITE_OK ){ | | | 5146 5147 5148 5149 5150 5151 5152 5153 5154 5155 5156 5157 5158 5159 5160 | ** probably did not sync it and we are required to always sync ** the journal before playing it back. */ if( isOpen(pPager->jfd) ){ assert( rc==SQLITE_OK ); rc = pagerSyncHotJournal(pPager); if( rc==SQLITE_OK ){ rc = pager_playback(pPager, !pPager->tempFile); pPager->eState = PAGER_OPEN; } }else if( !pPager->exclusiveMode ){ pagerUnlockDb(pPager, SHARED_LOCK); } if( rc!=SQLITE_OK ){ |
︙ | ︙ | |||
5207 5208 5209 5210 5211 5212 5213 | } if( pagerUseWal(pPager) ){ assert( rc==SQLITE_OK ); rc = pagerBeginReadTransaction(pPager); } | | | 5242 5243 5244 5245 5246 5247 5248 5249 5250 5251 5252 5253 5254 5255 5256 | } if( pagerUseWal(pPager) ){ assert( rc==SQLITE_OK ); rc = pagerBeginReadTransaction(pPager); } if( pPager->tempFile==0 && pPager->eState==PAGER_OPEN && rc==SQLITE_OK ){ rc = pagerPagecount(pPager, &pPager->dbSize); } failed: if( rc!=SQLITE_OK ){ assert( !MEMDB ); pager_unlock(pPager); |
︙ | ︙ | |||
5340 5341 5342 5343 5344 5345 5346 | void *pData = 0; rc = sqlite3OsFetch(pPager->fd, (i64)(pgno-1) * pPager->pageSize, pPager->pageSize, &pData ); if( rc==SQLITE_OK && pData ){ | | | 5375 5376 5377 5378 5379 5380 5381 5382 5383 5384 5385 5386 5387 5388 5389 | void *pData = 0; rc = sqlite3OsFetch(pPager->fd, (i64)(pgno-1) * pPager->pageSize, pPager->pageSize, &pData ); if( rc==SQLITE_OK && pData ){ if( pPager->eState>PAGER_READER || pPager->tempFile ){ pPg = sqlite3PagerLookup(pPager, pgno); } if( pPg==0 ){ rc = pagerAcquireMapPage(pPager, pgno, pData, &pPg); }else{ sqlite3OsUnfetch(pPager->fd, (i64)(pgno-1)*pPager->pageSize, pData); } |
︙ | ︙ | |||
5407 5408 5409 5410 5411 5412 5413 | /* The maximum page number is 2^31. Return SQLITE_CORRUPT if a page ** number greater than this, or the unused locking-page, is requested. */ if( pgno>PAGER_MAX_PGNO || pgno==PAGER_MJ_PGNO(pPager) ){ rc = SQLITE_CORRUPT_BKPT; goto pager_acquire_err; } | > | | 5442 5443 5444 5445 5446 5447 5448 5449 5450 5451 5452 5453 5454 5455 5456 5457 | /* The maximum page number is 2^31. Return SQLITE_CORRUPT if a page ** number greater than this, or the unused locking-page, is requested. */ if( pgno>PAGER_MAX_PGNO || pgno==PAGER_MJ_PGNO(pPager) ){ rc = SQLITE_CORRUPT_BKPT; goto pager_acquire_err; } assert( !isOpen(pPager->fd) || !MEMDB ); if( !isOpen(pPager->fd) || pPager->dbSize<pgno || noContent ){ if( pgno>pPager->mxPgno ){ rc = SQLITE_FULL; goto pager_acquire_err; } if( noContent ){ /* Failure to set the bits in the InJournal bit-vectors is benign. ** It merely means that we might do some extra work to journal a |
︙ | ︙ | |||
5549 5550 5551 5552 5553 5554 5555 | } /* Open the journal file if it is not already open. */ if( !isOpen(pPager->jfd) ){ if( pPager->journalMode==PAGER_JOURNALMODE_MEMORY ){ sqlite3MemJournalOpen(pPager->jfd); }else{ | < | > > | | > > | | | > < | | < < < | 5585 5586 5587 5588 5589 5590 5591 5592 5593 5594 5595 5596 5597 5598 5599 5600 5601 5602 5603 5604 5605 5606 5607 5608 5609 5610 5611 5612 5613 5614 5615 5616 | } /* Open the journal file if it is not already open. */ if( !isOpen(pPager->jfd) ){ if( pPager->journalMode==PAGER_JOURNALMODE_MEMORY ){ sqlite3MemJournalOpen(pPager->jfd); }else{ int flags = SQLITE_OPEN_READWRITE|SQLITE_OPEN_CREATE; int nSpill; if( pPager->tempFile ){ flags |= (SQLITE_OPEN_DELETEONCLOSE|SQLITE_OPEN_TEMP_JOURNAL); nSpill = sqlite3Config.nStmtSpill; }else{ flags |= SQLITE_OPEN_MAIN_JOURNAL; nSpill = jrnlBufferSize(pPager); } /* Verify that the database still has the same name as it did when ** it was originally opened. */ rc = databaseIsUnmoved(pPager); if( rc==SQLITE_OK ){ rc = sqlite3JournalOpen ( pVfs, pPager->zJournal, pPager->jfd, flags, nSpill ); } } assert( rc!=SQLITE_OK || isOpen(pPager->jfd) ); } /* Write the first journal header to the journal file and open |
︙ | ︙ | |||
5937 5938 5939 5940 5941 5942 5943 5944 5945 5946 5947 5948 5949 5950 | assert( assert_pager_state(pPager) ); if( pPager->errCode ){ return pPager->errCode; }else if( (pPg->flags & PGHDR_WRITEABLE)!=0 && pPager->dbSize>=pPg->pgno ){ if( pPager->nSavepoint ) return subjournalPageIfRequired(pPg); return SQLITE_OK; }else if( pPager->sectorSize > (u32)pPager->pageSize ){ return pagerWriteLargeSector(pPg); }else{ return pager_write(pPg); } } /* | > | 5973 5974 5975 5976 5977 5978 5979 5980 5981 5982 5983 5984 5985 5986 5987 | assert( assert_pager_state(pPager) ); if( pPager->errCode ){ return pPager->errCode; }else if( (pPg->flags & PGHDR_WRITEABLE)!=0 && pPager->dbSize>=pPg->pgno ){ if( pPager->nSavepoint ) return subjournalPageIfRequired(pPg); return SQLITE_OK; }else if( pPager->sectorSize > (u32)pPager->pageSize ){ assert( pPager->tempFile==0 ); return pagerWriteLargeSector(pPg); }else{ return pager_write(pPg); } } /* |
︙ | ︙ | |||
6168 6169 6170 6171 6172 6173 6174 6175 6176 6177 6178 6179 6180 6181 | || pPager->eState==PAGER_WRITER_DBMOD || pPager->eState==PAGER_ERROR ); assert( assert_pager_state(pPager) ); /* If a prior error occurred, report that error again. */ if( NEVER(pPager->errCode) ) return pPager->errCode; PAGERTRACE(("DATABASE SYNC: File=%s zMaster=%s nSize=%d\n", pPager->zFilename, zMaster, pPager->dbSize)); /* If no database changes have been made, return early. */ if( pPager->eState<PAGER_WRITER_CACHEMOD ) return SQLITE_OK; | > > > | > > | < | 6205 6206 6207 6208 6209 6210 6211 6212 6213 6214 6215 6216 6217 6218 6219 6220 6221 6222 6223 6224 6225 6226 6227 6228 6229 6230 6231 6232 6233 6234 | || pPager->eState==PAGER_WRITER_DBMOD || pPager->eState==PAGER_ERROR ); assert( assert_pager_state(pPager) ); /* If a prior error occurred, report that error again. */ if( NEVER(pPager->errCode) ) return pPager->errCode; /* Provide the ability to easily simulate an I/O error during testing */ if( (rc = sqlite3FaultSim(400))!=SQLITE_OK ) return rc; PAGERTRACE(("DATABASE SYNC: File=%s zMaster=%s nSize=%d\n", pPager->zFilename, zMaster, pPager->dbSize)); /* If no database changes have been made, return early. */ if( pPager->eState<PAGER_WRITER_CACHEMOD ) return SQLITE_OK; assert( MEMDB==0 || pPager->tempFile ); assert( isOpen(pPager->fd) || pPager->tempFile ); if( 0==pagerFlushOnCommit(pPager) ){ /* If this is an in-memory db, or no pages have been written to, or this ** function has already been called, it is mostly a no-op. However, any ** backup in progress needs to be restarted. */ sqlite3BackupRestart(pPager->pBackup); }else{ if( pagerUseWal(pPager) ){ PgHdr *pList = sqlite3PcacheDirtyList(pPager->pPCache); PgHdr *pPageOne = 0; if( pList==0 ){ /* Must have at least one page for the WAL commit flag. |
︙ | ︙ | |||
6518 6519 6520 6521 6522 6523 6524 | *pnVal += pPager->aStat[eStat - SQLITE_DBSTATUS_CACHE_HIT]; if( reset ){ pPager->aStat[eStat - SQLITE_DBSTATUS_CACHE_HIT] = 0; } } /* | | | | 6559 6560 6561 6562 6563 6564 6565 6566 6567 6568 6569 6570 6571 6572 6573 6574 6575 6576 | *pnVal += pPager->aStat[eStat - SQLITE_DBSTATUS_CACHE_HIT]; if( reset ){ pPager->aStat[eStat - SQLITE_DBSTATUS_CACHE_HIT] = 0; } } /* ** Return true if this is an in-memory or temp-file backed pager. */ int sqlite3PagerIsMemdb(Pager *pPager){ return pPager->tempFile; } /* ** Check that there are at least nSavepoint savepoints open. If there are ** currently less than nSavepoints open, then open one or more savepoints ** to make up the difference. If the number of savepoints is already ** equal to nSavepoint, then this function is a no-op. |
︙ | ︙ | |||
6801 6802 6803 6804 6805 6806 6807 | || pPager->eState==PAGER_WRITER_DBMOD ); assert( assert_pager_state(pPager) ); /* In order to be able to rollback, an in-memory database must journal ** the page we are moving from. */ | | | 6842 6843 6844 6845 6846 6847 6848 6849 6850 6851 6852 6853 6854 6855 6856 | || pPager->eState==PAGER_WRITER_DBMOD ); assert( assert_pager_state(pPager) ); /* In order to be able to rollback, an in-memory database must journal ** the page we are moving from. */ if( pPager->tempFile ){ rc = sqlite3PagerWrite(pPg); if( rc ) return rc; } /* If the page being moved is dirty and has not been saved by the latest ** savepoint, then save the current contents of the page into the ** sub-journal now. This is required to handle the following scenario: |
︙ | ︙ | |||
6858 6859 6860 6861 6862 6863 6864 | ** for the page moved there. */ pPg->flags &= ~PGHDR_NEED_SYNC; pPgOld = sqlite3PagerLookup(pPager, pgno); assert( !pPgOld || pPgOld->nRef==1 ); if( pPgOld ){ pPg->flags |= (pPgOld->flags&PGHDR_NEED_SYNC); | | | | 6899 6900 6901 6902 6903 6904 6905 6906 6907 6908 6909 6910 6911 6912 6913 6914 6915 6916 6917 6918 6919 6920 6921 6922 6923 6924 6925 6926 6927 6928 6929 6930 | ** for the page moved there. */ pPg->flags &= ~PGHDR_NEED_SYNC; pPgOld = sqlite3PagerLookup(pPager, pgno); assert( !pPgOld || pPgOld->nRef==1 ); if( pPgOld ){ pPg->flags |= (pPgOld->flags&PGHDR_NEED_SYNC); if( pPager->tempFile ){ /* Do not discard pages from an in-memory database since we might ** need to rollback later. Just move the page out of the way. */ sqlite3PcacheMove(pPgOld, pPager->dbSize+1); }else{ sqlite3PcacheDrop(pPgOld); } } origPgno = pPg->pgno; sqlite3PcacheMove(pPg, pgno); sqlite3PcacheMakeDirty(pPg); /* For an in-memory database, make sure the original page continues ** to exist, in case the transaction needs to roll back. Use pPgOld ** as the original page since it has already been allocated. */ if( pPager->tempFile ){ assert( pPgOld ); sqlite3PcacheMove(pPgOld, origPgno); sqlite3PagerUnrefNotNull(pPgOld); } if( needSyncPgno ){ /* If needSyncPgno is non-zero, then the journal file needs to be |
︙ | ︙ | |||
7128 7129 7130 7131 7132 7133 7134 | } #ifndef SQLITE_OMIT_VACUUM /* ** Unless this is an in-memory or temporary database, clear the pager cache. */ void sqlite3PagerClearCache(Pager *pPager){ | > | | 7169 7170 7171 7172 7173 7174 7175 7176 7177 7178 7179 7180 7181 7182 7183 7184 | } #ifndef SQLITE_OMIT_VACUUM /* ** Unless this is an in-memory or temporary database, clear the pager cache. */ void sqlite3PagerClearCache(Pager *pPager){ assert( MEMDB==0 || pPager->tempFile ); if( pPager->tempFile==0 ) pager_reset(pPager); } #endif #ifndef SQLITE_OMIT_WAL /* ** This function is called when the user invokes "PRAGMA wal_checkpoint", ** "PRAGMA wal_blocking_checkpoint" or calls the sqlite3_wal_checkpoint() |
︙ | ︙ |
Changes to src/pcache.c.
︙ | ︙ | |||
250 251 252 253 254 255 256 | assert( createFlag==0 || pCache->eCreate==eCreate ); assert( createFlag==0 || eCreate==1+(!pCache->bPurgeable||!pCache->pDirty) ); return sqlite3GlobalConfig.pcache2.xFetch(pCache->pCache, pgno, eCreate); } /* ** If the sqlite3PcacheFetch() routine is unable to allocate a new | | | 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 | assert( createFlag==0 || pCache->eCreate==eCreate ); assert( createFlag==0 || eCreate==1+(!pCache->bPurgeable||!pCache->pDirty) ); return sqlite3GlobalConfig.pcache2.xFetch(pCache->pCache, pgno, eCreate); } /* ** If the sqlite3PcacheFetch() routine is unable to allocate a new ** page because no clean pages are available for reuse and the cache ** size limit has been reached, then this routine can be invoked to ** try harder to allocate a page. This routine might invoke the stress ** callback to spill dirty pages to the journal. It will then try to ** allocate the new page and will only fail to allocate a new page on ** an OOM error. ** ** This routine should be invoked only after sqlite3PcacheFetch() fails. |
︙ | ︙ | |||
434 435 436 437 438 439 440 441 442 443 444 445 446 447 | */ void sqlite3PcacheCleanAll(PCache *pCache){ PgHdr *p; while( (p = pCache->pDirty)!=0 ){ sqlite3PcacheMakeClean(p); } } /* ** Clear the PGHDR_NEED_SYNC flag from all dirty pages. */ void sqlite3PcacheClearSyncFlags(PCache *pCache){ PgHdr *p; for(p=pCache->pDirty; p; p=p->pDirtyNext){ | > > > > > > > > > > > | 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 | */ void sqlite3PcacheCleanAll(PCache *pCache){ PgHdr *p; while( (p = pCache->pDirty)!=0 ){ sqlite3PcacheMakeClean(p); } } /* ** Clear the PGHDR_NEED_SYNC and PGHDR_WRITEABLE flag from all dirty pages. */ void sqlite3PcacheClearWritable(PCache *pCache){ PgHdr *p; for(p=pCache->pDirty; p; p=p->pDirtyNext){ p->flags &= ~(PGHDR_NEED_SYNC|PGHDR_WRITEABLE); } pCache->pSynced = pCache->pDirtyTail; } /* ** Clear the PGHDR_NEED_SYNC flag from all dirty pages. */ void sqlite3PcacheClearSyncFlags(PCache *pCache){ PgHdr *p; for(p=pCache->pDirty; p; p=p->pDirtyNext){ |
︙ | ︙ | |||
480 481 482 483 484 485 486 | for(p=pCache->pDirty; p; p=pNext){ pNext = p->pDirtyNext; /* This routine never gets call with a positive pgno except right ** after sqlite3PcacheCleanAll(). So if there are dirty pages, ** it must be that pgno==0. */ assert( p->pgno>0 ); | | | 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 | for(p=pCache->pDirty; p; p=pNext){ pNext = p->pDirtyNext; /* This routine never gets call with a positive pgno except right ** after sqlite3PcacheCleanAll(). So if there are dirty pages, ** it must be that pgno==0. */ assert( p->pgno>0 ); if( p->pgno>pgno ){ assert( p->flags&PGHDR_DIRTY ); sqlite3PcacheMakeClean(p); } } if( pgno==0 && pCache->nRefSum ){ sqlite3_pcache_page *pPage1; pPage1 = sqlite3GlobalConfig.pcache2.xFetch(pCache->pCache,1,0); |
︙ | ︙ | |||
671 672 673 674 675 676 677 678 679 680 681 682 683 684 | /* ** Return the size of the header added by this middleware layer ** in the page-cache hierarchy. */ int sqlite3HeaderSizePcache(void){ return ROUND8(sizeof(PgHdr)); } #if defined(SQLITE_CHECK_PAGES) || defined(SQLITE_DEBUG) /* ** For all dirty pages currently in the cache, invoke the specified ** callback. This is only used if the SQLITE_CHECK_PAGES macro is ** defined. */ | > > > > > > > > > > > | 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 | /* ** Return the size of the header added by this middleware layer ** in the page-cache hierarchy. */ int sqlite3HeaderSizePcache(void){ return ROUND8(sizeof(PgHdr)); } /* ** Return the number of dirty pages currently in the cache, as a percentage ** of the configured cache size. */ int sqlite3PCachePercentDirty(PCache *pCache){ PgHdr *pDirty; int nDirty = 0; int nCache = numberOfCachePages(pCache); for(pDirty=pCache->pDirty; pDirty; pDirty=pDirty->pDirtyNext) nDirty++; return nCache ? (int)(((i64)nDirty * 100) / nCache) : 0; } #if defined(SQLITE_CHECK_PAGES) || defined(SQLITE_DEBUG) /* ** For all dirty pages currently in the cache, invoke the specified ** callback. This is only used if the SQLITE_CHECK_PAGES macro is ** defined. */ |
︙ | ︙ |
Changes to src/pcache.h.
︙ | ︙ | |||
95 96 97 98 99 100 101 102 103 104 105 106 107 108 | PgHdr *sqlite3PcacheFetchFinish(PCache*, Pgno, sqlite3_pcache_page *pPage); void sqlite3PcacheRelease(PgHdr*); void sqlite3PcacheDrop(PgHdr*); /* Remove page from cache */ void sqlite3PcacheMakeDirty(PgHdr*); /* Make sure page is marked dirty */ void sqlite3PcacheMakeClean(PgHdr*); /* Mark a single page as clean */ void sqlite3PcacheCleanAll(PCache*); /* Mark all dirty list pages as clean */ /* Change a page number. Used by incr-vacuum. */ void sqlite3PcacheMove(PgHdr*, Pgno); /* Remove all pages with pgno>x. Reset the cache if x==0 */ void sqlite3PcacheTruncate(PCache*, Pgno x); | > | 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 | PgHdr *sqlite3PcacheFetchFinish(PCache*, Pgno, sqlite3_pcache_page *pPage); void sqlite3PcacheRelease(PgHdr*); void sqlite3PcacheDrop(PgHdr*); /* Remove page from cache */ void sqlite3PcacheMakeDirty(PgHdr*); /* Make sure page is marked dirty */ void sqlite3PcacheMakeClean(PgHdr*); /* Mark a single page as clean */ void sqlite3PcacheCleanAll(PCache*); /* Mark all dirty list pages as clean */ void sqlite3PcacheClearWritable(PCache*); /* Change a page number. Used by incr-vacuum. */ void sqlite3PcacheMove(PgHdr*, Pgno); /* Remove all pages with pgno>x. Reset the cache if x==0 */ void sqlite3PcacheTruncate(PCache*, Pgno x); |
︙ | ︙ | |||
168 169 170 171 172 173 174 175 176 | #endif void sqlite3PCacheSetDefault(void); /* Return the header size */ int sqlite3HeaderSizePcache(void); int sqlite3HeaderSizePcache1(void); #endif /* _PCACHE_H_ */ | > > > | 169 170 171 172 173 174 175 176 177 178 179 180 | #endif void sqlite3PCacheSetDefault(void); /* Return the header size */ int sqlite3HeaderSizePcache(void); int sqlite3HeaderSizePcache1(void); /* Number of dirty pages as a percentage of the configured cache size */ int sqlite3PCachePercentDirty(PCache*); #endif /* _PCACHE_H_ */ |
Changes to src/test3.c.
︙ | ︙ | |||
543 544 545 546 547 548 549 | Tcl_SetResult(interp, zBuf, TCL_VOLATILE); return TCL_OK; } /* ** Usage: btree_ismemdb ID ** | | > | > | 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 | Tcl_SetResult(interp, zBuf, TCL_VOLATILE); return TCL_OK; } /* ** Usage: btree_ismemdb ID ** ** Return true if the B-Tree is currently stored entirely in memory. */ static int btree_ismemdb( void *NotUsed, Tcl_Interp *interp, /* The TCL interpreter that invoked this command */ int argc, /* Number of arguments */ const char **argv /* Text of each argument */ ){ Btree *pBt; int res; sqlite3_file *pFile; if( argc!=2 ){ Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0], " ID\"", 0); return TCL_ERROR; } pBt = sqlite3TestTextToPtr(argv[1]); sqlite3_mutex_enter(pBt->db->mutex); sqlite3BtreeEnter(pBt); pFile = sqlite3PagerFile(sqlite3BtreePager(pBt)); res = (pFile->pMethods==0); sqlite3BtreeLeave(pBt); sqlite3_mutex_leave(pBt->db->mutex); Tcl_SetObjResult(interp, Tcl_NewBooleanObj(res)); return SQLITE_OK; } /* |
︙ | ︙ |
Changes to test/backup.test.
︙ | ︙ | |||
160 161 162 163 164 165 166 | } { sqlite3 db $zSrcFile sqlite3 db2 $zDestFile set db_dest db2 set file_dest temp }] { foreach rows_dest {0 3 10} { | | < | < < | | | | | | > > | 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 | } { sqlite3 db $zSrcFile sqlite3 db2 $zDestFile set db_dest db2 set file_dest temp }] { foreach rows_dest {0 3 10} { foreach pgsz_dest {512 1024 2048 4096} { foreach nPagePerStep {1 200} { # Open the databases. catch { delete_file test.db } catch { delete_file test2.db } eval $zOpenScript # Set to true if copying to an in-memory destination. Copying to an # in-memory destination is only possible if the initial destination # page size is the same as the source page size (in this case 1024 bytes). # set isMemDest [expr { $zDestFile eq ":memory:" || $file_dest eq "temp" }] if 0 { puts -nonewline "Test $iTest: src=$zSrcFile dest=$zDestFile" puts -nonewline " (as $db_dest.$file_dest)" puts -nonewline " rows_dest=$rows_dest pgsz_dest=$pgsz_dest" puts "" } if { $isMemDest==0 || $pgsz_dest==1024 || $rows_dest==0 } { # Set up the content of the source database. execsql { PRAGMA page_size = 1024; BEGIN; CREATE TABLE t1(a, b); CREATE INDEX i1 ON t1(a, b); |
︙ | ︙ |
Changes to test/cffault.test.
︙ | ︙ | |||
11 12 13 14 15 16 17 | # # This file contains fault-injection test cases for the # sqlite3_db_cacheflush API. # set testdir [file dirname $argv0] source $testdir/tester.tcl | | | 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 | # # This file contains fault-injection test cases for the # sqlite3_db_cacheflush API. # set testdir [file dirname $argv0] source $testdir/tester.tcl set testprefix cffault source $testdir/malloc_common.tcl # Run the supplied SQL on a copy of the database currently stored on # disk in file $dbfile. proc diskquery {dbfile sql} { forcecopy $dbfile dq.db sqlite3 dq dq.db |
︙ | ︙ |
Changes to test/lock.test.
︙ | ︙ | |||
419 420 421 422 423 424 425 | # At one point the following set of conditions would cause SQLite to # retain a RESERVED or EXCLUSIVE lock after the transaction was committed: # # * The journal-mode is set to something other than 'delete', and # * there exists one or more active read-only statements, and # * a transaction that modified zero database pages is committed. # | | | > | 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 | # At one point the following set of conditions would cause SQLite to # retain a RESERVED or EXCLUSIVE lock after the transaction was committed: # # * The journal-mode is set to something other than 'delete', and # * there exists one or more active read-only statements, and # * a transaction that modified zero database pages is committed. # #set temp_status unlocked #if {$TEMP_STORE>=2} {set temp_status unknown} set temp_status unknown do_test lock-7.1 { set STMT [sqlite3_prepare $DB "SELECT * FROM sqlite_master" -1 TAIL] sqlite3_step $STMT } {SQLITE_ROW} do_test lock-7.2 { execsql { PRAGMA lock_status } } [list main shared temp $temp_status] |
︙ | ︙ |
Changes to test/pagerfault.test.
︙ | ︙ | |||
680 681 682 683 684 685 686 | } # If TEMP_STORE is 2 or greater, then the database [db2] will be created # as an in-memory database. This test will not work in that case, as it # is not possible to change the page-size of an in-memory database. Even # using the backup API. # | | > > | | | | | | | | | | | > | < | 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 | } # If TEMP_STORE is 2 or greater, then the database [db2] will be created # as an in-memory database. This test will not work in that case, as it # is not possible to change the page-size of an in-memory database. Even # using the backup API. # # Update: It is no longer possible to change the page size of any temp # database after it has been created. # do_faultsim_test pagerfault-14b -prep { catch { db2 close } faultsim_restore_and_reopen sqlite3 db2 "" db2 eval { PRAGMA page_size = 4096; CREATE TABLE xx(a) } } -body { sqlite3_backup B db2 main db main B step 200 set rc [B finish] if {[string match SQLITE_IOERR_* $rc]} {set rc SQLITE_IOERR} if {$rc != "SQLITE_OK"} { error [sqlite3_test_errstr $rc] } set {} {} } -test { faultsim_test_result {1 {attempt to write a readonly database}} \ {1 {sqlite3_backup_init() failed}} } do_faultsim_test pagerfault-14c -prep { catch { db2 close } faultsim_restore_and_reopen sqlite3 db2 test.db2 db2 eval { |
︙ | ︙ |
Changes to test/pragma.test.
︙ | ︙ | |||
1079 1080 1081 1082 1083 1084 1085 | } {-450} } ; # ifcapable schema_version # Check to see if TEMP_STORE is memory or disk. Return strings # "memory" or "disk" as appropriate. # proc check_temp_store {} { | > > | > > > > > > > > > > > | 1079 1080 1081 1082 1083 1084 1085 1086 1087 1088 1089 1090 1091 1092 1093 1094 1095 1096 1097 1098 1099 1100 1101 1102 1103 1104 1105 1106 | } {-450} } ; # ifcapable schema_version # Check to see if TEMP_STORE is memory or disk. Return strings # "memory" or "disk" as appropriate. # proc check_temp_store {} { db eval { PRAGMA temp.cache_size = 1; CREATE TEMP TABLE IF NOT EXISTS a(b); DELETE FROM a; INSERT INTO a VALUES(randomblob(1000)); INSERT INTO a SELECT * FROM a; INSERT INTO a SELECT * FROM a; INSERT INTO a SELECT * FROM a; INSERT INTO a SELECT * FROM a; INSERT INTO a SELECT * FROM a; INSERT INTO a SELECT * FROM a; INSERT INTO a SELECT * FROM a; INSERT INTO a SELECT * FROM a; } db eval {PRAGMA database_list} { if {$name=="temp"} { set bt [btree_from_db db 1] if {[btree_ismemdb $bt]} { return "memory" } return "disk" |
︙ | ︙ |
Added test/tempfault.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 | # 2016 April 11 # # The author disclaims copyright to this source code. In place of # a legal notice, here is a blessing: # # May you do good and not evil. # May you find forgiveness for yourself and forgive others. # May you share freely, never taking more than you give. # #*********************************************************************** # # This file contains tests for fault-injection when SQLite is used with # a temp file database. # set testdir [file dirname $argv0] source $testdir/tester.tcl source $testdir/malloc_common.tcl set testprefix tempfault # sqlite3_memdebug_vfs_oom_test 0 do_faultsim_test 1 -faults * -prep { sqlite3 db "" db eval { PRAGMA page_size = 1024; CREATE TABLE t1(a, b); INSERT INTO t1 VALUES(1, 2); INSERT INTO t1 VALUES(3, 4); } } -body { execsql { INSERT INTO t1 VALUES(5, 6) } } -test { faultsim_test_result {0 {}} set rc [catch { execsql { SELECT * FROM t1 } } msg] if {$rc==0 && $msg != "1 2 3 4 5 6" && $msg != "1 2 3 4"} { error "data mismatch 1: $msg" } if {$testrc==0 && $msg != "1 2 3 4 5 6"} { error "data mismatch 2: $msg" } faultsim_integrity_check } do_faultsim_test 2 -faults * -prep { sqlite3 db "" db eval { PRAGMA page_size = 1024; PRAGMA cache_size = 10; CREATE TABLE t1(a, b); CREATE INDEX i1 ON t1(b, a); WITH x(i) AS (SELECT 1 UNION ALL SELECT i+1 FROM x WHERE i<100) INSERT INTO t1 SELECT randomblob(100), randomblob(100) FROM x; } } -body { execsql { UPDATE t1 SET a = randomblob(99) } } -test { faultsim_test_result {0 {}} faultsim_integrity_check db } catch { db close } do_faultsim_test 2.1 -faults * -prep { if {[info commands db]==""} { sqlite3 db "" execsql { PRAGMA page_size = 1024; PRAGMA cache_size = 10; CREATE TABLE t1(a, b); CREATE INDEX i1 ON t1(b, a); WITH x(i) AS (SELECT 1 UNION ALL SELECT i+1 FROM x WHERE i<100) INSERT INTO t1 SELECT randomblob(100), randomblob(100) FROM x; } } } -body { execsql { UPDATE t1 SET a = randomblob(99) } } -test { faultsim_test_result {0 {}} faultsim_integrity_check db } do_faultsim_test 3 -faults * -prep { sqlite3 db "" db eval { PRAGMA page_size = 1024; PRAGMA cache_size = 10; CREATE TABLE t1(a, b); CREATE INDEX i1 ON t1(b, a); WITH x(i) AS (SELECT 1 UNION ALL SELECT i+1 FROM x WHERE i<50) INSERT INTO t1 SELECT randomblob(100), randomblob(100) FROM x; } } -body { execsql { BEGIN; UPDATE t1 SET a = randomblob(99); SAVEPOINT abc; UPDATE t1 SET a = randomblob(98) WHERE (rowid%10)==0; ROLLBACK TO abc; UPDATE t1 SET a = randomblob(97) WHERE (rowid%5)==0; ROLLBACK TO abc; COMMIT; } } -test { faultsim_test_result {0 {}} faultsim_integrity_check db } do_faultsim_test 4 -faults * -prep { sqlite3 db "" db eval { PRAGMA page_size = 1024; PRAGMA cache_size = 10; CREATE TABLE t1(a, b); CREATE INDEX i1 ON t1(b, a); WITH x(i) AS (SELECT 1 UNION ALL SELECT i+1 FROM x WHERE i<50) INSERT INTO t1 SELECT randomblob(100), randomblob(100) FROM x; } } -body { execsql { BEGIN; UPDATE t1 SET a = randomblob(99); SAVEPOINT abc; UPDATE t1 SET a = randomblob(98) WHERE (rowid%10)==0; ROLLBACK TO abc; UPDATE t1 SET a = randomblob(97) WHERE (rowid%5)==0; ROLLBACK TO abc; COMMIT; } } -test { faultsim_test_result {0 {}} } sqlite3_memdebug_vfs_oom_test 1 finish_test |
Added test/temptable2.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 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 | # 2016 March 3 # # The author disclaims copyright to this source code. In place of # a legal notice, here is a blessing: # # May you do good and not evil. # May you find forgiveness for yourself and forgive others. # May you share freely, never taking more than you give. # #*********************************************************************** set testdir [file dirname $argv0] source $testdir/tester.tcl set testprefix temptable2 do_execsql_test 1.1 { CREATE TEMP TABLE t1(a, b); CREATE INDEX i1 ON t1(a, b); } do_execsql_test 1.2 { WITH x(i) AS ( SELECT 1 UNION ALL SELECT i+1 FROM x WHERE i<100000 ) INSERT INTO t1 SELECT randomblob(100), randomblob(100) FROM X; } {} do_execsql_test 1.3 { PRAGMA temp.integrity_check; } {ok} #------------------------------------------------------------------------- # reset_db do_execsql_test 2.1 { CREATE TEMP TABLE t2(a, b); INSERT INTO t2 VALUES(1, 2); } {} do_execsql_test 2.2 { BEGIN; INSERT INTO t2 VALUES(3, 4); SELECT * FROM t2; } {1 2 3 4} do_execsql_test 2.3 { ROLLBACK; SELECT * FROM t2; } {1 2} #------------------------------------------------------------------------- # reset_db do_execsql_test 3.1.1 { PRAGMA main.cache_size = 10; PRAGMA temp.cache_size = 10; CREATE TEMP TABLE t1(a, b); CREATE INDEX i1 ON t1(a, b); WITH x(i) AS ( SELECT 1 UNION ALL SELECT i+1 FROM x WHERE i<1000 ) INSERT INTO t1 SELECT randomblob(100), randomblob(100) FROM x; SELECT count(*) FROM t1; } {1000} do_execsql_test 3.1.2 { BEGIN; UPDATE t1 SET b=randomblob(100) WHERE (rowid%10)==0; ROLLBACK; } do_execsql_test 3.1.3 { SELECT count(*) FROM t1; } {1000} do_execsql_test 3.1.4 { PRAGMA temp.integrity_check } {ok} do_execsql_test 3.2.1 { BEGIN; UPDATE t1 SET b=randomblob(100) WHERE (rowid%10)==0; SAVEPOINT abc; UPDATE t1 SET b=randomblob(100) WHERE (rowid%10)==1; ROLLBACK TO abc; UPDATE t1 SET b=randomblob(100) WHERE (rowid%10)==2; COMMIT; } do_execsql_test 3.2.2 { PRAGMA temp.integrity_check } {ok} #------------------------------------------------------------------------- # reset_db do_execsql_test 4.1.1 { PRAGMA main.cache_size = 10; PRAGMA temp.cache_size = 10; CREATE TEMP TABLE t1(a, b); CREATE INDEX i1 ON t1(a, b); WITH x(i) AS ( SELECT 1 UNION ALL SELECT i+1 FROM x WHERE i<10 ) INSERT INTO t1 SELECT randomblob(100), randomblob(100) FROM x; SELECT count(*) FROM t1; PRAGMA temp.page_count; } {10 9} do_execsql_test 4.1.2 { BEGIN; UPDATE t1 SET b=randomblob(100); ROLLBACK; } do_execsql_test 4.1.3 { CREATE TEMP TABLE t2(a, b); CREATE INDEX i2 ON t2(a, b); WITH x(i) AS ( SELECT 1 UNION ALL SELECT i+1 FROM x WHERE i<500 ) INSERT INTO t2 SELECT randomblob(100), randomblob(100) FROM x; SELECT count(*) FROM t2; SELECT count(*) FROM t1; } {500 10} do_test 4.1.4 { set n [db one { PRAGMA temp.page_count }] expr ($n >280 && $n < 300) } 1 do_execsql_test 4.1.4 { PRAGMA temp.integrity_check } {ok} #------------------------------------------------------------------------- # reset_db do_execsql_test 5.1.1 { PRAGMA main.cache_size = 10; PRAGMA temp.cache_size = 10; CREATE TEMP TABLE t2(a, b); CREATE INDEX i2 ON t2(a, b); WITH x(i) AS ( SELECT 1 UNION ALL SELECT i+1 FROM x WHERE i<500 ) INSERT INTO t2 SELECT randomblob(100), randomblob(100) FROM x; CREATE TEMP TABLE t1(a, b); CREATE INDEX i1 ON t1(a, b); INSERT INTO t1 VALUES(1, 2); } # Test that the temp database is now much bigger than the configured # cache size (10 pages). do_test 5.1.2 { set n [db one { PRAGMA temp.page_count }] expr ($n > 270 && $n < 290) } {1} do_execsql_test 5.1.3 { BEGIN; UPDATE t1 SET a=2; UPDATE t2 SET a=randomblob(100); SELECT count(*) FROM t1; ROLLBACK; } {1} do_execsql_test 5.1.4 { UPDATE t2 SET a=randomblob(100); SELECT * FROM t1; } {1 2} do_execsql_test 5.1.5 { PRAGMA temp.integrity_check } {ok} #------------------------------------------------------------------------- # Test this: # # 1. Page is DIRTY at the start of a transaction. # 2. Page is written out as part of the transaction. # 3. Page is then read back in. # 4. Transaction is rolled back. Is the page now clean or dirty? # # This actually does work. Step 4 marks the page as clean. But it also # writes to the database file itself. So marking it clean is correct - # the page does match the contents of the db file. # reset_db do_execsql_test 6.1 { PRAGMA main.cache_size = 10; PRAGMA temp.cache_size = 10; CREATE TEMP TABLE t1(x); INSERT INTO t1 VALUES('one'); CREATE TEMP TABLE t2(a, b); CREATE INDEX i2 ON t2(a, b); WITH x(i) AS ( SELECT 1 UNION ALL SELECT i+1 FROM x WHERE i<500 ) INSERT INTO t2 SELECT randomblob(100), randomblob(100) FROM x; } do_execsql_test 6.2 { UPDATE t1 SET x='two'; -- step 1 BEGIN; UPDATE t2 SET a=randomblob(100); -- step 2 SELECT * FROM t1; -- step 3 ROLLBACK; -- step 4 SELECT count(*) FROM t2; SELECT * FROM t1; } {two 500 two} #------------------------------------------------------------------------- # reset_db sqlite3 db "" do_execsql_test 7.1 { PRAGMA auto_vacuum=INCREMENTAL; CREATE TABLE t1(x); INSERT INTO t1 VALUES(zeroblob(900)); INSERT INTO t1 VALUES(zeroblob(900)); INSERT INTO t1 SELECT x FROM t1; INSERT INTO t1 SELECT x FROM t1; INSERT INTO t1 SELECT x FROM t1; INSERT INTO t1 SELECT x FROM t1; BEGIN; DELETE FROM t1 WHERE rowid%2; PRAGMA incremental_vacuum(4); ROLLBACK; PRAGMA integrity_check; } {ok} #------------------------------------------------------------------------- # Try changing the page size using a backup operation when pages are # stored in main-memory only. # reset_db do_execsql_test 8.1 { PRAGMA auto_vacuum = OFF; CREATE TABLE t2(a, b); CREATE INDEX i2 ON t2(a, b); WITH x(i) AS ( SELECT 1 UNION ALL SELECT i+1 FROM x WHERE i<20 ) INSERT INTO t2 SELECT randomblob(100), randomblob(100) FROM x; PRAGMA page_count; } {13} do_test 8.2 { sqlite3 tmp "" execsql { PRAGMA auto_vacuum = OFF; PRAGMA page_size = 8192; CREATE TABLE t1(a, b); CREATE INDEX i1 ON t1(a, b); WITH x(i) AS ( SELECT 1 UNION ALL SELECT i+1 FROM x WHERE i<100 ) INSERT INTO t1 SELECT randomblob(100), randomblob(100) FROM x; PRAGMA page_count; } tmp } {10} do_test 8.3 { sqlite3_backup B tmp main db main B step 5 B finish } {SQLITE_READONLY} do_test 8.4 { execsql { SELECT count(*) FROM t1; PRAGMA integrity_check; PRAGMA page_size; } tmp } {100 ok 8192} do_test 8.5 { tmp eval { UPDATE t1 SET a=randomblob(100) } } {} do_test 8.6 { sqlite3_backup B tmp main db main B step 1000 B finish } {SQLITE_READONLY} tmp close #------------------------------------------------------------------------- # Try inserts and deletes with a large db in auto-vacuum mode. Check # foreach {tn mode} { 1 delete 2 wal } { reset_db sqlite3 db "" do_execsql_test 9.$tn.1.1 { PRAGMA cache_size = 15; PRAGMA auto_vacuum = 1; } execsql "PRAGMA journal_mode = $mode" do_execsql_test 9.$tn.1.2 { CREATE TABLE tx(a, b); CREATE INDEX i1 ON tx(a); CREATE INDEX i2 ON tx(b); WITH x(i) AS ( SELECT 1 UNION ALL SELECT i+1 FROM x WHERE i<1000 ) INSERT INTO tx SELECT randomblob(100), randomblob(100) FROM x; } for {set i 2} {$i<20} {incr i} { do_execsql_test 9.$tn.$i.1 { DELETE FROM tx WHERE (random()%3)==0 } do_execsql_test 9.$tn.$i.2 { PRAGMA integrity_check } ok do_execsql_test 9.$tn.$i.3 { WITH x(i) AS ( SELECT 1 UNION ALL SELECT i+1 FROM x WHERE i<400 ) INSERT INTO tx SELECT randomblob(100), randomblob(100) FROM x; } do_execsql_test 9.$tn.$i.4 { PRAGMA integrity_check } ok do_execsql_test 9.$tn.$i.5 { BEGIN; DELETE FROM tx WHERE (random()%3)==0; WITH x(i) AS ( SELECT 1 UNION ALL SELECT i+1 FROM x WHERE i<500 ) INSERT INTO tx SELECT randomblob(100), randomblob(100) FROM x; COMMIT; } do_execsql_test 9.$tn.$i.6 { PRAGMA integrity_check } ok } } #------------------------------------------------------------------------- # When using mmap mode with a temp file, SQLite must search the cache # before using a mapped page even when there is no write transaction # open. For a temp file, the on-disk version may not be up to date. # sqlite3 db "" do_execsql_test 10.0 { PRAGMA cache_size = 50; PRAGMA page_size = 1024; CREATE TABLE t1(a, b, PRIMARY KEY(a)) WITHOUT ROWID; CREATE INDEX i1 ON t1(a); CREATE TABLE t2(x, y); INSERT INTO t2 VALUES(1, 2); } do_execsql_test 10.1 { BEGIN; WITH x(i) AS ( SELECT 1 UNION ALL SELECT i+1 FROM x WHERE i<500 ) INSERT INTO t1 SELECT randomblob(100), randomblob(100) FROM x; COMMIT; INSERT INTO t2 VALUES(3, 4); } if {[permutation]!="journaltest"} { # The journaltest permutation does not support mmap, so this part of # the test is omitted. do_execsql_test 10.2 { PRAGMA mmap_size = 512000 } 512000 } do_execsql_test 10.3 { SELECT * FROM t2 } {1 2 3 4} do_execsql_test 10.4 { PRAGMA integrity_check } ok finish_test |