/ Check-in [fd4d38fa]
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:Integrated proxy locking file support for WAL journal mode and double free fix
Downloads: Tarball | ZIP archive | SQL archive
Timelines: family | ancestors | descendants | both | apple-osx
Files: files | file ages | folders
SHA1: fd4d38fa66de85676189ff4922fb1bf5f8cac9c4
User & Date: adam 2010-11-09 00:43:59
Context
2010-11-19
23:50
Merge all the latest changes from the trunk into the apple-osx branch. check-in: c8bc057c user: drh tags: apple-osx
2010-11-09
01:53
Back out the prior attempt to enable full-fsync for WAL and attempt the same thing using a completely different approach. check-in: f59949fa user: drh tags: apple-osx-exp
00:47
Experimental changes to test defaulting to fullfsync for WAL mode check-in: 77b343cf user: adam tags: apple-osx-exp
00:43
Integrated proxy locking file support for WAL journal mode and double free fix check-in: fd4d38fa user: adam tags: apple-osx
2010-09-10
23:16
fixed memory leak in proxy lock file error handling check-in: e01c5f3e user: adam tags: apple-osx
Changes
Hide Diffs Unified Diffs Ignore Whitespace Patch

Changes to src/main.c.

1662
1663
1664
1665
1666
1667
1668
1669
1670
1671
1672
1673
1674
1675
1676
      newLimit = aHardLimit[limitId];
    }
    db->aLimit[limitId] = newLimit;
  }
  return oldLimit;
}
#if defined(SQLITE_ENABLE_AUTO_PROFILE)
static void profile_sql(void *aux, const char *sql, uint64_t ns) {
#pragma unused(aux)
	fprintf(stderr, "Query: %s\n Execution Time: %llu ms\n", sql, ns / 1000000);
}
#endif

/*
** This routine does the work of opening a database on behalf of







|







1662
1663
1664
1665
1666
1667
1668
1669
1670
1671
1672
1673
1674
1675
1676
      newLimit = aHardLimit[limitId];
    }
    db->aLimit[limitId] = newLimit;
  }
  return oldLimit;
}
#if defined(SQLITE_ENABLE_AUTO_PROFILE)
static void profile_sql(void *aux, const char *sql, u64 ns) {
#pragma unused(aux)
	fprintf(stderr, "Query: %s\n Execution Time: %llu ms\n", sql, ns / 1000000);
}
#endif

/*
** This routine does the work of opening a database on behalf of

Changes to src/os_unix.c.

3565
3566
3567
3568
3569
3570
3571



3572
3573
3574
3575
3576
3577
3578
....
3627
3628
3629
3630
3631
3632
3633














3634
3635
3636
3637
3638
3639
3640
3641
3642
3643
3644
3645
3646
3647
3648
3649
3650
3651
3652
3653
3654
3655
3656
3657
3658
....
4146
4147
4148
4149
4150
4151
4152
4153
4154
4155
4156
4157
4158
4159
4160
....
4884
4885
4886
4887
4888
4889
4890
4891
4892
4893
4894
4895
4896
4897
4898
4899
4900
4901
4902
4903
4904
4905
4906
4907
4908
4909
4910
4911
4912
4913
4914
....
5514
5515
5516
5517
5518
5519
5520






















5521
5522
5523
5524
5525
5526
5527
....
5597
5598
5599
5600
5601
5602
5603
5604
5605
5606
5607
5608
5609
5610
5611
....
6241
6242
6243
6244
6245
6246
6247
6248
6249
6250
6251
6252
6253
6254
6255
....
6257
6258
6259
6260
6261
6262
6263
6264
6265
6266
6267



6268
6269
6270
6271
6272
6273
6274
    sqlite3_free(p->apRegion);
    if( p->h>=0 ) close(p->h);
    p->pInode->pShmNode = 0;
    sqlite3_free(p);
  }
}




/*
** Open a shared-memory area associated with open database file pDbFd.  
** This particular implementation uses mmapped files.
**
** The file used to implement shared-memory is in the same directory
** as the open database file and has the same name as the open database
** file with the "-shm" suffix added.  For example, if the database file
................................................................................
    ** with are subject to the current umask setting.
    */
    if( fstat(pDbFd->h, &sStat) ){
      rc = SQLITE_IOERR_FSTAT;
      goto shm_open_err;
    }















#ifdef SQLITE_SHM_DIRECTORY
    nShmFilename = sizeof(SQLITE_SHM_DIRECTORY) + 30;
#else
    nShmFilename = 5 + (int)strlen(pDbFd->zPath);
#endif
    pShmNode = sqlite3_malloc( sizeof(*pShmNode) + nShmFilename );
    if( pShmNode==0 ){
      rc = SQLITE_NOMEM;
      goto shm_open_err;
    }
    memset(pShmNode, 0, sizeof(*pShmNode));
    zShmFilename = pShmNode->zFilename = (char*)&pShmNode[1];
#ifdef SQLITE_SHM_DIRECTORY
    sqlite3_snprintf(nShmFilename, zShmFilename, 
                     SQLITE_SHM_DIRECTORY "/sqlite-shm-%x-%x",
                     (u32)sStat.st_ino, (u32)sStat.st_dev);
#else
    sqlite3_snprintf(nShmFilename, zShmFilename, "%s-shm", pDbFd->zPath);
