/ Check-in [0c2d169c]
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:More work on windows locking. Fix some problems with unix locking. There is still an assertion failure on windows locking in attach2.test. (CVS 1539)
Downloads: Tarball | ZIP archive | SQL archive
Timelines: family | ancestors | descendants | both | trunk
Files: files | file ages | folders
SHA1: 0c2d169cf3c0f36972015c952a2b46cb9a333881
User & Date: drh 2004-06-07 16:27:46
Context
2004-06-08
00:02
Remove the third argument from the sqlite3_open() API. (CVS 1540) check-in: 62e31f39 user: danielk1977 tags: trunk
2004-06-07
16:27
More work on windows locking. Fix some problems with unix locking. There is still an assertion failure on windows locking in attach2.test. (CVS 1539) check-in: 0c2d169c user: drh tags: trunk
10:00
Tables created with the CREATE TABLE <tbl> AS SELECT ... syntax now inherit column declaration types from the SELECT statement. (CVS 1538) check-in: 31c1668d user: danielk1977 tags: trunk
Changes
Hide Diffs Unified Diffs Ignore Whitespace Patch

Changes to src/btree.c.

5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
....
3709
3710
3711
3712
3713
3714
3715
3716
3717
3718
3719
3720
3721
3722
3723
3724
3725
3726
3727
3728
** a legal notice, here is a blessing:
**
**    May you do good and not evil.
**    May you find forgiveness for yourself and forgive others.
**    May you share freely, never taking more than you give.
**
*************************************************************************
** $Id: btree.c,v 1.160 2004/06/07 01:52:14 drh Exp $
**
** This file implements a external (disk-based) database using BTrees.
** For a detailed discussion of BTrees, refer to
**
**     Donald E. Knuth, THE ART OF COMPUTER PROGRAMMING, Volume 3:
**     "Sorting And Searching", pages 473-480. Addison-Wesley
**     Publishing Company, Reading, Massachusetts.
................................................................................
** is currently pointing to.
*/
int sqlite3BtreeFlags(BtCursor *pCur){
  MemPage *pPage = pCur->pPage;
  return pPage ? pPage->aData[pPage->hdrOffset] : 0;
}

/******************************************************************************
** The complete implementation of the BTree subsystem is above this line.
** All the code the follows is for testing and troubleshooting the BTree
** subsystem.  None of the code that follows is used during normal operation.
******************************************************************************/

