/ Check-in [ebf74015]
Login

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

Overview
Comment:Merge the checkpoint_fullfsync pragma and the superlock demonstration into the checkpoint-v2 experimental branch.
Downloads: Tarball | ZIP archive | SQL archive
Timelines: family | ancestors | descendants | both | blocking-checkpoint
Files: files | file ages | folders
SHA1: ebf74015f09fe241c1c6902dc8954f2b59ab41ec
User & Date: drh 2010-11-19 18:51:31
Original Comment: Merge the checkpoint_fullfsync pragma and the superlock demonstration into the checkpoint-v2 experimental branch.
Context
2011-02-02
16:34
Merge in the blocking-checkpoint enhancement, including the new sqlite3_wal_checkpoint_v2() interface and the PRAGMA wal_checkpoint(full) statement. check-in: bac7342c user: drh tags: trunk
2010-11-19
18:51
Merge the checkpoint_fullfsync pragma and the superlock demonstration into the checkpoint-v2 experimental branch. Closed-Leaf check-in: ebf74015 user: drh tags: blocking-checkpoint
18:36
Merge in the superlock demonstration changes. check-in: 570e79a8 user: drh tags: trunk
09:58
Add file test/tt3_checkpoint.c that adds a multi-threaded test for blocking checkpoints to threadtest3. check-in: 648dd157 user: dan tags: blocking-checkpoint
Changes
Hide Diffs Side-by-Side Diffs Ignore Whitespace Patch

Changes to main.mk.

   247    247     $(TOP)/src/test_osinst.c \
   248    248     $(TOP)/src/test_pcache.c \
   249    249     $(TOP)/src/test_quota.c \
   250    250     $(TOP)/src/test_rtree.c \
   251    251     $(TOP)/src/test_schema.c \
   252    252     $(TOP)/src/test_server.c \
   253    253     $(TOP)/src/test_stat.c \
          254  +  $(TOP)/src/test_superlock.c \
   254    255     $(TOP)/src/test_tclvar.c \
   255    256     $(TOP)/src/test_thread.c \
   256    257     $(TOP)/src/test_vfs.c \
   257    258     $(TOP)/src/test_wsd.c
   258    259   
   259    260   #TESTSRC += $(TOP)/ext/fts2/fts2_tokenizer.c
   260    261   #TESTSRC += $(TOP)/ext/fts3/fts3_tokenizer.c

Changes to src/btree.c.

  2101   2101   ** how well the database resists damage due to OS crashes and power
  2102   2102   ** failures.  Level 1 is the same as asynchronous (no syncs() occur and
  2103   2103   ** there is a high probability of damage)  Level 2 is the default.  There
  2104   2104   ** is a very low but non-zero probability of damage.  Level 3 reduces the
  2105   2105   ** probability of damage to near zero but with a write performance reduction.
  2106   2106   */
  2107   2107   #ifndef SQLITE_OMIT_PAGER_PRAGMAS
  2108         -int sqlite3BtreeSetSafetyLevel(Btree *p, int level, int fullSync){
         2108  +int sqlite3BtreeSetSafetyLevel(
         2109  +  Btree *p,              /* The btree to set the safety level on */
         2110  +  int level,             /* PRAGMA synchronous.  1=OFF, 2=NORMAL, 3=FULL */
         2111  +  int fullSync,          /* PRAGMA fullfsync. */
         2112  +  int ckptFullSync       /* PRAGMA checkpoint_fullfync */
         2113  +){
  2109   2114     BtShared *pBt = p->pBt;
  2110   2115     assert( sqlite3_mutex_held(p->db->mutex) );
         2116  +  assert( level>=1 && level<=3 );
  2111   2117     sqlite3BtreeEnter(p);
  2112         -  sqlite3PagerSetSafetyLevel(pBt->pPager, level, fullSync);
         2118  +  sqlite3PagerSetSafetyLevel(pBt->pPager, level, fullSync, ckptFullSync);
  2113   2119     sqlite3BtreeLeave(p);
  2114   2120     return SQLITE_OK;
  2115   2121   }
  2116   2122   #endif
  2117   2123   
  2118   2124   /*
  2119   2125   ** Return TRUE if the given btree is set to safety level 1.  In other

Changes to src/btree.h.

    71     71   #define BTREE_NO_READLOCK   2  /* Omit readlocks on readonly files */
    72     72   #define BTREE_MEMORY        4  /* This is an in-memory DB */
    73     73   #define BTREE_SINGLE        8  /* The file contains at most 1 b-tree */
    74     74   #define BTREE_UNORDERED    16  /* Use of a hash implementation is OK */
    75     75   
    76     76   int sqlite3BtreeClose(Btree*);
    77     77   int sqlite3BtreeSetCacheSize(Btree*,int);
    78         -int sqlite3BtreeSetSafetyLevel(Btree*,int,int);
           78  +int sqlite3BtreeSetSafetyLevel(Btree*,int,int,int);
    79     79   int sqlite3BtreeSyncDisabled(Btree*);
    80     80   int sqlite3BtreeSetPageSize(Btree *p, int nPagesize, int nReserve, int eFix);
    81     81   int sqlite3BtreeGetPageSize(Btree*);
    82     82   int sqlite3BtreeMaxPageCount(Btree*,int);
    83     83   u32 sqlite3BtreeLastPage(Btree*);
    84     84   int sqlite3BtreeSecureDelete(Btree*,int);
    85     85   int sqlite3BtreeGetReserve(Btree*);

Changes to src/main.c.

  2381   2381         Pager *pPager;
  2382   2382         sqlite3_file *fd;
  2383   2383         sqlite3BtreeEnter(pBtree);
  2384   2384         pPager = sqlite3BtreePager(pBtree);
  2385   2385         assert( pPager!=0 );
  2386   2386         fd = sqlite3PagerFile(pPager);
  2387   2387         assert( fd!=0 );
  2388         -      if( fd->pMethods ){
         2388  +      if( op==SQLITE_FCNTL_FILE_POINTER ){
         2389  +        *(sqlite3_file**)pArg = fd;
         2390  +        rc = SQLITE_OK;
         2391  +      }else if( fd->pMethods ){
  2389   2392           rc = sqlite3OsFileControl(fd, op, pArg);
  2390   2393         }
  2391   2394         sqlite3BtreeLeave(pBtree);
  2392   2395       }
  2393   2396     }
  2394   2397     sqlite3_mutex_leave(db->mutex);
  2395   2398     return rc;   