#endif
    pShmNode->h = -1;
    pDbFd->pInode->pShmNode = pShmNode;
    pShmNode->pInode = pDbFd->pInode;
    pShmNode->mutex = sqlite3_mutex_alloc(SQLITE_MUTEX_FAST);
    if( pShmNode->mutex==0 ){
      rc = SQLITE_NOMEM;
................................................................................
static int proxyClose(sqlite3_file*);
static int proxyLock(sqlite3_file*, int);
static int proxyUnlock(sqlite3_file*, int);
static int proxyCheckReservedLock(sqlite3_file*, int*);
IOMETHODS(
  proxyIoFinder,            /* Finder function name */
  proxyIoMethods,           /* sqlite3_io_methods object name */
  1,                        /* shared memory is disabled */
  proxyClose,               /* xClose method */
  proxyLock,                /* xLock method */
  proxyUnlock,              /* xUnlock method */
  proxyCheckReservedLock    /* xCheckReservedLock method */
)
#endif

................................................................................
    int useProxy = 0;

    /* SQLITE_FORCE_PROXY_LOCKING==1 means force always use proxy, 0 means 
    ** never use proxy, NULL means use proxy for non-local files only.  */
    if( envforce!=NULL ){
      useProxy = atoi(envforce)>0;
    }else{
      struct statfs fsInfo;
      if( statfs(zPath, &fsInfo) == -1 ){
        /* In theory, the close(fd) call is sub-optimal. If the file opened
        ** with fd is a database file, and there are other connections open
        ** on that file that are currently holding advisory locks on it,
        ** then the call to close() will cancel those locks. In practice,
        ** we're assuming that statfs() doesn't fail very often. At least
        ** not while other file descriptors opened by the same process on
        ** the same file are working.  */
        p->lastErrno = errno;
        if( dirfd>=0 ){
          close(dirfd); /* silently leak if fail, in error */
        }
        close(fd); /* silently leak if fail, in error */
        rc = SQLITE_IOERR_ACCESS;
        goto open_finished;
      }
      useProxy = !(fsInfo.f_flags&MNT_LOCAL);
    }
    if( useProxy ){
      rc = fillInUnixFile(pVfs, fd, dirfd, pFile, zPath, noLock, isDelete);
      if( rc==SQLITE_OK ){
        /* cache the pMethod in case the transform fails */
        const struct sqlite3_io_methods *pMethod = pFile->pMethods;
................................................................................
      start=i+1;
    }
    buf[i] = lockPath[i];
  }
  OSTRACE(("CREATELOCKPATH  proxy lock path=%s pid=%d\n", lockPath, getpid()));
  return 0;
}























/*
** Create a new VFS file descriptor (stored in memory obtained from
** sqlite3_malloc) and open the file named "path" in the file descriptor.
**
** The caller is responsible not only for closing the file descriptor
** but also for freeing the memory associated with the file descriptor.
................................................................................
  pNew->pUnused = pUnused;
  
  rc = fillInUnixFile(&dummyVfs, fd, dirfd, (sqlite3_file*)pNew, path, 0, 0);
  if( rc==SQLITE_OK ){
    *ppFile = pNew;
    return SQLITE_OK;
  }
  sqlite3_free(pNew->pUnused);
end_create_proxy:    
  close(fd); /* silently leak fd if error, we're already in error */
  sqlite3_free(pNew);
  sqlite3_free(pUnused);
  return rc;
}

