/ Check-in [f50a177b]
Login

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

Overview
Comment:Rework the changes for ticket #176 (check-ins (760) and (761)) to be more consistent with the rest of the source code. (CVS 768)
Downloads: Tarball | ZIP archive | SQL archive
Timelines: family | ancestors | descendants | both | trunk
Files: files | file ages | folders
SHA1:f50a177b4239bc7a83563ac9361e830ec04e81fb
User & Date: drh 2002-10-20 18:19:45
Context
2002-10-22
15:04
Take care to track ephemeral strings in the VDBE and make copies of ephemeral strings that need to be preserved. Ticket #177. (CVS 769) check-in: 562da534 user: drh tags: trunk
2002-10-20
18:19
Rework the changes for ticket #176 (check-ins (760) and (761)) to be more consistent with the rest of the source code. (CVS 768) check-in: f50a177b user: drh tags: trunk
16:00
The "table_info" pragma reports unspecified column types as "numeric" instead of "text". Fix for ticket #174. (CVS 767) check-in: 395ab5fa user: drh tags: trunk
Changes
Hide Diffs Unified Diffs Ignore Whitespace Patch

Changes to src/os.c.

86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
...
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
...
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
...
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
...
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
...
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
...
601
602
603
604
605
606
607

















608
609
610
611
612
613
614






615
616
617
618
619
620
621
622

623
624
625

626
627
628
629
630
631
632
633
634
635





636
637
638
639
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
...
728
729
730
731
732
733
734
735
736




737
738
739
740
741




742
743
744
745
746
747
748
749
...
779
780
781
782
783
784
785




786
787
788
789
790






791
792



793
794
795
796
797
798
799
...
827
828
829
830
831
832
833



834
835


836



837
838
839
840
841
842
843
844
845
846
847
** point to the same locking structure.  The locking structure keeps
** a reference count (so we will know when to delete it) and a "cnt"
** field that tells us its internal lock status.  cnt==0 means the
** file is unlocked.  cnt==-1 means the file has an exclusive lock.
** cnt>0 means there are cnt shared locks on the file.
**
** Any attempt to lock or unlock a file first checks the locking
** structure.  The fcntl() system call is only invoked to set a
** POSIX lock if the internal lock structure transitions between
** a locked and an unlocked state.
*/

/*
** An instance of the following structure serves as the key used
** to locate a particular lockInfo structure given its inode.
*/
struct inodeKey {
  dev_t dev;   /* Device number */
  ino_t ino;   /* Inode number */
};

/*
................................................................................
*/
struct lockInfo {
  struct inodeKey key;  /* The lookup key */
  int cnt;              /* 0: unlocked.  -1: write lock.  1...: read lock. */
  int nRef;             /* Number of pointers to this structure */
};

/*
** This hash table maps inodes (in the form of inodeKey structures) into
** pointers to lockInfo structures.
*/
static Hash lockHash = { SQLITE_HASH_BINARY, 0, 0, 0, 0, 0 };

/*
** Given a file descriptor, locate a lockInfo structure that describes
................................................................................
  int *pReadonly
){
#if OS_UNIX
  id->fd = open(zFilename, O_RDWR|O_CREAT, 0644);
  if( id->fd<0 ){
    id->fd = open(zFilename, O_RDONLY);
    if( id->fd<0 ){
      return SQLITE_CANTOPEN;
    }
    *pReadonly = 1;
  }else{
    *pReadonly = 0;
  }
  sqliteOsEnterMutex();
  id->pLock = findLockInfo(id->fd);
................................................................................
  }
  return SQLITE_OK;
#endif
#if OS_WIN
  HANDLE h;
  int fileflags;
  if( delFlag ){
    fileflags = FILE_ATTRIBUTE_TEMPORARY | FILE_FLAG_RANDOM_ACCESS
                     | FILE_FLAG_DELETE_ON_CLOSE;
  }else{
    fileflags = FILE_FLAG_RANDOM_ACCESS;
  }
  h = CreateFile(zFilename,
     GENERIC_READ | GENERIC_WRITE,
     0,
................................................................................
      int n = sqliteRandomByte() % sizeof(zChars);
      zBuf[j++] = zChars[n];
    }
    zBuf[j] = 0;
    if( !sqliteOsFileExists(zBuf) ) break;
  }
#endif
  return SQLITE_OK;
}

/*
** Close a file
*/
int sqliteOsClose(OsFile *id){
#if OS_UNIX
................................................................................
#endif
#if OS_WIN
  DWORD got;
  SimulateIOError(SQLITE_IOERR);
  if( !ReadFile(id->h, pBuf, amt, &got, 0) ){
    got = 0;
  }
  if( (int)got==amt ){
    return SQLITE_OK;
  }else{
    return SQLITE_IOERR;
  }
#endif
}