/*
** Print a disassembly of the given page on standard output.  This routine
** is used for debugging and testing only.
*/
#ifdef SQLITE_TEST
int sqlite3BtreePageDump(Btree *pBt, int pgno, int recursive){
  int rc;







|







 







<
<
<
<
<
<







5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
....
3709
3710
3711
3712
3713
3714
3715






3716
3717
3718
3719
3720
3721
3722
** a legal notice, here is a blessing:
**
**    May you do good and not evil.
**    May you find forgiveness for yourself and forgive others.
**    May you share freely, never taking more than you give.
**
*************************************************************************
** $Id: btree.c,v 1.161 2004/06/07 16:27:46 drh Exp $
**
** This file implements a external (disk-based) database using BTrees.
** For a detailed discussion of BTrees, refer to
**
**     Donald E. Knuth, THE ART OF COMPUTER PROGRAMMING, Volume 3:
**     "Sorting And Searching", pages 473-480. Addison-Wesley
**     Publishing Company, Reading, Massachusetts.
................................................................................
** is currently pointing to.
*/
int sqlite3BtreeFlags(BtCursor *pCur){
  MemPage *pPage = pCur->pPage;
  return pPage ? pPage->aData[pPage->hdrOffset] : 0;
}







/*
** Print a disassembly of the given page on standard output.  This routine
** is used for debugging and testing only.
*/
#ifdef SQLITE_TEST
int sqlite3BtreePageDump(Btree *pBt, int pgno, int recursive){
  int rc;

Changes to src/os.h.

85
86
87
88
89
90
91



















































92
93
94
95
96
97
98
** sqlite3OsLock().
*/
#define NO_LOCK         0
#define SHARED_LOCK     1
#define RESERVED_LOCK   2
#define PENDING_LOCK    3
#define EXCLUSIVE_LOCK  4




















































int sqlite3OsDelete(const char*);
int sqlite3OsFileExists(const char*);
int sqliteOsFileRename(const char*, const char*);
int sqlite3OsOpenReadWrite(const char*, OsFile*, int*);
int sqlite3OsOpenExclusive(const char*, OsFile*, int);
int sqlite3OsOpenReadOnly(const char*, OsFile*);







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







85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
** sqlite3OsLock().
*/
#define NO_LOCK         0
#define SHARED_LOCK     1
#define RESERVED_LOCK   2
#define PENDING_LOCK    3
#define EXCLUSIVE_LOCK  4

/*
** Windows file locking notes:
**
** We cannot use LockFileEx() or UnlockFileEx() on Win95/98/ME because
** those functions are not available.  So we use only LockFile() and
** UnlockFile().
**
** LockFile() prevents not just writing but also reading by other processes.
** (This is a design error on the part of Windows, but there is nothing
** we can do about that.)  So the region used for locking is at the
** end of the file where it is unlikely to ever interfere with an
** actual read attempt.
**
** A SHARED_LOCK is obtained by locking a single randomly-chosen 
** byte out of a specific range of bytes. The lock byte is obtained at 
** random so two separate readers can probably access the file at the 
** same time, unless they are unlucky and choose the same lock byte.
** An EXCLUSIVE_LOCK is obtained by locking all bytes in the range.
** There can only be one writer.  A RESERVED_LOCK is obtained by locking
** a single byte of the file that is designated as the reserved lock byte.
** A PENDING_LOCK is obtained by locking a designated byte different from
** the RESERVED_LOCK byte.
**
** On WinNT/2K/XP systems, LockFileEx() and UnlockFileEx() are available,
** which means we can use reader/writer locks.  When reader/writer locks
** are used, the lock is placed on the same range of bytes that is used
** for probabilistic locking in Win95/98/ME.  Hence, the locking scheme
** will support two or more Win95 readers or two or more WinNT readers.
** But a single Win95 reader will lock out all WinNT readers and a single
** WinNT reader will lock out all other Win95 readers.
**
** The following #defines specify the range of bytes used for locking.
** SHARED_SIZE is the number of bytes available in the pool from which
** a random byte is selected for a shared lock.  The pool of bytes for
** shared locks begins at SHARED_FIRST. 
**
** These #defines are available in os.h so that Unix can use the same
** byte ranges for locking.  This leaves open the possiblity of having
** clients on win95, winNT, and unix all talking to the same shared file
** and all locking correctly.
**
** Locking in windows is manditory.  For this reason, we cannot store
** actual data in the bytes used for locking.  The pager never allocates
** the pages involved in locking therefore.
*/
#define SHARED_SIZE       10238
#define SHARED_FIRST      (0x3fffffff - (SHARED_SIZE - 1))
#define RESERVED_BYTE     (SHARED_FIRST - 1)
#define PENDING_BYTE      (RESERVED_BYTE - 1)


int sqlite3OsDelete(const char*);
int sqlite3OsFileExists(const char*);
int sqliteOsFileRename(const char*, const char*);
int sqlite3OsOpenReadWrite(const char*, OsFile*, int*);
int sqlite3OsOpenExclusive(const char*, OsFile*, int);
int sqlite3OsOpenReadOnly(const char*, OsFile*);

Changes to src/os_common.h.

19
20
21
22
23
24
25

26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
*/


/*
** Macros for performance tracing.  Normally turned off.  Only works
** on i486 hardware.
*/

#if 0
static int last_page = 0;
__inline__ unsigned long long int hwtime(void){
  unsigned long long int x;
  __asm__("rdtsc\n\t"
          "mov %%edx, %%ecx\n\t"
          :"=A" (x));
  return x;
}
static unsigned long long int g_start;
static unsigned int elapse;
#define TIMER_START       g_start=hwtime()
#define TIMER_END         elapse=hwtime()-g_start
#define SEEK(X)           last_page=(X)
#define TRACE1(X)         sqlite3DebugPrintf(X)
#define TRACE2(X,Y)       sqlite3DebugPrintf(X,Y)
#define TRACE3(X,Y,Z)     sqlite3DebugPrintf(X,Y,Z)
#define TRACE4(X,Y,Z,A)   sqlite3DebugPrintf(X,Y,Z,A)
#define TRACE5(X,Y,Z,A,B) sqlite3DebugPrintf(X,Y,Z,A,B)
#else
#define TIMER_START
#define TIMER_END
#define SEEK(X)
#define TRACE1(X)
#define TRACE2(X,Y)
#define TRACE3(X,Y,Z)







>
|













|
|
|
|
|







19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
*/


/*
** Macros for performance tracing.  Normally turned off.  Only works
** on i486 hardware.
*/
int sqlite3_os_trace = 0;
#if 1
static int last_page = 0;
__inline__ unsigned long long int hwtime(void){
  unsigned long long int x;
  __asm__("rdtsc\n\t"
          "mov %%edx, %%ecx\n\t"
          :"=A" (x));
  return x;
}
static unsigned long long int g_start;
static unsigned int elapse;
#define TIMER_START       g_start=hwtime()
#define TIMER_END         elapse=hwtime()-g_start
#define SEEK(X)           last_page=(X)
#define TRACE1(X)         if( sqlite3_os_trace ) sqlite3DebugPrintf(X)
#define TRACE2(X,Y)       if( sqlite3_os_trace ) sqlite3DebugPrintf(X,Y)
#define TRACE3(X,Y,Z)     if( sqlite3_os_trace ) sqlite3DebugPrintf(X,Y,Z)
#define TRACE4(X,Y,Z,A)   if( sqlite3_os_trace ) sqlite3DebugPrintf(X,Y,Z,A)
#define TRACE5(X,Y,Z,A,B) if( sqlite3_os_trace ) sqlite3DebugPrintf(X,Y,Z,A,B)
#else
#define TIMER_START
#define TIMER_END
#define SEEK(X)
#define TRACE1(X)
#define TRACE2(X,Y)
#define TRACE3(X,Y,Z)

Changes to src/os_unix.c.

170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
...
640
641
642
643
644
645
646
647

648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674

675
676
677
678
679
680
681
682
683
684
685
686















687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708













709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
...
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759

760


761

762
763
764
765
766
767
768
...
773
774
775
776
777
778
779
780
781
782
783
784
785
786

787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804

805
806
807
808
809
810
811
**
** A single inode can have multiple file descriptors, so each OsFile
** structure contains a pointer to an instance of this object and this
** object keeps a count of the number of OsFiles pointing to it.
*/
struct lockInfo {
  struct lockKey key;  /* The lookup key */
  int cnt;             /* Number of locks held */
  int locktype;        /* One of SHARED_LOCK, RESERVED_LOCK etc. */
  int nRef;            /* Number of pointers to this structure */
};

/*
** An instance of the following structure serves as the key used
** to locate a particular openCnt structure given its inode.  This
................................................................................
  *pSize = buf.st_size;
  return SQLITE_OK;
}

/*
** This routine checks if there is a RESERVED lock held on the specified
** file by this or any other process. If such a lock is held, return
** non-zero, otherwise zero.

*/
int sqlite3OsCheckWriteLock(OsFile *id){
  int r = 0;

  sqlite3OsEnterMutex();

  /* Check if a thread in this process holds such a lock */
  if( id->pLock->locktype>SHARED_LOCK ){
    r = 1;
  }

  /* Otherwise see if some other process holds it. Just check the whole
  ** file for write-locks, rather than any specific bytes.
  */
  if( !r ){
    struct flock lock;
    lock.l_whence = SEEK_SET;
    lock.l_start = 0;
    lock.l_len = 0;
    lock.l_type = F_RDLCK;
    fcntl(id->fd, F_GETLK, &lock);
    if( lock.l_type!=F_UNLCK ){
      r = 1;
    }
  }
  
  sqlite3OsLeaveMutex();


  return r;
}

/*
** Lock the file with the lock specified by parameter locktype - one
** of the following:
**
** SHARED_LOCK
** RESERVED_LOCK
** PENDING_LOCK
** EXCLUSIVE_LOCK















*/
int sqlite3OsLock(OsFile *id, int locktype){
  int rc = SQLITE_OK;
  struct lockInfo *pLock = id->pLock;
  struct flock lock;
  int s;

  /* It is an error to request any kind of lock before a shared lock */
  if( locktype>SHARED_LOCK && id->locktype==0 ){
    rc = sqlite3OsLock(id, SHARED_LOCK);
    if( rc!=SQLITE_OK ) return rc;
  }
  assert( locktype==SHARED_LOCK || id->locktype!=0 );

  /* If there is already a lock of this type or more restrictive on the
  ** OsFile, do nothing. Don't use the end_lock: exit path, as
  ** sqlite3OsEnterMutex() hasn't been called yet.
  */
  if( id->locktype>=locktype ){
    return SQLITE_OK;
  }














  sqlite3OsEnterMutex();

  /* If some thread using this PID has a lock via a different OsFile*
  ** handle that precludes the requested lock, return BUSY.
  */
  if( (id->locktype!=pLock->locktype && 
      (pLock->locktype>RESERVED_LOCK || locktype!=SHARED_LOCK)) ||
      (locktype>RESERVED_LOCK && pLock->cnt>1)
  ){
    rc = SQLITE_BUSY;
    goto end_lock;
  }

  /* If a SHARED lock is requested, and some thread using this PID already
  ** has a SHARED or RESERVED lock, then increment reference counts and
................................................................................
  /* If control gets to this point, then actually go ahead and make
  ** operating system calls for the specified lock.
  */
  if( locktype==SHARED_LOCK ){
    assert( pLock->cnt==0 );
    assert( pLock->locktype==0 );
  
    /* Grab a read-lock on byte 2. This ensures that no other process
    ** has a PENDING lock.
    */
    lock.l_type = F_RDLCK;
    lock.l_start = 2;
    s = fcntl(id->fd, F_SETLK, &lock);
    if( s ){
      rc = (errno==EINVAL) ? SQLITE_NOLFS : SQLITE_BUSY;
      goto end_lock;
    }

    /* Now get a read-lock on byte 0 and renege on the byte 2 lock. */
    lock.l_start = 0;

    s = fcntl(id->fd, F_SETLK, &lock);


    lock.l_start = 2;

    lock.l_type = F_UNLCK;
    fcntl(id->fd, F_SETLK, &lock);
    if( s ){
      rc = (errno==EINVAL) ? SQLITE_NOLFS : SQLITE_BUSY;
    }else{
      id->locktype = SHARED_LOCK;
      id->pOpen->nLock++;
................................................................................
    ** assumed that there is a SHARED or greater lock on the file
    ** already.
    */
    assert( 0!=id->locktype );
    lock.l_type = F_WRLCK;
    switch( locktype ){
      case RESERVED_LOCK:
        lock.l_start = 1;
        break;
      case PENDING_LOCK:
        lock.l_start = 2;
        break;
      case EXCLUSIVE_LOCK:
        lock.l_start = 0;

        break;
      default:
        assert(0);
    }
    s = fcntl(id->fd, F_SETLK, &lock);
    if( s ){
      rc = (errno==EINVAL) ? SQLITE_NOLFS : SQLITE_BUSY;
    }
  }
  
  if( rc==SQLITE_OK ){
    id->locktype = locktype;
    pLock->locktype = locktype;
    assert( pLock->locktype==RESERVED_LOCK || pLock->cnt==1 );
  }

end_lock:
  sqlite3OsLeaveMutex();

  return rc;
}

/*
** Unlock the given file descriptor.  If the file descriptor was
** not previously locked, then this routine is a no-op.  If this
** library was compiled with large file support (LFS) but LFS is not







|







 







|
>




|






|
<




|
|
|







>








|
|
|
|
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>







|
|
<
<
<
<









>
>
>
>
>
>
>
>
>
>
>
>
>
|





|
|







 







|
|


|






|
|
>

>
>
|
>







 







|


|


|
>













<




>







170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
...
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660

661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711




712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
...
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
...
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829

830
831
832
833
834
835
836
837
838
839
840
841
**
** A single inode can have multiple file descriptors, so each OsFile
** structure contains a pointer to an instance of this object and this
** object keeps a count of the number of OsFiles pointing to it.
*/
struct lockInfo {
  struct lockKey key;  /* The lookup key */
  int cnt;             /* Number of SHARED locks held */
  int locktype;        /* One of SHARED_LOCK, RESERVED_LOCK etc. */
  int nRef;            /* Number of pointers to this structure */
};

/*
** An instance of the following structure serves as the key used
** to locate a particular openCnt structure given its inode.  This
................................................................................
  *pSize = buf.st_size;
  return SQLITE_OK;
}

/*
** This routine checks if there is a RESERVED lock held on the specified
** file by this or any other process. If such a lock is held, return
** non-zero.  If the file is unlocked or holds only SHARED locks, then
** return zero.
*/
int sqlite3OsCheckWriteLock(OsFile *id){
  int r = 0;

  sqlite3OsEnterMutex(); /* Needed because id->pLock is shared across threads */

  /* Check if a thread in this process holds such a lock */
  if( id->pLock->locktype>SHARED_LOCK ){
    r = 1;
  }

  /* Otherwise see if some other process holds it.

  */
  if( !r ){
    struct flock lock;
    lock.l_whence = SEEK_SET;
    lock.l_start = RESERVED_BYTE;
    lock.l_len = 1;
    lock.l_type = F_WRLCK;
    fcntl(id->fd, F_GETLK, &lock);
    if( lock.l_type!=F_UNLCK ){
      r = 1;
    }
  }
  
  sqlite3OsLeaveMutex();
  TRACE3("TEST WR-LOCK %d %d\n", id->fd, r);

  return r;
}

/*
** Lock the file with the lock specified by parameter locktype - one
** of the following:
**
**     (1) SHARED_LOCK
**     (2) RESERVED_LOCK
**     (3) PENDING_LOCK
**     (4) EXCLUSIVE_LOCK
**
** Locks are are hierarchical.  Getting a lock N implies getting all locks
** N-1, N-2, N-3, ....  So, for example, getting a PENDING lock
** implies a SHARED and a RESERVED lock.  This routine adds locks one
** at a time until the desired lock is acheived.  A locking failure might
** occur at any point.  When a failure occurs intermediate locks are
** retained.  For example, if a SHARED lock is held and this routine
** is called with EXCLUSIVE, it might obtain a RESERVED and PENDING lock
** but fail to get the EXCLUSIVE lock.  In that case, the file would be
** left in the PENDING lock state - it does not revert to SHARED.
**
** This routine will only increase a lock.  The sqlite3OsUnlock() routine
** erases all locks at once and returns us immediately to locking level 0.
** It is not possible to lower the locking level one step at a time.  You
** must go straight to locking level 0.
*/
int sqlite3OsLock(OsFile *id, int locktype){
  int rc = SQLITE_OK;
  struct lockInfo *pLock = id->pLock;
  struct flock lock;
  int s;

  TRACE5("LOCK %d %d was %d(%d)\n",
          id->fd, locktype, id->locktype, pLock->locktype);





  /* If there is already a lock of this type or more restrictive on the
  ** OsFile, do nothing. Don't use the end_lock: exit path, as
  ** sqlite3OsEnterMutex() hasn't been called yet.
  */
  if( id->locktype>=locktype ){
    return SQLITE_OK;
  }

  /* Make sure locking is sequential.  In other words, make sure we have
  ** SHARED before trying for RESERVED, and that we have RESERVED before
  ** trying for PENDING, and that we have PENDING before trying for
  ** EXCLUSIVE.
  */
  while( locktype>id->locktype+1 ){
    rc = sqlite3OsLock(id, id->locktype+1);
    if( rc!=SQLITE_OK ){
      return rc;
    }
  }
  assert( locktype==id->locktype+1 );

  sqlite3OsEnterMutex();  /* Needed because pLock is shared across threads */

  /* If some thread using this PID has a lock via a different OsFile*
  ** handle that precludes the requested lock, return BUSY.
  */
  if( (id->locktype!=pLock->locktype && 
          (pLock->locktype>=PENDING_LOCK || locktype>SHARED_LOCK))
   || (locktype==EXCLUSIVE_LOCK && pLock->cnt>1)
  ){
    rc = SQLITE_BUSY;
    goto end_lock;
  }

  /* If a SHARED lock is requested, and some thread using this PID already
  ** has a SHARED or RESERVED lock, then increment reference counts and
................................................................................
  /* If control gets to this point, then actually go ahead and make
  ** operating system calls for the specified lock.
  */
  if( locktype==SHARED_LOCK ){
    assert( pLock->cnt==0 );
    assert( pLock->locktype==0 );
  
    /* Temporarily grab a PENDING lock.  This prevents new SHARED locks from
    ** being formed if a PENDING lock is already held.
    */
    lock.l_type = F_RDLCK;
    lock.l_start = PENDING_BYTE;
    s = fcntl(id->fd, F_SETLK, &lock);
    if( s ){
      rc = (errno==EINVAL) ? SQLITE_NOLFS : SQLITE_BUSY;
      goto end_lock;
    }

    /* Now get the read-lock */
    lock.l_start = SHARED_FIRST;
    lock.l_len = SHARED_SIZE;
    s = fcntl(id->fd, F_SETLK, &lock);

    /* Drop the temporary PENDING lock */
    lock.l_start = PENDING_BYTE;
    lock.l_len = 1L;
    lock.l_type = F_UNLCK;
    fcntl(id->fd, F_SETLK, &lock);
    if( s ){
      rc = (errno==EINVAL) ? SQLITE_NOLFS : SQLITE_BUSY;
    }else{
      id->locktype = SHARED_LOCK;
      id->pOpen->nLock++;
................................................................................
    ** assumed that there is a SHARED or greater lock on the file
    ** already.
    */
    assert( 0!=id->locktype );
    lock.l_type = F_WRLCK;
    switch( locktype ){
      case RESERVED_LOCK:
        lock.l_start = RESERVED_BYTE;
        break;
      case PENDING_LOCK:
        lock.l_start = PENDING_BYTE;
        break;
      case EXCLUSIVE_LOCK:
        lock.l_start = SHARED_FIRST;
        lock.l_len = SHARED_SIZE;
        break;
      default:
        assert(0);
    }
    s = fcntl(id->fd, F_SETLK, &lock);
    if( s ){
      rc = (errno==EINVAL) ? SQLITE_NOLFS : SQLITE_BUSY;
    }
  }
  
  if( rc==SQLITE_OK ){
    id->locktype = locktype;
    pLock->locktype = locktype;

  }

end_lock:
  sqlite3OsLeaveMutex();
  TRACE4("LOCK %d %d %s\n", id->fd, locktype, rc==SQLITE_OK ? "ok" : "failed");
  return rc;
}

/*
** Unlock the given file descriptor.  If the file descriptor was
** not previously locked, then this routine is a no-op.  If this
** library was compiled with large file support (LFS) but LFS is not

Changes to src/os_win.c.

309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
...
527
528
529
530
531
532
533

534
535
536
537
538


539
540
541
542
543
544
545
546
547
  DWORD upperBits, lowerBits;
  SimulateIOError(SQLITE_IOERR);
  lowerBits = GetFileSize(id->h, &upperBits);
  *pSize = (((off_t)upperBits)<<32) + lowerBits;
  return SQLITE_OK;
}

/*
** Windows file locking notes:
**
** We cannot use LockFileEx() or UnlockFileEx() on Win95/98/ME because
** those functions are not available.  So we use only LockFile() and
** UnlockFile().
**
** LockFile() prevents not just writing but also reading by other processes.
** (This is a design error on the part of Windows, but there is nothing
** we can do about that.)  So the region used for locking is at the
** end of the file where it is unlikely to ever interfere with an
** actual read attempt.
**
** A SHARED_LOCK is obtained by locking a single randomly-chosen 
** byte out of a specific range of bytes. The lock byte is obtained at 
** random so two separate readers can probably access the file at the 
** same time, unless they are unlucky and choose the same lock byte.
** An EXCLUSIVE_LOCK is obtained by locking all bytes in the range.
** There can only be one writer.  A RESERVED_LOCK is obtained by locking
** a single byte of the file that is designated as the reserved lock byte.
** A PENDING_LOCK is obtained by locking a designated byte different from
** the RESERVED_LOCK byte.
**
** On WinNT/2K/XP systems, LockFileEx() and UnlockFileEx() are available,
** which means we can use reader/writer locks.  When reader/writer locks
** are used, the lock is placed on the same range of bytes that is used
** for probabilistic locking in Win95/98/ME.  Hence, the locking scheme
** will support two or more Win95 readers or two or more WinNT readers.
** But a single Win95 reader will lock out all WinNT readers and a single
** WinNT reader will lock out all other Win95 readers.
**
** The following #defines specify the range of bytes used for locking.
** SHARED_SIZE is the number of bytes available in the pool from which
** a random byte is selected for a shared lock.  The pool of bytes for
** shared locks begins at SHARED_FIRST.  
*/
#define SHARED_SIZE       10238
#define SHARED_FIRST      (0x3fffffff - (SHARED_SIZE - 1))
#define RESERVED_BYTE     (SHARED_FIRST - 1)
#define PENDING_BYTE      (RESERVED_BYTE - 1)

/*
** Return true (non-zero) if we are running under WinNT, Win2K or WinXP.
** Return false (zero) for Win95, Win98, or WinME.
**
** Here is an interesting observation:  Win95, Win98, and WinME lack
** the LockFileEx() API.  But we can still statically link against that
** API as long as we don't call it win running Win95/98/ME.  A call to
................................................................................
** file by this or any other process. If such a lock is held, return
** non-zero, otherwise zero.
*/
int sqlite3OsCheckWriteLock(OsFile *id){
  int rc;
  if( id->locktype>=RESERVED_LOCK ){
    rc = 1;

  }else{
    rc = LockFile(id->h, RESERVED_BYTE, 0, 1, 0);
    if( rc ){
      UnlockFile(id->h, RESERVED_BYTE, 0, 1, 0);
    }


  }
  return 0;
}

/*
** Unlock the given file descriptor.  If the file descriptor was
** not previously locked, then this routine is a no-op.  If this
** library was compiled with large file support (LFS) but LFS is not
** available on the host, then an SQLITE_NOLFS is returned.







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







 







>





>
>

|







309
310
311
312
313
314
315









































316
317
318
319
320
321
322
...
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
  DWORD upperBits, lowerBits;
  SimulateIOError(SQLITE_IOERR);
  lowerBits = GetFileSize(id->h, &upperBits);
  *pSize = (((off_t)upperBits)<<32) + lowerBits;
  return SQLITE_OK;
}










































/*
** Return true (non-zero) if we are running under WinNT, Win2K or WinXP.
** Return false (zero) for Win95, Win98, or WinME.
**
** Here is an interesting observation:  Win95, Win98, and WinME lack
** the LockFileEx() API.  But we can still statically link against that
** API as long as we don't call it win running Win95/98/ME.  A call to
................................................................................
** file by this or any other process. If such a lock is held, return
** non-zero, otherwise zero.
*/
int sqlite3OsCheckWriteLock(OsFile *id){
  int rc;
  if( id->locktype>=RESERVED_LOCK ){
    rc = 1;
    TRACE3("TEST WR-LOCK %d %d (local)\n", id->h, rc);
  }else{
    rc = LockFile(id->h, RESERVED_BYTE, 0, 1, 0);
    if( rc ){
      UnlockFile(id->h, RESERVED_BYTE, 0, 1, 0);
    }
    rc = !rc;
    TRACE3("TEST WR-LOCK %d %d (remote)\n", id->h, rc);
  }
  return rc;
}

/*
** Unlock the given file descriptor.  If the file descriptor was
** not previously locked, then this routine is a no-op.  If this
** library was compiled with large file support (LFS) but LFS is not
** available on the host, then an SQLITE_NOLFS is returned.

Changes to src/pager.c.

14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
....
1550
1551
1552
1553
1554
1555
1556
1557
1558



1559
1560
1561
1562


1563
1564
1565
1566
1567
1568
1569
1570
1571
1572
1573
1574
1575
1576
1577
** The pager is used to access a database disk file.  It implements
** atomic commit and rollback through the use of a journal file that
** is separate from the database file.  The pager also implements file
** locking to prevent two processes from writing the same database
** file simultaneously, or one process from reading the database while
** another is writing.
**
** @(#) $Id: pager.c,v 1.112 2004/06/04 10:38:31 danielk1977 Exp $
*/
#include "os.h"         /* Must be first to enable large file support */
#include "sqliteInt.h"
#include "pager.h"
#include <assert.h>
#include <string.h>

................................................................................
  if( pList==0 ) return SQLITE_OK;
  pPager = pList->pPager;

  /* At this point there may be either a RESERVED or EXCLUSIVE lock on the
  ** database file. If there is already an EXCLUSIVE lock, the following
  ** calls to sqlite3OsLock() are no-ops.
  **
  ** The upgrade from a RESERVED to PENDING lock cannot return SQLITE_BUSY,
  ** unless someone is not following the locking protocol. 



  **
  ** The upgrade from PENDING to EXCLUSIVE can return SQLITE_BUSY. It's
  ** not totally clear that the busy-callback should be invoked here
  ** though. (?)


  */
  rc = sqlite3OsLock(&pPager->fd, PENDING_LOCK);
  if( rc==SQLITE_BUSY ){
    return SQLITE_PROTOCOL;
  }
  if( rc!=SQLITE_OK ){
    return rc;
  }
  do {
    rc = sqlite3OsLock(&pPager->fd, EXCLUSIVE_LOCK);
  }while( rc==SQLITE_BUSY && 
      pPager->pBusyHandler && 
      pPager->pBusyHandler->xFunc && 
      pPager->pBusyHandler->xFunc(pPager->pBusyHandler->pArg, "", busy++)
  );







|







 







|
|
>
>
>

|
<
<
>
>

<
<
<
<
<
<
<







14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
....
1550
1551
1552
1553
1554
1555
1556
1557
1558
1559
1560
1561
1562
1563


1564
1565
1566







1567
1568
1569
1570
1571
1572
1573
** The pager is used to access a database disk file.  It implements
** atomic commit and rollback through the use of a journal file that
** is separate from the database file.  The pager also implements file
** locking to prevent two processes from writing the same database
** file simultaneously, or one process from reading the database while
** another is writing.
**
** @(#) $Id: pager.c,v 1.113 2004/06/07 16:27:46 drh Exp $
*/
#include "os.h"         /* Must be first to enable large file support */
#include "sqliteInt.h"
#include "pager.h"
#include <assert.h>
#include <string.h>

................................................................................
  if( pList==0 ) return SQLITE_OK;
  pPager = pList->pPager;

  /* At this point there may be either a RESERVED or EXCLUSIVE lock on the
  ** database file. If there is already an EXCLUSIVE lock, the following
  ** calls to sqlite3OsLock() are no-ops.
  **
  ** The upgrade from a RESERVED to PENDING might return SQLITE_BUSY on
  ** windows because the windows locking mechanism acquires a transient
  ** PENDING lock during its attempts to get a SHARED lock.  So if another
  ** process were trying to get a SHARED lock at the same time this process
  ** is upgrading from RESERVED to PENDING, the two could collide.
  **
  ** The upgrade from PENDING to EXCLUSIVE can return SQLITE_BUSY if there


  ** are still active readers that were created before the PENDING lock
  ** was acquired.
  */







  do {
    rc = sqlite3OsLock(&pPager->fd, EXCLUSIVE_LOCK);
  }while( rc==SQLITE_BUSY && 
      pPager->pBusyHandler && 
      pPager->pBusyHandler->xFunc && 
      pPager->pBusyHandler->xFunc(pPager->pBusyHandler->pArg, "", busy++)
  );

Changes to src/printf.c.

806
807
808
809
810
811
812
813

814
815
816
817
818
819
820
*/
void sqlite3DebugPrintf(const char *zFormat, ...){
  va_list ap;
  char zBuf[500];
  va_start(ap, zFormat);
  base_vprintf(0, 0, zBuf, sizeof(zBuf), zFormat, ap);
  va_end(ap);
  fprintf(stderr,"%s", zBuf);

}
#endif

/*
** The following four routines implement the varargs versions of the
** sqlite3_exec() and sqlite3_get_table() interfaces.  See the sqlite.h
** header files for a more detailed description of how these interfaces







|
>







806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
*/
void sqlite3DebugPrintf(const char *zFormat, ...){
  va_list ap;
  char zBuf[500];
  va_start(ap, zFormat);
  base_vprintf(0, 0, zBuf, sizeof(zBuf), zFormat, ap);
  va_end(ap);
  fprintf(stdout,"%s", zBuf);
  fflush(stdout);
}
#endif

/*
** The following four routines implement the varargs versions of the
** sqlite3_exec() and sqlite3_get_table() interfaces.  See the sqlite.h
** header files for a more detailed description of how these interfaces

Changes to src/test1.c.

9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
....
1909
1910
1911
1912
1913
1914
1915

1916
1917
1918
1919
1920
1921
1922
....
1925
1926
1927
1928
1929
1930
1931


1932
1933
1934
1935
**    May you share freely, never taking more than you give.
**
*************************************************************************
** Code for testing the printf() interface to SQLite.  This code
** is not included in the SQLite library.  It is used for automated
** testing of the SQLite library.
**
** $Id: test1.c,v 1.69 2004/06/01 14:09:29 danielk1977 Exp $
*/
#include "sqliteInt.h"
#include "tcl.h"
#include "os.h"
#include <stdlib.h>
#include <string.h>

................................................................................
     { "sqlite3OsOpenReadWrite",test_sqlite3OsOpenReadWrite, 0 },
     { "sqlite3OsClose",        test_sqlite3OsClose, 0 },
     { "sqlite3OsLock",         test_sqlite3OsLock, 0 },
     { "sqlite3OsUnlock",       test_sqlite3OsUnlock, 0 },

  };
  int i;


  for(i=0; i<sizeof(aCmd)/sizeof(aCmd[0]); i++){
    Tcl_CreateCommand(interp, aCmd[i].zName, aCmd[i].xProc, 0, 0);
  }
  for(i=0; i<sizeof(aObjCmd)/sizeof(aObjCmd[0]); i++){
    Tcl_CreateObjCommand(interp, aObjCmd[i].zName, 
        aObjCmd[i].xProc, aObjCmd[i].clientData, 0);
................................................................................
      (char*)&sqlite3_search_count, TCL_LINK_INT);
  Tcl_LinkVar(interp, "sqlite_interrupt_count", 
      (char*)&sqlite3_interrupt_count, TCL_LINK_INT);
  Tcl_LinkVar(interp, "sqlite_open_file_count", 
      (char*)&sqlite3_open_file_count, TCL_LINK_INT);
  Tcl_LinkVar(interp, "sqlite_current_time", 
      (char*)&sqlite3_current_time, TCL_LINK_INT);


  Tcl_LinkVar(interp, "sqlite_static_bind_value",
      (char*)&sqlite_static_bind_value, TCL_LINK_STRING);
  return TCL_OK;
}







|







 







>







 







>
>




9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
....
1909
1910
1911
1912
1913
1914
1915
1916
1917
1918
1919
1920
1921
1922
1923
....
1926
1927
1928
1929
1930
1931
1932
1933
1934
1935
1936
1937
1938
**    May you share freely, never taking more than you give.
**
*************************************************************************
** Code for testing the printf() interface to SQLite.  This code
** is not included in the SQLite library.  It is used for automated
** testing of the SQLite library.
**
** $Id: test1.c,v 1.70 2004/06/07 16:27:46 drh Exp $
*/
#include "sqliteInt.h"
#include "tcl.h"
#include "os.h"
#include <stdlib.h>
#include <string.h>

................................................................................
     { "sqlite3OsOpenReadWrite",test_sqlite3OsOpenReadWrite, 0 },
     { "sqlite3OsClose",        test_sqlite3OsClose, 0 },
     { "sqlite3OsLock",         test_sqlite3OsLock, 0 },
     { "sqlite3OsUnlock",       test_sqlite3OsUnlock, 0 },

  };
  int i;
  extern int sqlite3_os_trace;

  for(i=0; i<sizeof(aCmd)/sizeof(aCmd[0]); i++){
    Tcl_CreateCommand(interp, aCmd[i].zName, aCmd[i].xProc, 0, 0);
  }
  for(i=0; i<sizeof(aObjCmd)/sizeof(aObjCmd[0]); i++){
    Tcl_CreateObjCommand(interp, aObjCmd[i].zName, 
        aObjCmd[i].xProc, aObjCmd[i].clientData, 0);
................................................................................
      (char*)&sqlite3_search_count, TCL_LINK_INT);
  Tcl_LinkVar(interp, "sqlite_interrupt_count", 
      (char*)&sqlite3_interrupt_count, TCL_LINK_INT);
  Tcl_LinkVar(interp, "sqlite_open_file_count", 
      (char*)&sqlite3_open_file_count, TCL_LINK_INT);
  Tcl_LinkVar(interp, "sqlite_current_time", 
      (char*)&sqlite3_current_time, TCL_LINK_INT);
  Tcl_LinkVar(interp, "sqlite_os_trace",
      (char*)&sqlite3_os_trace, TCL_LINK_INT);
  Tcl_LinkVar(interp, "sqlite_static_bind_value",
      (char*)&sqlite_static_bind_value, TCL_LINK_STRING);
  return TCL_OK;
}

Changes to src/vdbeaux.c.

918
919
920
921
922
923
924
925

926
927
928
929
930
931
932




933





934
935
936
937
938
939
940
....
1032
1033
1034
1035
1036
1037
1038
1039
1040
1041
1042
1043
1044
1045
1046
  /* If there are any write-transactions at all, invoke the commit hook */
  if( needXcommit && db->xCommitCallback ){
    if( db->xCommitCallback(db->pCommitArg) ){
      return SQLITE_CONSTRAINT;
    }
  }

  /* The simple case - if less than two databases have write-transactions

  ** active, there is no need for the master-journal.
  */
  if( nTrans<2 ){
    for(i=0; i<db->nDb; i++){ 
      Btree *pBt = db->aDb[i].pBt;
      if( pBt ){
        int rc2 = sqlite3BtreeCommit(db->aDb[i].pBt);




        if( rc==SQLITE_OK ) rc = rc2;





      }
    }
  }

  /* The complex case - There is a multi-file write-transaction active.
  ** This requires a master journal file to ensure the transaction is
  ** committed atomicly.
................................................................................
    for(i=0; i<db->nDb; i++){ 
      Btree *pBt = db->aDb[i].pBt;
      if( pBt ){
        sqlite3BtreeCommit(pBt);
      }
    }
  }
  return SQLITE_OK;
}

/* 
** This routine checks that the sqlite3.activeVdbeCnt count variable
** matches the number of vdbe's in the list sqlite3.pVdbe that are
** currently active. An assertion fails if the two counts do not match.
**







|
>
|

|
|


|
>
>
>
>
|
>
>
>
>
>







 







|







918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
....
1042
1043
1044
1045
1046
1047
1048
1049
1050
1051
1052
1053
1054
1055
1056
  /* If there are any write-transactions at all, invoke the commit hook */
  if( needXcommit && db->xCommitCallback ){
    if( db->xCommitCallback(db->pCommitArg) ){
      return SQLITE_CONSTRAINT;
    }
  }

  /* The simple case - no more than one database file (not counting the TEMP
  ** database) has a transaction active.   There is no need for the
  ** master-journal.
  */
  if( nTrans<=1 ){
    for(i=0; rc==SQLITE_OK && i<db->nDb; i++){ 
      Btree *pBt = db->aDb[i].pBt;
      if( pBt ){
        rc = sqlite3BtreeSync(pBt, 0);
      }
    }

    /* Do the commit only if all databases successfully synced */
    if( rc==SQLITE_OK ){
      for(i=0; i<db->nDb; i++){
        Btree *pBt = db->aDb[i].pBt;
        if( pBt ){
          sqlite3BtreeCommit(pBt);
        }
      }
    }
  }

  /* The complex case - There is a multi-file write-transaction active.
  ** This requires a master journal file to ensure the transaction is
  ** committed atomicly.
................................................................................
    for(i=0; i<db->nDb; i++){ 
      Btree *pBt = db->aDb[i].pBt;
      if( pBt ){
        sqlite3BtreeCommit(pBt);
      }
    }
  }
  return rc;
}

/* 
** This routine checks that the sqlite3.activeVdbeCnt count variable
** matches the number of vdbe's in the list sqlite3.pVdbe that are
** currently active. An assertion fails if the two counts do not match.
**

Changes to test/attach2.test.

8
9
10
11
12
13
14
15
16
17
18
19
20
21

22



23
24
25
26
27
28
29
..
45
46
47
48
49
50
51
52


53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73

74
75
76
77
78
79


80
81
82
83
84
85
86
87
88
89
90
91
92
...
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
...
142
143
144
145
146
147
148

149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164

165
166
167


168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186




187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
...
228
229
230
231
232
233
234
235
236
237
238
239
#    May you share freely, never taking more than you give.
#
#***********************************************************************
# This file implements regression tests for SQLite library.  The
# focus of this script is testing the ATTACH and DETACH commands
# and related functionality.
#
# $Id: attach2.test,v 1.11 2004/05/31 12:34:54 danielk1977 Exp $
#


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


# Ticket #354



#
do_test attach2-1.1 {
  db eval {
    CREATE TABLE t1(a,b);
    CREATE INDEX x1 ON t1(a);
  }
  file delete -force test2.db
................................................................................
  foreach {idx name file} [execsql {PRAGMA database_list} $db] {
    lappend list $idx $name
  }
  return $list
}
db eval {DETACH t2}
do_test attach2-2.1 {
  # lock test2.db then try to attach it.  Should get an error.


  db2 eval {BEGIN}
  db2 eval {UPDATE t1 SET a = 0 WHERE 0}
  catchsql {
    ATTACH 'test2.db' AS t2;
  }
} {1 {database is locked}}
do_test attach2-2.2 {
  # make sure test2.db did not get attached.
  db_list db
} {0 main 1 temp}
do_test attach2-2.3 {
  # unlock test2.db and try to attach again.  should work this time.
  db2 eval {COMMIT}
  catchsql {
    ATTACH 'test2.db' AS t2;
  }
} {0 {}}
do_test attach2-2.4 {
  db_list db
} {0 main 1 temp 2 t2}
do_test attach2-2.5 {

  catchsql {
    SELECT name FROM t2.sqlite_master;
  }
} {0 {t1 x1}}
do_test attach2-2.6 {
  # lock test2.db and try to read from it.  should get an error.


  db2 eval BEGIN
  db2 eval {UPDATE t1 SET a = 0 WHERE 0}
  catchsql {
    SELECT name FROM t2.sqlite_master;
  }
} {1 {database is locked}}
do_test attach2-2.7 {
  # but we can still read from test1.db even though test2.db is locked.
  catchsql {
    SELECT name FROM main.sqlite_master;
  }
} {0 {t1 x1}}
do_test attach2-2.8 {
................................................................................
} {}
do_test attach2-2.12 {
  catchsql {
    COMMIT
  }
} {1 {cannot commit - no transaction is active}}

# Ticket #574:  Make sure it works usingi the non-callback API
#
do_test attach2-3.1 {
  db close
  set DB [sqlite db test.db]
  set rc [catch {sqlite3_prepare $DB "ATTACH 'test2.db' AS t2" -1 TAIL} VM]
  if {$rc} {lappend rc $VM}
  sqlite3_finalize $VM
................................................................................
  set rc
} {0}

db close
for {set i 2} {$i<=15} {incr i} {
  catch {db$i close}
}


# Tests attach2-4.* test that read-locks work correctly with attached
# databases.
do_test attach2-4.1 {
  sqlite db test.db
  sqlite db2 test.db
  execsql {ATTACH 'test2.db' as file2}
  execsql {ATTACH 'test2.db' as file2} db2
} {}

do_test attach2-4.2 {
  # Handle 'db' read-locks the main file
  execsql {BEGIN}
  execsql {SELECT * FROM t1}
} {}
do_test attach2-4.3 {

  execsql {SELECT * FROM t1} db2
} {}
do_test attach2-4.4 {


  set r [catch { 
    execsql {
      INSERT INTO t1 VALUES(1, 2)
    } db2 
  } msg]
  list $r $msg
} {1 {database is locked}}
do_test attach2-4.5 {
  # Handle 'db2' write-locks file2
  execsql {BEGIN} db2
  execsql {INSERT INTO file2.t1 VALUES(1, 2)} db2
} {}
do_test attach2-4.6 {
  set r [catch { 
    execsql {
      SELECT * FROM file2.t1;
    } 
  } msg]
  list $r $msg




} {1 {database is locked}}
do_test attach2-4.7 {
  # Ensure handle 'db' retains the lock on the main file after
  # failing to obtain a read-lock on file2.
  set r [catch { 
    execsql {
      INSERT INTO t1 VALUES(1, 2)
    } db2 
  } msg]
  list $r $msg
} {1 {database is locked}}
do_test attach2-4.8 {
  # Read lock the main file with db2. Now both handles have a read lock
  # on the main file, db2 has a write-lock on file2.
  execsql {SELECT * FROM t1} db2
} {}
do_test attach2-4.9 {
  # Try to upgrade the handle 'db' lock.
  set r [catch { 
    execsql {
      INSERT INTO t1 VALUES(1, 2)
    } 
  } msg]
  list $r $msg
} {1 {database is locked}}
do_test attach2-4.10 {
  # Release the locks held by handle 'db2'
  execsql {COMMIT} db2
} {}
do_test attach2-4.11 {
................................................................................
} {}

db close
db2 close
file delete -force test2.db

finish_test












|
|
|




>

>
>
>







 







|
>
>





|

|

|
<
<
|
<
<
|
<
<
<
<

>





|
>
>





|







 







|







 







>











|




>



>
>








|



|
|
|
|
|
|
|
>
>
>
>



|
<
|
|
|
<
<


|





<
|
|
|
<







 







<
<
<
<
<
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
..
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68


69


70




71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
...
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
...
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199

200
201
202


203
204
205
206
207
208
209
210

211
212
213

214
215
216
217
218
219
220
...
232
233
234
235
236
237
238





#    May you share freely, never taking more than you give.
#
#***********************************************************************
# This file implements regression tests for SQLite library.  The
# focus of this script is testing the ATTACH and DETACH commands
# and related functionality.
#
# $Id: attach2.test,v 1.12 2004/06/07 16:27:47 drh Exp $
#
set sqlite_os_trace 0

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


# Ticket #354
#
# Databases test.db and test2.db contain identical schemas.  Make
# sure we can attach test2.db from test.db.
#
do_test attach2-1.1 {
  db eval {
    CREATE TABLE t1(a,b);
    CREATE INDEX x1 ON t1(a);
  }
  file delete -force test2.db
................................................................................
  foreach {idx name file} [execsql {PRAGMA database_list} $db] {
    lappend list $idx $name
  }
  return $list
}
db eval {DETACH t2}
do_test attach2-2.1 {
  # lock test2.db then try to attach it.  This is no longer an error because
  # db2 just RESERVES the database.  It does not obtain a write-lock until
  # we COMMIT.
  db2 eval {BEGIN}
  db2 eval {UPDATE t1 SET a = 0 WHERE 0}
  catchsql {
    ATTACH 'test2.db' AS t2;
  }
} {0 {}}
do_test attach2-2.2 {
  # make sure test2.db did get attached.
  db_list db
} {0 main 1 temp 2 t2}


db2 eval {COMMIT}







do_test attach2-2.5 {
  # Make sure we can read test2.db from db
  catchsql {
    SELECT name FROM t2.sqlite_master;
  }
} {0 {t1 x1}}
do_test attach2-2.6 {
  # lock test2.db and try to read from it.  This should still work because
  # the lock is only a RESERVED lock which does not prevent reading.
  #
  db2 eval BEGIN
  db2 eval {UPDATE t1 SET a = 0 WHERE 0}
  catchsql {
    SELECT name FROM t2.sqlite_master;
  }
} {0 {t1 x1}}
do_test attach2-2.7 {
  # but we can still read from test1.db even though test2.db is locked.
  catchsql {
    SELECT name FROM main.sqlite_master;
  }
} {0 {t1 x1}}
do_test attach2-2.8 {
................................................................................
} {}
do_test attach2-2.12 {
  catchsql {
    COMMIT
  }
} {1 {cannot commit - no transaction is active}}

# Ticket #574:  Make sure it works using the non-callback API
#
do_test attach2-3.1 {
  db close
  set DB [sqlite db test.db]
  set rc [catch {sqlite3_prepare $DB "ATTACH 'test2.db' AS t2" -1 TAIL} VM]
  if {$rc} {lappend rc $VM}
  sqlite3_finalize $VM
................................................................................
  set rc
} {0}

db close
for {set i 2} {$i<=15} {incr i} {
  catch {db$i close}
}
set sqlite_os_trace 0

# Tests attach2-4.* test that read-locks work correctly with attached
# databases.
do_test attach2-4.1 {
  sqlite db test.db
  sqlite db2 test.db
  execsql {ATTACH 'test2.db' as file2}
  execsql {ATTACH 'test2.db' as file2} db2
} {}

do_test attach2-4.2 {
  # Handle 'db' read-locks test.db
  execsql {BEGIN}
  execsql {SELECT * FROM t1}
} {}
do_test attach2-4.3 {
  # The read lock held by db does not prevent db2 from reading test.db
  execsql {SELECT * FROM t1} db2
} {}
do_test attach2-4.4 {
  # db is only holding a read lock on test.db, so we should not be able
  # to commit a write to test.db from db2
  set r [catch { 
    execsql {
      INSERT INTO t1 VALUES(1, 2)
    } db2 
  } msg]
  list $r $msg
} {1 {database is locked}}
do_test attach2-4.5 {
  # Handle 'db2' reserves file2.
  execsql {BEGIN} db2
  execsql {INSERT INTO file2.t1 VALUES(1, 2)} db2
} {}
do_test attach2-4.6.1 {
  # Reads are allowed against a reserved database.
  catchsql {
    SELECT * FROM file2.t1;
  }
} {0 {}}
do_test attach2-4.6.2 {
  # Writes against a reserved database are not allowed.
  catchsql {
    UPDATE file2.t1 SET a=0;
  }
} {1 {database is locked}}
do_test attach2-4.7 {
  # Ensure handle 'db' retains the lock on the main file after
  # failing to obtain a write-lock on file2.

  catchsql {
    INSERT INTO t1 VALUES(1, 2)
  } db2 


} {1 {database is locked}}
do_test attach2-4.8 {
  # Read lock the main file with db2. Now both db and db2 have a read lock
  # on the main file, db2 has a write-lock on file2.
  execsql {SELECT * FROM t1} db2
} {}
do_test attach2-4.9 {
  # Try to upgrade the handle 'db' lock.

  catchsql {
    INSERT INTO t1 VALUES(1, 2)
  }

  list $r $msg
} {1 {database is locked}}
do_test attach2-4.10 {
  # Release the locks held by handle 'db2'
  execsql {COMMIT} db2
} {}
do_test attach2-4.11 {
................................................................................
} {}

db close
db2 close
file delete -force test2.db

finish_test