................................................................................
** This routine handles sqlite3_file_control() calls that are specific
** to proxy locking.
*/
static int proxyFileControl(sqlite3_file *id, int op, void *pArg){
  switch( op ){
    case SQLITE_GET_LOCKPROXYFILE: {
      unixFile *pFile = (unixFile*)id;
      if( pFile->pMethod == &proxyIoMethods ){
        proxyLockingContext *pCtx = (proxyLockingContext*)pFile->lockingContext;
        proxyTakeConch(pFile);
        if( pCtx->lockProxyPath ){
          *(const char **)pArg = pCtx->lockProxyPath;
        }else{
          *(const char **)pArg = ":auto: (not held)";
        }
................................................................................
        *(const char **)pArg = NULL;
      }
      return SQLITE_OK;
    }
    case SQLITE_SET_LOCKPROXYFILE: {
      unixFile *pFile = (unixFile*)id;
      int rc = SQLITE_OK;
      int isProxyStyle = (pFile->pMethod == &proxyIoMethods);
      if( pArg==NULL || (const char *)pArg==0 ){
        if( isProxyStyle ){
          /* turn off proxy locking - not supported */



          rc = SQLITE_ERROR /*SQLITE_PROTOCOL? SQLITE_MISUSE?*/;
        }else{
          /* turn off proxy locking - already off - NOOP */
          rc = SQLITE_OK;
        }
      }else{
        const char *proxyPath = (const char *)pArg;







>
>
>







 







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



|













|







 







|







 







<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<







 







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







 







<







 







|







 







|


|
>
>
>







3565
3566
3567
3568
3569
3570
3571
3572
3573
3574
3575
3576
3577
3578
3579
3580
3581
....
3630
3631
3632
3633
3634
3635
3636
3637
3638
3639
3640
3641
3642
3643
3644
3645
3646
3647
3648
3649
3650
3651
3652
3653
3654
3655
3656
3657
3658
3659
3660
3661
3662
3663
3664
3665
3666
3667
3668
3669
3670
3671
3672
3673
3674
3675
....
4163
4164
4165
4166
4167
4168
4169
4170
4171
4172
4173
4174
4175
4176
4177
....
4901
4902
4903
4904
4905
4906
4907

















4908
4909
4910
4911
4912
4913
4914
....
5514
5515
5516
5517
5518
5519
5520
5521
5522
5523
5524
5525
5526
5527
5528
5529
5530
5531
5532
5533
5534
5535
5536
5537
5538
5539
5540
5541
5542
5543
5544
5545
5546
5547
5548
5549
....
5619
5620
5621
5622
5623
5624
5625

5626
5627
5628
5629
5630
5631
5632
....
6262
6263
6264
6265
6266
6267
6268
6269
6270
6271
6272
6273
6274
6275
6276
....
6278
6279
6280
6281
6282
6283
6284
6285
6286
6287
6288
6289
6290
6291
6292
6293
6294
6295
6296
6297
6298
    sqlite3_free(p->apRegion);
    if( p->h>=0 ) close(p->h);
    p->pInode->pShmNode = 0;
    sqlite3_free(p);
  }
}

static int isProxyLockingMode(unixFile *);
static const char *proxySharedMemoryBasePath(unixFile *);

/*
** Open a shared-memory area associated with open database file pDbFd.  
** This particular implementation uses mmapped files.
**
** The file used to implement shared-memory is in the same directory
** as the open database file and has the same name as the open database
** file with the "-shm" suffix added.  For example, if the database file
................................................................................
    ** with are subject to the current umask setting.
    */
    if( fstat(pDbFd->h, &sStat) ){
      rc = SQLITE_IOERR_FSTAT;
      goto shm_open_err;
    }

    const char *zBasePath = pDbFd->zPath;
#if defined(__APPLE__) && SQLITE_ENABLE_LOCKING_STYLE
    /* If pDbFd is configured with proxy locking mode, use the local 
     ** lock file path to determine the -shm file path
     */
    if( isProxyLockingMode(pDbFd) ){
      zBasePath = proxySharedMemoryBasePath(pDbFd);
      if( !zBasePath ){
        rc = SQLITE_CANTOPEN_BKPT;
        goto shm_open_err;
      }
    }
#endif
    
#ifdef SQLITE_SHM_DIRECTORY
    nShmFilename = sizeof(SQLITE_SHM_DIRECTORY) + 30;
#else
    nShmFilename = 5 + (int)strlen(zBasePath);
#endif
    pShmNode = sqlite3_malloc( sizeof(*pShmNode) + nShmFilename );
    if( pShmNode==0 ){
      rc = SQLITE_NOMEM;
      goto shm_open_err;
    }
    memset(pShmNode, 0, sizeof(*pShmNode));
    zShmFilename = pShmNode->zFilename = (char*)&pShmNode[1];
#ifdef SQLITE_SHM_DIRECTORY
    sqlite3_snprintf(nShmFilename, zShmFilename, 
                     SQLITE_SHM_DIRECTORY "/sqlite-shm-%x-%x",
                     (u32)sStat.st_ino, (u32)sStat.st_dev);
#else
    sqlite3_snprintf(nShmFilename, zShmFilename, "%s-shm", zBasePath);
#endif
    pShmNode->h = -1;
    pDbFd->pInode->pShmNode = pShmNode;
    pShmNode->pInode = pDbFd->pInode;
    pShmNode->mutex = sqlite3_mutex_alloc(SQLITE_MUTEX_FAST);
    if( pShmNode->mutex==0 ){
      rc = SQLITE_NOMEM;
................................................................................
static int proxyClose(sqlite3_file*);
static int proxyLock(sqlite3_file*, int);
static int proxyUnlock(sqlite3_file*, int);
static int proxyCheckReservedLock(sqlite3_file*, int*);
IOMETHODS(
  proxyIoFinder,            /* Finder function name */
  proxyIoMethods,           /* sqlite3_io_methods object name */
  2,                        /* shared memory is enabled */
  proxyClose,               /* xClose method */
  proxyLock,                /* xLock method */
  proxyUnlock,              /* xUnlock method */
  proxyCheckReservedLock    /* xCheckReservedLock method */
)
#endif

................................................................................
    int useProxy = 0;

    /* SQLITE_FORCE_PROXY_LOCKING==1 means force always use proxy, 0 means 
    ** never use proxy, NULL means use proxy for non-local files only.  */
    if( envforce!=NULL ){
      useProxy = atoi(envforce)>0;
    }else{

















      useProxy = !(fsInfo.f_flags&MNT_LOCAL);
    }
    if( useProxy ){
      rc = fillInUnixFile(pVfs, fd, dirfd, pFile, zPath, noLock, isDelete);
      if( rc==SQLITE_OK ){
        /* cache the pMethod in case the transform fails */
        const struct sqlite3_io_methods *pMethod = pFile->pMethods;
................................................................................
      start=i+1;
    }
    buf[i] = lockPath[i];
  }
  OSTRACE(("CREATELOCKPATH  proxy lock path=%s pid=%d\n", lockPath, getpid()));
  return 0;
}

static int isProxyLockingMode(unixFile *pFile) {
  return (pFile->pMethod == &proxyIoMethods) ? 1 : 0;
}

/*
** Return the shared memory base path based on the lock proxy file if the 
** lock proxy file is hosted on a shared memory compatible FS
*/
static const char *proxySharedMemoryBasePath(unixFile *pFile) {
  proxyLockingContext *pCtx;
  unixFile *pLockFile;
  
  assert(pFile!=NULL && pFile->lockingContext!=NULL);
  assert(pFile->pMethod == &proxyIoMethods);
  pCtx = ((proxyLockingContext *)(pFile->lockingContext));
  pLockFile = pCtx->lockProxy;
  if( pLockFile->pMethod->iVersion>=2 && pLockFile->pMethod->xShmMap!=0 ){
    return pCtx->lockProxyPath;
  }
  return NULL;
}

/*
** Create a new VFS file descriptor (stored in memory obtained from
** sqlite3_malloc) and open the file named "path" in the file descriptor.
**
** The caller is responsible not only for closing the file descriptor
** but also for freeing the memory associated with the file descriptor.
................................................................................
  pNew->pUnused = pUnused;
  
  rc = fillInUnixFile(&dummyVfs, fd, dirfd, (sqlite3_file*)pNew, path, 0, 0);
  if( rc==SQLITE_OK ){
    *ppFile = pNew;
    return SQLITE_OK;
  }

end_create_proxy:    
  close(fd); /* silently leak fd if error, we're already in error */
  sqlite3_free(pNew);
  sqlite3_free(pUnused);
  return rc;
}

................................................................................
** This routine handles sqlite3_file_control() calls that are specific
** to proxy locking.
*/
static int proxyFileControl(sqlite3_file *id, int op, void *pArg){
  switch( op ){
    case SQLITE_GET_LOCKPROXYFILE: {
      unixFile *pFile = (unixFile*)id;
      if( isProxyLockingMode(pFile) ){
        proxyLockingContext *pCtx = (proxyLockingContext*)pFile->lockingContext;
        proxyTakeConch(pFile);
        if( pCtx->lockProxyPath ){
          *(const char **)pArg = pCtx->lockProxyPath;
        }else{
          *(const char **)pArg = ":auto: (not held)";
        }
................................................................................
        *(const char **)pArg = NULL;
      }
      return SQLITE_OK;
    }
    case SQLITE_SET_LOCKPROXYFILE: {
      unixFile *pFile = (unixFile*)id;
      int rc = SQLITE_OK;
      int isProxyStyle = isProxyLockingMode(pFile);
      if( pArg==NULL || (const char *)pArg==0 ){
        if( isProxyStyle ){
          /* turn off proxy locking - not supported.  If support is added for
          ** switching proxy locking mode off then it will need to fail if
          ** the journal mode is WAL mode. 
          */
          rc = SQLITE_ERROR /*SQLITE_PROTOCOL? SQLITE_MISUSE?*/;
        }else{
          /* turn off proxy locking - already off - NOOP */
          rc = SQLITE_OK;
        }
      }else{
        const char *proxyPath = (const char *)pArg;

Changes to test/fallocate.test.

72
73
74
75
76
77
78

79
80
81
82
83
84
85
# The following tests - fallocate-2.* - test that things work in WAL
# mode as well.
#
set skipwaltests [expr {
  [permutation]=="journaltest" || [permutation]=="inmemory_journal"
}]
ifcapable !wal { set skipwaltests 1 }


if {!$skipwaltests} {
  db close
  file delete -force test.db
  sqlite3 db test.db
  file_control_chunksize_test db main [expr 32*1024]
  







>







72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
# The following tests - fallocate-2.* - test that things work in WAL
# mode as well.
#
set skipwaltests [expr {
  [permutation]=="journaltest" || [permutation]=="inmemory_journal"
}]
ifcapable !wal { set skipwaltests 1 }
if {![wal_is_ok]} { set skipwaltests 1 }

if {!$skipwaltests} {
  db close
  file delete -force test.db
  sqlite3 db test.db
  file_control_chunksize_test db main [expr 32*1024]
  

Changes to test/lock6.test.

122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
...
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
    set lockpath [execsql {
      PRAGMA lock_proxy_file=":auto:";
      PRAGMA lock_proxy_file;
    } db]
    set lockpath
  } {{:auto: (not held)}}

  set lpp [pwd]/notmine
  do_test lock6-1.4.1 {
    execsql "PRAGMA lock_proxy_file='$lpp'"
    catchsql {
      select * from sqlite_master;
    } db
  } {1 {database is locked}}

................................................................................
        SELECT * FROM sqlite_master;
      }
    }
  } {}

  catch {testfixture $::tf1 {db close}}

  set lpp [pwd]/mine
  do_test lock6-1.6 {
    execsql "PRAGMA lock_proxy_file='$lpp'"
    execsql {
      select * from sqlite_master;
    } db
  } {}
  







|







 







|







122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
...
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
    set lockpath [execsql {
      PRAGMA lock_proxy_file=":auto:";
      PRAGMA lock_proxy_file;
    } db]
    set lockpath
  } {{:auto: (not held)}}

  set lpp [exec mktemp -t fail]
  do_test lock6-1.4.1 {
    execsql "PRAGMA lock_proxy_file='$lpp'"
    catchsql {
      select * from sqlite_master;
    } db
  } {1 {database is locked}}

................................................................................
        SELECT * FROM sqlite_master;
      }
    }
  } {}

  catch {testfixture $::tf1 {db close}}

  set lpp [exec mktemp -t ok]
  do_test lock6-1.6 {
    execsql "PRAGMA lock_proxy_file='$lpp'"
    execsql {
      select * from sqlite_master;
    } db
  } {}
  

