/ Check-in [b733afc1]
Login
SQLite training in Houston TX on 2019-11-05 (details)
Part of the 2019 Tcl Conference

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

Overview
Comment:Add some support for wal mode to the hack on this branch.
Downloads: Tarball | ZIP archive | SQL archive
Timelines: family | ancestors | descendants | both | server-edition
Files: files | file ages | folders
SHA3-256: b733afc1d0abc09861903ce8e27a8f2462ec871967f5d3dc2847b31bb28b55f3
User & Date: dan 2017-05-08 20:15:23
Context
2017-05-09
16:32
Fix a problem with wrapping the log file in server mode. check-in: 270b7d1e user: dan tags: server-edition
2017-05-08
20:15
Add some support for wal mode to the hack on this branch. check-in: b733afc1 user: dan tags: server-edition
2017-05-06
16:04
Update this branch with latest trunk changes. check-in: ed6bad67 user: dan tags: server-edition
Changes
Hide Diffs Side-by-Side Diffs Ignore Whitespace Patch

Changes to src/pager.c.

  5368   5368           ** to be the right size but is not actually valid. Avoid this
  5369   5369           ** possibility by unmapping the db here. */
  5370   5370           if( USEFETCH(pPager) ){
  5371   5371             sqlite3OsUnfetch(pPager->fd, 0, 0);
  5372   5372           }
  5373   5373         }
  5374   5374       }
         5375  +
         5376  +    rc = pagerServerConnect(pPager);
  5375   5377   
  5376   5378       /* If there is a WAL file in the file-system, open this database in WAL
  5377   5379       ** mode. Otherwise, the following function call is a no-op.
  5378   5380       */
  5379         -    rc = pagerOpenWalIfPresent(pPager);
         5381  +    if( rc==SQLITE_OK ){
         5382  +      rc = pagerOpenWalIfPresent(pPager);
         5383  +    }
  5380   5384   #ifndef SQLITE_OMIT_WAL
  5381   5385       assert( pPager->pWal==0 || rc==SQLITE_OK );
  5382   5386   #endif
  5383         -
  5384         -    if( rc==SQLITE_OK && pagerUseWal(pPager)==0 ){
  5385         -      rc = pagerServerConnect(pPager);
  5386         -    }
  5387   5387     }
  5388   5388   
  5389   5389   #ifdef SQLITE_SERVER_EDITION
  5390   5390     if( pagerIsServer(pPager) ){
  5391   5391       assert( rc==SQLITE_OK );
  5392   5392       pager_reset(pPager);
  5393   5393       rc = sqlite3ServerBegin(pPager->pServer);
  5394         -  }else
         5394  +  }
  5395   5395   #endif
  5396   5396     if( pagerUseWal(pPager) ){
  5397   5397       assert( rc==SQLITE_OK );
  5398   5398       rc = pagerBeginReadTransaction(pPager);
  5399   5399     }
  5400   5400   
  5401   5401     if( pPager->tempFile==0 && pPager->eState==PAGER_OPEN && rc==SQLITE_OK ){
................................................................................
  7451   7451   
  7452   7452   /*
  7453   7453   ** Return true if the underlying VFS for the given pager supports the
  7454   7454   ** primitives necessary for write-ahead logging.
  7455   7455   */
  7456   7456   int sqlite3PagerWalSupported(Pager *pPager){
  7457   7457     const sqlite3_io_methods *pMethods = pPager->fd->pMethods;
  7458         -  if( pPager->noLock ) return 0;
         7458  +  if( pPager->noLock && !pagerIsServer(pPager) ) return 0;
  7459   7459     return pPager->exclusiveMode || (pMethods->iVersion>=2 && pMethods->xShmMap);
  7460   7460   }
  7461   7461   
  7462   7462   /*
  7463   7463   ** Attempt to take an exclusive lock on the database file. If a PENDING lock
  7464   7464   ** is obtained instead, immediately release it.
  7465   7465   */
................................................................................
  7546   7546       /* Close any rollback journal previously open */
  7547   7547       sqlite3OsClose(pPager->jfd);
  7548   7548   
  7549   7549       rc = pagerOpenWal(pPager);
  7550   7550       if( rc==SQLITE_OK ){
  7551   7551         pPager->journalMode = PAGER_JOURNALMODE_WAL;
  7552   7552         pPager->eState = PAGER_OPEN;
         7553  +      sqlite3WalServer(pPager->pWal, pPager->pServer);
  7553   7554       }
  7554   7555     }else{
  7555   7556       *pbOpen = 1;
  7556   7557     }
  7557   7558   
  7558   7559     return rc;
  7559   7560   }

