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 |
Timelines: | family | ancestors | descendants | both | trunk |
Files: | files | file ages | folders |
SHA1: |
0c2d169cf3c0f36972015c952a2b46cb |
User & Date: | drh 2004-06-07 16:27:46.000 |
Context
2004-06-08
| ||
00:02 | Remove the third argument from the sqlite3_open() API. (CVS 1540) (check-in: 62e31f396c 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: 0c2d169cf3 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: 31c1668dbc user: danielk1977 tags: trunk) | |
Changes
Changes to src/btree.c.
1 2 3 4 5 6 7 8 9 10 11 | /* ** 2004 April 6 ** ** The author disclaims copyright to this source code. In place of ** 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. ** ************************************************************************* | | | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 | /* ** 2004 April 6 ** ** The author disclaims copyright to this source code. In place of ** 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. |
︙ | ︙ | |||
3709 3710 3711 3712 3713 3714 3715 | ** is currently pointing to. */ int sqlite3BtreeFlags(BtCursor *pCur){ MemPage *pPage = pCur->pPage; return pPage ? pPage->aData[pPage->hdrOffset] : 0; } | < < < < < < | 3709 3710 3711 3712 3713 3714 3715 3716 3717 3718 3719 3720 3721 3722 | ** 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 | */ /* ** Macros for performance tracing. Normally turned off. Only works ** on i486 hardware. */ | > | | | | | | | 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 | ** ** 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 */ | | | 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 | ** ** 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 |
︙ | ︙ | |||
640 641 642 643 644 645 646 | *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 | | > | | < | | | > | | | | > > > > > > > > > > > > > > > < | < < < | > > > > > > > > > > > > > | | | | 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 | *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 |
︙ | ︙ | |||
740 741 742 743 744 745 746 | /* 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 ); | | | | | | > > > | > | | | > < > | 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 798 799 800 801 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 | /* 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++; pLock->cnt = 1; } }else{ /* The request was for a RESERVED, PENDING or EXCLUSIVE lock. It is ** 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 | DWORD upperBits, lowerBits; SimulateIOError(SQLITE_IOERR); lowerBits = GetFileSize(id->h, &upperBits); *pSize = (((off_t)upperBits)<<32) + lowerBits; return SQLITE_OK; } | < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < | 309 310 311 312 313 314 315 316 317 318 319 320 321 322 | 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 |
︙ | ︙ | |||
527 528 529 530 531 532 533 534 535 536 537 538 539 | ** 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); } } | > > > | | 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 | ** 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 | ** 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. ** | | | 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 | ** 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> |
︙ | ︙ | |||
1550 1551 1552 1553 1554 1555 1556 | 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. ** | | | > > > | < > | < < < < < < < | 1550 1551 1552 1553 1554 1555 1556 1557 1558 1559 1560 1561 1562 1563 1564 1565 1566 1567 1568 1569 1570 1571 1572 1573 | 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 | */ 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); | | > | 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 | ** 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. ** | | | 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 | ** 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> |
︙ | ︙ | |||
1909 1910 1911 1912 1913 1914 1915 1916 1917 1918 1919 1920 1921 1922 1923 1924 1925 1926 1927 1928 1929 1930 1931 1932 1933 1934 1935 | { "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); } Tcl_LinkVar(interp, "sqlite_search_count", (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; } | > > > | 1909 1910 1911 1912 1913 1914 1915 1916 1917 1918 1919 1920 1921 1922 1923 1924 1925 1926 1927 1928 1929 1930 1931 1932 1933 1934 1935 1936 1937 1938 | { "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); } Tcl_LinkVar(interp, "sqlite_search_count", (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 | /* If there are any write-transactions at all, invoke the commit hook */ if( needXcommit && db->xCommitCallback ){ if( db->xCommitCallback(db->pCommitArg) ){ return SQLITE_CONSTRAINT; } } | | > | | | | > > > > | > > > > > | 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 | /* 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. |
︙ | ︙ | |||
1032 1033 1034 1035 1036 1037 1038 | for(i=0; i<db->nDb; i++){ Btree *pBt = db->aDb[i].pBt; if( pBt ){ sqlite3BtreeCommit(pBt); } } } | | | 1042 1043 1044 1045 1046 1047 1048 1049 1050 1051 1052 1053 1054 1055 1056 | 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 | # 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. # | | | > > > > | 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 | # 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 |
︙ | ︙ | |||
45 46 47 48 49 50 51 | foreach {idx name file} [execsql {PRAGMA database_list} $db] { lappend list $idx $name } return $list } db eval {DETACH t2} do_test attach2-2.1 { | | > > | | | < < | < < | < < < < > | > > | | 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 | 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 { |
︙ | ︙ | |||
121 122 123 124 125 126 127 | } {} do_test attach2-2.12 { catchsql { COMMIT } } {1 {cannot commit - no transaction is active}} | | > | > > > | | | | | | | | > > > > | < | | | < < | < | | | < | 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 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 | } {} 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} do_test attach2-3.2 { set rc [catch {sqlite3_prepare $DB "DETACH 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 { |
︙ | ︙ | |||
228 229 230 231 232 233 234 | } {} db close db2 close file delete -force test2.db finish_test | < < < < < | 232 233 234 235 236 237 238 | } {} db close db2 close file delete -force test2.db finish_test |