Changes to test/lock_proxy.test.

23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
...
131
132
133
134
135
136
137
138
139
140
141
142
  finish_test
  return
}
db close
file delete -force test.db.lock

#####################################################################
ifcapable lock_proxy_pragmas {
  set ::using_proxy 0
  foreach {name value} [array get env SQLITE_FORCE_PROXY_LOCKING] {
    set ::using_proxy $value
  }

  # enable the proxy locking for these tests
  set env(SQLITE_FORCE_PROXY_LOCKING) "1"
................................................................................
  set sqlite_hostid_num 0
}

#####################################################################

file delete -force test.db

ifcapable lock_proxy_pragmas {
  set env(SQLITE_FORCE_PROXY_LOCKING) $::using_proxy
}

finish_test







|







 







|




23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
...
131
132
133
134
135
136
137
138
139
140
141
142
  finish_test
  return
}
db close
file delete -force test.db.lock

#####################################################################
ifcapable lock_proxy_pragmas&&prefer_proxy_locking {
  set ::using_proxy 0
  foreach {name value} [array get env SQLITE_FORCE_PROXY_LOCKING] {
    set ::using_proxy $value
  }

  # enable the proxy locking for these tests
  set env(SQLITE_FORCE_PROXY_LOCKING) "1"
................................................................................
  set sqlite_hostid_num 0
}