Changes to src/pager.c.

   611    611     sqlite3_vfs *pVfs;          /* OS functions to use for IO */
   612    612     u8 exclusiveMode;           /* Boolean. True if locking_mode==EXCLUSIVE */
   613    613     u8 journalMode;             /* One of the PAGER_JOURNALMODE_* values */
   614    614     u8 useJournal;              /* Use a rollback journal on this file */
   615    615     u8 noReadlock;              /* Do not bother to obtain readlocks */
   616    616     u8 noSync;                  /* Do not sync the journal if true */
   617    617     u8 fullSync;                /* Do extra syncs of the journal for robustness */
   618         -  u8 sync_flags;              /* One of SYNC_NORMAL or SYNC_FULL */
          618  +  u8 ckptSyncFlags;           /* SYNC_NORMAL or SYNC_FULL for checkpoint */
          619  +  u8 syncFlags;               /* SYNC_NORMAL or SYNC_FULL otherwise */
   619    620     u8 tempFile;                /* zFilename is a temporary file */
   620    621     u8 readOnly;                /* True for a read-only database */
   621    622     u8 memDb;                   /* True to inhibit all file I/O */
   622    623   
   623    624     /**************************************************************************
   624    625     ** The following block contains those class members that change during
   625    626     ** routine opertion.  Class members not in this block are either fixed
................................................................................
  1295   1296       if( doTruncate || iLimit==0 ){
  1296   1297         rc = sqlite3OsTruncate(pPager->jfd, 0);
  1297   1298       }else{
  1298   1299         static const char zeroHdr[28] = {0};
  1299   1300         rc = sqlite3OsWrite(pPager->jfd, zeroHdr, sizeof(zeroHdr), 0);
  1300   1301       }
  1301   1302       if( rc==SQLITE_OK && !pPager->noSync ){
  1302         -      rc = sqlite3OsSync(pPager->jfd, SQLITE_SYNC_DATAONLY|pPager->sync_flags);
         1303  +      rc = sqlite3OsSync(pPager->jfd, SQLITE_SYNC_DATAONLY|pPager->syncFlags);
  1303   1304       }
  1304   1305   
  1305   1306       /* At this point the transaction is committed but the write lock 
  1306   1307       ** is still held on the file. If there is a size limit configured for 
  1307   1308       ** the persistent journal and the journal file currently consumes more
  1308   1309       ** space than that limit allows for, truncate it now. There is no need
  1309   1310       ** to sync the file following this operation.
................................................................................
  2747   2748       zMaster = pPager->pTmpSpace;
  2748   2749       rc = readMasterJournal(pPager->jfd, zMaster, pPager->pVfs->mxPathname+1);
  2749   2750       testcase( rc!=SQLITE_OK );
  2750   2751     }
  2751   2752     if( rc==SQLITE_OK && !pPager->noSync 
  2752   2753      && (pPager->eState>=PAGER_WRITER_DBMOD || pPager->eState==PAGER_OPEN)
  2753   2754     ){
  2754         -    rc = sqlite3OsSync(pPager->fd, pPager->sync_flags);
         2755  +    rc = sqlite3OsSync(pPager->fd, pPager->syncFlags);
  2755   2756     }
  2756   2757     if( rc==SQLITE_OK ){
  2757   2758       rc = pager_end_transaction(pPager, zMaster[0]!='\0');
  2758   2759       testcase( rc!=SQLITE_OK );
  2759   2760     }
  2760   2761     if( rc==SQLITE_OK && zMaster[0] && res ){
  2761   2762       /* If there was a master journal and this routine will return success,
................................................................................
  2921   2922   ** changed. 
  2922   2923   */ 
  2923   2924   static int pagerWalFrames(
  2924   2925     Pager *pPager,                  /* Pager object */
  2925   2926     PgHdr *pList,                   /* List of frames to log */
  2926   2927     Pgno nTruncate,                 /* Database size after this commit */
  2927   2928     int isCommit,                   /* True if this is a commit */
  2928         -  int sync_flags                  /* Flags to pass to OsSync() (or 0) */
         2929  +  int syncFlags                   /* Flags to pass to OsSync() (or 0) */
  2929   2930   ){
  2930   2931     int rc;                         /* Return code */
  2931   2932   
  2932   2933     assert( pPager->pWal );
  2933   2934     rc = sqlite3WalFrames(pPager->pWal, 
  2934         -      pPager->pageSize, pList, nTruncate, isCommit, sync_flags
         2935  +      pPager->pageSize, pList, nTruncate, isCommit, syncFlags
  2935   2936     );
  2936   2937     if( rc==SQLITE_OK && pPager->pBackup ){
  2937   2938       PgHdr *p;
  2938   2939       for(p=pList; p; p=p->pDirty){
  2939   2940         sqlite3BackupUpdate(pPager->pBackup, p->pgno, (u8 *)p->pData);
  2940   2941       }
  2941   2942     }
................................................................................
  3256   3257   **    FULL      The journal is synced twice before writes begin on the
  3257   3258   **              database (with some additional information - the nRec field
  3258   3259   **              of the journal header - being written in between the two
  3259   3260   **              syncs).  If we assume that writing a
  3260   3261   **              single disk sector is atomic, then this mode provides
  3261   3262   **              assurance that the journal will not be corrupted to the
  3262   3263   **              point of causing damage to the database during rollback.
         3264  +**
         3265  +** The above is for a rollback-journal mode.  For WAL mode, OFF continues
         3266  +** to mean that no syncs ever occur.  NORMAL means that the WAL is synced
         3267  +** prior to the start of checkpoint and that the database file is synced
         3268  +** at the conclusion of the checkpoint if the entire content of the WAL
         3269  +** was written back into the database.  But no sync operations occur for
         3270  +** an ordinary commit in NORMAL mode with WAL.  FULL means that the WAL
         3271  +** file is synced following each commit operation, in addition to the
         3272  +** syncs associated with NORMAL.
         3273  +**
         3274  +** Do not confuse synchronous=FULL with SQLITE_SYNC_FULL.  The
         3275  +** SQLITE_SYNC_FULL macro means to use the MacOSX-style full-fsync
         3276  +** using fcntl(F_FULLFSYNC).  SQLITE_SYNC_NORMAL means to do an
         3277  +** ordinary fsync() call.  There is no difference between SQLITE_SYNC_FULL
         3278  +** and SQLITE_SYNC_NORMAL on platforms other than MacOSX.  But the
         3279  +** synchronous=FULL versus synchronous=NORMAL setting determines when
         3280  +** the xSync primitive is called and is relevant to all platforms.
  3263   3281   **
  3264   3282   ** Numeric values associated with these states are OFF==1, NORMAL=2,
  3265   3283   ** and FULL=3.
  3266   3284   */
  3267   3285   #ifndef SQLITE_OMIT_PAGER_PRAGMAS
  3268         -void sqlite3PagerSetSafetyLevel(Pager *pPager, int level, int bFullFsync){
         3286  +void sqlite3PagerSetSafetyLevel(
         3287  +  Pager *pPager,        /* The pager to set safety level for */
         3288  +  int level,            /* PRAGMA synchronous.  1=OFF, 2=NORMAL, 3=FULL */  
         3289  +  int bFullFsync,       /* PRAGMA fullfsync */
         3290  +  int bCkptFullFsync    /* PRAGMA checkpoint_fullfsync */
         3291  +){
         3292  +  assert( level>=1 && level<=3 );
  3269   3293     pPager->noSync =  (level==1 || pPager->tempFile) ?1:0;
  3270   3294     pPager->fullSync = (level==3 && !pPager->tempFile) ?1:0;
  3271         -  pPager->sync_flags = (bFullFsync?SQLITE_SYNC_FULL:SQLITE_SYNC_NORMAL);
         3295  +  if( pPager->noSync ){
         3296  +    pPager->syncFlags = 0;
         3297  +    pPager->ckptSyncFlags = 0;
         3298  +  }else if( bFullFsync ){
         3299  +    pPager->syncFlags = SQLITE_SYNC_FULL;
         3300  +    pPager->ckptSyncFlags = SQLITE_SYNC_FULL;
         3301  +  }else if( bCkptFullFsync ){
         3302  +    pPager->syncFlags = SQLITE_SYNC_NORMAL;
         3303  +    pPager->ckptSyncFlags = SQLITE_SYNC_FULL;
         3304  +  }else{
         3305  +    pPager->syncFlags = SQLITE_SYNC_NORMAL;
         3306  +    pPager->ckptSyncFlags = SQLITE_SYNC_NORMAL;
         3307  +  }
  3272   3308   }
  3273   3309   #endif
  3274   3310   
  3275   3311   /*
  3276   3312   ** The following global variable is incremented whenever the library
  3277   3313   ** attempts to open a temporary file.  This information is used for
  3278   3314   ** testing and analysis only.  
................................................................................
  3650   3686     u8 *pTmp = (u8 *)pPager->pTmpSpace;
  3651   3687   
  3652   3688     disable_simulated_io_errors();
  3653   3689     sqlite3BeginBenignMalloc();
  3654   3690     /* pPager->errCode = 0; */
  3655   3691     pPager->exclusiveMode = 0;
  3656   3692   #ifndef SQLITE_OMIT_WAL
  3657         -  sqlite3WalClose(pPager->pWal,
  3658         -    (pPager->noSync ? 0 : pPager->sync_flags), 
  3659         -    pPager->pageSize, pTmp
  3660         -  );
         3693  +  sqlite3WalClose(pPager->pWal, pPager->ckptSyncFlags, pPager->pageSize, pTmp);
  3661   3694     pPager->pWal = 0;
  3662   3695   #endif
  3663   3696     pager_reset(pPager);
  3664   3697     if( MEMDB ){
  3665   3698       pager_unlock(pPager);
  3666   3699     }else{
  3667   3700       /* If it is open, sync the journal file before calling UnlockAndRollback.
................................................................................
  3819   3852           ** for garbage data to be appended to the file, the nRec field
  3820   3853           ** is populated with 0xFFFFFFFF when the journal header is written
  3821   3854           ** and never needs to be updated.
  3822   3855           */
  3823   3856           if( pPager->fullSync && 0==(iDc&SQLITE_IOCAP_SEQUENTIAL) ){
  3824   3857             PAGERTRACE(("SYNC journal of %d\n", PAGERID(pPager)));
  3825   3858             IOTRACE(("JSYNC %p\n", pPager))
  3826         -          rc = sqlite3OsSync(pPager->jfd, pPager->sync_flags);
         3859  +          rc = sqlite3OsSync(pPager->jfd, pPager->syncFlags);
  3827   3860             if( rc!=SQLITE_OK ) return rc;
  3828   3861           }
  3829   3862           IOTRACE(("JHDR %p %lld\n", pPager, pPager->journalHdr));
  3830   3863           rc = sqlite3OsWrite(
  3831   3864               pPager->jfd, zHeader, sizeof(zHeader), pPager->journalHdr
  3832   3865           );
  3833   3866           if( rc!=SQLITE_OK ) return rc;
  3834   3867         }
  3835   3868         if( 0==(iDc&SQLITE_IOCAP_SEQUENTIAL) ){
  3836   3869           PAGERTRACE(("SYNC journal of %d\n", PAGERID(pPager)));
  3837   3870           IOTRACE(("JSYNC %p\n", pPager))
  3838         -        rc = sqlite3OsSync(pPager->jfd, pPager->sync_flags| 
  3839         -          (pPager->sync_flags==SQLITE_SYNC_FULL?SQLITE_SYNC_DATAONLY:0)
         3871  +        rc = sqlite3OsSync(pPager->jfd, pPager->syncFlags| 
         3872  +          (pPager->syncFlags==SQLITE_SYNC_FULL?SQLITE_SYNC_DATAONLY:0)
  3840   3873           );
  3841   3874           if( rc!=SQLITE_OK ) return rc;
  3842   3875         }
  3843   3876   
  3844   3877         pPager->journalHdr = pPager->journalOff;
  3845   3878         if( newHdr && 0==(iDc&SQLITE_IOCAP_SAFE_APPEND) ){
  3846   3879           pPager->nRec = 0;
................................................................................
  4422   4455     pPager->exclusiveMode = (u8)tempFile; 
  4423   4456     pPager->changeCountDone = pPager->tempFile;
  4424   4457     pPager->memDb = (u8)memDb;
  4425   4458     pPager->readOnly = (u8)readOnly;
  4426   4459     assert( useJournal || pPager->tempFile );
  4427   4460     pPager->noSync = pPager->tempFile;
  4428   4461     pPager->fullSync = pPager->noSync ?0:1;
  4429         -  pPager->sync_flags = SQLITE_SYNC_NORMAL;
         4462  +  pPager->syncFlags = pPager->noSync ? 0 : SQLITE_SYNC_NORMAL;
         4463  +  pPager->ckptSyncFlags = pPager->syncFlags;
  4430   4464     /* pPager->pFirst = 0; */
  4431   4465     /* pPager->pFirstSynced = 0; */
  4432   4466     /* pPager->pLast = 0; */
  4433   4467     pPager->nExtra = (u16)nExtra;
  4434   4468     pPager->journalSizeLimit = SQLITE_DEFAULT_JOURNAL_SIZE_LIMIT;
  4435   4469     assert( isOpen(pPager->fd) || tempFile );
  4436   4470     setSectorSize(pPager);
................................................................................
  5545   5579       /* Release the page reference. */
  5546   5580       sqlite3PagerUnref(pPgHdr);
  5547   5581     }
  5548   5582     return rc;
  5549   5583   }
  5550   5584   
  5551   5585   /*
  5552         -** Sync the pager file to disk. This is a no-op for in-memory files
         5586  +** Sync the database file to disk. This is a no-op for in-memory databases
  5553   5587   ** or pages with the Pager.noSync flag set.
  5554   5588   **
  5555         -** If successful, or called on a pager for which it is a no-op, this
         5589  +** If successful, or if called on a pager for which it is a no-op, this
  5556   5590   ** function returns SQLITE_OK. Otherwise, an IO error code is returned.
  5557   5591   */
  5558   5592   int sqlite3PagerSync(Pager *pPager){
  5559   5593     int rc;                              /* Return code */
  5560   5594     assert( !MEMDB );
  5561   5595     if( pPager->noSync ){
  5562   5596       rc = SQLITE_OK;
  5563   5597     }else{
  5564         -    rc = sqlite3OsSync(pPager->fd, pPager->sync_flags);
         5598  +    rc = sqlite3OsSync(pPager->fd, pPager->syncFlags);
  5565   5599     }
  5566   5600     return rc;
  5567   5601   }
  5568   5602   
  5569   5603   /*
  5570   5604   ** This function may only be called while a write-transaction is active in
  5571   5605   ** rollback. If the connection is in WAL mode, this call is a no-op. 
................................................................................
  5646   5680       */
  5647   5681       sqlite3BackupRestart(pPager->pBackup);
  5648   5682     }else{
  5649   5683       if( pagerUseWal(pPager) ){
  5650   5684         PgHdr *pList = sqlite3PcacheDirtyList(pPager->pPCache);
  5651   5685         if( pList ){
  5652   5686           rc = pagerWalFrames(pPager, pList, pPager->dbSize, 1, 
  5653         -            (pPager->fullSync ? pPager->sync_flags : 0)
         5687  +            (pPager->fullSync ? pPager->syncFlags : 0)
  5654   5688           );
  5655   5689         }
  5656   5690         if( rc==SQLITE_OK ){
  5657   5691           sqlite3PcacheCleanAll(pPager->pPCache);
  5658   5692         }
  5659   5693       }else{
  5660   5694         /* The following block updates the change-counter. Exactly how it
................................................................................
  5777   5811           assert( pPager->eState==PAGER_WRITER_DBMOD );
  5778   5812           rc = pager_truncate(pPager, nNew);
  5779   5813           if( rc!=SQLITE_OK ) goto commit_phase_one_exit;
  5780   5814         }
  5781   5815     
  5782   5816         /* Finally, sync the database file. */
  5783   5817         if( !pPager->noSync && !noSync ){
  5784         -        rc = sqlite3OsSync(pPager->fd, pPager->sync_flags);
         5818  +        rc = sqlite3OsSync(pPager->fd, pPager->syncFlags);
  5785   5819         }
  5786   5820         IOTRACE(("DBSYNC %p\n", pPager))
  5787   5821       }
  5788   5822     }
  5789   5823   
  5790   5824   commit_phase_one_exit:
  5791   5825     if( rc==SQLITE_OK && !pagerUseWal(pPager) ){
................................................................................
  6523   6557   ** Parameter eMode is one of SQLITE_CHECKPOINT_PASSIVE, FULL or RESTART.
  6524   6558   */
  6525   6559   int sqlite3PagerCheckpoint(Pager *pPager, int eMode, int *pnLog, int *pnCkpt){
  6526   6560     int rc = SQLITE_OK;
  6527   6561     if( pPager->pWal ){
  6528   6562       rc = sqlite3WalCheckpoint(pPager->pWal, eMode,
  6529   6563           pPager->xBusyHandler, pPager->pBusyHandlerArg,
  6530         -        (pPager->noSync ? 0 : pPager->sync_flags),
  6531         -        pPager->pageSize, (u8 *)pPager->pTmpSpace,
         6564  +        pPager->ckptSyncFlags, pPager->pageSize, (u8 *)pPager->pTmpSpace,
  6532   6565           pnLog, pnCkpt
  6533   6566       );
  6534   6567     }
  6535   6568     return rc;
  6536   6569   }
  6537   6570   
  6538   6571   int sqlite3PagerWalCallback(Pager *pPager){
................................................................................
  6678   6711       
  6679   6712     /* Checkpoint and close the log. Because an EXCLUSIVE lock is held on
  6680   6713     ** the database file, the log and log-summary files will be deleted.
  6681   6714     */
  6682   6715     if( rc==SQLITE_OK && pPager->pWal ){
  6683   6716       rc = pagerExclusiveLock(pPager);
  6684   6717       if( rc==SQLITE_OK ){
  6685         -      rc = sqlite3WalClose(pPager->pWal,
  6686         -          (pPager->noSync ? 0 : pPager->sync_flags), 
  6687         -          pPager->pageSize, (u8*)pPager->pTmpSpace
  6688         -      );
         6718  +      rc = sqlite3WalClose(pPager->pWal, pPager->ckptSyncFlags,
         6719  +                           pPager->pageSize, (u8*)pPager->pTmpSpace);
  6689   6720         pPager->pWal = 0;
  6690   6721       }
  6691   6722     }
  6692   6723     return rc;
  6693   6724   }
  6694   6725   
  6695   6726   #ifdef SQLITE_HAS_CODEC

Changes to src/pager.h.

    99     99   int sqlite3PagerReadFileheader(Pager*, int, unsigned char*);
   100    100   
   101    101   /* Functions used to configure a Pager object. */
   102    102   void sqlite3PagerSetBusyhandler(Pager*, int(*)(void *), void *);
   103    103   int sqlite3PagerSetPagesize(Pager*, u32*, int);
   104    104   int sqlite3PagerMaxPageCount(Pager*, int);
   105    105   void sqlite3PagerSetCachesize(Pager*, int);
   106         -void sqlite3PagerSetSafetyLevel(Pager*,int,int);
          106  +void sqlite3PagerSetSafetyLevel(Pager*,int,int,int);
   107    107   int sqlite3PagerLockingMode(Pager *, int);
   108    108   int sqlite3PagerSetJournalMode(Pager *, int);
   109    109   int sqlite3PagerGetJournalMode(Pager*);
   110    110   int sqlite3PagerOkToChangeJournalMode(Pager*);
   111    111   i64 sqlite3PagerJournalSizeLimit(Pager *, i64);
   112    112   sqlite3_backup **sqlite3PagerBackupPtr(Pager*);
   113    113   

Changes to src/pragma.c.

   168    168     } aPragma[] = {
   169    169       { "full_column_names",        SQLITE_FullColNames  },
   170    170       { "short_column_names",       SQLITE_ShortColNames },
   171    171       { "count_changes",            SQLITE_CountRows     },
   172    172       { "empty_result_callbacks",   SQLITE_NullCallback  },
   173    173       { "legacy_file_format",       SQLITE_LegacyFileFmt },
   174    174       { "fullfsync",                SQLITE_FullFSync     },
          175  +    { "checkpoint_fullfsync",     SQLITE_CkptFullFSync },
   175    176       { "reverse_unordered_selects", SQLITE_ReverseOrder  },
   176    177   #ifndef SQLITE_OMIT_AUTOMATIC_INDEX
   177    178       { "automatic_index",          SQLITE_AutoIndex     },
   178    179   #endif
   179    180   #ifdef SQLITE_DEBUG
   180    181       { "sql_trace",                SQLITE_SqlTrace      },
   181    182       { "vdbe_listing",             SQLITE_VdbeListing   },
................................................................................
  1515   1516     /*
  1516   1517     ** Reset the safety level, in case the fullfsync flag or synchronous
  1517   1518     ** setting changed.
  1518   1519     */
  1519   1520   #ifndef SQLITE_OMIT_PAGER_PRAGMAS
  1520   1521     if( db->autoCommit ){
  1521   1522       sqlite3BtreeSetSafetyLevel(pDb->pBt, pDb->safety_level,
  1522         -               (db->flags&SQLITE_FullFSync)!=0);
         1523  +               (db->flags&SQLITE_FullFSync)!=0,
         1524  +               (db->flags&SQLITE_CkptFullFSync)!=0);
  1523   1525     }
  1524   1526   #endif
  1525   1527   pragma_out:
  1526   1528     sqlite3DbFree(db, zLeft);
  1527   1529     sqlite3DbFree(db, zRight);
  1528   1530   }
  1529   1531   
  1530   1532   #endif /* SQLITE_OMIT_PRAGMA */

