Many hyperlinks are disabled.
Use anonymous login
to enable hyperlinks.
Overview
Comment: | Experimental implementation of pessimistic page-level locking based on rollback mode. |
---|---|
Downloads: | Tarball | ZIP archive |
Timelines: | family | ancestors | descendants | both | server-edition |
Files: | files | file ages | folders |
SHA3-256: |
64ecf7c7e512827e8a5a42f9f3ad92ff |
User & Date: | dan 2017-04-26 20:45:00.409 |
Context
2017-04-27
| ||
13:05 | If possible, delete the journal file when a database connection is closed. (check-in: d5b5326df2 user: dan tags: server-edition) | |
2017-04-26
| ||
20:45 | Experimental implementation of pessimistic page-level locking based on rollback mode. (check-in: 64ecf7c7e5 user: dan tags: server-edition) | |
17:21 | Add new test file cachespill.test. (check-in: 2d0b64316d user: dan tags: trunk) | |
Changes
Changes to main.mk.
︙ | ︙ | |||
65 66 67 68 69 70 71 72 73 74 75 76 77 78 | icu.o insert.o json1.o legacy.o loadext.o \ main.o malloc.o mem0.o mem1.o mem2.o mem3.o mem5.o \ memjournal.o \ mutex.o mutex_noop.o mutex_unix.o mutex_w32.o \ notify.o opcodes.o os.o os_unix.o os_win.o \ pager.o pcache.o pcache1.o pragma.o prepare.o printf.o \ random.o resolve.o rowset.o rtree.o select.o sqlite3rbu.o status.o \ table.o threads.o tokenize.o treeview.o trigger.o \ update.o userauth.o util.o vacuum.o \ vdbeapi.o vdbeaux.o vdbeblob.o vdbemem.o vdbesort.o \ vdbetrace.o wal.o walker.o where.o wherecode.o whereexpr.o \ utf.o vtab.o LIBOBJ += sqlite3session.o | > | 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 | icu.o insert.o json1.o legacy.o loadext.o \ main.o malloc.o mem0.o mem1.o mem2.o mem3.o mem5.o \ memjournal.o \ mutex.o mutex_noop.o mutex_unix.o mutex_w32.o \ notify.o opcodes.o os.o os_unix.o os_win.o \ pager.o pcache.o pcache1.o pragma.o prepare.o printf.o \ random.o resolve.o rowset.o rtree.o select.o sqlite3rbu.o status.o \ server.o \ table.o threads.o tokenize.o treeview.o trigger.o \ update.o userauth.o util.o vacuum.o \ vdbeapi.o vdbeaux.o vdbeblob.o vdbemem.o vdbesort.o \ vdbetrace.o wal.o walker.o where.o wherecode.o whereexpr.o \ utf.o vtab.o LIBOBJ += sqlite3session.o |
︙ | ︙ | |||
140 141 142 143 144 145 146 147 148 149 150 151 152 153 | $(TOP)/src/pragma.h \ $(TOP)/src/prepare.c \ $(TOP)/src/printf.c \ $(TOP)/src/random.c \ $(TOP)/src/resolve.c \ $(TOP)/src/rowset.c \ $(TOP)/src/select.c \ $(TOP)/src/status.c \ $(TOP)/src/shell.c \ $(TOP)/src/sqlite.h.in \ $(TOP)/src/sqlite3ext.h \ $(TOP)/src/sqliteInt.h \ $(TOP)/src/sqliteLimit.h \ $(TOP)/src/table.c \ | > > | 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 | $(TOP)/src/pragma.h \ $(TOP)/src/prepare.c \ $(TOP)/src/printf.c \ $(TOP)/src/random.c \ $(TOP)/src/resolve.c \ $(TOP)/src/rowset.c \ $(TOP)/src/select.c \ $(TOP)/src/server.c \ $(TOP)/src/server.h \ $(TOP)/src/status.c \ $(TOP)/src/shell.c \ $(TOP)/src/sqlite.h.in \ $(TOP)/src/sqlite3ext.h \ $(TOP)/src/sqliteInt.h \ $(TOP)/src/sqliteLimit.h \ $(TOP)/src/table.c \ |
︙ | ︙ |
Changes to src/pager.c.
︙ | ︙ | |||
702 703 704 705 706 707 708 709 710 711 712 713 714 715 | #endif char *pTmpSpace; /* Pager.pageSize bytes of space for tmp use */ PCache *pPCache; /* Pointer to page cache object */ #ifndef SQLITE_OMIT_WAL Wal *pWal; /* Write-ahead log used by "journal_mode=wal" */ char *zWal; /* File name for write-ahead log */ #endif }; /* ** Indexes for use with Pager.aStat[]. The Pager.aStat[] array contains ** the values accessed by passing SQLITE_DBSTATUS_CACHE_HIT, CACHE_MISS ** or CACHE_WRITE to sqlite3_db_status(). */ | > > > | 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716 717 718 | #endif char *pTmpSpace; /* Pager.pageSize bytes of space for tmp use */ PCache *pPCache; /* Pointer to page cache object */ #ifndef SQLITE_OMIT_WAL Wal *pWal; /* Write-ahead log used by "journal_mode=wal" */ char *zWal; /* File name for write-ahead log */ #endif #ifdef SQLITE_SERVER_EDITION Server *pServer; #endif }; /* ** Indexes for use with Pager.aStat[]. The Pager.aStat[] array contains ** the values accessed by passing SQLITE_DBSTATUS_CACHE_HIT, CACHE_MISS ** or CACHE_WRITE to sqlite3_db_status(). */ |
︙ | ︙ | |||
831 832 833 834 835 836 837 838 839 840 841 842 843 844 | #else # define pagerUseWal(x) 0 # define pagerRollbackWal(x) 0 # define pagerWalFrames(v,w,x,y) 0 # define pagerOpenWalIfPresent(z) SQLITE_OK # define pagerBeginReadTransaction(z) SQLITE_OK #endif #ifndef NDEBUG /* ** Usage: ** ** assert( assert_pager_state(pPager) ); ** | > > > > > > | 834 835 836 837 838 839 840 841 842 843 844 845 846 847 848 849 850 851 852 853 | #else # define pagerUseWal(x) 0 # define pagerRollbackWal(x) 0 # define pagerWalFrames(v,w,x,y) 0 # define pagerOpenWalIfPresent(z) SQLITE_OK # define pagerBeginReadTransaction(z) SQLITE_OK #endif #ifdef SQLITE_SERVER_EDITION # define pagerIsServer(x) ((x)->pServer!=0) #else # define pagerIsServer(x) 0 #endif #ifndef NDEBUG /* ** Usage: ** ** assert( assert_pager_state(pPager) ); ** |
︙ | ︙ | |||
1128 1129 1130 1131 1132 1133 1134 1135 1136 1137 1138 1139 1140 1141 | */ static int pagerUnlockDb(Pager *pPager, int eLock){ int rc = SQLITE_OK; assert( !pPager->exclusiveMode || pPager->eLock==eLock ); assert( eLock==NO_LOCK || eLock==SHARED_LOCK ); assert( eLock!=NO_LOCK || pagerUseWal(pPager)==0 ); if( isOpen(pPager->fd) ){ assert( pPager->eLock>=eLock ); rc = pPager->noLock ? SQLITE_OK : sqlite3OsUnlock(pPager->fd, eLock); if( pPager->eLock!=UNKNOWN_LOCK ){ pPager->eLock = (u8)eLock; } IOTRACE(("UNLOCK %p %d\n", pPager, eLock)) | > | 1137 1138 1139 1140 1141 1142 1143 1144 1145 1146 1147 1148 1149 1150 1151 | */ static int pagerUnlockDb(Pager *pPager, int eLock){ int rc = SQLITE_OK; assert( !pPager->exclusiveMode || pPager->eLock==eLock ); assert( eLock==NO_LOCK || eLock==SHARED_LOCK ); assert( eLock!=NO_LOCK || pagerUseWal(pPager)==0 ); assert( eLock!=NO_LOCK || pagerIsServer(pPager)==0 ); if( isOpen(pPager->fd) ){ assert( pPager->eLock>=eLock ); rc = pPager->noLock ? SQLITE_OK : sqlite3OsUnlock(pPager->fd, eLock); if( pPager->eLock!=UNKNOWN_LOCK ){ pPager->eLock = (u8)eLock; } IOTRACE(("UNLOCK %p %d\n", pPager, eLock)) |
︙ | ︙ | |||
1803 1804 1805 1806 1807 1808 1809 1810 1811 1812 1813 1814 1815 1816 | || pPager->eState==PAGER_ERROR ); sqlite3BitvecDestroy(pPager->pInJournal); pPager->pInJournal = 0; releaseAllSavepoints(pPager); if( pagerUseWal(pPager) ){ assert( !isOpen(pPager->jfd) ); sqlite3WalEndReadTransaction(pPager->pWal); pPager->eState = PAGER_OPEN; }else if( !pPager->exclusiveMode ){ int rc; /* Error code returned by pagerUnlockDb() */ int iDc = isOpen(pPager->fd)?sqlite3OsDeviceCharacteristics(pPager->fd):0; | > > > > > > | 1813 1814 1815 1816 1817 1818 1819 1820 1821 1822 1823 1824 1825 1826 1827 1828 1829 1830 1831 1832 | || pPager->eState==PAGER_ERROR ); sqlite3BitvecDestroy(pPager->pInJournal); pPager->pInJournal = 0; releaseAllSavepoints(pPager); #ifdef SQLITE_SERVER_EDITION if( pagerIsServer(pPager) ){ sqlite3ServerEnd(pPager->pServer); pPager->eState = PAGER_OPEN; }else #endif if( pagerUseWal(pPager) ){ assert( !isOpen(pPager->jfd) ); sqlite3WalEndReadTransaction(pPager->pWal); pPager->eState = PAGER_OPEN; }else if( !pPager->exclusiveMode ){ int rc; /* Error code returned by pagerUnlockDb() */ int iDc = isOpen(pPager->fd)?sqlite3OsDeviceCharacteristics(pPager->fd):0; |
︙ | ︙ | |||
2101 2102 2103 2104 2105 2106 2107 2108 2109 2110 2111 2112 2113 2114 | } if( rc==SQLITE_OK && bCommit && isOpen(pPager->fd) ){ rc = sqlite3OsFileControl(pPager->fd, SQLITE_FCNTL_COMMIT_PHASETWO, 0); if( rc==SQLITE_NOTFOUND ) rc = SQLITE_OK; } if( !pPager->exclusiveMode && (!pagerUseWal(pPager) || sqlite3WalExclusiveMode(pPager->pWal, 0)) ){ rc2 = pagerUnlockDb(pPager, SHARED_LOCK); pPager->changeCountDone = 0; } pPager->eState = PAGER_READER; | > > > > > | 2117 2118 2119 2120 2121 2122 2123 2124 2125 2126 2127 2128 2129 2130 2131 2132 2133 2134 2135 | } if( rc==SQLITE_OK && bCommit && isOpen(pPager->fd) ){ rc = sqlite3OsFileControl(pPager->fd, SQLITE_FCNTL_COMMIT_PHASETWO, 0); if( rc==SQLITE_NOTFOUND ) rc = SQLITE_OK; } #ifdef SQLITE_SERVER_EDITION if( pagerIsServer(pPager) ){ rc2 = sqlite3ServerReleaseWriteLocks(pPager->pServer); }else #endif if( !pPager->exclusiveMode && (!pagerUseWal(pPager) || sqlite3WalExclusiveMode(pPager->pWal, 0)) ){ rc2 = pagerUnlockDb(pPager, SHARED_LOCK); pPager->changeCountDone = 0; } pPager->eState = PAGER_READER; |
︙ | ︙ | |||
4074 4075 4076 4077 4078 4079 4080 4081 4082 4083 4084 4085 4086 4087 | pPager->exclusiveMode = 0; #ifndef SQLITE_OMIT_WAL assert( db || pPager->pWal==0 ); sqlite3WalClose(pPager->pWal, db, pPager->ckptSyncFlags, pPager->pageSize, (db && (db->flags & SQLITE_NoCkptOnClose) ? 0 : pTmp) ); pPager->pWal = 0; #endif pager_reset(pPager); if( MEMDB ){ pager_unlock(pPager); }else{ /* If it is open, sync the journal file before calling UnlockAndRollback. ** If this is not done, then an unsynced portion of the open journal | > > > > > > > | 4095 4096 4097 4098 4099 4100 4101 4102 4103 4104 4105 4106 4107 4108 4109 4110 4111 4112 4113 4114 4115 | pPager->exclusiveMode = 0; #ifndef SQLITE_OMIT_WAL assert( db || pPager->pWal==0 ); sqlite3WalClose(pPager->pWal, db, pPager->ckptSyncFlags, pPager->pageSize, (db && (db->flags & SQLITE_NoCkptOnClose) ? 0 : pTmp) ); pPager->pWal = 0; #endif #ifdef SQLITE_SERVER_EDITION if( pPager->pServer ){ sqlite3ServerDisconnect(pPager->pServer, pPager->fd); pPager->pServer = 0; sqlite3_free(pPager->zJournal); } #endif pager_reset(pPager); if( MEMDB ){ pager_unlock(pPager); }else{ /* If it is open, sync the journal file before calling UnlockAndRollback. ** If this is not done, then an unsynced portion of the open journal |
︙ | ︙ | |||
5046 5047 5048 5049 5050 5051 5052 5053 5054 5055 5056 5057 5058 5059 | } } } } return rc; } /* ** This function is called to obtain a shared lock on the database file. ** It is illegal to call sqlite3PagerGet() until after this function ** has been successfully called. If a shared-lock is already held when ** this function is called, it is a no-op. ** | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 5074 5075 5076 5077 5078 5079 5080 5081 5082 5083 5084 5085 5086 5087 5088 5089 5090 5091 5092 5093 5094 5095 5096 5097 5098 5099 5100 5101 5102 5103 5104 5105 5106 5107 5108 5109 5110 5111 5112 5113 5114 5115 5116 5117 5118 5119 5120 5121 5122 5123 5124 5125 5126 5127 5128 5129 5130 5131 5132 5133 5134 5135 5136 5137 5138 5139 5140 5141 5142 5143 5144 5145 5146 5147 5148 5149 5150 5151 5152 5153 5154 5155 5156 5157 5158 5159 | } } } } return rc; } #ifdef SQLITE_SERVER_EDITION static int pagerServerConnect(Pager *pPager){ int rc = SQLITE_OK; if( pPager->tempFile==0 ){ int iClient = 0; pPager->noLock = 1; pPager->journalMode = PAGER_JOURNALMODE_PERSIST; rc = sqlite3ServerConnect(pPager, &pPager->pServer, &iClient); if( rc==SQLITE_OK ){ pPager->zJournal = sqlite3_mprintf( "%s-journal%d", pPager->zFilename, iClient ); if( pPager->zJournal==0 ){ rc = SQLITE_NOMEM_BKPT; } } } return rc; } int sqlite3PagerRollbackJournal(Pager *pPager, int iClient){ int rc; char *zJrnl = sqlite3_mprintf("%s-journal%d", pPager->zFilename, iClient); if( zJrnl ){ int bExists = 0; sqlite3_file *jfd = 0; sqlite3_vfs * const pVfs = pPager->pVfs; rc = sqlite3OsAccess(pVfs, zJrnl, SQLITE_ACCESS_EXISTS, &bExists); if( rc==SQLITE_OK && bExists ){ int flags = SQLITE_OPEN_READWRITE|SQLITE_OPEN_MAIN_JOURNAL; rc = sqlite3OsOpenMalloc(pVfs, zJrnl, &jfd, flags, &flags); } assert( rc==SQLITE_OK || jfd==0 ); if( jfd ){ sqlite3_file *saved_jfd = pPager->jfd; u8 saved_eState = pPager->eState; u8 saved_eLock = pPager->eLock; i64 saved_journalOff = pPager->journalOff; i64 saved_journalHdr = pPager->journalHdr; pPager->eLock = EXCLUSIVE_LOCK; pPager->eState = PAGER_WRITER_DBMOD; pPager->jfd = jfd; rc = pagerSyncHotJournal(pPager); if( rc==SQLITE_OK ) rc = pager_playback(pPager, 1); pPager->jfd = saved_jfd; pPager->eState = saved_eState; pPager->eLock = saved_eLock; pPager->journalOff = saved_journalOff; pPager->journalHdr = saved_journalHdr; sqlite3OsCloseFree(jfd); if( rc==SQLITE_OK ){ rc = sqlite3OsDelete(pVfs, zJrnl, 0); } } sqlite3_free(zJrnl); }else{ rc = SQLITE_NOMEM_BKPT; } return rc; } #else # define pagerServerConnect(pPager) SQLITE_OK #endif /* ** This function is called to obtain a shared lock on the database file. ** It is illegal to call sqlite3PagerGet() until after this function ** has been successfully called. If a shared-lock is already held when ** this function is called, it is a no-op. ** |
︙ | ︙ | |||
5086 5087 5088 5089 5090 5091 5092 | ** 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 ); | | > > | 5186 5187 5188 5189 5190 5191 5192 5193 5194 5195 5196 5197 5198 5199 5200 5201 5202 | ** 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) && !pagerIsServer(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 ){ |
︙ | ︙ | |||
5265 5266 5267 5268 5269 5270 5271 | /* If there is a WAL file in the file-system, open this database in WAL ** mode. Otherwise, the following function call is a no-op. */ rc = pagerOpenWalIfPresent(pPager); #ifndef SQLITE_OMIT_WAL assert( pPager->pWal==0 || rc==SQLITE_OK ); #endif | | > > | > > > > > > > > > | 5367 5368 5369 5370 5371 5372 5373 5374 5375 5376 5377 5378 5379 5380 5381 5382 5383 5384 5385 5386 5387 5388 5389 5390 5391 5392 5393 | /* If there is a WAL file in the file-system, open this database in WAL ** mode. Otherwise, the following function call is a no-op. */ rc = pagerOpenWalIfPresent(pPager); #ifndef SQLITE_OMIT_WAL assert( pPager->pWal==0 || rc==SQLITE_OK ); #endif if( rc==SQLITE_OK && pagerUseWal(pPager)==0 ){ rc = pagerServerConnect(pPager); } } #ifdef SQLITE_SERVER_EDITION if( pagerIsServer(pPager) ){ assert( rc==SQLITE_OK ); pager_reset(pPager); rc = sqlite3ServerBegin(pPager->pServer); }else #endif 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); |
︙ | ︙ | |||
5560 5561 5562 5563 5564 5565 5566 5567 5568 5569 5570 5571 5572 5573 | */ int sqlite3PagerGet( Pager *pPager, /* The pager open on the database file */ Pgno pgno, /* Page number to fetch */ DbPage **ppPage, /* Write a pointer to the page here */ int flags /* PAGER_GET_XXX flags */ ){ return pPager->xGet(pPager, pgno, ppPage, flags); } /* ** Acquire a page if it is already in the in-memory cache. Do ** not read the page from disk. Return a pointer to the page, ** or 0 if the page is not in cache. | > > > > > > | 5673 5674 5675 5676 5677 5678 5679 5680 5681 5682 5683 5684 5685 5686 5687 5688 5689 5690 5691 5692 | */ int sqlite3PagerGet( Pager *pPager, /* The pager open on the database file */ Pgno pgno, /* Page number to fetch */ DbPage **ppPage, /* Write a pointer to the page here */ int flags /* PAGER_GET_XXX flags */ ){ #ifdef SQLITE_SERVER_EDITION if( pagerIsServer(pPager) ){ int rc = sqlite3ServerLock(pPager->pServer, pgno, 0); if( rc!=SQLITE_OK ) return rc; } #endif return pPager->xGet(pPager, pgno, ppPage, flags); } /* ** Acquire a page if it is already in the in-memory cache. Do ** not read the page from disk. Return a pointer to the page, ** or 0 if the page is not in cache. |
︙ | ︙ | |||
5861 5862 5863 5864 5865 5866 5867 5868 5869 5870 5871 5872 5873 5874 | || pPager->eState==PAGER_WRITER_CACHEMOD || pPager->eState==PAGER_WRITER_DBMOD ); assert( assert_pager_state(pPager) ); assert( pPager->errCode==0 ); assert( pPager->readOnly==0 ); CHECK_PAGE(pPg); /* The journal file needs to be opened. Higher level routines have already ** obtained the necessary locks to begin the write-transaction, but the ** rollback journal might not yet be open. Open it now if this is the case. ** ** This is done before calling sqlite3PcacheMakeDirty() on the page. ** Otherwise, if it were done after calling sqlite3PcacheMakeDirty(), then | > > > > > > > | 5980 5981 5982 5983 5984 5985 5986 5987 5988 5989 5990 5991 5992 5993 5994 5995 5996 5997 5998 5999 6000 | || pPager->eState==PAGER_WRITER_CACHEMOD || pPager->eState==PAGER_WRITER_DBMOD ); assert( assert_pager_state(pPager) ); assert( pPager->errCode==0 ); assert( pPager->readOnly==0 ); CHECK_PAGE(pPg); #ifdef SQLITE_SERVER_EDITION if( pagerIsServer(pPager) ){ rc = sqlite3ServerLock(pPager->pServer, pPg->pgno, 1); if( rc!=SQLITE_OK ) return rc; } #endif /* The journal file needs to be opened. Higher level routines have already ** obtained the necessary locks to begin the write-transaction, but the ** rollback journal might not yet be open. Open it now if this is the case. ** ** This is done before calling sqlite3PcacheMakeDirty() on the page. ** Otherwise, if it were done after calling sqlite3PcacheMakeDirty(), then |
︙ | ︙ | |||
6140 6141 6142 6143 6144 6145 6146 | # define DIRECT_MODE 0 assert( isDirectMode==0 ); UNUSED_PARAMETER(isDirectMode); #else # define DIRECT_MODE isDirectMode #endif | > > | > | 6266 6267 6268 6269 6270 6271 6272 6273 6274 6275 6276 6277 6278 6279 6280 6281 6282 6283 | # define DIRECT_MODE 0 assert( isDirectMode==0 ); UNUSED_PARAMETER(isDirectMode); #else # define DIRECT_MODE isDirectMode #endif if( 0==pagerIsServer(pPager) && !pPager->changeCountDone && ALWAYS(pPager->dbSize>0) ){ PgHdr *pPgHdr; /* Reference to page 1 */ assert( !pPager->tempFile && isOpen(pPager->fd) ); /* Open page 1 of the file for writing. */ rc = sqlite3PagerGet(pPager, 1, &pPgHdr, 0); assert( pPgHdr==0 || rc==SQLITE_OK ); |
︙ | ︙ |
Changes to src/pager.h.
︙ | ︙ | |||
231 232 233 234 235 236 237 238 239 | void sqlite3PagerRefdump(Pager*); void disable_simulated_io_errors(void); void enable_simulated_io_errors(void); #else # define disable_simulated_io_errors() # define enable_simulated_io_errors() #endif #endif /* SQLITE_PAGER_H */ | > > > > | 231 232 233 234 235 236 237 238 239 240 241 242 243 | void sqlite3PagerRefdump(Pager*); void disable_simulated_io_errors(void); void enable_simulated_io_errors(void); #else # define disable_simulated_io_errors() # define enable_simulated_io_errors() #endif #ifdef SQLITE_SERVER_EDITION int sqlite3PagerRollbackJournal(Pager*, int); #endif #endif /* SQLITE_PAGER_H */ |
Added src/server.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 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 | /* ** 2017 April 24 ** ** 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. ** ************************************************************************* */ #include "sqliteInt.h" /* ** HMA file layout: ** ** 4 bytes - DMS slot. All connections read-lock this slot. ** ** 16*4 bytes - locking slots. Connections hold a read-lock on a locking slot ** when they are connected, a write lock when they have an open ** transaction. ** ** N*4 bytes - Page locking slots. N is HMA_PAGELOCK_SLOTS. ** ** Page lock slot format: ** ** Least significant HMA_CLIENT_SLOTS used for read-locks. If bit 0 is set, ** client 0 holds a read-lock. ** ** If (v) is the value of the locking slot and (v>>HMA_CLIENT_SLOTS) is ** not zero, then the write-lock holder is client ((v>>HMA_CLIENT_SLOTS)-1). ** */ #ifdef SQLITE_SERVER_EDITION #define HMA_CLIENT_SLOTS 16 #define HMA_PAGELOCK_SLOTS (256*1024) #define HMA_FILE_SIZE (4 + 4*HMA_CLIENT_SLOTS + 4*HMA_PAGELOCK_SLOTS) #include "unistd.h" #include "fcntl.h" #include "sys/mman.h" #include "sys/types.h" #include "sys/stat.h" typedef struct ServerHMA ServerHMA; struct ServerGlobal { sqlite3_mutex *mutex; ServerHMA *pHma; }; static struct ServerGlobal g_server; /* ** There is one instance of the following structure for each distinct ** HMA file opened by clients within this process. */ struct ServerHMA { char *zName; /* hma file path */ int fd; /* Fd open on hma file */ int nClient; /* Current number of clients */ Server *aClient[HMA_CLIENT_SLOTS]; /* Local (this process) clients */ u32 *aMap; /* MMapped hma file */ ServerHMA *pNext; /* Next HMA in this process */ dev_t st_dev; ino_t st_ino; }; struct Server { ServerHMA *pHma; /* Hma file object */ int iClient; /* Client id */ Pager *pPager; /* Associated pager object */ int nAlloc; /* Allocated size of aLock[] array */ int nLock; /* Number of entries in aLock[] */ u32 *aLock; /* Mapped lock file */ }; #define SERVER_WRITE_LOCK 3 #define SERVER_READ_LOCK 2 #define SERVER_NO_LOCK 1 static int posixLock(int fd, int iSlot, int eLock, int bBlock){ int res; struct flock l; short aType[4] = {0, F_UNLCK, F_RDLCK, F_WRLCK}; assert( eLock==SERVER_WRITE_LOCK || eLock==SERVER_READ_LOCK || eLock==SERVER_NO_LOCK ); memset(&l, 0, sizeof(l)); l.l_type = aType[eLock]; l.l_whence = SEEK_SET; l.l_start = iSlot*sizeof(u32); l.l_len = 1; res = fcntl(fd, (bBlock ? F_SETLKW : F_SETLK), &l); return (res==0 ? SQLITE_OK : SQLITE_BUSY); } static int serverMapFile(ServerHMA *p){ assert( p->aMap==0 ); p->aMap = mmap(0, HMA_FILE_SIZE, PROT_READ|PROT_WRITE, MAP_SHARED, p->fd, 0); if( p->aMap==0 ){ return SQLITE_ERROR; } return SQLITE_OK; } static void serverDecrHmaRefcount(ServerHMA *pHma){ if( pHma ){ pHma->nClient--; if( pHma->nClient<=0 ){ ServerHMA **pp; if( pHma->aMap ) munmap(pHma->aMap, HMA_FILE_SIZE); if( pHma->fd>=0 ) close(pHma->fd); for(pp=&g_server.pHma; *pp!=pHma; pp=&(*pp)->pNext); *pp = pHma->pNext; sqlite3_free(pHma); } } } static int serverOpenHma(Pager *pPager, const char *zPath, ServerHMA **ppHma){ struct stat sStat; /* Structure populated by stat() */ int res; /* result of stat() */ int rc = SQLITE_OK; /* Return code */ ServerHMA *pHma = 0; assert( sqlite3_mutex_held(g_server.mutex) ); res = stat(zPath, &sStat); if( res!=0 ){ sqlite3_log(SQLITE_CANTOPEN, "Failed to stat(%s)", zPath); rc = SQLITE_ERROR; }else{ for(pHma=g_server.pHma; pHma; pHma=pHma->pNext){ if( sStat.st_dev==pHma->st_dev && sStat.st_ino==pHma->st_ino ) break; } if( pHma==0 ){ int nPath = strlen(zPath); int nByte = sizeof(ServerHMA) + nPath+1 + 4; pHma = (ServerHMA*)sqlite3_malloc(nByte); if( pHma==0 ){ rc = SQLITE_NOMEM; }else{ int i; memset(pHma, 0, nByte); pHma->zName = (char*)&pHma[1]; pHma->nClient = 1; pHma->st_dev = sStat.st_dev; pHma->st_ino = sStat.st_ino; pHma->pNext = g_server.pHma; g_server.pHma = pHma; memcpy(pHma->zName, zPath, nPath); memcpy(&pHma->zName[nPath], "-hma", 5); pHma->fd = open(pHma->zName, O_RDWR|O_CREAT, 0644); if( pHma->fd<0 ){ sqlite3_log(SQLITE_CANTOPEN, "Failed to open(%s)", pHma->zName); rc = SQLITE_ERROR; } if( rc==SQLITE_OK ){ /* Write-lock the DMS slot. If successful, initialize the hma file. */ rc = posixLock(pHma->fd, 0, SERVER_WRITE_LOCK, 0); if( rc==SQLITE_OK ){ res = ftruncate(pHma->fd, HMA_FILE_SIZE); if( res!=0 ){ sqlite3_log(SQLITE_CANTOPEN, "Failed to ftruncate(%s)", pHma->zName ); rc = SQLITE_ERROR; } if( rc==SQLITE_OK ){ rc = serverMapFile(pHma); } if( rc==SQLITE_OK ){ memset(pHma->aMap, 0, HMA_FILE_SIZE); }else{ rc = SQLITE_ERROR; } }else{ rc = serverMapFile(pHma); } for(i=0; rc==SQLITE_OK && i<HMA_CLIENT_SLOTS; i++){ rc = sqlite3PagerRollbackJournal(pPager, i); } if( rc==SQLITE_OK ){ rc = posixLock(pHma->fd, 0, SERVER_READ_LOCK, 1); } } if( rc!=SQLITE_OK ){ serverDecrHmaRefcount(pHma); pHma = 0; } } }else{ pHma->nClient++; } } *ppHma = pHma; return rc; } static u32 *serverPageLockSlot(Server *p, Pgno pgno){ int iSlot = pgno % HMA_PAGELOCK_SLOTS; return &p->pHma->aMap[1 + HMA_CLIENT_SLOTS + iSlot]; } static u32 *serverClientSlot(Server *p, int iClient){ return &p->pHma->aMap[1 + iClient]; } /* ** Close the "connection" and *-hma file. This deletes the object passed ** as the first argument. */ void sqlite3ServerDisconnect(Server *p, sqlite3_file *dbfd){ if( p->pHma ){ ServerHMA *pHma = p->pHma; sqlite3_mutex_enter(g_server.mutex); if( p->iClient>=0 ){ u32 *pSlot = serverClientSlot(p, p->iClient); *pSlot = 0; assert( pHma->aClient[p->iClient]==p ); pHma->aClient[p->iClient] = 0; posixLock(pHma->fd, p->iClient+1, SERVER_NO_LOCK, 0); } if( dbfd && pHma->nClient==1 && SQLITE_OK==sqlite3OsLock(dbfd, SQLITE_LOCK_EXCLUSIVE) ){ unlink(pHma->zName); } serverDecrHmaRefcount(pHma); sqlite3_mutex_leave(g_server.mutex); } sqlite3_free(p->aLock); sqlite3_free(p); } static int serverRollbackClient(Server *p, int iBlock){ int rc; sqlite3_log(SQLITE_NOTICE, "Rolling back failed client %d", iBlock); /* Roll back any journal file for client iBlock. */ rc = sqlite3PagerRollbackJournal(p->pPager, iBlock); /* Clear any locks held by client iBlock from the HMA file. */ if( rc==SQLITE_OK ){ int i; for(i=0; i<HMA_PAGELOCK_SLOTS; i++){ u32 *pSlot = serverPageLockSlot(p, (Pgno)i); u32 v = *pSlot; while( 1 ){ u32 n = v & ~(1 << iBlock); if( (v>>HMA_CLIENT_SLOTS)==iBlock+1 ){ n = n & ((1<<HMA_CLIENT_SLOTS)-1); } if( __sync_val_compare_and_swap(pSlot, v, n)==v ) break; v = *pSlot; } } } return rc; } /* ** Open the *-hma file and "connect" to the system. */ int sqlite3ServerConnect( Pager *pPager, Server **ppOut, int *piClient ){ const char *zPath = sqlite3PagerFilename(pPager, 0); int rc = SQLITE_OK; Server *p; p = (Server*)sqlite3_malloc(sizeof(Server)); if( p==0 ){ rc = SQLITE_NOMEM; }else{ memset(p, 0, sizeof(Server)); p->iClient = -1; p->pPager = pPager; sqlite3_mutex_enter(g_server.mutex); rc = serverOpenHma(pPager, zPath, &p->pHma); /* File is now mapped. Find a free client slot. */ if( rc==SQLITE_OK ){ int i; Server **aClient = p->pHma->aClient; int fd = p->pHma->fd; for(i=0; i<HMA_CLIENT_SLOTS; i++){ if( aClient[i]==0 ){ int res = posixLock(fd, i+1, SERVER_WRITE_LOCK, 0); if( res==SQLITE_OK ){ u32 *pSlot = serverClientSlot(p, i); if( *pSlot ){ rc = serverRollbackClient(p, i); } posixLock(fd, i+1, (!rc ? SERVER_READ_LOCK : SERVER_NO_LOCK), 0); break; } } } if( rc==SQLITE_OK ){ if( i>HMA_CLIENT_SLOTS ){ rc = SQLITE_BUSY; }else{ u32 *pSlot = serverClientSlot(p, i); *piClient = p->iClient = i; aClient[i] = p; *pSlot = 1; } } } sqlite3_mutex_leave(g_server.mutex); } if( rc!=SQLITE_OK ){ sqlite3ServerDisconnect(p, 0); p = 0; } *ppOut = p; return rc; } static int serverOvercomeLock(Server *p, int bWrite, u32 v, int *pbRetry){ int rc = SQLITE_OK; int bLocal = 0; int iBlock = ((int)(v>>HMA_CLIENT_SLOTS))-1; if( iBlock<0 ){ for(iBlock=0; iBlock<HMA_CLIENT_SLOTS; iBlock++){ if( iBlock!=p->iClient && (v & (1<<iBlock)) ) break; } } assert( iBlock<HMA_CLIENT_SLOTS ); sqlite3_mutex_enter(g_server.mutex); if( p->pHma->aClient[iBlock] ){ bLocal = 1; }else{ rc = posixLock(p->pHma->fd, iBlock+1, SERVER_WRITE_LOCK, 0); } if( bLocal==0 && rc==SQLITE_OK ){ rc = serverRollbackClient(p, iBlock); /* Release the lock on slot iBlock */ posixLock(p->pHma->fd, iBlock+1, SERVER_NO_LOCK, 0); if( rc==SQLITE_OK ){ *pbRetry = 1; } }else{ assert( rc==SQLITE_OK || rc==SQLITE_BUSY ); rc = SQLITE_OK; } sqlite3_mutex_leave(g_server.mutex); return rc; } /* ** Begin a transaction. */ int sqlite3ServerBegin(Server *p){ return posixLock(p->pHma->fd, p->iClient+1, SERVER_WRITE_LOCK, 0); } /* ** End a transaction (and release all locks). */ int sqlite3ServerEnd(Server *p){ int i; for(i=0; i<p->nLock; i++){ u32 *pSlot = serverPageLockSlot(p, p->aLock[i]); while( 1 ){ u32 v = *pSlot; u32 n = v; if( (v>>HMA_CLIENT_SLOTS)==p->iClient+1 ){ n = n & ((1 << HMA_CLIENT_SLOTS)-1); } n = n & ~(1 << p->iClient); if( __sync_val_compare_and_swap(pSlot, v, n)==v ) break; } } p->nLock = 0; return posixLock(p->pHma->fd, p->iClient+1, SERVER_READ_LOCK, 0); } /* ** Release all write-locks. */ int sqlite3ServerReleaseWriteLocks(Server *p){ int rc = SQLITE_OK; return rc; } /* ** Lock page pgno for reading (bWrite==0) or writing (bWrite==1). */ int sqlite3ServerLock(Server *p, Pgno pgno, int bWrite){ int rc = SQLITE_OK; /* Grow the aLock[] array, if required */ if( p->nLock==p->nAlloc ){ int nNew = p->nAlloc ? p->nAlloc*2 : 128; u32 *aNew; aNew = (u32*)sqlite3_realloc(p->aLock, sizeof(u32)*nNew); if( aNew==0 ){ rc = SQLITE_NOMEM_BKPT; }else{ p->aLock = aNew; p->nAlloc = nNew; } } if( rc==SQLITE_OK ){ u32 *pSlot = serverPageLockSlot(p, pgno); u32 v = *pSlot; /* Check if the required lock is already held. If so, exit this function ** early. Otherwise, add an entry to the aLock[] array to record the fact ** that the lock may need to be released. */ if( bWrite ){ int iLock = ((int)(v>>HMA_CLIENT_SLOTS)) - 1; if( iLock==p->iClient ) goto server_lock_out; if( iLock<0 ){ p->aLock[p->nLock++] = pgno; } }else{ if( v & (1<<p->iClient) ) goto server_lock_out; p->aLock[p->nLock++] = pgno; } while( 1 ){ u32 n; while( (bWrite && (v & ~(1 << p->iClient))) || (v >> HMA_CLIENT_SLOTS) ){ int bRetry = 0; rc = serverOvercomeLock(p, bWrite, v, &bRetry); if( rc!=SQLITE_OK ) goto server_lock_out; if( bRetry==0 ){ /* There is a conflicting lock. Cannot obtain this lock. */ sqlite3_log(SQLITE_BUSY_DEADLOCK, "Conflict at page %d", (int)pgno); rc = SQLITE_BUSY_DEADLOCK; goto server_lock_out; } v = *pSlot; } if( bWrite ){ n = v | ((p->iClient+1) << HMA_CLIENT_SLOTS); }else{ n = v | (1 << p->iClient); } if( __sync_val_compare_and_swap(pSlot, v, n)==v ) break; v = *pSlot; } } server_lock_out: return rc; } #endif /* ifdef SQLITE_SERVER_EDITION */ |
Changes to src/sqlite.h.in.
︙ | ︙ | |||
493 494 495 496 497 498 499 500 501 502 503 504 505 506 | #define SQLITE_IOERR_GETTEMPPATH (SQLITE_IOERR | (25<<8)) #define SQLITE_IOERR_CONVPATH (SQLITE_IOERR | (26<<8)) #define SQLITE_IOERR_VNODE (SQLITE_IOERR | (27<<8)) #define SQLITE_IOERR_AUTH (SQLITE_IOERR | (28<<8)) #define SQLITE_LOCKED_SHAREDCACHE (SQLITE_LOCKED | (1<<8)) #define SQLITE_BUSY_RECOVERY (SQLITE_BUSY | (1<<8)) #define SQLITE_BUSY_SNAPSHOT (SQLITE_BUSY | (2<<8)) #define SQLITE_CANTOPEN_NOTEMPDIR (SQLITE_CANTOPEN | (1<<8)) #define SQLITE_CANTOPEN_ISDIR (SQLITE_CANTOPEN | (2<<8)) #define SQLITE_CANTOPEN_FULLPATH (SQLITE_CANTOPEN | (3<<8)) #define SQLITE_CANTOPEN_CONVPATH (SQLITE_CANTOPEN | (4<<8)) #define SQLITE_CORRUPT_VTAB (SQLITE_CORRUPT | (1<<8)) #define SQLITE_READONLY_RECOVERY (SQLITE_READONLY | (1<<8)) #define SQLITE_READONLY_CANTLOCK (SQLITE_READONLY | (2<<8)) | > | 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 | #define SQLITE_IOERR_GETTEMPPATH (SQLITE_IOERR | (25<<8)) #define SQLITE_IOERR_CONVPATH (SQLITE_IOERR | (26<<8)) #define SQLITE_IOERR_VNODE (SQLITE_IOERR | (27<<8)) #define SQLITE_IOERR_AUTH (SQLITE_IOERR | (28<<8)) #define SQLITE_LOCKED_SHAREDCACHE (SQLITE_LOCKED | (1<<8)) #define SQLITE_BUSY_RECOVERY (SQLITE_BUSY | (1<<8)) #define SQLITE_BUSY_SNAPSHOT (SQLITE_BUSY | (2<<8)) #define SQLITE_BUSY_DEADLOCK (SQLITE_BUSY | (3<<8)) #define SQLITE_CANTOPEN_NOTEMPDIR (SQLITE_CANTOPEN | (1<<8)) #define SQLITE_CANTOPEN_ISDIR (SQLITE_CANTOPEN | (2<<8)) #define SQLITE_CANTOPEN_FULLPATH (SQLITE_CANTOPEN | (3<<8)) #define SQLITE_CANTOPEN_CONVPATH (SQLITE_CANTOPEN | (4<<8)) #define SQLITE_CORRUPT_VTAB (SQLITE_CORRUPT | (1<<8)) #define SQLITE_READONLY_RECOVERY (SQLITE_READONLY | (1<<8)) #define SQLITE_READONLY_CANTLOCK (SQLITE_READONLY | (2<<8)) |
︙ | ︙ |
Changes to src/sqliteInt.h.
︙ | ︙ | |||
1097 1098 1099 1100 1101 1102 1103 1104 1105 1106 1107 1108 1109 1110 | */ #include "btree.h" #include "vdbe.h" #include "pager.h" #include "pcache.h" #include "os.h" #include "mutex.h" /* The SQLITE_EXTRA_DURABLE compile-time option used to set the default ** synchronous setting to EXTRA. It is no longer supported. */ #ifdef SQLITE_EXTRA_DURABLE # warning Use SQLITE_DEFAULT_SYNCHRONOUS=3 instead of SQLITE_EXTRA_DURABLE # define SQLITE_DEFAULT_SYNCHRONOUS 3 | > | 1097 1098 1099 1100 1101 1102 1103 1104 1105 1106 1107 1108 1109 1110 1111 | */ #include "btree.h" #include "vdbe.h" #include "pager.h" #include "pcache.h" #include "os.h" #include "mutex.h" #include "server.h" /* The SQLITE_EXTRA_DURABLE compile-time option used to set the default ** synchronous setting to EXTRA. It is no longer supported. */ #ifdef SQLITE_EXTRA_DURABLE # warning Use SQLITE_DEFAULT_SYNCHRONOUS=3 instead of SQLITE_EXTRA_DURABLE # define SQLITE_DEFAULT_SYNCHRONOUS 3 |
︙ | ︙ |
Changes to src/vdbeaux.c.
︙ | ︙ | |||
2635 2636 2637 2638 2639 2640 2641 | /* Lock all btrees used by the statement */ sqlite3VdbeEnter(p); /* Check for one of the special errors */ mrc = p->rc & 0xff; isSpecialError = mrc==SQLITE_NOMEM || mrc==SQLITE_IOERR | | > | | | | | | 2635 2636 2637 2638 2639 2640 2641 2642 2643 2644 2645 2646 2647 2648 2649 2650 2651 2652 2653 2654 2655 2656 2657 2658 2659 2660 2661 2662 2663 2664 | /* Lock all btrees used by the statement */ sqlite3VdbeEnter(p); /* Check for one of the special errors */ mrc = p->rc & 0xff; isSpecialError = mrc==SQLITE_NOMEM || mrc==SQLITE_IOERR || mrc==SQLITE_INTERRUPT || mrc==SQLITE_FULL || p->rc==SQLITE_BUSY_DEADLOCK; if( isSpecialError ){ /* If the query was read-only and the error code is SQLITE_INTERRUPT ** or SQLITE_BUSY_SERVER, no rollback is necessary. Otherwise, at ** least a savepoint transaction must be rolled back to restore the ** database to a consistent state. ** ** Even if the statement is read-only, it is important to perform ** a statement or transaction rollback operation. If the error ** occurred while writing to the journal, sub-journal or database ** file as part of an effort to free up cache space (see function ** pagerStress() in pager.c), the rollback is required to restore ** the pager to a consistent state. */ if( !p->readOnly || (mrc!=SQLITE_INTERRUPT && mrc!=SQLITE_BUSY) ){ if( (mrc==SQLITE_NOMEM || mrc==SQLITE_FULL) && p->usesStmtJournal ){ eStatementOp = SAVEPOINT_ROLLBACK; }else{ /* We are forced to roll back the active transaction. Before doing ** so, abort any other statements this handle currently has active. */ sqlite3RollbackAll(db, SQLITE_ABORT_ROLLBACK); |
︙ | ︙ |
Changes to test/permutations.test.
︙ | ︙ | |||
268 269 270 271 272 273 274 275 276 277 278 279 280 281 | fts3corrupt3.test fts3misc.test } test_suite "fts5" -prefix "" -description { All FTS5 tests. } -files [glob -nocomplain $::testdir/../ext/fts5/test/*.test] test_suite "fts5-light" -prefix "" -description { All FTS5 tests. } -files [ test_set \ [glob -nocomplain $::testdir/../ext/fts5/test/*.test] \ -exclude *corrupt* *fault* *big* *fts5aj* | > > > > > > > > | 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 | fts3corrupt3.test fts3misc.test } test_suite "fts5" -prefix "" -description { All FTS5 tests. } -files [glob -nocomplain $::testdir/../ext/fts5/test/*.test] test_suite "server" -prefix "" -description { All server-edition tests. } -files [ test_set \ [glob -nocomplain $::testdir/server*.test] \ -exclude *server1.test ] test_suite "fts5-light" -prefix "" -description { All FTS5 tests. } -files [ test_set \ [glob -nocomplain $::testdir/../ext/fts5/test/*.test] \ -exclude *corrupt* *fault* *big* *fts5aj* |
︙ | ︙ |
Added test/server2.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 | # 2017 April 25 # # 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 implements regression tests for SQLite library. The # focus of this script is testing the server mode of SQLite. # set testdir [file dirname $argv0] source $testdir/tester.tcl set testprefix server2 #------------------------------------------------------------------------- # Check that the *-hma file is deleted correctly. # do_execsql_test 1.0 { CREATE TABLE t1(a, b); } {} do_test 1.1 { file exists test.db-hma } {1} do_test 1.2 { db close file exists test.db-hma } {0} do_test 1.3 { sqlite3 db test.db db eval { CREATE TABLE t2(a, b) } sqlite3 db2 test.db db2 eval { CREATE TABLE t3(a, b) } file exists test.db-hma } {1} do_test 1.4 { db2 close file exists test.db-hma } {1} do_test 1.5 { db close file exists test.db-hma } {0} #------------------------------------------------------------------------- # reset_db sqlite3 db2 test.db do_execsql_test 2.0 { CREATE TABLE t1(a, b); CREATE TABLE t2(c, d); } # Two concurrent transactions committed. # do_test 2.1 { db eval { BEGIN; INSERT INTO t1 VALUES(1, 2); } db2 eval { BEGIN; INSERT INTO t2 VALUES(3, 4); } } {} do_test 2.2 { lsort [glob test.db*] } {test.db test.db-hma test.db-journal0 test.db-journal1} do_test 2.3.1 { db eval COMMIT } {} do_test 2.3.2 { db2 eval COMMIT } {} do_execsql_test 2.4 {SELECT * FROM t1, t2} {1 2 3 4} do_test 2.5 { lsort [glob test.db*] } {test.db test.db-hma test.db-journal0 test.db-journal1} do_test 2.6 { execsql {BEGIN} execsql {INSERT INTO t1 VALUES(5, 6)} execsql {BEGIN} db2 catchsql {INSERT INTO t1 VALUES(7, 8)} db2 } {1 {database is locked}} do_test 2.7 { # Transaction is automatically rolled back in this case. sqlite3_get_autocommit db2 } {1} do_test 2.8 { execsql COMMIT execsql { SELECT * FROM t1 } db2 } {1 2 5 6} finish_test |
Added test/server3.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 | # 2017 April 25 # # 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 implements regression tests for SQLite library. The # focus of this script is testing the server mode of SQLite. # set testdir [file dirname $argv0] source $testdir/tester.tcl source $testdir/lock_common.tcl set testprefix server3 db close do_multiclient_test tn { do_test $tn.1 { sql1 { CREATE TABLE t1(a, b) } sql2 { CREATE TABLE t2(a, b) } } {} do_test $tn.2 { sql1 { INSERT INTO t2 VALUES(1, 2); BEGIN; INSERT INTO t1 VALUES(1, 2); } } {} do_test $tn.3 { csql2 { SELECT * FROM t1 } } {1 {database is locked}} do_test $tn.4 { csql2 { SELECT * FROM t1 } } {1 {database is locked}} do_test $tn.5 { sql2 { SELECT * FROM t2 } } {1 2} } finish_test |
Added test/servercrash.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 | # 2017 April 27 # # 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 servercrash ifcapable !crashtest { finish_test return } do_not_use_codec do_execsql_test 1.0 { PRAGMA page_siBlockze = 4096; PRAGMA auto_vacuum = OFF; CREATE TABLE t1(a, b); CREATE TABLE t2(c, d); INSERT INTO t1 VALUES(1, 2), (3, 4); INSERT INTO t2 VALUES(1, 2), (3, 4); } for {set i 0} {$i < 10} {incr i} { do_test 1.$i.1 { crashsql -delay 1 -file test.db { INSERT INTO t1 VALUES(5, 6) } } {1 {child process exited abnormally}} do_execsql_test 1.$i.2 { SELECT * FROM t1 } {1 2 3 4} } for {set i 0} {$i < 10} {incr i} { do_test 2.$i.1 { crashsql -delay 1 -file test.db { INSERT INTO t1 VALUES(5, 6) } } {1 {child process exited abnormally}} do_test 2.$i.2 { sqlite3 dbX test.db execsql { SELECT * FROM t1 } dbX } {1 2 3 4} dbX close } db close for {set i 0} {$i < 10} {incr i} { do_test 3.$i.1 { crashsql -delay 1 -file test.db { INSERT INTO t1 VALUES(5, 6) } } {1 {child process exited abnormally}} sqlite3 db test.db do_execsql_test 3.$i.2 { SELECT * FROM t1 } {1 2 3 4} db close } finish_test |
Changes to test/tester.tcl.
︙ | ︙ | |||
582 583 584 585 586 587 588 589 590 591 592 593 594 595 | # Create a test database # proc reset_db {} { catch {db close} forcedelete test.db forcedelete test.db-journal forcedelete test.db-wal sqlite3 db ./test.db set ::DB [sqlite3_connection_pointer db] if {[info exists ::SETUP_SQL]} { db eval $::SETUP_SQL } } reset_db | > > > > | 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 | # Create a test database # proc reset_db {} { catch {db close} forcedelete test.db forcedelete test.db-journal forcedelete test.db-wal for {set i 0} {$i < 16} {incr i} { forcedelete test.db-journal$i } sqlite3 db ./test.db set ::DB [sqlite3_connection_pointer db] if {[info exists ::SETUP_SQL]} { db eval $::SETUP_SQL } } reset_db |
︙ | ︙ |
Changes to tool/mksqlite3c.tcl.
︙ | ︙ | |||
110 111 112 113 114 115 116 117 118 119 120 121 122 123 | os_win.h os.h pager.h parse.h pcache.h pragma.h rtree.h sqlite3session.h sqlite3.h sqlite3ext.h sqlite3rbu.h sqliteicu.h sqliteInt.h sqliteLimit.h | > | 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 | os_win.h os.h pager.h parse.h pcache.h pragma.h rtree.h server.h sqlite3session.h sqlite3.h sqlite3ext.h sqlite3rbu.h sqliteicu.h sqliteInt.h sqliteLimit.h |
︙ | ︙ | |||
315 316 317 318 319 320 321 322 323 324 325 326 327 328 | bitvec.c pcache.c pcache1.c rowset.c pager.c wal.c btmutex.c btree.c backup.c vdbemem.c vdbeaux.c | > | 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 | bitvec.c pcache.c pcache1.c rowset.c pager.c wal.c server.c btmutex.c btree.c backup.c vdbemem.c vdbeaux.c |
︙ | ︙ |