#####################################################################

file delete -force test.db

ifcapable lock_proxy_pragmas&&prefer_proxy_locking {
  set env(SQLITE_FORCE_PROXY_LOCKING) $::using_proxy
}

finish_test

Changes to test/pager1.test.

1126
1127
1128
1129
1130
1131
1132
1133
1134
1135
1136
1137
1138
1139
1140
1141
1142
1143
....
2272
2273
2274
2275
2276
2277
2278

2279
2280
2281
2282
2283
2284
2285
2286
2287
2288
2289
2290
2291
2292
2293
2294
2295
2296

2297
2298
2299
2300
2301
2302
2303
    }] {
      do_execsql_test pager1-7.1.$tn.1 $sql $res
      catch { set J -1 ; set J [file size test.db-journal] }
      catch { set W -1 ; set W [file size test.db-wal] }
      do_test pager1-7.1.$tn.2 { list $J $W } [list $js $ws]
    }
  }
} else {
  do_test pager1-7.1.wal_is_not_ok {
    execsql { PRAGMA journal_mode = WAL }
  } {delete}
}

do_test pager1-7.2.1 {
  faultsim_delete_and_reopen
  execsql {
    PRAGMA locking_mode = EXCLUSIVE;
    CREATE TABLE t1(a, b);
................................................................................

#-------------------------------------------------------------------------
# Test that attempting to open a write-transaction with 
# locking_mode=exclusive in WAL mode fails if there are other clients on 
# the same database.
#
catch { db close }

do_multiclient_test tn {
  do_test pager1-28.$tn.1 {
    sql1 { 
      PRAGMA journal_mode = WAL;
      CREATE TABLE t1(a, b);
      INSERT INTO t1 VALUES('a', 'b');
    }
  } {wal}
  do_test pager1-28.$tn.2 { sql2 { SELECT * FROM t1 } } {a b}

  do_test pager1-28.$tn.3 { sql1 { PRAGMA locking_mode=exclusive } } {exclusive}
  do_test pager1-28.$tn.4 { 
    csql1 { BEGIN; INSERT INTO t1 VALUES('c', 'd'); }
  } {1 {database is locked}}
  code2 { db2 close ; sqlite3 db2 test.db }
  do_test pager1-28.$tn.4 { 
    sql1 { INSERT INTO t1 VALUES('c', 'd'); COMMIT }
  } {}

}

#-------------------------------------------------------------------------
# Normally, when changing from journal_mode=PERSIST to DELETE the pager
# attempts to delete the journal file. However, if it cannot obtain a
# RESERVED lock on the database file, this step is skipped.
#







<
<
<
<







 







>
|
|
|
|
|
|
|
|
|

|
|
|
|
|
|
|
|
>







1126
1127
1128
1129
1130
1131
1132




1133
1134
1135
1136
1137
1138
1139
....
2268
2269
2270
2271
2272
2273
2274
2275
2276
2277
2278
2279
2280
2281
2282
2283
2284
2285
2286
2287
2288
2289
2290
2291
2292
2293
2294
2295
2296
2297
2298
2299
2300
2301
    }] {
      do_execsql_test pager1-7.1.$tn.1 $sql $res
      catch { set J -1 ; set J [file size test.db-journal] }
      catch { set W -1 ; set W [file size test.db-wal] }
      do_test pager1-7.1.$tn.2 { list $J $W } [list $js $ws]
    }
  }




}