Changes to src/sqlite.h.in.

   533    533   **
   534    534   ** When the SQLITE_SYNC_DATAONLY flag is used, it means that the
   535    535   ** sync operation only needs to flush data to mass storage.  Inode
   536    536   ** information need not be flushed. If the lower four bits of the flag
   537    537   ** equal SQLITE_SYNC_NORMAL, that means to use normal fsync() semantics.
   538    538   ** If the lower four bits equal SQLITE_SYNC_FULL, that means
   539    539   ** to use Mac OS X style fullsync instead of fsync().
          540  +**
          541  +** Do not confuse the SQLITE_SYNC_NORMAL and SQLITE_SYNC_FULL flags
          542  +** with the [PRAGMA synchronous]=NORMAL and [PRAGMA synchronous]=FULL
          543  +** settings.  The [synchronous pragma] determines when calls to the
          544  +** xSync VFS method occur and applies uniformly across all platforms.
          545  +** The SQLITE_SYNC_NORMAL and SQLITE_SYNC_FULL flags determine how
          546  +** energetic or rigorous or forceful the sync operations are and
          547  +** only make a difference on Mac OSX for the default SQLite code.
          548  +** (Third-party VFS implementations might also make the distinction
          549  +** between SQLITE_SYNC_NORMAL and SQLITE_SYNC_FULL, but among the
          550  +** operating systems natively supported by SQLite, only Mac OSX
          551  +** cares about the difference.)
   540    552   */
   541    553   #define SQLITE_SYNC_NORMAL        0x00002
   542    554   #define SQLITE_SYNC_FULL          0x00003
   543    555   #define SQLITE_SYNC_DATAONLY      0x00010
   544    556   
   545    557   /*
   546    558   ** CAPI3REF: OS Interface Open File Handle
................................................................................
   701    713   */
   702    714   #define SQLITE_FCNTL_LOCKSTATE        1
   703    715   #define SQLITE_GET_LOCKPROXYFILE      2
   704    716   #define SQLITE_SET_LOCKPROXYFILE      3
   705    717   #define SQLITE_LAST_ERRNO             4
   706    718   #define SQLITE_FCNTL_SIZE_HINT        5
   707    719   #define SQLITE_FCNTL_CHUNK_SIZE       6
          720  +#define SQLITE_FCNTL_FILE_POINTER     7
          721  +
   708    722   
   709    723   /*
   710    724   ** CAPI3REF: Mutex Handle
   711    725   **
   712    726   ** The mutex module within SQLite defines [sqlite3_mutex] to be an
   713    727   ** abstract type for a mutex object.  The SQLite core never looks
   714    728   ** at the internal representation of an [sqlite3_mutex].  It only
................................................................................
  5230   5244   
  5231   5245   /*
  5232   5246   ** CAPI3REF: Low-Level Control Of Database Files
  5233   5247   **
  5234   5248   ** ^The [sqlite3_file_control()] interface makes a direct call to the
  5235   5249   ** xFileControl method for the [sqlite3_io_methods] object associated
  5236   5250   ** with a particular database identified by the second argument. ^The
  5237         -** name of the database "main" for the main database or "temp" for the
         5251  +** name of the database is "main" for the main database or "temp" for the
  5238   5252   ** TEMP database, or the name that appears after the AS keyword for
  5239   5253   ** databases that are added using the [ATTACH] SQL command.
  5240   5254   ** ^A NULL pointer can be used in place of "main" to refer to the
  5241   5255   ** main database file.
  5242   5256   ** ^The third and fourth parameters to this routine
  5243   5257   ** are passed directly through to the second and third parameters of
  5244   5258   ** the xFileControl method.  ^The return value of the xFileControl
  5245   5259   ** method becomes the return value of this routine.
         5260  +**
         5261  +** ^The SQLITE_FCNTL_FILE_POINTER value for the op parameter causes
         5262  +** a pointer to the underlying [sqlite3_file] object to be written into
         5263  +** the space pointed to by the 4th parameter.  ^The SQLITE_FCNTL_FILE_POINTER
         5264  +** case is a short-circuit path which does not actually invoke the
         5265  +** underlying sqlite3_io_methods.xFileControl method.
  5246   5266   **
  5247   5267   ** ^If the second parameter (zDbName) does not match the name of any
  5248   5268   ** open database file, then SQLITE_ERROR is returned.  ^This error
  5249   5269   ** code is not remembered and will not be recalled by [sqlite3_errcode()]
  5250   5270   ** or [sqlite3_errmsg()].  The underlying xFileControl method might
  5251   5271   ** also return SQLITE_ERROR.  There is no way to distinguish between
  5252   5272   ** an incorrect zDbName and an SQLITE_ERROR return from the underlying

Changes to src/sqliteInt.h.

   910    910   #define SQLITE_WriteSchema    0x00010000  /* OK to update SQLITE_MASTER */
   911    911   #define SQLITE_NoReadlock     0x00020000  /* Readlocks are omitted when 
   912    912                                             ** accessing read-only databases */
   913    913   #define SQLITE_IgnoreChecks   0x00040000  /* Do not enforce check constraints */
   914    914   #define SQLITE_ReadUncommitted 0x0080000  /* For shared-cache mode */
   915    915   #define SQLITE_LegacyFileFmt  0x00100000  /* Create new databases in format 1 */
   916    916   #define SQLITE_FullFSync      0x00200000  /* Use full fsync on the backend */
   917         -#define SQLITE_LoadExtension  0x00400000  /* Enable load_extension */
          917  +#define SQLITE_CkptFullFSync  0x00400000  /* Use full fsync for checkpoint */
   918    918   #define SQLITE_RecoveryMode   0x00800000  /* Ignore schema errors */
   919    919   #define SQLITE_ReverseOrder   0x01000000  /* Reverse unordered SELECTs */
   920    920   #define SQLITE_RecTriggers    0x02000000  /* Enable recursive triggers */
   921    921   #define SQLITE_ForeignKeys    0x04000000  /* Enforce foreign key constraints  */
   922    922   #define SQLITE_AutoIndex      0x08000000  /* Enable automatic indexes */
   923    923   #define SQLITE_PreferBuiltin  0x10000000  /* Preference to built-in funcs */
          924  +#define SQLITE_LoadExtension  0x20000000  /* Enable load_extension */
   924    925   
   925    926   /*
   926    927   ** Bits of the sqlite3.flags field that are used by the
   927    928   ** sqlite3_test_control(SQLITE_TESTCTRL_OPTIMIZATIONS,...) interface.
   928    929   ** These must be the low-order bits of the flags field.
   929    930   */
   930    931   #define SQLITE_QueryFlattener 0x01        /* Disable query flattening */