................................................................................
#if OS_WIN
  SimulateIOError(SQLITE_IOERR);
  *pSize = GetFileSize(id->h, 0);
  return SQLITE_OK;
#endif
}


















/*
** Windows file locking notes:
**
** We cannot use LockFileEx() or UnlockFileEx() because those functions
** are not available under Win95/98/ME.  So we use only LockFile() and
** UnlockFile().
**






** A read lock is obtained by locking a single random byte in the
** range of 1 to MX_LOCKBYTE.  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.  A write lock
** is obtained by locking all bytes in the range of 1 to MX_LOCKBYTE.
** There can only be one writer.
**
** A lock is obtained on byte 0 before acquiring either a read lock or

** a write lock.  This prevents two processes from attempting to get a
** lock at a same time.  The semantics of sqliteOsReadLock() require that
** if there is already a write lock, that lock is converted into a read

** lock atomically.  The lock on byte 0 allows us to drop the old write
** lock and get the read lock without another process jumping into the
** middle and messing us up.  The same argument applies to sqliteOsWriteLock().
**
** Locks must be obtained in an area that does not overlap the "real data area"
** otherwise read/write operations will conflict with lock operations. Locking beyond EOF
** is allowed in windows.
**
** There are a finite number of read locks under windows.  That number
** is determined by the following variable:





*/

#define MX_LOCKBYTE 0xFFF0

#if OS_WIN

// get the platform id to decide how to calculate the lock offset


int mkPlatformId(void){

 static long init=0;
 static long lock=0;

 static int pid=VER_PLATFORM_WIN32_WINDOWS;
 OSVERSIONINFOA info;

 while (!init) {
  if (InterlockedIncrement(&lock)==1)
   {
    info.dwOSVersionInfoSize=sizeof(info);
    if (GetVersionEx(&info)) pid=info.dwPlatformId;
    init=1;
   }
  else
   Sleep(1);
  }
 return pid;
}

// locks and unlocks beyond eof. uses platformid to move the lock as far as possible.
int mklock(HANDLE h, WORD base, WORD size)
{
 if (mkPlatformId()==VER_PLATFORM_WIN32_WINDOWS)
  return LockFile(h,0xFFFF0000+base,0,size,0);
 else
  return LockFile(h,base,0xFFFFFFFF,size,0);
}

int mkunlock(HANDLE h, WORD base, WORD size)
{
 if (mkPlatformId()==VER_PLATFORM_WIN32_WINDOWS)
  return UnlockFile(h,0xFFFF0000+base,0,size,0);
 else
  return UnlockFile(h,base,0xFFFFFFFF,size,0);
}

//obtain the sync lock on a handle

void synclock(HANDLE h){
 while (!mklock(h,0,1)) Sleep(1);
}

void syncunlock(HANDLE h){
 mkunlock(h,0,1);
}

#endif

/*
** Change the status of the lock on the file "id" to be a readlock.
** If the file was write locked, then this reduces the lock to a read.
** If the file was read locked, then this acquires a new read lock.
**
** Return SQLITE_OK on success and SQLITE_BUSY on failure.
*/

