SQLite

Check-in [2584df3d42]
Login

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

Overview
Comment:Fix a problem causing a lock to be held past the end of a transaction. Use a blocking lock to take the read-lock on page 1 taken by all transactions.
Downloads: Tarball | ZIP archive
Timelines: family | ancestors | descendants | both | server-edition
Files: files | file ages | folders
SHA3-256: 2584df3d42ece69d37f31f3655cd0d4760914bea73d4f4ccb7f2a7aa47f80f49
User & Date: dan 2017-05-10 16:18:05.140
Context
2017-05-12
18:52
Require exclusive access to the db to wrap the wal file. Have "PRAGMA wal_checkpoint = restart" block for this. (check-in: cbf44ed975 user: dan tags: server-edition)
2017-05-10
16:18
Fix a problem causing a lock to be held past the end of a transaction. Use a blocking lock to take the read-lock on page 1 taken by all transactions. (check-in: 2584df3d42 user: dan tags: server-edition)
13:46
Use a blocking call to obtain the wal-mode WRITER lock in some cases. (check-in: 4464ca1d68 user: dan tags: server-edition)
Changes
Unified Diff Ignore Whitespace Patch
Changes to src/pager.c.
5389
5390
5391
5392
5393
5394
5395
5396
5397
5398
5399
5400
5401
5402
5403
#ifdef SQLITE_SERVER_EDITION
  if( pagerIsServer(pPager) ){
    assert( rc==SQLITE_OK );
    pager_reset(pPager);
    rc = sqlite3ServerBegin(pPager->pServer);
  }
#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);
  }







|







5389
5390
5391
5392
5393
5394
5395
5396
5397
5398
5399
5400
5401
5402
5403
#ifdef SQLITE_SERVER_EDITION
  if( pagerIsServer(pPager) ){
    assert( rc==SQLITE_OK );
    pager_reset(pPager);
    rc = sqlite3ServerBegin(pPager->pServer);
  }
#endif
  if( rc==SQLITE_OK && 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);
  }
Changes to src/server.c.
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
** Begin a transaction.
*/
int sqlite3ServerBegin(Server *p){
#if 1
  int rc = posixLock(p->pHma->fd, p->iClient+1, SERVER_WRITE_LOCK, 1);
  if( rc ) return rc;
#endif
  return sqlite3ServerLock(p, 1, 0, 0);
}