do_test pager1-7.2.1 {
  faultsim_delete_and_reopen
  execsql {
    PRAGMA locking_mode = EXCLUSIVE;
    CREATE TABLE t1(a, b);
................................................................................

#-------------------------------------------------------------------------
# Test that attempting to open a write-transaction with 
# locking_mode=exclusive in WAL mode fails if there are other clients on 
# the same database.
#
catch { db close }
if {[wal_is_ok]} {
  do_multiclient_test tn {
    do_test pager1-28.$tn.1 {
      sql1 { 
        PRAGMA journal_mode = WAL;
        CREATE TABLE t1(a, b);
        INSERT INTO t1 VALUES('a', 'b');
      }
    } {wal}
    do_test pager1-28.$tn.2 { sql2 { SELECT * FROM t1 } } {a b}

    do_test pager1-28.$tn.3 { sql1 { PRAGMA locking_mode=exclusive } } {exclusive}
    do_test pager1-28.$tn.4 { 
      csql1 { BEGIN; INSERT INTO t1 VALUES('c', 'd'); }
    } {1 {database is locked}}
    code2 { db2 close ; sqlite3 db2 test.db }
    do_test pager1-28.$tn.4 { 
      sql1 { INSERT INTO t1 VALUES('c', 'd'); COMMIT }
    } {}
  }
}

#-------------------------------------------------------------------------
# Normally, when changing from journal_mode=PERSIST to DELETE the pager
# attempts to delete the journal file. However, if it cannot obtain a
# RESERVED lock on the database file, this step is skipped.
#

Changes to test/pragma.test.

1288
1289
1290
1291
1292
1293
1294

1295




1296
1297
1298
1299
1300
1301
1302
....
1303
1304
1305
1306
1307
1308
1309
1310
1311
1312
1313
1314
1315
1316
1317
....
1328
1329
1330
1331
1332
1333
1334
1335
1336
1337
1338
1339
1340
1341
1342
1343
1344
1345
1346
1347
1348
1349
1350
1351
1352
1353
....
1395
1396
1397
1398
1399
1400
1401
1402
1403
1404
1405
1406
1407
1408
1409
....
1424
1425
1426
1427
1428
1429
1430
1431
1432
1433
1434
1435
1436
1437
1438
1439
1440
1441
1442
1443
1444
1445
1446
1447
1448
1449
1450
1451
1452
1453
1454
1455
1456
1457
1458
1459
1460
....
1465
1466
1467
1468
1469
1470
1471
1472
1473
1474
1475
1476
1477
1478
1479
1480
1481
1482
1483
1484
1485
1486
1487
1488
1489
....
1492
1493
1494
1495
1496
1497
1498
1499
1500
1501
1502
1503
1504
1505
1506
1507
1508
1509
1510
1511
}

# Reset the sqlite3_temp_directory variable for the next run of tests:
sqlite3 dbX :memory:
dbX eval {PRAGMA temp_store_directory = ""}
dbX close


ifcapable lock_proxy_pragmas&&prefer_proxy_locking {




  set sqlite_hostid_num 1

  set using_proxy 0
  foreach {name value} [array get env SQLITE_FORCE_PROXY_LOCKING] {
    set using_proxy $value
  }

................................................................................
  # Test the lock_proxy_file pragmas.
  #
  db close
  set env(SQLITE_FORCE_PROXY_LOCKING) "0"

  sqlite3 db test.db
  # set lock proxy name and then query it via pragma interface
  set lpp [pwd]/proxyfile_1
  do_test pragma-16.1 {
    execsql "PRAGMA lock_proxy_file='$lpp'"
    execsql "select * from sqlite_master"
    execsql "PRAGMA lock_proxy_file"
  } $lpp

  # 2 database connections can share a lock proxy file
................................................................................
      PRAGMA lock_proxy_file=":auto:";
      select * from sqlite_master;
    } db2
    execsql "PRAGMA lock_proxy_file" db2
  } $lpp

  db2 close
  set lpp2 [pwd]/proxyfile_2

  # 2nd database connection cannot override the lock proxy file
  do_test pragma-16.3 {
    sqlite3 db2 test.db
    execsql "PRAGMA lock_proxy_file='$lpp2'" db2
    catchsql {
      select * from sqlite_master;
    } db2
  } {1 {database is locked}}

  set lpp3 [pwd]/unused_proxyfile

  # lock proxy file can be renamed if no other connections are active
  do_test pragma-16.4 {
    db2 close
    db close
    sqlite3 db2 test.db
    execsql "PRAGMA lock_proxy_file='$lpp3'" db2
................................................................................
    sqlite3 db test2.db
    catchsql {
      select * from sqlite_master;
    } 
  } {1 {database is locked}}

  db2 close
  set lpp4 [pwd]/proxyfile_4

  # check that db is unlocked after first host connection closes 
  do_test pragma-16.8.1 {
    execsql "PRAGMA lock_proxy_file='$lpp4'" 
    execsql "select * from sqlite_master"
    execsql "PRAGMA lock_proxy_file"
  } $lpp4
................................................................................
      PRAGMA lock_proxy_file=":auto:";
      PRAGMA lock_proxy_file;
    } db]
    string match "*proxytest.db:auto:" $lockpath2
  } {1}

  # ensure creating directories for a lock proxy file works
  set lpp5 [pwd]/proxytest/sub/dir/lock
  db close
  file delete -force proxytest
  do_test pragma-16.10.1 {
    sqlite3 db proxytest.db
    execsql "PRAGMA lock_proxy_file='$lpp5'" 
    set lockpath2 [execsql {
      PRAGMA lock_proxy_file;
    } db]
    string match "*proxytest/sub/dir/lock" $lockpath2
  } {1}

  # ensure that after deleting the path, setting ":auto:" works correctly
  db close
  file delete -force proxytest
  do_test pragma-16.10.2 {
    sqlite3 db proxytest.db
    set lockpath2 [execsql {
      PRAGMA lock_proxy_file=":auto:";
      create table if not exists pt(y);
      PRAGMA lock_proxy_file;
    } db]
    string match "*proxytest/sub/dir/lock" $lockpath2
  } {1}

  # ensure that if the path can not be created (file instead of dir)
  # setting :auto: deals with it by creating a new autonamed lock file
  db close
  file delete -force proxytest
  close [open "proxytest" a]