Changes to src/tclsqlite.c.

  3576   3576       extern int Sqlitetestbackup_Init(Tcl_Interp*);
  3577   3577       extern int Sqlitetestintarray_Init(Tcl_Interp*);
  3578   3578       extern int Sqlitetestvfs_Init(Tcl_Interp *);
  3579   3579       extern int SqlitetestStat_Init(Tcl_Interp*);
  3580   3580       extern int Sqlitetestrtree_Init(Tcl_Interp*);
  3581   3581       extern int Sqlitequota_Init(Tcl_Interp*);
  3582   3582       extern int Sqlitemultiplex_Init(Tcl_Interp*);
         3583  +    extern int SqliteSuperlock_Init(Tcl_Interp*);
  3583   3584   
  3584   3585       Sqliteconfig_Init(interp);
  3585   3586       Sqlitetest1_Init(interp);
  3586   3587       Sqlitetest2_Init(interp);
  3587   3588       Sqlitetest3_Init(interp);
  3588   3589       Sqlitetest4_Init(interp);
  3589   3590       Sqlitetest5_Init(interp);
................................................................................
  3607   3608       Sqlitetestbackup_Init(interp);
  3608   3609       Sqlitetestintarray_Init(interp);
  3609   3610       Sqlitetestvfs_Init(interp);
  3610   3611       SqlitetestStat_Init(interp);
  3611   3612       Sqlitetestrtree_Init(interp);
  3612   3613       Sqlitequota_Init(interp);
  3613   3614       Sqlitemultiplex_Init(interp);
         3615  +    SqliteSuperlock_Init(interp);
  3614   3616   
  3615   3617       Tcl_CreateObjCommand(interp,"load_testfixture_extensions",init_all_cmd,0,0);
  3616   3618   
  3617   3619   #ifdef SQLITE_SSE
  3618   3620       Sqlitetestsse_Init(interp);
  3619   3621   #endif
  3620   3622     }