Changes to src/wal.c.

   450    450     u32 nCkpt;                 /* Checkpoint sequence counter in the wal-header */
   451    451   #ifdef SQLITE_DEBUG
   452    452     u8 lockError;              /* True if a locking error has occurred */
   453    453   #endif
   454    454   #ifdef SQLITE_ENABLE_SNAPSHOT
   455    455     WalIndexHdr *pSnapshot;    /* Start transaction here if not NULL */
   456    456   #endif
          457  +#ifdef SQLITE_SERVER_EDITION
          458  +  Server *pServer;
          459  +#endif
   457    460   };
          461  +
          462  +#ifdef SQLITE_SERVER_EDITION
          463  +# define walIsServer(p) ((p)->pServer!=0)
          464  +#else
          465  +# define walIsServer(p) 0
          466  +#endif
   458    467   
   459    468   /*
   460    469   ** Candidate values for Wal.exclusiveMode.
   461    470   */
   462    471   #define WAL_NORMAL_MODE     0
   463    472   #define WAL_EXCLUSIVE_MODE  1     
   464    473   #define WAL_HEAPMEMORY_MODE 2
................................................................................
  1256   1265         sqlite3_free((void *)pWal->apWiData[i]);
  1257   1266         pWal->apWiData[i] = 0;
  1258   1267       }
  1259   1268     }else{
  1260   1269       sqlite3OsShmUnmap(pWal->pDbFd, isDelete);
  1261   1270     }
  1262   1271   }
         1272  +
         1273  +#ifdef SQLITE_SERVER_EDITION
         1274  +int sqlite3WalServer(Wal *pWal, Server *pServer){
         1275  +  assert( pWal->pServer==0 );
         1276  +  pWal->pServer = pServer;
         1277  +  return SQLITE_OK;
         1278  +}
         1279  +#endif
  1263   1280   
  1264   1281   /* 
  1265   1282   ** Open a connection to the WAL file zWalName. The database file must 
  1266   1283   ** already be opened on connection pDbFd. The buffer that zWalName points
  1267   1284   ** to must remain valid for the lifetime of the returned Wal* handle.
  1268   1285   **
  1269   1286   ** A SHARED lock should be held on the database file when this function
................................................................................
  2245   2262         }
  2246   2263       }
  2247   2264       if( rc!=SQLITE_OK ){
  2248   2265         return rc;
  2249   2266       }
  2250   2267     }
  2251   2268   
         2269  +  assert( rc==SQLITE_OK );
         2270  +  if( walIsServer(pWal) ) return SQLITE_OK;
         2271  +
  2252   2272     pInfo = walCkptInfo(pWal);
  2253   2273     if( !useWal && pInfo->nBackfill==pWal->hdr.mxFrame 
  2254   2274   #ifdef SQLITE_ENABLE_SNAPSHOT
  2255   2275      && (pWal->pSnapshot==0 || pWal->hdr.mxFrame==0
  2256   2276        || 0==memcmp(&pWal->hdr, pWal->pSnapshot, sizeof(WalIndexHdr)))
  2257   2277   #endif
  2258   2278     ){
................................................................................
  2584   2604   ){
  2585   2605     u32 iRead = 0;                  /* If !=0, WAL frame to return data from */
  2586   2606     u32 iLast = pWal->hdr.mxFrame;  /* Last page in WAL for this reader */
  2587   2607     int iHash;                      /* Used to loop through N hash tables */
  2588   2608     int iMinHash;
  2589   2609   
  2590   2610     /* This routine is only be called from within a read transaction. */
  2591         -  assert( pWal->readLock>=0 || pWal->lockError );
         2611  +  assert( walIsServer(pWal) || pWal->readLock>=0 || pWal->lockError );
         2612  +
         2613  +  if( walIsServer(pWal) ){
         2614  +    /* A server mode connection must read from the most recent snapshot. */
         2615  +    iLast = walIndexHdr(pWal)->mxFrame;
         2616  +  }
  2592   2617   
  2593   2618     /* If the "last page" field of the wal-index header snapshot is 0, then
  2594   2619     ** no data will be read from the wal under any circumstances. Return early
  2595   2620     ** in this case as an optimization.  Likewise, if pWal->readLock==0, 
  2596   2621     ** then the WAL is ignored by the reader so return early, as if the 
  2597   2622     ** WAL were empty.
  2598   2623     */