................................................................................
      create table if not exists zz(y);
      PRAGMA lock_proxy_file;
    } db]
    string match "*proxytest.db:auto:" $lockpath2
  } {1}

  # make sure we can deal with ugly file paths correctly
  set lpp6 [pwd]/./././////./proxytest/../proxytest/sub/dir/lock
  db close
  file delete -force proxytest
  do_test pragma-16.10.4 {
    sqlite3 db proxytest.db
    execsql "PRAGMA lock_proxy_file='$lpp6'" 
    set lockpath2 [execsql {
      create table if not exists aa(bb);
      PRAGMA lock_proxy_file;
    } db]
    string match "*proxytest/sub/dir/lock" $lockpath2
  } {1}

  # ensure that if the path can not be created (perm), setting :auto: deals
  db close
  file delete -force proxytest
  do_test pragma-16.10.5 {
    sqlite3 db proxytest.db
................................................................................
      create table if not exists bb(bb);
    }
    db close
    file delete -force proxytest
    file mkdir proxytest
    file attributes proxytest -permission 0000
    sqlite3 db proxytest.db
    set lockpath2 [execsql {
      PRAGMA lock_proxy_file=":auto:";
      create table if not exists cc(bb);
      PRAGMA lock_proxy_file;
    } db]
    string match "*proxytest.db:auto:" $lockpath2
  } {1}

  # ensure that if the path can not be created, locking fails
  db close
  do_test pragma-16.10.6 {
    sqlite3 db proxytest.db
    execsql "PRAGMA lock_proxy_file='$lpp5'" 







>
|
>
>
>
>







 







|







 







|










|







 







|







 







|








|







|




|







 







|





|



|







 







|




|







1288
1289
1290
1291
1292
1293
1294
1295
1296
1297
1298
1299
1300
1301
1302
1303
1304
1305
1306
1307
....
1308
1309
1310
1311
1312
1313
1314
1315
1316
1317
1318
1319
1320
1321
1322
....
1333
1334
1335
1336
1337
1338
1339
1340
1341
1342
1343
1344
1345
1346
1347
1348
1349
1350
1351
1352
1353
1354
1355
1356
1357
1358
....
1400
1401
1402
1403
1404
1405
1406
1407
1408
1409
1410
1411
1412
1413
1414
....
1429
1430
1431
1432
1433
1434
1435
1436
1437
1438
1439
1440
1441
1442
1443
1444
1445
1446
1447
1448
1449
1450
1451
1452
1453
1454
1455
1456
1457
1458
1459
1460
1461
1462
1463
1464
1465
....
1470
1471
1472
1473
1474
1475
1476
1477
1478
1479
1480
1481
1482
1483
1484
1485
1486
1487
1488
1489
1490
1491
1492
1493
1494
....
1497
1498
1499
1500
1501
1502
1503
1504
1505
1506
1507
1508
1509
1510
1511
1512
1513
1514
1515
1516
}

# Reset the sqlite3_temp_directory variable for the next run of tests:
sqlite3 dbX :memory:
dbX eval {PRAGMA temp_store_directory = ""}
dbX close

set skip_lock_proxy_tests [path_is_dos "."]
ifcapable !lock_proxy_pragmas&&prefer_proxy_locking {
  set skip_lock_proxy_tests 1
}

if !$skip_lock_proxy_tests {
  set sqlite_hostid_num 1

  set using_proxy 0
  foreach {name value} [array get env SQLITE_FORCE_PROXY_LOCKING] {
    set using_proxy $value
  }

................................................................................
  # Test the lock_proxy_file pragmas.
  #
  db close
  set env(SQLITE_FORCE_PROXY_LOCKING) "0"

  sqlite3 db test.db
  # set lock proxy name and then query it via pragma interface
  set lpp [exec mktemp -t 'proxy1']
  do_test pragma-16.1 {
    execsql "PRAGMA lock_proxy_file='$lpp'"
    execsql "select * from sqlite_master"
    execsql "PRAGMA lock_proxy_file"
  } $lpp

  # 2 database connections can share a lock proxy file
................................................................................
      PRAGMA lock_proxy_file=":auto:";
      select * from sqlite_master;
    } db2
    execsql "PRAGMA lock_proxy_file" db2
  } $lpp

  db2 close
  set lpp2 [exec mktemp -t 'proxy2']

  # 2nd database connection cannot override the lock proxy file
  do_test pragma-16.3 {
    sqlite3 db2 test.db
    execsql "PRAGMA lock_proxy_file='$lpp2'" db2
    catchsql {
      select * from sqlite_master;
    } db2
  } {1 {database is locked}}

  set lpp3 [exec mktemp -t 'proxy3']

  # lock proxy file can be renamed if no other connections are active
  do_test pragma-16.4 {
    db2 close
    db close
    sqlite3 db2 test.db
    execsql "PRAGMA lock_proxy_file='$lpp3'" db2
................................................................................
    sqlite3 db test2.db
    catchsql {
      select * from sqlite_master;
    } 
  } {1 {database is locked}}

  db2 close
  set lpp4 [exec mktemp -t 'proxy4']

  # check that db is unlocked after first host connection closes 
  do_test pragma-16.8.1 {
    execsql "PRAGMA lock_proxy_file='$lpp4'" 
    execsql "select * from sqlite_master"
    execsql "PRAGMA lock_proxy_file"
  } $lpp4
................................................................................
      PRAGMA lock_proxy_file=":auto:";
      PRAGMA lock_proxy_file;
    } db]
    string match "*proxytest.db:auto:" $lockpath2
  } {1}

  # ensure creating directories for a lock proxy file works
  set lpp5 [exec mktemp -d -t "proxy5"]/sub/dir/lock
  db close
  file delete -force proxytest
  do_test pragma-16.10.1 {
    sqlite3 db proxytest.db
    execsql "PRAGMA lock_proxy_file='$lpp5'" 
    set lockpath2 [execsql {
      PRAGMA lock_proxy_file;
    } db]
    string match "*sub/dir/lock" $lockpath2
  } {1}

  # ensure that after deleting the path, setting ":auto:" works correctly
  db close
  file delete -force proxytest
  do_test pragma-16.10.2 {
    sqlite3 db proxytest.db
    set lockpath3 [execsql {
      PRAGMA lock_proxy_file=":auto:";
      create table if not exists pt(y);
      PRAGMA lock_proxy_file;
    } db]
    string match "*sub/dir/lock" $lockpath3
  } {1}

  # ensure that if the path can not be created (file instead of dir)
  # setting :auto: deals with it by creating a new autonamed lock file
  db close
  file delete -force proxytest
  close [open "proxytest" a]