Added src/test_superlock.c.

            1  +/*
            2  +** 2010 November 19
            3  +**
            4  +** The author disclaims copyright to this source code.  In place of
            5  +** a legal notice, here is a blessing:
            6  +**
            7  +**    May you do good and not evil.
            8  +**    May you find forgiveness for yourself and forgive others.
            9  +**    May you share freely, never taking more than you give.
           10  +**
           11  +*************************************************************************
           12  +** Example code for obtaining an exclusive lock on an SQLite database
           13  +** file. This method is complicated, but works for both WAL and rollback
           14  +** mode database files. The interface to the example code in this file 
           15  +** consists of the following two functions:
           16  +**
           17  +**   sqlite3demo_superlock()
           18  +**   sqlite3demo_superunlock()
           19  +*/
           20  +
           21  +#include <sqlite3.h>
           22  +#include <string.h>               /* memset(), strlen() */
           23  +#include <assert.h>               /* assert() */
           24  +
           25  +/*
           26  +** A structure to collect a busy-handler callback and argument and a count
           27  +** of the number of times it has been invoked.
           28  +*/
           29  +struct SuperlockBusy {
           30  +  int (*xBusy)(void*,int);        /* Pointer to busy-handler function */
           31  +  void *pBusyArg;                 /* First arg to pass to xBusy */
           32  +  int nBusy;                      /* Number of times xBusy has been invoked */
           33  +};
           34  +typedef struct SuperlockBusy SuperlockBusy;
           35  +
           36  +/*
           37  +** The pCtx pointer passed to this function is actually a pointer to a
           38  +** SuperlockBusy structure. Invoke the busy-handler function encapsulated
           39  +** by the structure and return the result.
           40  +*/
           41  +static int superlockBusyHandler(void *pCtx, int UNUSED){
           42  +  SuperlockBusy *pBusy = (SuperlockBusy *)pCtx;
           43  +  if( pBusy->xBusy==0 ) return 0;
           44  +  return pBusy->xBusy(pBusy->pBusyArg, pBusy->nBusy++);
           45  +}
           46  +
           47  +/*
           48  +** This function is used to determine if the main database file for 
           49  +** connection db is open in WAL mode or not. If no error occurs and the
           50  +** database file is in WAL mode, set *pbWal to true and return SQLITE_OK.
           51  +** If it is not in WAL mode, set *pbWal to false.
           52  +**
           53  +** If an error occurs, return an SQLite error code. The value of *pbWal
           54  +** is undefined in this case.
           55  +*/
           56  +static int superlockIsWal(sqlite3 *db, int *pbWal){
           57  +  int rc;                         /* Return Code */
           58  +  sqlite3_stmt *pStmt;            /* Compiled PRAGMA journal_mode statement */
           59  +
           60  +  rc = sqlite3_prepare(db, "PRAGMA main.journal_mode", -1, &pStmt, 0);
           61  +  if( rc!=SQLITE_OK ) return rc;
           62  +
           63  +  *pbWal = 0;
           64  +  if( SQLITE_ROW==sqlite3_step(pStmt) ){
           65  +    const char *zMode = (const char *)sqlite3_column_text(pStmt, 0);
           66  +    if( zMode && strlen(zMode)==3 && sqlite3_strnicmp("wal", zMode, 3)==0 ){
           67  +      *pbWal = 1;
           68  +    }
           69  +  }
           70  +
           71  +  return sqlite3_finalize(pStmt);
           72  +}
           73  +
           74  +/*
           75  +** Obtain an exclusive shm-lock on nByte bytes starting at offset idx
           76  +** of the file fd. If the lock cannot be obtained immediately, invoke
           77  +** the busy-handler until either it is obtained or the busy-handler
           78  +** callback returns 0.
           79  +*/
           80  +static int superlockShmLock(
           81  +  sqlite3_file *fd,               /* Database file handle */
           82  +  int idx,                        /* Offset of shm-lock to obtain */
           83  +  int nByte,                      /* Number of consective bytes to lock */
           84  +  SuperlockBusy *pBusy            /* Busy-handler wrapper object */
           85  +){
           86  +  int rc;
           87  +  int (*xShmLock)(sqlite3_file*, int, int, int) = fd->pMethods->xShmLock;
           88  +  do {
           89  +    rc = xShmLock(fd, idx, nByte, SQLITE_SHM_LOCK|SQLITE_SHM_EXCLUSIVE);
           90  +  }while( rc==SQLITE_BUSY && superlockBusyHandler((void *)pBusy, 0) );
           91  +  return rc;
           92  +}
           93  +
           94  +/*
           95  +** Obtain the extra locks on the database file required for WAL databases.
           96  +** Invoke the supplied busy-handler as required.
           97  +*/
           98  +static int superlockWalLock(
           99  +  sqlite3 *db,                    /* Database handle open on WAL database */
          100  +  SuperlockBusy *pBusy            /* Busy handler wrapper object */
          101  +){
          102  +  int rc;                         /* Return code */
          103  +  sqlite3_file *fd = 0;           /* Main database file handle */
          104  +  void volatile *p = 0;           /* Pointer to first page of shared memory */
          105  +  int nBusy = 0;                  /* Number of calls already made to xBusy */
          106  +
          107  +  /* Obtain a pointer to the sqlite3_file object open on the main db file. */
          108  +  rc = sqlite3_file_control(db, "main", SQLITE_FCNTL_FILE_POINTER, (void *)&fd);
          109  +  if( rc!=SQLITE_OK ) return rc;
          110  +
          111  +  /* Obtain the "recovery" lock. Normally, this lock is only obtained by
          112  +  ** clients running database recovery.  
          113  +  */
          114  +  rc = superlockShmLock(fd, 2, 1, pBusy);
          115  +  if( rc!=SQLITE_OK ) return rc;
          116  +
          117  +  /* Zero the start of the first shared-memory page. This means that any
          118  +  ** clients that open read or write transactions from this point on will
          119  +  ** have to run recovery before proceeding. Since they need the "recovery"
          120  +  ** lock that this process is holding to do that, no new read or write
          121  +  ** transactions may now be opened. Nor can a checkpoint be run, for the
          122  +  ** same reason.
          123  +  */
          124  +  rc = fd->pMethods->xShmMap(fd, 0, 32*1024, 1, &p);
          125  +  if( rc!=SQLITE_OK ) return rc;
          126  +  memset((void *)p, 0, 32);
          127  +
          128  +  /* Obtain exclusive locks on all the "read-lock" slots. Once these locks
          129  +  ** are held, it is guaranteed that there are no active reader, writer or 
          130  +  ** checkpointer clients.
          131  +  */
          132  +  rc = superlockShmLock(fd, 3, SQLITE_SHM_NLOCK-3, pBusy);
          133  +  return rc;
          134  +}
          135  +
          136  +/*
          137  +** Obtain a superlock on the database file identified by zPath, using the
          138  +** locking primitives provided by VFS zVfs. If successful, SQLITE_OK is
          139  +** returned and output variable *ppLock is populated with an opaque handle
          140  +** that may be used with sqlite3demo_superunlock() to release the lock.
          141  +**
          142  +** If an error occurs, *ppLock is set to 0 and an SQLite error code 
          143  +** (e.g. SQLITE_BUSY) is returned.
          144  +**
          145  +** If a required lock cannot be obtained immediately and the xBusy parameter
          146  +** to this function is not NULL, then xBusy is invoked in the same way
          147  +** as a busy-handler registered with SQLite (using sqlite3_busy_handler())
          148  +** until either the lock can be obtained or the busy-handler function returns
          149  +** 0 (indicating "give up").
          150  +*/
          151  +int sqlite3demo_superlock(
          152  +  const char *zPath,              /* Path to database file to lock */
          153  +  const char *zVfs,               /* VFS to use to access database file */
          154  +  int (*xBusy)(void*,int),        /* Busy handler callback */
          155  +  void *pBusyArg,                 /* Context arg for busy handler */
          156  +  void **ppLock                   /* OUT: Context to pass to superunlock() */
          157  +){
          158  +  sqlite3 *db = 0;                /* Database handle open on zPath */
          159  +  SuperlockBusy busy = {0, 0, 0}; /* Busy handler wrapper object */
          160  +  int rc;                         /* Return code */
          161  +
          162  +  /* Open a database handle on the file to superlock. */
          163  +  rc = sqlite3_open_v2(
          164  +      zPath, &db, SQLITE_OPEN_READWRITE|SQLITE_OPEN_CREATE, zVfs
          165  +  );
          166  +
          167  +  /* Install a busy-handler and execute a BEGIN EXCLUSIVE. If this is not
          168  +  ** a WAL database, this is all we need to do.  
          169  +  **
          170  +  ** A wrapper function is used to invoke the busy-handler instead of
          171  +  ** registering the busy-handler function supplied by the user directly
          172  +  ** with SQLite. This is because the same busy-handler function may be
          173  +  ** invoked directly later on when attempting to obtain the extra locks
          174  +  ** required in WAL mode. By using the wrapper, we are able to guarantee
          175  +  ** that the "nBusy" integer parameter passed to the users busy-handler
          176  +  ** represents the total number of busy-handler invocations made within
          177  +  ** this call to sqlite3demo_superlock(), including any made during the
          178  +  ** "BEGIN EXCLUSIVE".
          179  +  */
          180  +  if( rc==SQLITE_OK ){
          181  +    busy.xBusy = xBusy;
          182  +    busy.pBusyArg = pBusyArg;
          183  +    sqlite3_busy_handler(db, superlockBusyHandler, (void *)&busy);
          184  +    rc = sqlite3_exec(db, "BEGIN EXCLUSIVE", 0, 0, 0);
          185  +  }
          186  +
          187  +  /* If the BEGIN EXCLUSIVE was executed successfully and this is a WAL
          188  +  ** database, call superlockWalLock() to obtain the extra locks required
          189  +  ** to prevent readers, writers and/or checkpointers from accessing the
          190  +  ** db while this process is holding the superlock.
          191  +  **
          192  +  ** Before attempting any WAL locks, commit the transaction started above
          193  +  ** to drop the WAL read and write locks currently held. Otherwise, the
          194  +  ** new WAL locks may conflict with the old.
          195  +  */
          196  +  if( rc==SQLITE_OK ){
          197  +    int bWal;                     /* True for a WAL database, false otherwise */
          198  +    if( SQLITE_OK==(rc = superlockIsWal(db, &bWal)) && bWal ){
          199  +      rc = sqlite3_exec(db, "COMMIT", 0, 0, 0);
          200  +      if( rc==SQLITE_OK ){
          201  +        rc = superlockWalLock(db, &busy);
          202  +      }
          203  +    }
          204  +  }
          205  +
          206  +  if( rc!=SQLITE_OK ){
          207  +    sqlite3_close(db);
          208  +    *ppLock = 0;
          209  +  }else{
          210  +    *ppLock = (void *)db;
          211  +  }
          212  +
          213  +  return rc;
          214  +}
          215  +
          216  +/*
          217  +** Release a superlock held on a database file. The argument passed to 
          218  +** this function must have been obtained from a successful call to
          219  +** sqlite3demo_superlock().
          220  +*/
          221  +void sqlite3demo_superunlock(void *pLock){
          222  +  sqlite3_close((sqlite3 *)pLock);
          223  +}
          224  +
          225  +/*
          226  +** End of example code. Everything below here is the test harness.
          227  +**************************************************************************
          228  +**************************************************************************
          229  +*************************************************************************/
          230  +
          231  +
          232  +#ifdef SQLITE_TEST
          233  +
          234  +#include <tcl.h>
          235  +
          236  +struct InterpAndScript {
          237  +  Tcl_Interp *interp;
          238  +  Tcl_Obj *pScript;
          239  +};
          240  +typedef struct InterpAndScript InterpAndScript;
          241  +
          242  +static void superunlock_del(ClientData cd){
          243  +  sqlite3demo_superunlock((void *)cd);
          244  +}
          245  +
          246  +static int superunlock_cmd(
          247  +  ClientData cd,
          248  +  Tcl_Interp *interp,
          249  +  int objc,
          250  +  Tcl_Obj *CONST objv[]
          251  +){
          252  +  if( objc!=1 ){
          253  +    Tcl_WrongNumArgs(interp, 1, objv, "");
          254  +    return TCL_ERROR;
          255  +  }
          256  +  Tcl_DeleteCommand(interp, Tcl_GetString(objv[0]));
          257  +  return TCL_OK;
          258  +}
          259  +
          260  +static int superlock_busy(void *pCtx, int nBusy){
          261  +  InterpAndScript *p = (InterpAndScript *)pCtx;
          262  +  Tcl_Obj *pEval;                 /* Script to evaluate */
          263  +  int iVal = 0;                   /* Value to return */
          264  +
          265  +  pEval = Tcl_DuplicateObj(p->pScript);
          266  +  Tcl_IncrRefCount(pEval);
          267  +  Tcl_ListObjAppendElement(p->interp, pEval, Tcl_NewIntObj(nBusy));
          268  +  Tcl_EvalObjEx(p->interp, pEval, TCL_EVAL_GLOBAL);
          269  +  Tcl_GetIntFromObj(p->interp, Tcl_GetObjResult(p->interp), &iVal);
          270  +  Tcl_DecrRefCount(pEval);
          271  +
          272  +  return iVal;
          273  +}
          274  +
          275  +/*
          276  +** Tclcmd: sqlite3demo_superlock CMDNAME PATH VFS BUSY-HANDLER-SCRIPT
          277  +*/
          278  +static int superlock_cmd(
          279  +  ClientData cd,
          280  +  Tcl_Interp *interp,
          281  +  int objc,
          282  +  Tcl_Obj *CONST objv[]
          283  +){
          284  +  void *pLock;                    /* Lock context */
          285  +  char *zPath;
          286  +  char *zVfs = 0;
          287  +  InterpAndScript busy = {0, 0};
          288  +  int (*xBusy)(void*,int) = 0;    /* Busy handler callback */
          289  +  int rc;                         /* Return code from sqlite3demo_superlock() */
          290  +
          291  +  if( objc<3 || objc>5 ){
          292  +    Tcl_WrongNumArgs(
          293  +        interp, 1, objv, "CMDNAME PATH ?VFS? ?BUSY-HANDLER-SCRIPT?");
          294  +    return TCL_ERROR;
          295  +  }
          296  +
          297  +  zPath = Tcl_GetString(objv[2]);
          298  +
          299  +  if( objc>3 ){
          300  +    zVfs = Tcl_GetString(objv[3]);
          301  +    if( strlen(zVfs)==0 ) zVfs = 0;
          302  +  }
          303  +  if( objc>4 ){
          304  +    busy.interp = interp;
          305  +    busy.pScript = objv[4];
          306  +    xBusy = superlock_busy;
          307  +  }
          308  +
          309  +  rc = sqlite3demo_superlock(zPath, zVfs, xBusy, &busy, &pLock);
          310  +  assert( rc==SQLITE_OK || pLock==0 );
          311  +  assert( rc!=SQLITE_OK || pLock!=0 );
          312  +
          313  +  if( rc!=SQLITE_OK ){
          314  +    Tcl_ResetResult(interp);
          315  +    Tcl_AppendResult(interp, sqlite3ErrStr(rc), 0);
          316  +    return TCL_ERROR;
          317  +  }
          318  +
          319  +  Tcl_CreateObjCommand(
          320  +      interp, Tcl_GetString(objv[1]), superunlock_cmd, pLock, superunlock_del
          321  +  );
          322  +  Tcl_SetObjResult(interp, objv[1]);
          323  +  return TCL_OK;
          324  +}
          325  +
          326  +int SqliteSuperlock_Init(Tcl_Interp *interp){
          327  +  Tcl_CreateObjCommand(interp, "sqlite3demo_superlock", superlock_cmd, 0, 0);
          328  +  return TCL_OK;
          329  +}
          330  +#endif

