/ Check-in [2e59b1d0]
Login

Many hyperlinks are disabled.
Use anonymous login to enable hyperlinks.

Overview
Comment:Fix for #2854. "BEGIN EXCLUSIVE" excludes other shared cache users from using the database. (CVS 4642)
Downloads: Tarball | ZIP archive | SQL archive
Timelines: family | ancestors | descendants | both | trunk
Files: files | file ages | folders
SHA1: 2e59b1d07ee422bd799b5b7aeea44ebc998d9481
User & Date: danielk1977 2007-12-21 04:47:26
Context
2007-12-27
15:12
Fix a race condition that can occur when reloading the database schema in shared-cache mode. (CVS 4643) check-in: b37babef user: danielk1977 tags: trunk
2007-12-21
04:47
Fix for #2854. "BEGIN EXCLUSIVE" excludes other shared cache users from using the database. (CVS 4642) check-in: 2e59b1d0 user: danielk1977 tags: trunk
00:02
Fix some issues with lemon. Tickets #2852 and #2835. (CVS 4641) check-in: 5283e0d1 user: drh tags: trunk
Changes
Hide Diffs Unified Diffs Ignore Whitespace Patch

Changes to src/btree.c.

5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
..
98
99
100
101
102
103
104







105
106
107
108
109
110
111
...
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
....
1846
1847
1848
1849
1850
1851
1852












1853
1854
1855
1856
1857
1858
1859
....
1878
1879
1880
1881
1882
1883
1884






1885
1886
1887
1888
1889
1890
1891
** 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.
**
*************************************************************************
** $Id: btree.c,v 1.433 2007/12/13 21:54:11 drh Exp $
**
** This file implements a external (disk-based) database using BTrees.
** See the header comment on "btreeInt.h" for additional information.
** Including a description of file format and an overview of operation.
*/
#include "btreeInt.h"

................................................................................

  assert( sqlite3BtreeHoldsMutex(p) );
  
  /* This is a no-op if the shared-cache is not enabled */
  if( !p->sharable ){
    return SQLITE_OK;
  }








  /* This (along with lockTable()) is where the ReadUncommitted flag is
  ** dealt with. If the caller is querying for a read-lock and the flag is
  ** set, it is unconditionally granted - even if there are write-locks
  ** on the table. If a write-lock is requested, the ReadUncommitted flag
  ** is not considered.
  **
................................................................................

#ifndef SQLITE_OMIT_SHARED_CACHE
/*
** Release all the table locks (locks obtained via calls to the lockTable()
** procedure) held by Btree handle p.
*/
static void unlockAllTables(Btree *p){

  BtLock **ppIter = &p->pBt->pLock;

  assert( sqlite3BtreeHoldsMutex(p) );
  assert( p->sharable || 0==*ppIter );

  while( *ppIter ){
    BtLock *pLock = *ppIter;

    if( pLock->pBtree==p ){
      *ppIter = pLock->pNext;
      sqlite3_free(pLock);
    }else{
      ppIter = &pLock->pNext;
    }
  }




}
#endif /* SQLITE_OMIT_SHARED_CACHE */

static void releasePage(MemPage *pPage);  /* Forward reference */