/*
** End a transaction (and release all locks).
*/
int sqlite3ServerEnd(Server *p){
  int i;







|







409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
** Begin a transaction.
*/
int sqlite3ServerBegin(Server *p){
#if 1
  int rc = posixLock(p->pHma->fd, p->iClient+1, SERVER_WRITE_LOCK, 1);
  if( rc ) return rc;
#endif
  return sqlite3ServerLock(p, 1, 0, 1);
}

/*
** End a transaction (and release all locks).
*/
int sqlite3ServerEnd(Server *p){
  int i;
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489

490
491
492
493
494
495
496

    /* 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, bBlock, v, &bRetry);
        if( rc!=SQLITE_OK ) goto server_lock_out;







<
<
<


<


>







475
476
477
478
479
480
481



482
483

484
485
486
487
488
489
490
491
492
493

    /* 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;



    }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, bBlock, v, &bRetry);
        if( rc!=SQLITE_OK ) goto server_lock_out;
Added src/server.h.




































































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
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
/*
** 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.
**
*************************************************************************
*/

#ifdef SQLITE_SERVER_EDITION

#ifndef SQLITE_SERVER_H
#define SQLITE_SERVER_H


typedef struct Server Server;

int sqlite3ServerConnect(Pager *pPager, Server **ppOut, int *piClient);

void sqlite3ServerDisconnect(Server *p, sqlite3_file *dbfd);

int sqlite3ServerBegin(Server *p);
int sqlite3ServerEnd(Server *p);
int sqlite3ServerReleaseWriteLocks(Server *p);

int sqlite3ServerLock(Server *p, Pgno pgno, int bWrite, int bBlock);

#endif /* SQLITE_SERVER_H */

#endif /* SQLITE_SERVER_EDITION */
Changes to src/wal.c.
2606
2607
2608
2609
2610
2611
2612
2613
2614
2615
2616
2617
2618
2619
2620
  u32 iLast = pWal->hdr.mxFrame;  /* Last page in WAL for this reader */
  int iHash;                      /* Used to loop through N hash tables */
  int iMinHash;

  /* This routine is only be called from within a read transaction. */
  assert( walIsServer(pWal) || pWal->readLock>=0 || pWal->lockError );

  if( walIsServer(pWal) ){
    /* A server mode connection must read from the most recent snapshot. */
    iLast = walIndexHdr(pWal)->mxFrame;
  }

  /* If the "last page" field of the wal-index header snapshot is 0, then
  ** no data will be read from the wal under any circumstances. Return early
  ** in this case as an optimization.  Likewise, if pWal->readLock==0, 







|







2606
2607
2608
2609
2610
2611
2612
2613
2614
2615
2616
2617
2618
2619
2620
  u32 iLast = pWal->hdr.mxFrame;  /* Last page in WAL for this reader */
  int iHash;                      /* Used to loop through N hash tables */
  int iMinHash;

  /* This routine is only be called from within a read transaction. */
  assert( walIsServer(pWal) || pWal->readLock>=0 || pWal->lockError );

  if( walIsServer(pWal) && pWal->writeLock==0 ){
    /* A server mode connection must read from the most recent snapshot. */
    iLast = walIndexHdr(pWal)->mxFrame;
  }

  /* If the "last page" field of the wal-index header snapshot is 0, then
  ** no data will be read from the wal under any circumstances. Return early
  ** in this case as an optimization.  Likewise, if pWal->readLock==0, 
2808
2809
2810
2811
2812
2813
2814
2815
2816
2817
2818
2819
2820
2821
2822
** returned to the caller.
**
** Otherwise, if the callback function does not return an error, this
** function returns SQLITE_OK.
*/
int sqlite3WalUndo(Wal *pWal, int (*xUndo)(void *, Pgno), void *pUndoCtx){
  int rc = SQLITE_OK;
  if( ALWAYS(pWal->writeLock) ){
    Pgno iMax = pWal->hdr.mxFrame;
    Pgno iFrame;
  
    /* Restore the clients cache of the wal-index header to the state it
    ** was in before the client began writing to the database. 
    */
    memcpy(&pWal->hdr, (void *)walIndexHdr(pWal), sizeof(WalIndexHdr));







|







2808
2809
2810
2811
2812
2813
2814
2815
2816
2817
2818
2819
2820
2821
2822
** returned to the caller.
**
** Otherwise, if the callback function does not return an error, this
** function returns SQLITE_OK.
*/
int sqlite3WalUndo(Wal *pWal, int (*xUndo)(void *, Pgno), void *pUndoCtx){
  int rc = SQLITE_OK;
  if( pWal->writeLock ){
    Pgno iMax = pWal->hdr.mxFrame;
    Pgno iFrame;
  
    /* Restore the clients cache of the wal-index header to the state it
    ** was in before the client began writing to the database. 
    */
    memcpy(&pWal->hdr, (void *)walIndexHdr(pWal), sizeof(WalIndexHdr));
Changes to test/serverwal.test.
81
82
83
84
85
86
87























88
89
90
  execsql {
    PRAGMA wal_checkpoint;
    INSERT INTO ttt VALUES(11, 12);
    INSERT INTO ttt VALUES(13, 14);
  }
  expr {$N == [file size test.db-wal]}
} {1}
























finish_test








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



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
  execsql {
    PRAGMA wal_checkpoint;
    INSERT INTO ttt VALUES(11, 12);
    INSERT INTO ttt VALUES(13, 14);
  }
  expr {$N == [file size test.db-wal]}
} {1}

#-------------------------------------------------------------------------
# That ROLLBACK appears to work.
#
reset_db
do_execsql_test 4.0 {
  PRAGMA cache_size = 10;
  CREATE TABLE ttt(a, b);
  CREATE INDEX yyy ON ttt(b, a);
  PRAGMA journal_mode = wal;
  WITH s(i) AS (
    SELECT 1 UNION ALL SELECT i+1 FROM s WHERE i<100
  )
  INSERT INTO ttt SELECT randomblob(100), randomblob(100) FROM s;
} {wal}

do_execsql_test 4.1 {
  PRAGMA integrity_check;
  BEGIN;
    UPDATE ttt SET b=a;
  ROLLBACK;
  PRAGMA integrity_check;
} {ok ok}

finish_test