Added test/superlock.test.

            1  +# 2010 November 19
            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  +#
           12  +
           13  +set testdir [file dirname $argv0]
           14  +source $testdir/tester.tcl
           15  +source $testdir/lock_common.tcl
           16  +
           17  +set testprefix superlock
           18  +
           19  +do_execsql_test 1.1 {
           20  +  CREATE TABLE t1(a, b);
           21  +  INSERT INTO t1 VALUES(1, 2);
           22  +  PRAGMA journal_mode = DELETE;
           23  +} {delete}
           24  +
           25  +do_test 1.2 { sqlite3demo_superlock unlock test.db } {unlock}
           26  +do_catchsql_test 1.3 { SELECT * FROM t1 } {1 {database is locked}}
           27  +do_test 1.4 { unlock } {}
           28  +
           29  +do_execsql_test 2.1 { 
           30  +  INSERT INTO t1 VALUES(3, 4);
           31  +  PRAGMA journal_mode = WAL;
           32  +} {wal}
           33  +
           34  +do_test 2.2 { sqlite3demo_superlock unlock test.db } {unlock}
           35  +do_catchsql_test 2.3 { SELECT * FROM t1 }           {1 {database is locked}}
           36  +do_catchsql_test 2.4 { INSERT INTO t1 VALUES(5, 6)} {1 {database is locked}}
           37  +do_catchsql_test 2.5 { PRAGMA wal_checkpoint }      {1 {database is locked}}
           38  +do_test 2.6 { unlock } {}
           39  +
           40  +do_execsql_test 3.1 { INSERT INTO t1 VALUES(3, 4) } 
           41  +
           42  +do_test 3.2 { sqlite3demo_superlock unlock test.db } {unlock}
           43  +do_catchsql_test 3.3 { SELECT * FROM t1 }           {1 {database is locked}}
           44  +do_catchsql_test 3.4 { INSERT INTO t1 VALUES(5, 6)} {1 {database is locked}}
           45  +do_catchsql_test 3.5 { PRAGMA wal_checkpoint }      {1 {database is locked}}
           46  +do_test 3.6 { unlock } {}
           47  +
           48  +do_execsql_test 4.1 { PRAGMA wal_checkpoint } {}
           49  +
           50  +do_test 4.2 { sqlite3demo_superlock unlock test.db } {unlock}
           51  +do_catchsql_test 4.3 { SELECT * FROM t1 }           {1 {database is locked}}
           52  +do_catchsql_test 4.4 { INSERT INTO t1 VALUES(5, 6)} {1 {database is locked}}
           53  +do_catchsql_test 4.5 { PRAGMA wal_checkpoint }      {1 {database is locked}}
           54  +do_test 4.6 { unlock } {}
           55  +
           56  +do_multiclient_test tn {
           57  +  proc busyhandler {x} {
           58  +    switch -- $x {
           59  +      1 { sql1 "COMMIT" }
           60  +      2 { sql2 "COMMIT" }
           61  +      3 { sql3 "COMMIT" }
           62  +    }
           63  +    lappend ::busylist $x
           64  +    return 1
           65  +  }
           66  +  set ::busylist [list]
           67  +
           68  +  do_test 5.$tn.1 {
           69  +    sql1 {
           70  +      CREATE TABLE t1(a, b);
           71  +      PRAGMA journal_mode = WAL;
           72  +      INSERT INTO t1 VALUES(1, 2);
           73  +    }
           74  +  } {wal}
           75  +
           76  +  do_test 5.$tn.2 {
           77  +    sql1 { BEGIN ; SELECT * FROM t1 }
           78  +    sql2 { BEGIN ; INSERT INTO t1 VALUES(3, 4) }
           79  +    sql3 { BEGIN ; SELECT * FROM t1 }
           80  +  } {1 2}
           81  +
           82  +  do_test 5.$tn.3 {
           83  +    set ::busylist [list]
           84  +    sqlite3demo_superlock unlock test.db "" busyhandler
           85  +    set ::busylist
           86  +  } {0 1 2 3}
           87  +
           88  +  do_test 5.$tn.4 { csql2 { SELECT * FROM t1 } } {1 {database is locked}}
           89  +  do_test 5.$tn.5 { 
           90  +    csql3 { INSERT INTO t1 VALUES(5, 6) } 
           91  +  } {1 {database is locked}}
           92  +  do_test 5.$tn.6 { csql1 "PRAGMA wal_checkpoint" } {1 {database is locked}}
           93  +
           94  +  do_test 5.$tn.7 { unlock } {}
           95  +}
           96  +
           97  +
           98  +finish_test