/*
** Verify that the cursor holds a mutex on the BtShared
................................................................................
  ** requested, return SQLITE_BUSY.
  */
  if( pBt->inTransaction==TRANS_WRITE && wrflag ){
    rc = SQLITE_BUSY;
    goto trans_begun;
  }













  do {
    if( pBt->pPage1==0 ){
      rc = lockBtree(pBt);
    }

    if( rc==SQLITE_OK && wrflag ){
      if( pBt->readOnly ){
................................................................................
    if( p->inTrans==TRANS_NONE ){
      pBt->nTransaction++;
    }
    p->inTrans = (wrflag?TRANS_WRITE:TRANS_READ);
    if( p->inTrans>pBt->inTransaction ){
      pBt->inTransaction = p->inTrans;
    }






  }


trans_begun:
  btreeIntegrity(p);
  sqlite3BtreeLeave(p);
  return rc;







|







 







>
>
>
>
>
>
>







 







>
|






>







>
>
>
>







 







>
>
>
>
>
>
>
>
>
>
>
>







 







>
>
>
>
>
>







5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
..
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
...
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
....
1859
1860
1861
1862
1863
1864
1865
1866
1867
1868
1869
1870
1871
1872
1873
1874
1875
1876
1877
1878
1879
1880
1881
1882
1883
1884
....
1903
1904
1905
1906
1907
1908
1909
1910
1911
1912
1913
1914
1915
1916
1917
1918
1919
1920
1921
1922
** 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.
**
*************************************************************************
** $Id: btree.c,v 1.434 2007/12/21 04:47:26 danielk1977 Exp $
**
** This file implements a external (disk-based) database using BTrees.
** See the header comment on "btreeInt.h" for additional information.
** Including a description of file format and an overview of operation.
*/
#include "btreeInt.h"

................................................................................

  assert( sqlite3BtreeHoldsMutex(p) );
  
  /* This is a no-op if the shared-cache is not enabled */
  if( !p->sharable ){
    return SQLITE_OK;
  }

  /* If some other connection is holding an exclusive lock, the
  ** requested lock may not be obtained.
  */
  if( pBt->pExclusive && pBt->pExclusive!=p ){
    return SQLITE_LOCKED;
  }

  /* This (along with lockTable()) is where the ReadUncommitted flag is
  ** dealt with. If the caller is querying for a read-lock and the flag is
  ** set, it is unconditionally granted - even if there are write-locks
  ** on the table. If a write-lock is requested, the ReadUncommitted flag
  ** is not considered.
  **
................................................................................

#ifndef SQLITE_OMIT_SHARED_CACHE
/*
** Release all the table locks (locks obtained via calls to the lockTable()
** procedure) held by Btree handle p.
*/
static void unlockAllTables(Btree *p){
  BtShared *pBt = p->pBt;
  BtLock **ppIter = &pBt->pLock;

  assert( sqlite3BtreeHoldsMutex(p) );
  assert( p->sharable || 0==*ppIter );

  while( *ppIter ){
    BtLock *pLock = *ppIter;
    assert( pBt->pExclusive==0 || pBt->pExclusive==pLock->pBtree );
    if( pLock->pBtree==p ){
      *ppIter = pLock->pNext;
      sqlite3_free(pLock);
    }else{
      ppIter = &pLock->pNext;
    }
  }

  if( pBt->pExclusive==p ){
    pBt->pExclusive = 0;
  }
}
#endif /* SQLITE_OMIT_SHARED_CACHE */

static void releasePage(MemPage *pPage);  /* Forward reference */

/*
** Verify that the cursor holds a mutex on the BtShared
................................................................................
  ** requested, return SQLITE_BUSY.
  */
  if( pBt->inTransaction==TRANS_WRITE && wrflag ){
    rc = SQLITE_BUSY;
    goto trans_begun;
  }

#ifndef SQLITE_OMIT_SHARED_CACHE
  if( wrflag>1 ){
    BtLock *pIter;
    for(pIter=pBt->pLock; pIter; pIter=pIter->pNext){
      if( pIter->pBtree!=p ){
        rc = SQLITE_BUSY;
        goto trans_begun;
      }
    }
  }
#endif

  do {
    if( pBt->pPage1==0 ){
      rc = lockBtree(pBt);
    }

    if( rc==SQLITE_OK && wrflag ){
      if( pBt->readOnly ){
................................................................................
    if( p->inTrans==TRANS_NONE ){
      pBt->nTransaction++;
    }
    p->inTrans = (wrflag?TRANS_WRITE:TRANS_READ);
    if( p->inTrans>pBt->inTransaction ){
      pBt->inTransaction = p->inTrans;
    }
#ifndef SQLITE_OMIT_SHARED_CACHE
    if( wrflag>1 ){
      assert( !pBt->pExclusive );
      pBt->pExclusive = p;
    }
#endif
  }


trans_begun:
  btreeIntegrity(p);
  sqlite3BtreeLeave(p);
  return rc;

Changes to src/btreeInt.h.

5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
...
391
392
393
394
395
396
397

398
399
400
401
402
403
404
** 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.
**
*************************************************************************
** $Id: btreeInt.h,v 1.14 2007/12/07 18:55:28 drh Exp $
**
** This file implements a external (disk-based) database using BTrees.
** For a detailed discussion of BTrees, refer to
**
**     Donald E. Knuth, THE ART OF COMPUTER PROGRAMMING, Volume 3:
**     "Sorting And Searching", pages 473-480. Addison-Wesley
**     Publishing Company, Reading, Massachusetts.
................................................................................
  void (*xFreeSchema)(void*);  /* Destructor for BtShared.pSchema */
  sqlite3_mutex *mutex; /* Non-recursive mutex required to access this struct */
  BusyHandler busyHdr;  /* The busy handler for this btree */
#ifndef SQLITE_OMIT_SHARED_CACHE
  int nRef;             /* Number of references to this structure */
  BtShared *pNext;      /* Next on a list of sharable BtShared structs */
  BtLock *pLock;        /* List of locks held on this shared-btree struct */

#endif
};

/*
** An instance of the following structure is used to hold information
** about a cell.  The parseCellPtr() function fills in this structure
** based on information extract from the raw disk page.







|







 







>







5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
...
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
** 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.
**
*************************************************************************
** $Id: btreeInt.h,v 1.15 2007/12/21 04:47:26 danielk1977 Exp $
**
** This file implements a external (disk-based) database using BTrees.
** For a detailed discussion of BTrees, refer to
**
**     Donald E. Knuth, THE ART OF COMPUTER PROGRAMMING, Volume 3:
**     "Sorting And Searching", pages 473-480. Addison-Wesley
**     Publishing Company, Reading, Massachusetts.
................................................................................
  void (*xFreeSchema)(void*);  /* Destructor for BtShared.pSchema */
  sqlite3_mutex *mutex; /* Non-recursive mutex required to access this struct */
  BusyHandler busyHdr;  /* The busy handler for this btree */
#ifndef SQLITE_OMIT_SHARED_CACHE
  int nRef;             /* Number of references to this structure */
  BtShared *pNext;      /* Next on a list of sharable BtShared structs */
  BtLock *pLock;        /* List of locks held on this shared-btree struct */
  Btree *pExclusive;    /* Btree with an EXCLUSIVE lock on the whole db */
#endif
};

/*
** An instance of the following structure is used to hold information
** about a cell.  The parseCellPtr() function fills in this structure
** based on information extract from the raw disk page.

Added test/tkt2854.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
# 2007 December 20
#
# 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.
#
#***********************************************************************
#
# $Id: tkt2854.test,v 1.1 2007/12/21 04:47:27 danielk1977 Exp $

set testdir [file dirname $argv0]
source $testdir/tester.tcl
db close

ifcapable !shared_cache {
  finish_test
  return
}

set ::enable_shared_cache [sqlite3_enable_shared_cache 1]

# Open 3 database connections. Connection "db" and "db2" share a cache.
# Connection "db3" has its own cache.
#
do_test tkt2854-1.1 {
  sqlite3 db test.db
  sqlite3 db2 test.db
  sqlite3 db3 ./test.db

  db eval {
    CREATE TABLE abc(a, b, c);
  }
} {}

# Check that an exclusive lock cannot be obtained if some other 
# shared-cache connection has a read-lock on a table.
#
do_test tkt2854-1.2 {
  execsql { 
    BEGIN;
    SELECT * FROM abc;
  } db2
} {}
do_test tkt2854-1.3 {
  catchsql { BEGIN EXCLUSIVE } db
} {1 {database is locked}}
do_test tkt2854-1.4 {
  execsql { SELECT * FROM abc } db3
} {}
do_test tkt2854-1.5 {
  catchsql { INSERT INTO abc VALUES(1, 2, 3) } db3
} {1 {database is locked}}
do_test tkt2854-1.6 {
  execsql { COMMIT } db2
} {}

# Check that an exclusive lock prevents other shared-cache users from
# starting a transaction.
#
do_test tkt2854-1.7 {
  set ::DB2 [sqlite3_connection_pointer db2]
  set ::STMT1 [sqlite3_prepare $DB2 "SELECT * FROM abc" -1 TAIL]
  set ::STMT2 [sqlite3_prepare $DB2 "BEGIN EXCLUSIVE" -1 TAIL]
  set ::STMT3 [sqlite3_prepare $DB2 "BEGIN IMMEDIATE" -1 TAIL]
  set ::STMT4 [sqlite3_prepare $DB2 "BEGIN" -1 TAIL]
  set ::STMT5 [sqlite3_prepare $DB2 "COMMIT" -1 TAIL]
  execsql { BEGIN EXCLUSIVE } db
} {}
do_test tkt2854-1.8 {
  catchsql { BEGIN EXCLUSIVE } db2
} {1 {database schema is locked: main}}
do_test tkt2854-1.9 {
  catchsql { BEGIN IMMEDIATE } db2
} {1 {database schema is locked: main}}
do_test tkt2854-1.10 {
  # This fails because the schema of main cannot be verified.
  catchsql { BEGIN } db2
} {1 {database schema is locked: main}}

# Check that an exclusive lock prevents other shared-cache users from
# reading the database. Use stored statements so that the error occurs
# at the b-tree level, not the schema level.
#
do_test tkt2854-1.11 {
  list [sqlite3_step $::STMT1] [sqlite3_finalize $::STMT1]
} {SQLITE_ERROR SQLITE_LOCKED}
do_test tkt2854-1.12 {
  list [sqlite3_step $::STMT2] [sqlite3_finalize $::STMT2]
} {SQLITE_BUSY SQLITE_BUSY}
do_test tkt2854-1.13 {
  list [sqlite3_step $::STMT3] [sqlite3_finalize $::STMT3]
} {SQLITE_BUSY SQLITE_BUSY}
do_test tkt2854-1.14 {
  # A regular "BEGIN" doesn't touch any databases. So it succeeds.
  list [sqlite3_step $::STMT4] [sqlite3_finalize $::STMT4]
} {SQLITE_DONE SQLITE_OK}
do_test tkt2854-1.15 {
  # As does a COMMIT.
  list [sqlite3_step $::STMT5] [sqlite3_finalize $::STMT5]
} {SQLITE_DONE SQLITE_OK}

# Try to read the database using connection "db3" (which does not share
# a cache with "db"). The database should be locked.
do_test tkt2854-1.16 {
  catchsql { SELECT * FROM abc } db3
} {1 {database is locked}}
do_test tkt2854-1.17 {
  execsql { COMMIT } db
} {}
do_test tkt2854-1.18 {
  execsql { SELECT * FROM abc } db2
} {}

# Check that if an attempt to obtain an exclusive lock fails because an
# attached db cannot be locked, the internal exclusive flag used by
# shared-cache users is correctly cleared.
do_test tkt2854-1.19 {
  file delete -force test2.db test2.db-journal
  sqlite3 db4 test2.db
  execsql { CREATE TABLE def(d, e, f) } db4
  execsql { ATTACH 'test2.db' AS aux } db
} {}
do_test tkt2854-1.20 {
  execsql {BEGIN IMMEDIATE} db4
  catchsql {BEGIN EXCLUSIVE} db
} {1 {database is locked}}
do_test tkt2854-1.21 {
  execsql {SELECT * FROM abc} db2
} {}

db close
db2 close
db3 close
db4 close
sqlite3_enable_shared_cache $::enable_shared_cache
finish_test