................................................................................
  2696   2721     return sqlite3OsRead(pWal->pWalFd, pOut, (nOut>sz ? sz : nOut), iOffset);
  2697   2722   }
  2698   2723   
  2699   2724   /* 
  2700   2725   ** Return the size of the database in pages (or zero, if unknown).
  2701   2726   */
  2702   2727   Pgno sqlite3WalDbsize(Wal *pWal){
  2703         -  if( pWal && ALWAYS(pWal->readLock>=0) ){
         2728  +  if( pWal && (walIsServer(pWal) || ALWAYS(pWal->readLock>=0)) ){
  2704   2729       return pWal->hdr.nPage;
  2705   2730     }
  2706   2731     return 0;
  2707   2732   }
  2708   2733   
  2709   2734   
  2710   2735   /* 
................................................................................
  2721   2746   ** There can only be a single writer active at a time.
  2722   2747   */
  2723   2748   int sqlite3WalBeginWriteTransaction(Wal *pWal){
  2724   2749     int rc;
  2725   2750   
  2726   2751     /* Cannot start a write transaction without first holding a read
  2727   2752     ** transaction. */
  2728         -  assert( pWal->readLock>=0 );
         2753  +  assert( walIsServer(pWal) || pWal->readLock>=0 );
  2729   2754     assert( pWal->writeLock==0 && pWal->iReCksum==0 );
  2730   2755   
  2731   2756     if( pWal->readOnly ){
  2732   2757       return SQLITE_READONLY;
  2733   2758     }
         2759  +
         2760  +  /* For a server connection, do nothing at this point. */
         2761  +  if( walIsServer(pWal) ){
         2762  +    return SQLITE_OK;
         2763  +  }
  2734   2764   
  2735   2765     /* Only one writer allowed at a time.  Get the write lock.  Return
  2736   2766     ** SQLITE_BUSY if unable.
  2737   2767     */
  2738   2768     rc = walLockExclusive(pWal, WAL_WRITE_LOCK, 1);
  2739   2769     if( rc ){
  2740   2770       return rc;
................................................................................
  3054   3084     int szFrame;                    /* The size of a single frame */
  3055   3085     i64 iOffset;                    /* Next byte to write in WAL file */
  3056   3086     WalWriter w;                    /* The writer */
  3057   3087     u32 iFirst = 0;                 /* First frame that may be overwritten */
  3058   3088     WalIndexHdr *pLive;             /* Pointer to shared header */
  3059   3089   
  3060   3090     assert( pList );
  3061         -  assert( pWal->writeLock );
         3091  +  assert( pWal->writeLock || walIsServer(pWal) );
         3092  +  if( pWal->writeLock==0 ){
         3093  +    int bDummy = 0;
         3094  +    rc = walLockExclusive(pWal, WAL_WRITE_LOCK, 1);
         3095  +    if( rc==SQLITE_OK ){
         3096  +      pWal->writeLock = 1;
         3097  +      rc = walIndexTryHdr(pWal, &bDummy);
         3098  +    }
         3099  +    if( rc!=SQLITE_OK ){
         3100  +      return rc;
         3101  +    }
         3102  +  }
  3062   3103   
  3063   3104     /* If this frame set completes a transaction, then nTruncate>0.  If
  3064   3105     ** nTruncate==0 then this frame set does not complete the transaction. */
  3065   3106     assert( (isCommit!=0)==(nTruncate!=0) );
  3066   3107   
  3067   3108   #if defined(SQLITE_TEST) && defined(SQLITE_DEBUG)
  3068   3109     { int cnt; for(cnt=0, p=pList; p; p=p->pDirty, cnt++){}

Changes to src/wal.h.

   139    139   ** stored in each frame (i.e. the db page-size when the WAL was created).
   140    140   */
   141    141   int sqlite3WalFramesize(Wal *pWal);
   142    142   #endif
   143    143   
   144    144   /* Return the sqlite3_file object for the WAL file */
   145    145   sqlite3_file *sqlite3WalFile(Wal *pWal);
          146  +
          147  +#ifdef SQLITE_SERVER_EDITION
          148  +int sqlite3WalServer(Wal *pWal, Server *pServer);
          149  +#endif
   146    150   
   147    151   #endif /* ifndef SQLITE_OMIT_WAL */
   148    152   #endif /* SQLITE_WAL_H */

Added test/serverwal.test.

            1  +# 2017 April 25
            2  +#
            3  +# The author disclaims copyright to this source code.  In place of
            4  +# a legal notice, here is a blessing:
            5  +#
            6  +#    May you do good and not evil.
            7  +#    May you find forgiveness for yourself and forgive others.
            8  +#    May you share freely, never taking more than you give.
            9  +#
           10  +#***********************************************************************
           11  +# This file implements regression tests for SQLite library.  The
           12  +# focus of this script is testing the server mode of SQLite.
           13  +#
           14  +
           15  +
           16  +set testdir [file dirname $argv0]
           17  +source $testdir/tester.tcl
           18  +set testprefix serverwal
           19  +
           20  +# Check files are created and deleted as expected.
           21  +#
           22  +do_execsql_test 1.0 {
           23  +  PRAGMA journal_mode = wal;
           24  +} {wal}
           25  +do_execsql_test 1.1 {
           26  +  CREATE TABLE t1(a, b);
           27  +}
           28  +do_execsql_test 1.2 {
           29  +  SELECT * FROM t1;
           30  +} {}
           31  +do_test 1.3 {
           32  +  lsort [glob test.db*]
           33  +} {test.db test.db-hma test.db-shm test.db-wal}
           34  +do_test 1.4 {
           35  +  db close
           36  +  glob test.db*
           37  +} {test.db}
           38  +
           39  +# Two concurrent transactions.
           40  +#
           41  +do_test 2.0 {
           42  +  sqlite3 db  test.db
           43  +  sqlite3 db2 test.db
           44  +  db eval {
           45  +    CREATE TABLE t2(a, b);
           46  +  }
           47  +} {}
           48  +do_test 2.1 {
           49  +  execsql {
           50  +    BEGIN;
           51  +      INSERT INTO t1 VALUES(1, 2);
           52  +  } db
           53  +  execsql {
           54  +    BEGIN;
           55  +      INSERT INTO t2 VALUES(1, 2);
           56  +  } db2
           57  +} {}
           58  +do_test 2.2 {
           59  +  execsql COMMIT db
           60  +  execsql COMMIT db2
           61  +} {}
           62  +
           63  +
           64  +finish_test