/ Check-in [2584df3d]
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 | SQL 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
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: cbf44ed9 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: 2584df3d user: dan tags: server-edition
13:46
Use a blocking call to obtain the wal-mode WRITER lock in some cases. check-in: 4464ca1d user: dan tags: server-edition
Changes
Hide Diffs Unified Diffs 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
...
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489

490
491
492
493
494
495
496
** 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;
................................................................................

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







|







 







<
<
<


<


>







409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
...
475
476
477
478
479
480
481



482
483

484
485
486
487
488
489
490
491
492
493
** 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;
................................................................................

    /* 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
....
2808
2809
2810
2811
2812
2813
2814
2815
2816
2817
2818
2819
2820
2821
2822
  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, 
................................................................................
** 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));







|







 







|







2606
2607
2608
2609
2610
2611
2612
2613
2614
2615
2616
2617
2618
2619
2620
....
2808
2809
2810
2811
2812
2813
2814
2815
2816
2817
2818
2819
2820
2821
2822
  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, 
................................................................................
** 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