int sqliteOsReadLock(OsFile *id){
#if OS_UNIX
  int rc;
  sqliteOsEnterMutex();
  if( id->pLock->cnt>0 ){
    if( !id->locked ){
      id->pLock->cnt++;
................................................................................
  return rc;
#endif
#if OS_WIN
  int rc;
  if( id->locked>0 ){
    rc = SQLITE_OK;
  }else{
    int lk = (sqliteRandomInteger() & 0x7ffffff)%MX_LOCKBYTE + 1;
    int res;





    synclock(id->h);
    if (id->locked<0) mkunlock(id->h,1,MX_LOCKBYTE); // release write lock if we have it
    res=mklock(id->h,lk,1);
    syncunlock(id->h);





    if( res ){
      id->locked = lk;
      rc = SQLITE_OK;
    }else{
      rc = SQLITE_BUSY;
    }
  }
................................................................................
#endif
#if OS_WIN
  int rc;
  if( id->locked<0 ){
    rc = SQLITE_OK;
  }else{
    int res;





    synclock(id->h);
    if (id->locked>0) mkunlock(id->h,id->locked,1); // release read lock
    res=mklock(id->h,1,MX_LOCKBYTE);
    syncunlock(id->h);







    if(res){



      id->locked = -1;
      rc = SQLITE_OK;
    }else{
      rc = SQLITE_BUSY;
    }
  }
  return rc;
................................................................................
  }
  sqliteOsLeaveMutex();
  id->locked = 0;
  return rc;
#endif
#if OS_WIN
  int rc;



  if(id->locked<0 ) {
    mkunlock(id->h,1,MX_LOCKBYTE);


  }else if (id->locked>0) {



    mkunlock(id->h,id->locked,1);
  }
  id->locked = 0;
  return SQLITE_OK;
#endif
}

/*
** Get information to seed the random number generator.  The seed
** is written into the buffer zBuf[256].  The calling function must
** supply a sufficiently large buffer.







|






|







 







|







 







|







 







|







 







|







 







|







 







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







>
>
>
>
>
>
|
|
|
|
|


<
>
|
|
|
>
|
|
|

<
<
<
<
<
<
>
>
>
>
>

<
<
<
<
<
<
<
>
|
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<








<







 







|

>
>
>
>
|
<
<
<
<
>
>
>
>
|







 







>
>
>
>
|
<
<
<
<
>
>
>
>
>
>
|
<
>
>
>







 







>
>
>
|
<
>
>
|
>
>
>
|

<
|







86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
...
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
...
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
...
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
...
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
...
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
...
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
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
...
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
...
751
752
753
754
755
756
757
758
759
760
761
762




763
764
765
766
767
768
769

770
771
772
773
774
775
776
777
778
779
...
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
** point to the same locking structure.  The locking structure keeps
** a reference count (so we will know when to delete it) and a "cnt"
** field that tells us its internal lock status.  cnt==0 means the
** file is unlocked.  cnt==-1 means the file has an exclusive lock.
** cnt>0 means there are cnt shared locks on the file.
**
** Any attempt to lock or unlock a file first checks the locking
** structure.  The fcntl() system call is only invoked to set a 
** POSIX lock if the internal lock structure transitions between
** a locked and an unlocked state.
*/

/*
** An instance of the following structure serves as the key used
** to locate a particular lockInfo structure given its inode. 
*/
struct inodeKey {
  dev_t dev;   /* Device number */
  ino_t ino;   /* Inode number */
};

/*
................................................................................
*/
struct lockInfo {
  struct inodeKey key;  /* The lookup key */
  int cnt;              /* 0: unlocked.  -1: write lock.  1...: read lock. */
  int nRef;             /* Number of pointers to this structure */
};

/* 
** This hash table maps inodes (in the form of inodeKey structures) into
** pointers to lockInfo structures.
*/
static Hash lockHash = { SQLITE_HASH_BINARY, 0, 0, 0, 0, 0 };

/*
** Given a file descriptor, locate a lockInfo structure that describes
................................................................................
  int *pReadonly
){
#if OS_UNIX
  id->fd = open(zFilename, O_RDWR|O_CREAT, 0644);
  if( id->fd<0 ){
    id->fd = open(zFilename, O_RDONLY);
    if( id->fd<0 ){
      return SQLITE_CANTOPEN; 
    }
    *pReadonly = 1;
  }else{
    *pReadonly = 0;
  }
  sqliteOsEnterMutex();
  id->pLock = findLockInfo(id->fd);
................................................................................
  }
  return SQLITE_OK;
#endif
#if OS_WIN
  HANDLE h;
  int fileflags;
  if( delFlag ){
    fileflags = FILE_ATTRIBUTE_TEMPORARY | FILE_FLAG_RANDOM_ACCESS 
                     | FILE_FLAG_DELETE_ON_CLOSE;
  }else{
    fileflags = FILE_FLAG_RANDOM_ACCESS;
  }
  h = CreateFile(zFilename,
     GENERIC_READ | GENERIC_WRITE,
     0,
................................................................................
      int n = sqliteRandomByte() % sizeof(zChars);
      zBuf[j++] = zChars[n];
    }
    zBuf[j] = 0;
    if( !sqliteOsFileExists(zBuf) ) break;
  }
#endif
  return SQLITE_OK; 
}

/*
** Close a file
*/
int sqliteOsClose(OsFile *id){
#if OS_UNIX
................................................................................
#endif
#if OS_WIN
  DWORD got;
  SimulateIOError(SQLITE_IOERR);
  if( !ReadFile(id->h, pBuf, amt, &got, 0) ){
    got = 0;
  }
  if( got==(DWORD)amt ){
    return SQLITE_OK;
  }else{
    return SQLITE_IOERR;
  }
#endif
}

................................................................................
#if OS_WIN
  SimulateIOError(SQLITE_IOERR);
  *pSize = GetFileSize(id->h, 0);
  return SQLITE_OK;
#endif
}

