-C Merge\slatest\strunk\schanges\swith\sthis\sbranch.
-D 2017-07-07T16:40:28.683
+C Support\sread-only\sMVCC\stransactions\sin\sserver-mode.\sStarted\susing\s"BEGIN\nREADONLY".
+D 2017-07-08T20:46:17.624
F Makefile.in 081e48dfe7f995d57ce1a88ddf4d2917b4349158648a6cd45b42beae30de3a12
F Makefile.linux-gcc 7bc79876b875010e8c8f9502eb935ca92aa3c434
F Makefile.msc 4ebb1d257cac7fb1bcb4ba59278416d410ff1c4bf59447a9c37a415f3516056a
F src/backup.c faf17e60b43233c214aae6a8179d24503a61e83b
F src/bitvec.c 17ea48eff8ba979f1f5b04cc484c7bb2be632f33
F src/btmutex.c 0e9ce2d56159b89b9bc8e197e023ee11e39ff8ca
-F src/btree.c 6ff4027f896c6d7b653e4ef20c4fc7ee52ca0510dec16e76cce0b34d4eb8ead7
+F src/btree.c 0e1bcecde07e3e022a1a1f16cdfa6817267c25eb4a48ea21fe9359b973837762
F src/btree.h 3edc5329bc59534d2d15b4f069a9f54b779a7e51289e98fa481ae3c0e526a5ca
F src/btreeInt.h a392d353104b4add58b4a59cb185f5d5693dde832c565b77d8d4c343ed98f610
-F src/build.c 74108007d286232fb4290464ee5452fa860c26215f8caa0e6c7cbf69a6fafe8f
+F src/build.c 32107046e8cda19a917b4a397175c7eb5f2e234d19fae2fc6164859e4a3d4704
F src/callback.c 8e14b60d1ed1c87c02cb5f121ecda99224f2aea6524a77ee6f72c9b5c7110f84
F src/complete.c a3634ab1e687055cd002e11b8f43eb75c17da23e
F src/ctime.c 928954802b1397d9fb1378c7eb702c94b4735bbab1d5793e21b6a77734f56a1b
F src/os_unix.c d3c3a186ceb27d363f7a5e281ece4eb32b56e173a331e153a761d280034e3212
F src/os_win.c 2a6c73eef01c51a048cc4ddccd57f981afbec18a
F src/os_win.h 7b073010f1451abe501be30d12f6bc599824944a
-F src/pager.c 542bf64a097f84ab37319b90b5f2db1a4aab21968252c9e9d242eb85cf75f55d
-F src/pager.h 81d2c28e92e543cb1ed977f44810d04c34be2a23d7782bb02548d86a4b9ae657
-F src/parse.y 71bf06b02f567232deabe258641b03e833e93c4dee61a120765ad74c13e7faec
+F src/pager.c 7d0bec5dc1411cf3a5771d8608953f2eaa856dcd506c3a444060897139f7eb21
+F src/pager.h 857d21f70acefc76e5bae4584f0133e754a2448b197d1e79b7179f26f9a11bc1
+F src/parse.y f732ca9ef4dbee9ba19d6d30d2d1f7ac0834d8642515dedbd8f6ebd16ab2a8e3
F src/pcache.c 62835bed959e2914edd26afadfecce29ece0e870
F src/pcache.h 521bb9610d38ef17a3cc9b5ddafd4546c2ea67fa3d0e464823d73c2a28d50e11
F src/pcache1.c 1195a21fe28e223e024f900b2011e80df53793f0356a24caace4188b098540dc
F src/resolve.c 6aa1fb1212e601f65b983ee1215d69a591986c8f97a3805c425c625a53839539
F src/rowset.c 7b7e7e479212e65b723bf40128c7b36dc5afdfac
F src/select.c c9f7b7196e196e93979665680d055a789938b8e025556097bf484b184c0dd576
-F src/server.c 4b76241f63e07683fe30db6f6b8a334867b75c4cc5e87a8374b649cf6b8b0daa
-F src/server.h 31645080b17836bcb193a3e5ea8e60d7ae3e4cee59e8f9450b17cdbf452a2b1e
+F src/server.c f7b99d79a1b83d258bc0ef96ba715f8dd02749c79cd297d0dd976b4e3a4d2b46
+F src/server.h a8477ae61546a9a4c8e06dd31936b06abf18bd086ae6a183ed57f721a3a194b4
F src/shell.c a2b148e4ca8eb81b96e1050207c83d4a16ea6bf9182374faee4dd2a43628c291
F src/sqlite.h.in 7c783bb985825a13dd4c8294aade14bc4714275e142e2a5b21fc2a618cd669fb
F src/sqlite3.rc 5121c9e10c3964d5755191c80dd1180c122fc3a8
F src/sqlite3ext.h 58fd0676d3111d02e62e5a35992a7d3da5d3f88753acc174f2d37b774fbbdd28
-F src/sqliteInt.h e6e015985dbab8b60617b039e2eb7ce79fa06494b088acabee4fd3c1c123abc8
+F src/sqliteInt.h 908c37c3afa8ef795159541cebe909ff52612a9bb75301841db1afd1a242e746
F src/sqliteLimit.h 1513bfb7b20378aa0041e7022d04acb73525de35b80b252f1b83fedb4de6a76b
F src/status.c a9e66593dfb28a9e746cba7153f84d49c1ddc4b1
F src/table.c b46ad567748f24a326d9de40e5b9659f96ffff34
F src/tclsqlite.c 2c29b0b76e91edfd1b43bf135c32c8674710089197327682b6b7e6af88062c3d
F src/test1.c 1c0726cdf7389ed053a9b9aa0dc3c63f3b9bbc607a25decae6549682008510b3
-F src/test2.c 3efb99ab7f1fc8d154933e02ae1378bac9637da5
+F src/test2.c 824e16d2ff3b57dc3680a5635d049cc889492f95910368ec1ffe2ad44ca45a7f
F src/test3.c b8434949dfb8aff8dfa082c8b592109e77844c2135ed3c492113839b6956255b
F src/test4.c 18ec393bb4d0ad1de729f0b94da7267270f3d8e6
F src/test5.c 328aae2c010c57a9829d255dc099d6899311672d
F src/utf.c 699001c79f28e48e9bcdf8a463da029ea660540c
F src/util.c fc081ec6f63448dcd80d3dfad35baecfa104823254a815b081a4d9fe76e1db23
F src/vacuum.c 874c0f2f15ab2908748297d587d22d485ea96d55aaec91d4775dddb2e24d2ecf
-F src/vdbe.c adc8a378710ec2376101483cc8a5f499539ee9bbebfb2a784f3370704d5d44ad
+F src/vdbe.c e4493b09cc603a1b031c982785e94bbe637799c9e7fc974d37036c04ad2d79e0
F src/vdbe.h dde459b1e8a02b8445ecfd5959f38cd5ebb6b0ad392d491d8b159ac8193d231a
F src/vdbeInt.h 8dd7548d86a98b77b6ed910d8014d0e9e0666356b56f3d16ab7063aad4f102f5
F src/vdbeapi.c 899d8f021c89ab348708b3a9b00b855f5ecc3c0f949a75359a61a3c621021281
F src/vdbetrace.c 41963d5376f0349842b5fc4aaaaacd7d9cdc0834
F src/vtab.c 35b9bdc2b41de32a417141d12097bcc4e29a77ed7cdb8f836d1d2305d946b61b
F src/vxworks.h d2988f4e5a61a4dfe82c6524dd3d6e4f2ce3cdb9
-F src/wal.c 8f71654244baf38b95a277e79677d5444737c326182ba81476bc101c001f2e07
+F src/wal.c 1b208938ba3c33cc419f6240d914f68a147e9abb336c578d4c153dd1579d40f8
F src/wal.h 739d92494eb18b6d8f3e353e66c10eb8f94534bafd336ece9f3f60235317ea08
F src/walker.c a7ca64ce08a83a20d32186fbe06bca9234e348cfcf07959ee322fdc3e8a6173a
F src/where.c e7cc80943459ade4ccaeb5c7c10f8a0db8b797e0b0f2509de7efc557b76bf7b6
F test/pcache.test c8acbedd3b6fd0f9a7ca887a83b11d24a007972b
F test/pcache2.test af7f3deb1a819f77a6d0d81534e97d1cf62cd442
F test/percentile.test 4243af26b8f3f4555abe166f723715a1f74c77ff
-F test/permutations.test 676872085bdaa9a4c789bffd2f1ebbebc22262773bf695e05d19d0085de308f4
+F test/permutations.test 9e7fb13778ca3b7aa357df382fbb0933a90980223f25b3fc9db964e33ff2aaa2
F test/pragma.test 1e94755164a3a3264cd39836de4bebcb7809e5f8
F test/pragma2.test e5d5c176360c321344249354c0c16aec46214c9f
F test/pragma3.test 14c12bc5352b1e100e0b6b44f371053a81ccf8ed
F test/server1.test 46803bd3fe8b99b30dbc5ff38ffc756f5c13a118
F test/server2.test 277b4b1f142232e1f992ea17b587474c32e41e2f321c01acdd49ef5ffee99ee6
F test/server3.test 3667fea050a248508d46b5eccb8a5561055f802016dc0d701e6b6f035ecc55ae
+F test/server4.test 039d93f1d46011261edc16cb076673ca7acb377d81e5314df9267477d6dd93e7
F test/servercrash.test 816c132b26af008067cab2913783f67006d4003e3988f3f3ee1075742f6e0a6c
F test/serverwal.test 2d066ba70db35e28f0115a57385cf606513c56d6e2861119c2defb7471e4e2d9
F test/session.test 78fa2365e93d3663a6e933f86e7afc395adf18be
F tool/max-limits.c cbb635fbb37ae4d05f240bfb5b5270bb63c54439
F tool/mkautoconfamal.sh e855df211ecbcc7131dee817110ff386cfb112f7
F tool/mkctimec.tcl dd183b73ae1c28249669741c250525f0407e579a70482371668fd5f130d9feb3
-F tool/mkkeywordhash.c 2e852ac0dfdc5af18886dc1ce7e9676d11714ae3df0a282dc7d90b3a0fe2033c
+F tool/mkkeywordhash.c 969c50301da61d73c4c6d0661c1d1abc6c25ba35f1212832d0b12965f90505ab
F tool/mkmsvcmin.tcl cbd93f1cfa3a0a9ae56fc958510aa3fc3ac65e29cb111716199e3d0e66eefaa4
F tool/mkopcodec.tcl d1b6362bd3aa80d5520d4d6f3765badf01f6c43c
F tool/mkopcodeh.tcl a01d2c1d8a6205b03fc635adf3735b4c523befd3
F vsixtest/vsixtest.vcxproj.data 2ed517e100c66dc455b492e1a33350c1b20fbcdc
F vsixtest/vsixtest.vcxproj.filters 37e51ffedcdb064aad6ff33b6148725226cd608e
F vsixtest/vsixtest_TemporaryKey.pfx e5b1b036facdb453873e7084e1cae9102ccc67a0
-P a3a9a2e1899cc963315590ef4666972e9d92986843706a551962ed16661a19b2 281ad505d7a112c5524ea9fb195dfc46f819a58f79f42b37b12fbfe46a089ff2
-R 176ad25effe94068fc26940426749751
+P 216c757f9289af818482253f77b0b2c9b09e8a478caf67d70a4a6da2fabbc642
+R aec492ec515bdebef9944fc5b7624f8f
U dan
-Z fbcc844a03f0648283866c1e95fd38a8
+Z 17a24e9dfd744bc2511bd64d17405f97
-216c757f9289af818482253f77b0b2c9b09e8a478caf67d70a4a6da2fabbc642
\ No newline at end of file
+5a043aa8dd0751e644c495a59deea5fe05da905f49c664d978fe477f9240bc37
\ No newline at end of file
assert( sqlite3_mutex_held(pBt->mutex) );
assert( pBt->pPage1==0 );
- rc = sqlite3PagerSharedLock(pBt->pPager);
+ rc = sqlite3PagerSharedLock(pBt->pPager, pBt->db->readonlyTrans);
if( rc!=SQLITE_OK ) return rc;
rc = btreeGetPage(pBt, 1, &pPage1, 0);
if( rc!=SQLITE_OK ) return rc;
}
v = sqlite3GetVdbe(pParse);
if( !v ) return;
- if( type!=TK_DEFERRED ){
+ if( type!=TK_DEFERRED && type!=TK_READONLY ){
for(i=0; i<db->nDb; i++){
sqlite3VdbeAddOp2(v, OP_Transaction, i, (type==TK_EXCLUSIVE)+1);
sqlite3VdbeUsesBtree(v, i);
}
}
- sqlite3VdbeAddOp0(v, OP_AutoCommit);
+ sqlite3VdbeAddOp3(v, OP_AutoCommit, 0, 0, type);
}
/*
}else
#endif
{
- i64 iOffset = (pgno-1)*(i64)pPager->pageSize;
- rc = sqlite3OsRead(pPager->fd, pPg->pData, pgsz, iOffset);
- if( rc==SQLITE_IOERR_SHORT_READ ){
- rc = SQLITE_OK;
+#ifdef SQLITE_SERVER_EDITION
+ u8 *pData = 0;
+ if( pagerIsServer(pPager) ){
+ sqlite3ServerReadPage(pPager->pServer, pgno, &pData);
+ if( pData ){
+ memcpy(pPg->pData, pData, pgsz);
+ }
}
+ if( pData==0 ){
+#endif
+ i64 iOffset = (pgno-1)*(i64)pPager->pageSize;
+ rc = sqlite3OsRead(pPager->fd, pPg->pData, pgsz, iOffset);
+ if( rc==SQLITE_IOERR_SHORT_READ ){
+ rc = SQLITE_OK;
+ }
+#ifdef SQLITE_SERVER_EDITION
+ if( pagerIsServer(pPager) ){
+ sqlite3ServerEndReadPage(pPager->pServer, pgno);
+ }
+ }
+#endif
+
}
if( pgno==1 ){
** occurs while locking the database, checking for a hot-journal file or
** rolling back a journal file, the IO error code is returned.
*/
-int sqlite3PagerSharedLock(Pager *pPager){
+int sqlite3PagerSharedLock(Pager *pPager, int bReadonly){
int rc = SQLITE_OK; /* Return code */
/* This routine is only called from b-tree and only when there are no
assert( rc==SQLITE_OK );
assert( sqlite3PagerRefcount(pPager)==0 );
pager_reset(pPager);
- rc = sqlite3ServerBegin(pPager->pServer);
- if( rc==SQLITE_OK){
+ rc = sqlite3ServerBegin(pPager->pServer, bReadonly);
+ if( rc==SQLITE_OK ){
rc = sqlite3ServerLock(pPager->pServer, 1, 0, 0);
}
}
p->pgno = pPg->pgno;
p->pNext = pPager->pServerPage;
pPager->pServerPage = p;
+ memcpy(p->aData, pPg->pData, pPager->pageSize);
}
#endif
int state = pPager->eState;
assert( state==PAGER_OPEN || state==PAGER_READER );
if( state==PAGER_OPEN ){
- rc = sqlite3PagerSharedLock(pPager);
+ rc = sqlite3PagerSharedLock(pPager, 0);
}
if( pPager->eState==PAGER_READER ){
assert( rc==SQLITE_OK );
int sqlite3PagerRollback(Pager*);
int sqlite3PagerOpenSavepoint(Pager *pPager, int n);
int sqlite3PagerSavepoint(Pager *pPager, int op, int iSavepoint);
-int sqlite3PagerSharedLock(Pager *pPager);
+int sqlite3PagerSharedLock(Pager *pPager, int bReadonly);
#ifndef SQLITE_OMIT_WAL
int sqlite3PagerCheckpoint(Pager *pPager, sqlite3*, int, int*, int*);
trans_opt ::= TRANSACTION nm.
%type transtype {int}
transtype(A) ::= . {A = TK_DEFERRED;}
+transtype(A) ::= READONLY(X). {A = @X; /*A-overwrites-X*/}
transtype(A) ::= DEFERRED(X). {A = @X; /*A-overwrites-X*/}
transtype(A) ::= IMMEDIATE(X). {A = @X; /*A-overwrites-X*/}
transtype(A) ::= EXCLUSIVE(X). {A = @X; /*A-overwrites-X*/}
struct Server {
ServerDb *pDb; /* Database object */
Pager *pPager; /* Associated pager object */
+ int eTrans; /* One of the SERVER_TRANS_xxx values */
int iTransId; /* Current transaction id (or -1) */
- int iCommitId; /* Current comit id (or 0) */
+ int iCommitId; /* Current commit id (or 0) */
int nAlloc; /* Allocated size of aLock[] array */
int nLock; /* Number of entries in aLock[] */
u32 *aLock; /* Mapped lock file */
Server *pNext; /* Next in pCommit or pReader list */
};
+/*
+** Possible values for Server.eTrans.
+*/
+#define SERVER_TRANS_NONE 0
+#define SERVER_TRANS_READONLY 1
+#define SERVER_TRANS_READWRITE 2
+
#define SERVER_WRITE_LOCK 3
#define SERVER_READ_LOCK 2
#define SERVER_NO_LOCK 1
/*
** Begin a transaction.
*/
-int sqlite3ServerBegin(Server *p){
+int sqlite3ServerBegin(Server *p, int bReadonly){
int rc = SQLITE_OK;
- if( p->iTransId<0 ){
+ if( p->eTrans==SERVER_TRANS_NONE ){
int id;
ServerDb *pDb = p->pDb;
u32 t;
+ assert( p->iTransId<0 );
+ assert( p->pNext==0 );
sqlite3_mutex_enter(pDb->mutex);
- /* Find a transaction id to use */
- rc = SQLITE_BUSY;
- t = pDb->transmask;
- for(id=0; id<HMA_MAX_TRANSACTIONID; id++){
- if( (t & (1 << id))==0 ){
- t = t | (1 << id);
- rc = SQLITE_OK;
- break;
+ if( bReadonly ){
+ Server *pIter;
+ p->iCommitId = pDb->iNextCommit;
+ for(pIter=pDb->pCommit; pIter; pIter=pIter->pNext){
+ if( pIter->iCommitId<p->iCommitId ){
+ p->iCommitId = pIter->iCommitId;
+ }
}
+ p->pNext = pDb->pReader;
+ pDb->pReader = p;
+ p->eTrans = SERVER_TRANS_READONLY;
+ }else{
+ /* Find a transaction id to use */
+ rc = SQLITE_BUSY;
+ t = pDb->transmask;
+ for(id=0; id<HMA_MAX_TRANSACTIONID; id++){
+ if( (t & (1 << id))==0 ){
+ t = t | (1 << id);
+ rc = SQLITE_OK;
+ break;
+ }
+ }
+ pDb->transmask = t;
+ p->eTrans = SERVER_TRANS_READWRITE;
}
- pDb->transmask = t;
sqlite3_mutex_leave(pDb->mutex);
- if( rc==SQLITE_OK ){
+ if( rc==SQLITE_OK && bReadonly==0 ){
ServerJournal *pJrnl = &pDb->aJrnl[id];
sqlite3PagerServerJournal(p->pPager, pJrnl->jfd, pJrnl->zJournal);
p->iTransId = id;
*/
int sqlite3ServerEnd(Server *p){
int rc = SQLITE_OK;
- Server **pp;
- ServerDb *pDb = p->pDb;
- ServerPage *pFree = 0;
- ServerPage *pPg = 0;
- sqlite3_mutex_enter(pDb->mutex);
-
- serverReleaseLocks(p);
+ if( p->eTrans!=SERVER_TRANS_NONE ){
+ Server **pp;
+ ServerDb *pDb = p->pDb;
+ ServerPage *pFree = 0;
+ ServerPage *pPg = 0;
- /* Clear the bit in the transaction mask. */
- pDb->transmask &= ~((u32)1 << p->iTransId);
+ sqlite3_mutex_enter(pDb->mutex);
- /* If this connection is in the committers list, remove it. */
- for(pp=&pDb->pCommit; *pp; pp = &((*pp)->pNext)){
- if( *pp==p ){
+ if( p->eTrans==SERVER_TRANS_READONLY ){
+ /* Remove the connection from the readers list */
+ for(pp=&pDb->pReader; *pp!=p; pp = &((*pp)->pNext));
*pp = p->pNext;
- break;
- }
- }
+ }else{
+ serverReleaseLocks(p);
- /* See if it is possible to free any ServerPage records. If so, remove
- ** them from the linked list and hash table, but do not call sqlite3_free()
- ** on them until the mutex has been released. */
- if( pDb->pPgFirst ){
- Server *pIter;
- int iOldest = 0x7FFFFFFF;
- for(pIter=pDb->pReader; pIter; pIter=pIter->pNext){
- iOldest = MIN(iOldest, pIter->iCommitId);
- }
- for(pIter=pDb->pCommit; pIter; pIter=pIter->pNext){
- iOldest = MIN(iOldest, pIter->iCommitId);
+ /* Clear the bit in the transaction mask. */
+ pDb->transmask &= ~((u32)1 << p->iTransId);
+
+ /* If this connection is in the committers list, remove it. */
+ for(pp=&pDb->pCommit; *pp; pp = &((*pp)->pNext)){
+ if( *pp==p ){
+ *pp = p->pNext;
+ break;
+ }
+ }
}
- pFree = pDb->pPgFirst;
- for(pPg=pDb->pPgFirst; pPg && pPg->iCommitId<iOldest; pPg=pPg->pNext){
- if( pPg->pHashPrev ){
- pPg->pHashPrev->pHashNext = pPg->pHashNext;
- }else{
- int iHash = pPg->pgno % HMA_HASH_SIZE;
- assert( pDb->apPg[iHash]==pPg );
- pDb->apPg[iHash] = pPg->pHashNext;
+ /* See if it is possible to free any ServerPage records. If so, remove
+ ** them from the linked list and hash table, but do not call sqlite3_free()
+ ** on them until the mutex has been released. */
+ if( pDb->pPgFirst ){
+ Server *pIter;
+ int iOldest = 0x7FFFFFFF;
+ for(pIter=pDb->pReader; pIter; pIter=pIter->pNext){
+ iOldest = MIN(iOldest, pIter->iCommitId);
}
- if( pPg->pHashNext ){
- pPg->pHashNext->pHashPrev = pPg->pHashPrev;
+ for(pIter=pDb->pCommit; pIter; pIter=pIter->pNext){
+ iOldest = MIN(iOldest, pIter->iCommitId);
+ }
+
+ pFree = pDb->pPgFirst;
+ for(pPg=pDb->pPgFirst; pPg && pPg->iCommitId<iOldest; pPg=pPg->pNext){
+ if( pPg->pHashPrev ){
+ pPg->pHashPrev->pHashNext = pPg->pHashNext;
+ }else{
+ int iHash = pPg->pgno % HMA_HASH_SIZE;
+ assert( pDb->apPg[iHash]==pPg );
+ pDb->apPg[iHash] = pPg->pHashNext;
+ }
+ if( pPg->pHashNext ){
+ pPg->pHashNext->pHashPrev = pPg->pHashPrev;
+ }
+ }
+ if( pPg==0 ){
+ pDb->pPgFirst = pDb->pPgLast = 0;
+ }else{
+ pDb->pPgFirst = pPg;
}
}
- if( pPg==0 ){
- pDb->pPgFirst = pDb->pPgLast = 0;
- }else{
- pDb->pPgFirst = pPg;
- }
- }
- sqlite3_mutex_leave(pDb->mutex);
+ sqlite3_mutex_leave(pDb->mutex);
- /* Call sqlite3_free() on any pages that were unlinked from the hash
- ** table above. */
- while( pFree && pFree!=pPg ){
- ServerPage *pNext = pFree->pNext;
- sqlite3_free(pFree);
- pFree = pNext;
- }
+ /* Call sqlite3_free() on any pages that were unlinked from the hash
+ ** table above. */
+ while( pFree && pFree!=pPg ){
+ ServerPage *pNext = pFree->pNext;
+ sqlite3_free(pFree);
+ pFree = pNext;
+ }
- p->iTransId = -1;
- p->iCommitId = 0;
+ p->pNext = 0;
+ p->eTrans = SERVER_TRANS_NONE;
+ p->iTransId = -1;
+ p->iCommitId = 0;
+ }
return rc;
}
ServerDb *pDb = p->pDb;
int rc = SQLITE_OK;
ServerPage *pIter;
- ServerPage *pNext;
if( pPg==0 ) return SQLITE_OK;
/* Assign a commit id to this transaction */
assert( p->iCommitId==0 );
+ assert( p->eTrans==SERVER_TRANS_READWRITE );
+ assert( p->iTransId>=0 );
+
p->iCommitId = pDb->iNextCommit++;
/* Iterate through all pages. For each:
*/
int sqlite3ServerLock(Server *p, Pgno pgno, int bWrite, int bBlock){
int rc = SQLITE_OK;
- ServerDb *pDb = p->pDb;
- int iWriter;
- int bSkip = 0;
- u32 *pSlot;
- assert( p->iTransId>=0 );
- assert( p->nLock<=p->nAlloc );
- if( p->nLock==p->nAlloc ){
- int nNew = p->nLock ? p->nLock*2 : 256;
- u32 *aNew = sqlite3_realloc(p->aLock, nNew*sizeof(u32));
- if( aNew==0 ) return SQLITE_NOMEM_BKPT;
- memset(&aNew[p->nLock], 0, sizeof(u32) * (nNew - p->nLock));
- p->nAlloc = nNew;
- p->aLock = aNew;
- }
+ assert( p->eTrans==SERVER_TRANS_READWRITE
+ || p->eTrans==SERVER_TRANS_READONLY
+ );
+ if( p->eTrans==SERVER_TRANS_READWRITE ){
+ ServerDb *pDb = p->pDb;
+ int iWriter;
+ int bSkip = 0;
+ u32 *pSlot;
+
+ assert( p->iTransId>=0 );
+ assert( p->nLock<=p->nAlloc );
+ if( p->nLock==p->nAlloc ){
+ int nNew = p->nLock ? p->nLock*2 : 256;
+ u32 *aNew = sqlite3_realloc(p->aLock, nNew*sizeof(u32));
+ if( aNew==0 ) return SQLITE_NOMEM_BKPT;
+ memset(&aNew[p->nLock], 0, sizeof(u32) * (nNew - p->nLock));
+ p->nAlloc = nNew;
+ p->aLock = aNew;
+ }
- sqlite3_mutex_enter(pDb->mutex);
+ sqlite3_mutex_enter(pDb->mutex);
- pSlot = &pDb->aSlot[pgno % HMA_PAGELOCK_SLOTS];
- assert( slotGetWriter(*pSlot)<0
- || slotReaderMask(*pSlot)==0
- || slotReaderMask(*pSlot)==(1 << slotGetWriter(*pSlot))
- );
+ pSlot = &pDb->aSlot[pgno % HMA_PAGELOCK_SLOTS];
+ assert( slotGetWriter(*pSlot)<0
+ || slotReaderMask(*pSlot)==0
+ || slotReaderMask(*pSlot)==(1 << slotGetWriter(*pSlot))
+ );
- iWriter = slotGetWriter(*pSlot);
- if( iWriter==p->iTransId || (bWrite==0 && (*pSlot & (1<<p->iTransId))) ){
- bSkip = 1;
- }else if( iWriter>=0 ){
- rc = SQLITE_BUSY_DEADLOCK;
- }else if( bWrite ){
- if( (slotReaderMask(*pSlot) & ~(1 << p->iTransId))==0 ){
- *pSlot += ((p->iTransId + 1) << HMA_MAX_TRANSACTIONID);
- }else{
+ iWriter = slotGetWriter(*pSlot);
+ if( iWriter==p->iTransId || (bWrite==0 && (*pSlot & (1<<p->iTransId))) ){
+ bSkip = 1;
+ }else if( iWriter>=0 ){
rc = SQLITE_BUSY_DEADLOCK;
+ }else if( bWrite ){
+ if( (slotReaderMask(*pSlot) & ~(1 << p->iTransId))==0 ){
+ *pSlot += ((p->iTransId + 1) << HMA_MAX_TRANSACTIONID);
+ }else{
+ rc = SQLITE_BUSY_DEADLOCK;
+ }
+ }else{
+ *pSlot |= (1 << p->iTransId);
}
- }else{
- *pSlot |= (1 << p->iTransId);
- }
- assert( slotGetWriter(*pSlot)<0
- || slotReaderMask(*pSlot)==0
- || slotReaderMask(*pSlot)==(1 << slotGetWriter(*pSlot))
- );
+ assert( slotGetWriter(*pSlot)<0
+ || slotReaderMask(*pSlot)==0
+ || slotReaderMask(*pSlot)==(1 << slotGetWriter(*pSlot))
+ );
- sqlite3_mutex_leave(pDb->mutex);
+ sqlite3_mutex_leave(pDb->mutex);
- if( bSkip==0 ){
- p->aLock[p->nLock++] = pgno;
+ if( bSkip==0 ){
+ p->aLock[p->nLock++] = pgno;
+ }
}
+
return rc;
}
return 0;
}
+static void serverIncrSlowReader(u32 *pSlot, int n){
+ assert( n==1 || n==-1 );
+ *pSlot += (n * (1 << HMA_SLOT_RLWL_BITS));
+}
+
+void sqlite3ServerReadPage(Server *p, Pgno pgno, u8 **ppData){
+ if( p->eTrans==SERVER_TRANS_READONLY ){
+ ServerDb *pDb = p->pDb;
+ ServerPage *pIter;
+ ServerPage *pBest = 0;
+ int iHash = pgno % HMA_HASH_SIZE;
+
+ sqlite3_mutex_enter(pDb->mutex);
+
+ /* Search the hash table for the oldest version of page pgno with
+ ** a commit-id greater than or equal to Server.iCommitId. */
+ for(pIter=pDb->apPg[iHash]; pIter; pIter=pIter->pHashNext){
+ if( pIter->iCommitId>=p->iCommitId
+ && (pBest==0 || pIter->iCommitId<pBest->iCommitId)
+ ){
+ pBest = pIter;
+ }
+ }
+
+ if( pBest ){
+ *ppData = pBest->aData;
+ }else{
+ u32 *pSlot = &pDb->aSlot[pgno % HMA_PAGELOCK_SLOTS];
+ serverIncrSlowReader(pSlot, 1);
+ }
+
+ sqlite3_mutex_leave(pDb->mutex);
+ }
+}
+
+void sqlite3ServerEndReadPage(Server *p, Pgno pgno){
+ if( p->eTrans==SERVER_TRANS_READONLY ){
+ ServerDb *pDb = p->pDb;
+ u32 *pSlot = &pDb->aSlot[pgno % HMA_PAGELOCK_SLOTS];
+ sqlite3_mutex_enter(pDb->mutex);
+ serverIncrSlowReader(pSlot, -1);
+ assert( slotGetSlowReaders(*pSlot)>=0 );
+ sqlite3_mutex_leave(pDb->mutex);
+ }
+}
+
#endif /* ifdef SQLITE_SERVER_EDITION */
int sqlite3ServerConnect(Pager *pPager, Server **ppOut);
void sqlite3ServerDisconnect(Server *p, sqlite3_file *dbfd);
-int sqlite3ServerBegin(Server *p);
+int sqlite3ServerBegin(Server *p, int bReadonly);
int sqlite3ServerPreCommit(Server*, ServerPage*);
int sqlite3ServerEnd(Server *p);
int sqlite3ServerHasLock(Server *p, Pgno pgno, int bWrite);
+void sqlite3ServerReadPage(Server*, Pgno, u8**);
+void sqlite3ServerEndReadPage(Server*, Pgno);
+
#endif /* SQLITE_SERVER_H */
#endif /* SQLITE_SERVER_EDITION */
u16 dbOptFlags; /* Flags to enable/disable optimizations */
u8 enc; /* Text encoding */
u8 autoCommit; /* The auto-commit flag. */
+ u8 readonlyTrans; /* Transaction opened with BEGIN READONLY */
u8 temp_store; /* 1: file 2: memory 0: default */
u8 mallocFailed; /* True if we have seen a malloc failure */
u8 bBenignMalloc; /* Do not require OOMs if true */
}
pPager = sqlite3TestTextToPtr(argv[1]);
if( Tcl_GetInt(interp, argv[2], &pgno) ) return TCL_ERROR;
- rc = sqlite3PagerSharedLock(pPager);
+ rc = sqlite3PagerSharedLock(pPager, 0);
if( rc==SQLITE_OK ){
rc = sqlite3PagerGet(pPager, pgno, &pPage, 0);
}
break;
}
-/* Opcode: AutoCommit P1 P2 * * *
+/* Opcode: AutoCommit P1 P2 P3 * *
**
** Set the database auto-commit flag to P1 (1 or 0). If P2 is true, roll
** back any currently active btree transactions. If there are any active
iRollback = pOp->p2;
assert( desiredAutoCommit==1 || desiredAutoCommit==0 );
assert( desiredAutoCommit==1 || iRollback==0 );
+ assert( desiredAutoCommit==0 || pOp->p3==0 );
assert( db->nVdbeActive>0 ); /* At least this one VM is active */
assert( p->bIsReader );
sqlite3CloseSavepoints(db);
if( p->rc==SQLITE_OK ){
rc = SQLITE_DONE;
+ db->readonlyTrans = (pOp->p3==TK_READONLY);
}else{
rc = SQLITE_ERROR;
}
*/
if( eMode!=SQLITE_CHECKPOINT_PASSIVE ){
if( walIsServer(pWal) ){
- rc = sqlite3ServerBegin(pWal->pServer);
+ rc = sqlite3ServerBegin(pWal->pServer, 0);
if( rc!=SQLITE_OK ) goto ckpt_out;
if( eMode>=SQLITE_CHECKPOINT_RESTART ){
/* Exclusive lock on page 1. This is exclusive access to the db. */
All server-edition tests.
} -files [
test_set \
- select1.test server2.test server3.test
+ select1.test server2.test server3.test server4.test
]
test_suite "fts5-light" -prefix "" -description {
--- /dev/null
+# 2017 July 09
+#
+# 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.
+# Specifically, that "BEGIN READONLY" starts a read-only MVCC
+# transaction.
+#
+
+
+set testdir [file dirname $argv0]
+source $testdir/tester.tcl
+source $testdir/lock_common.tcl
+set testprefix server3
+
+sqlite3 db2 test.db
+
+do_execsql_test 1.0 {
+ CREATE TABLE t1(x);
+ INSERT INTO t1 VALUES(1);
+ CREATE TABLE t2(x);
+ INSERT INTO t2 VALUES(1);
+ BEGIN;
+ INSERT INTO t1 VALUES(2);
+ INSERT INTO t2 VALUES(2);
+}
+
+do_execsql_test -db db2 1.1 {
+ BEGIN READONLY;
+ SELECT * FROM t1;
+} {1}
+
+do_execsql_test 1.2 {
+ COMMIT;
+ INSERT INTO t1 VALUES(3);
+ SELECT * FROM t1;
+} {1 2 3}
+
+do_execsql_test 1.2a {
+ INSERT INTO t2 VALUES(3);
+} {}
+
+do_execsql_test -db db2 1.3 {
+ SELECT * FROM t2;
+} {1}
+
+do_execsql_test -db db2 1.4 {
+ ROLLBACK;
+ SELECT * FROM t1;
+} {1 2 3}
+
+
+finish_test
+
{ "PRIMARY", "TK_PRIMARY", ALWAYS },
{ "QUERY", "TK_QUERY", EXPLAIN },
{ "RAISE", "TK_RAISE", TRIGGER },
+ { "READONLY", "TK_READONLY", ALWAYS },
{ "RECURSIVE", "TK_RECURSIVE", CTE },
{ "REFERENCES", "TK_REFERENCES", FKEY },
{ "REGEXP", "TK_LIKE_KW", ALWAYS },