................................................................................
      create table if not exists zz(y);
      PRAGMA lock_proxy_file;
    } db]
    string match "*proxytest.db:auto:" $lockpath2
  } {1}

  # make sure we can deal with ugly file paths correctly
  set lpp6 [exec mktemp -d -t "proxy6"]/./././////./proxytest/../proxytest/sub/dir/lock
  db close
  file delete -force proxytest
  do_test pragma-16.10.4 {
    sqlite3 db proxytest.db
    execsql "PRAGMA lock_proxy_file='$lpp6'" 
    set lockpath4 [execsql {
      create table if not exists aa(bb);
      PRAGMA lock_proxy_file;
    } db]
    string match "*proxytest/sub/dir/lock" $lockpath4
  } {1}

  # ensure that if the path can not be created (perm), setting :auto: deals
  db close
  file delete -force proxytest
  do_test pragma-16.10.5 {
    sqlite3 db proxytest.db
................................................................................
      create table if not exists bb(bb);
    }
    db close
    file delete -force proxytest
    file mkdir proxytest
    file attributes proxytest -permission 0000
    sqlite3 db proxytest.db
    set lockpath5 [execsql {
      PRAGMA lock_proxy_file=":auto:";
      create table if not exists cc(bb);
      PRAGMA lock_proxy_file;
    } db]
    string match "*proxytest.db:auto:" $lockpath5
  } {1}

  # ensure that if the path can not be created, locking fails
  db close
  do_test pragma-16.10.6 {
    sqlite3 db proxytest.db
    execsql "PRAGMA lock_proxy_file='$lpp5'" 

Changes to test/tester.tcl.

1179
1180
1181
1182
1183
1184
1185
1186
1187
1188
1189
1190
1191
1192
1193
    } 
  }
  return 0
}

proc wal_is_ok {} {
  if { [forced_proxy_locking] } {
    return 0
  }
  if { ![path_is_local "."] } {
    return 0
  }
  if { [path_is_dos "."] } {
    return 0
  }







|







1179
1180
1181
1182
1183
1184
1185
1186
1187
1188
1189
1190
1191
1192
1193
    } 
  }
  return 0
}

proc wal_is_ok {} {
  if { [forced_proxy_locking] } {
    return 1
  }
  if { ![path_is_local "."] } {
    return 0
  }
  if { [path_is_dos "."] } {
    return 0
  }

Changes to test/wal3.test.

613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
db close
db2 close
T delete

#-------------------------------------------------------------------------
# 
do_test wal3-8.1 {
  file delete -force test.db test.db-journal test.db wal
  sqlite3 db test.db
  sqlite3 db2 test.db
  execsql {
    PRAGMA journal_mode = WAL;
    CREATE TABLE b(c);
    INSERT INTO b VALUES('Tehran');
    INSERT INTO b VALUES('Qom');







|







613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
db close
db2 close
T delete

#-------------------------------------------------------------------------
# 
do_test wal3-8.1 {
  file delete -force test.db test.db-journal test.db wal .test.db-conch
  sqlite3 db test.db
  sqlite3 db2 test.db
  execsql {
    PRAGMA journal_mode = WAL;
    CREATE TABLE b(c);
    INSERT INTO b VALUES('Tehran');
    INSERT INTO b VALUES('Qom');

Changes to test/walmode.test.

31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
  do_test walmode-0.3 {
    execsql { PRAGMA main.journal_mode }
  } {delete}

  finish_test
  return
}
if { ![wal_is_ok] } {
  do_test walmode-0.1 {
    execsql { PRAGMA journal_mode = wal }
  } {delete}
  do_test walmode-0.2 {
    execsql { PRAGMA main.journal_mode = wal }
  } {delete}
  do_test walmode-0.3 {







|







31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
  do_test walmode-0.3 {
    execsql { PRAGMA main.journal_mode }
  } {delete}

  finish_test
  return
}
if { ![wal_is_ok] && ![path_is_dos "."]} {
  do_test walmode-0.1 {
    execsql { PRAGMA journal_mode = wal }
  } {delete}
  do_test walmode-0.2 {
    execsql { PRAGMA main.journal_mode = wal }
  } {delete}
  do_test walmode-0.3 {

Changes to test/walshared.test.

11
12
13
14
15
16
17







18
19
20
21
22
23
24
# This file implements regression tests for SQLite library.  The
# focus of this file is testing the operation of the library in
# "PRAGMA journal_mode=WAL" mode with shared-cache turned on.
#

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







db close
set ::enable_shared_cache [sqlite3_enable_shared_cache 1]

sqlite3 db  test.db
sqlite3 db2 test.db

do_test walshared-1.0 {







>
>
>
>
>
>
>







11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
# This file implements regression tests for SQLite library.  The
# focus of this file is testing the operation of the library in
# "PRAGMA journal_mode=WAL" mode with shared-cache turned on.
#

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

ifcapable !wal {finish_test ; return }
if { ![wal_is_ok] } {
  finish_test 
  return 
}

db close
set ::enable_shared_cache [sqlite3_enable_shared_cache 1]

sqlite3 db  test.db
sqlite3 db2 test.db

do_test walshared-1.0 {