#if OS_WIN
/*
** Return true (non-zero) if we are running under WinNT, Win2K or WinXP.
** Return false (zero) for Win95, Win98, or WinME.
*/
int isNT(void){
  static osType = 0;   /* 0=unknown 1=win95 2=winNT */
  if( osType==0 ){
    OSVERSIONINFO sInfo;
    sInfo.dwOSVersionInfoSize = sizeof(sInfo);
    GetVersionEx(&sInfo);
    osType = sInfo.dwPlatformId==VER_PLATFORM_WIN32_NT ? 2 : 1;
  }
  return osType==2;
}
#endif

/*
** Windows file locking notes:
**
** We cannot use LockFileEx() or UnlockFileEx() because those functions
** are not available under Win95/98/ME.  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 database read 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.
** A database write lock is obtained by locking all bytes in the range.
** There can only be one writer.
**

** A lock is obtained on the first byte of the lock range before acquiring
** either a read lock or a write lock.  This prevents two processes from
** attempting to get a lock at a same time.  The semantics of 
** sqliteOsReadLock() require that if there is already a write lock, that
** lock is converted into a read lock atomically.  The lock on the first
** byte allows us to drop the old write lock and get the read lock without
** another process jumping into the middle and messing us up.  The same
** argument applies to sqliteOsWriteLock().
**






** The following #defines specify the range of bytes used for locking.
** N_LOCKBYTE is the number of bytes available for doing the locking.
** The first byte used to hold the lock while the lock is changing does
** not count toward this number.  FIRST_LOCKBYTE is the address of
** the first byte in the range of bytes used for locking.
*/







#define N_LOCKBYTE       10239
#define FIRST_LOCKBYTE   (0xffffffff - N_LOCKBYTE)

















































/*
** Change the status of the lock on the file "id" to be a readlock.
** If the file was write locked, then this reduces the lock to a read.
** If the file was read locked, then this acquires a new read lock.
**
** Return SQLITE_OK on success and SQLITE_BUSY on failure.
*/

int sqliteOsReadLock(OsFile *id){
#if OS_UNIX
  int rc;
  sqliteOsEnterMutex();
  if( id->pLock->cnt>0 ){
    if( !id->locked ){
      id->pLock->cnt++;
................................................................................
  return rc;
#endif
#if OS_WIN
  int rc;
  if( id->locked>0 ){
    rc = SQLITE_OK;
  }else{
    int lk = (sqliteRandomInteger() & 0x7ffffff)%N_LOCKBYTE+1;
    int res;
    int cnt = 100;
    int page = isNT() ? 0xffffffff : 0;
    while( cnt-->0 && (res = LockFile(id->h, FIRST_LOCKBYTE, page, 1, 0))==0 ){
      Sleep(1);
    }




    if( res ){
      UnlockFile(id->h, FIRST_LOCKBYTE+1, page, N_LOCKBYTE, 0);
      res = LockFile(id->h, FIRST_LOCKBYTE+lk, page, 1, 0);
      UnlockFile(id->h, FIRST_LOCKBYTE, page, 1, 0);
    }
    if( res ){
      id->locked = lk;
      rc = SQLITE_OK;
    }else{
      rc = SQLITE_BUSY;
    }
  }
................................................................................
#endif
#if OS_WIN
  int rc;
  if( id->locked<0 ){
    rc = SQLITE_OK;
  }else{
    int res;
    int cnt = 100;
    int page = isNT() ? 0xffffffff : 0;
    while( cnt-->0 && (res = LockFile(id->h, FIRST_LOCKBYTE, page, 1, 0))==0 ){
      Sleep(1);
    }




    if( res ){
      if( id->locked==0 
            || UnlockFile(id->h, FIRST_LOCKBYTE + id->locked, page, 1, 0) ){
        res = LockFile(id->h, FIRST_LOCKBYTE+1, page, N_LOCKBYTE, 0);
      }else{
        res = 0;
      }

      UnlockFile(id->h, FIRST_LOCKBYTE, page, 1, 0);
    }
    if( res ){
      id->locked = -1;
      rc = SQLITE_OK;
    }else{
      rc = SQLITE_BUSY;
    }
  }
  return rc;
................................................................................
  }
  sqliteOsLeaveMutex();
  id->locked = 0;
  return rc;
#endif
#if OS_WIN
  int rc;
  int page = isNT() ? 0xffffffff : 0;
  if( id->locked==0 ){
    rc = SQLITE_OK;
  }else if( id->locked<0 ){

    UnlockFile(id->h, FIRST_LOCKBYTE+1, page, N_LOCKBYTE, 0);
    rc = SQLITE_OK;
    id->locked = 0;
  }else{
    UnlockFile(id->h, FIRST_LOCKBYTE+id->locked, page, 1, 0);
    rc = SQLITE_OK;
    id->locked = 0;
  }

  return rc;
#endif
}

/*
** Get information to seed the random number generator.  The seed
** is written into the buffer zBuf[256].  The calling function must
** supply a sufficiently large buffer.