-C Adjust\sthe\sbitvec\srelated\ssqlite3_log\smessages\sadded\sby\s[9527089b].
-D 2017-05-26T18:18:51.758
+C Enhance\sthe\slog\smessages\semitted\swhen\sa\spage\sconflict\sis\sdetected.
+D 2017-05-29T14:27:37.696
F Makefile.in 1cc758ce3374a32425e4d130c2fe7b026b20de5b8843243de75f087c0a2661fb
F Makefile.linux-gcc 7bc79876b875010e8c8f9502eb935ca92aa3c434
F Makefile.msc 8eeb80162074004e906b53d7340a12a14c471a83743aab975947e95ce061efcc
F src/backup.c faf17e60b43233c214aae6a8179d24503a61e83b
F src/bitvec.c c77b7f5759e413c1c8b53267d633c952e66db79c1171964c7e24c0f92f5019cf
F src/btmutex.c 0e9ce2d56159b89b9bc8e197e023ee11e39ff8ca
-F src/btree.c ca942fd75c1b791bd3fdf647e283473acf63e97d2252077216cdc1d56cb3a25e
+F src/btree.c 3c5409453a17294ed8fe7b44dc87c0434b2418f447ad768f4624ead69d5dfa18
F src/btree.h 14e99cc2b666beb60322173c761d16b668ec2e07c18bbb74e8a49fe85946f8a0
-F src/btreeInt.h 42c3e3d9534aed0b99ee68678b0311c33134c7c015037a319900eddd148584d6
+F src/btreeInt.h 7429915fc8f51bbd78b7ac023aa4afbe5b9660fc1e6970f144b07540a34a4623
F src/build.c ba3f389668754c407805bbc5f8ab140f063ba6b04a6a86f63006b63b3c7319a8
F src/callback.c 2e76147783386374bf01b227f752c81ec872d730
F src/complete.c a3634ab1e687055cd002e11b8f43eb75c17da23e
F src/vdbetrace.c 41963d5376f0349842b5fc4aaaaacd7d9cdc0834
F src/vtab.c 35b9bdc2b41de32a417141d12097bcc4e29a77ed7cdb8f836d1d2305d946b61b
F src/vxworks.h d2988f4e5a61a4dfe82c6524dd3d6e4f2ce3cdb9
-F src/wal.c 0c4faf368f51abb3f077fa5c175920b73b6a07584688a287c8d77296b6af582e
+F src/wal.c 4b857e74548bb0f9bc0c7f56517327421f925b1263ac65a6e58e9c176194857e
F src/wal.h 79378c5cd2127f32060d4952d827a7abdd490f8f9e5cbb14a4c39657ed99ad15
F src/walker.c b71a992b413b3a022572eccf29ef4b4890223791
F src/where.c c6352f15be5031907c68bcbde96cad1a6da20e9f4051d10168a59235de9a8566
F test/concurrent2.test 9dfbeb0a323733fe1d13443371734bb94a674dbf777f464365475903873111f8
F test/concurrent3.test 0a5f7e3036d1eccf0782d7153ac21f5f222e9468
F test/concurrent4.test 989c6575225f9c4ef5d2392a9b9d0405665567c7501a3e44129598794d9b1b5f
+F test/concurrent5.test 06fd0b5294586d170a3b12d68153394fbfe642c8346e249a6240bc6ababc5f4b
F test/conflict.test 029faa2d81a0d1cafb5f88614beb663d972c01db
F test/conflict2.test bb0b94cf7196c64a3cbd815c66d3ee98c2fecd9c
F test/conflict3.test a83db76a6c3503b2fa057c7bfb08c318d8a422202d8bc5b86226e078e5b49ff9
F vsixtest/vsixtest.vcxproj.data 2ed517e100c66dc455b492e1a33350c1b20fbcdc
F vsixtest/vsixtest.vcxproj.filters 37e51ffedcdb064aad6ff33b6148725226cd608e
F vsixtest/vsixtest_TemporaryKey.pfx e5b1b036facdb453873e7084e1cae9102ccc67a0
-P 9df0195780306a26709e87daabf9d426856aebf2082060b98818f2d48c7472ec
-R ef448b68266e15a0b11a8140036a504f
+P a7e0e7a4835314a04e065839a7f88b074eda795da2b2da5741b2afc096421a32
+R beb58ff81a99fbcff0806ad7e4f3b8ee
U dan
-Z e753798d60e27266ad4ce7eb0092b295
+Z a8b4d17828fb801d4b09ca76008d7042
-a7e0e7a4835314a04e065839a7f88b074eda795da2b2da5741b2afc096421a32
\ No newline at end of file
+92618492b048867af38922825f3d094eaaa2dd919b1ed2f7372483cc53f892bf
\ No newline at end of file
return rc;
}
+#ifndef SQLITE_OMIT_CONCURRENT
+/*
+** Set the value of the MemPage.pgnoRoot variable, if it exists.
+*/
+static void setMempageRoot(MemPage *pPg, u32 pgnoRoot){
+ pPg->pgnoRoot = pgnoRoot;
+}
+#else
+# define setMempageRoot(x,y)
+#endif
+
/*
** Release a MemPage. This should be called once for each prior
** call to btreeGetPage.
*/
static int moveToChild(BtCursor *pCur, u32 newPgno){
BtShared *pBt = pCur->pBt;
+ int rc;
assert( cursorOwnsBtShared(pCur) );
assert( pCur->eState==CURSOR_VALID );
pCur->curFlags &= ~(BTCF_ValidNKey|BTCF_ValidOvfl);
pCur->aiIdx[pCur->iPage++] = pCur->ix;
pCur->ix = 0;
- return getAndInitPage(pBt, newPgno, &pCur->apPage[pCur->iPage],
+ rc = getAndInitPage(pBt, newPgno, &pCur->apPage[pCur->iPage],
pCur, pCur->curPagerFlags);
+ if( rc==SQLITE_OK ){
+ setMempageRoot(pCur->apPage[pCur->iPage], pCur->pgnoRoot);
+ }
+ return rc;
}
#ifdef SQLITE_DEBUG
pCur->eState = CURSOR_INVALID;
return rc;
}
+ setMempageRoot(pCur->apPage[0], pCur->pgnoRoot);
pCur->iPage = 0;
pCur->curIntKey = pCur->apPage[0]->intKey;
}
int iParentIdx, /* Index of "the page" in pParent */
u8 *aOvflSpace, /* page-size bytes of space for parent ovfl */
int isRoot, /* True if pParent is a root-page */
- int bBulk /* True if this call is part of a bulk load */
+ int bBulk, /* True if this call is part of a bulk load */
+ Pgno pgnoRoot /* Root page of b-tree being balanced */
){
BtShared *pBt; /* The whole database */
int nMaxCells = 0; /* Allocated size of apCell, szCell, aFrom. */
memset(apOld, 0, (i+1)*sizeof(MemPage*));
goto balance_cleanup;
}
+ setMempageRoot(apOld[i], pgnoRoot);
+
nMaxCells += 1+apOld[i]->nCell+apOld[i]->nOverflow;
if( (i--)==0 ) break;
*/
u8 *pSpace = sqlite3PageMalloc(pCur->pBt->pageSize);
rc = balance_nonroot(pParent, iIdx, pSpace, iPage==1,
- pCur->hints&BTREE_BULKLOAD);
+ pCur->hints&BTREE_BULKLOAD, pCur->pgnoRoot);
if( pFree ){
/* If pFree is not NULL, it points to the pSpace buffer used
** by a previous call to balance_nonroot(). Its contents are
BtShared *pBt, /* The BTree that contains the table */
Pgno pgno, /* Page number to clear */
int freePageFlag, /* Deallocate page if true */
- int *pnChange /* Add number of Cells freed to this counter */
+ int *pnChange, /* Add number of Cells freed to this counter */
+ Pgno pgnoRoot
){
MemPage *pPage;
int rc;
}
rc = getAndInitPage(pBt, pgno, &pPage, 0, 0);
if( rc ) return rc;
+ setMempageRoot(pPage, pgnoRoot);
if( pPage->bBusy ){
rc = SQLITE_CORRUPT_BKPT;
goto cleardatabasepage_out;
for(i=0; i<pPage->nCell; i++){
pCell = findCell(pPage, i);
if( !pPage->leaf ){
- rc = clearDatabasePage(pBt, get4byte(pCell), 1, pnChange);
+ rc = clearDatabasePage(pBt, get4byte(pCell), 1, pnChange, pgnoRoot);
if( rc ) goto cleardatabasepage_out;
}
rc = clearCell(pPage, pCell, &info);
if( rc ) goto cleardatabasepage_out;
}
if( !pPage->leaf ){
- rc = clearDatabasePage(pBt, get4byte(&pPage->aData[hdr+8]), 1, pnChange);
+ rc = clearDatabasePage(
+ pBt, get4byte(&pPage->aData[hdr+8]), 1, pnChange, pgnoRoot
+ );
if( rc ) goto cleardatabasepage_out;
}else if( pnChange ){
assert( pPage->intKey || CORRUPT_DB );
** is the root of a table b-tree - if it is not, the following call is
** a no-op). */
invalidateIncrblobCursors(p, (Pgno)iTable, 0, 1);
- rc = clearDatabasePage(pBt, (Pgno)iTable, 0, pnChange);
+ rc = clearDatabasePage(pBt, (Pgno)iTable, 0, pnChange, (Pgno)iTable);
}
sqlite3BtreeLeave(p);
return rc;
DbPage *pDbPage; /* Pager page handle */
u16 (*xCellSize)(MemPage*,u8*); /* cellSizePtr method */
void (*xParseCell)(MemPage*,u8*,CellInfo*); /* btreeParseCell method */
+#ifndef SQLITE_OMIT_CONCURRENT
+ u32 pgnoRoot; /* Root page of b-tree that this page belongs to */
+#endif
};
/*
#ifndef SQLITE_OMIT_WAL
#include "wal.h"
+#include "btreeInt.h"
/*
** Trace output macros
rc = SQLITE_BUSY_SNAPSHOT;
}
}else if( sqlite3BitvecTestNotNull(pAllRead, aPgno[i]) ){
- sqlite3_log(SQLITE_OK,
- "cannot commit CONCURRENT transaction (conflict at page %d)",
- (int)aPgno[i]
- );
- rc = SQLITE_BUSY_SNAPSHOT;
+ PgHdr *pPg = 0;
+ rc = sqlite3PagerGet(pPage1->pPager, aPgno[i], &pPg, 0);
+ if( rc==SQLITE_OK ){
+ Pgno pgnoRoot = 0;
+ int bWrite = -1;
+ if( pPg ){
+ pgnoRoot = ((MemPage*)sqlite3PagerGetExtra(pPg))->pgnoRoot;
+ bWrite = sqlite3PagerIswriteable(pPg);
+ sqlite3PagerUnref(pPg);
+ }
+ sqlite3_log(SQLITE_OK,
+ "cannot commit CONCURRENT transaction "
+ "- conflict at page %d "
+ "(%s page; part of b-tree with root page %d)",
+ (int)aPgno[i],
+ (bWrite==0?"read-only":(bWrite>0?"read/write":"unknown")),
+ (int)pgnoRoot
+ );
+ rc = SQLITE_BUSY_SNAPSHOT;
+ }
}else if( (pPg = sqlite3PagerLookup(pPager, aPgno[i])) ){
/* Page aPgno[i], which is present in the pager cache, has been
** modified since the current CONCURRENT transaction was started.
--- /dev/null
+# 2017 May 26
+#
+# 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
+source $testdir/lock_common.tcl
+source $testdir/wal_common.tcl
+set ::testprefix concurrent5
+
+ifcapable !concurrent {
+ finish_test
+ return
+}
+
+db close
+sqlite3_shutdown
+test_sqlite3_log [list lappend ::log]
+set ::log [list]
+
+sqlite3 db test.db
+
+proc do_test_conflict_msg {tn msg} {
+ set msg "cannot commit CONCURRENT transaction - [string trim $msg]"
+ uplevel [list do_test $tn {lindex $::log end} $msg]
+}
+
+do_execsql_test 1.0 {
+ PRAGMA journal_mode = wal;
+ CREATE TABLE t1(x, y);
+ CREATE TABLE t2(c);
+ WITH s(i) AS (SELECT 1 UNION ALL SELECT i+1 FROM s WHERE i<100)
+ INSERT INTO t1 SELECT randomblob(400), randomblob(400) FROM s;
+} {wal}
+
+sqlite3 db2 test.db
+
+do_test 1.1.1 {
+ set ::log [list]
+ db2 eval {
+ BEGIN CONCURRENT;
+ SELECT count(*) FROM t1;
+ INSERT INTO t2 VALUES(10);
+ }
+
+ db eval {
+ INSERT INTO t1 VALUES(randomblob(200), randomblob(200));
+ }
+
+ catchsql COMMIT db2
+} {1 {database is locked}}
+do_test_conflict_msg 1.1.2 {
+ conflict at page 2 (read-only page; part of b-tree with root page 2)
+}
+
+do_test 1.2.1 {
+ set ::log [list]
+ db2 eval {
+ ROLLBACK;
+ BEGIN CONCURRENT;
+ INSERT INTO t1 VALUES(11, 12);
+ }
+
+ db eval {
+ INSERT INTO t1 VALUES(12, 11);
+ }
+
+ catchsql COMMIT db2
+} {1 {database is locked}}
+
+do_test_conflict_msg 1.2.2 {
+ conflict at page 105 (read/write page; part of b-tree with root page 2)
+}
+
+do_test 1.3.1 {
+ set ::log [list]
+ db2 eval {
+ ROLLBACK;
+ BEGIN CONCURRENT;
+ INSERT INTO t2 VALUES('x');
+ }
+
+ db eval {
+ INSERT INTO t2 VALUES('y');
+ }
+
+ catchsql COMMIT db2
+} {1 {database is locked}}
+
+do_test_conflict_msg 1.3.2 {
+ conflict at page 3 (read/write page; part of b-tree with root page 3)
+}
+
+db close
+db2 close
+sqlite3_shutdown
+test_sqlite3_log
+sqlite3_initialize
+finish_test
+