Many hyperlinks are disabled.
Use anonymous login
to enable hyperlinks.
Overview
Comment: | Add the sqlite3_log_hook() interface for scheduling checkpoints. |
---|---|
Downloads: | Tarball | ZIP archive |
Timelines: | family | ancestors | descendants | both | wal |
Files: | files | file ages | folders |
SHA1: |
9bda601455705475075e33bfa85687bc |
User & Date: | dan 2010-04-19 18:03:52.000 |
Context
2010-04-20
| ||
18:53 | Use the read and write version fields of the database header to mark a database as operating in wal-mode. (check-in: 96bef18c14 user: dan tags: wal) | |
2010-04-19
| ||
18:03 | Add the sqlite3_log_hook() interface for scheduling checkpoints. (check-in: 9bda601455 user: dan tags: wal) | |
2010-04-17
| ||
18:50 | Add some comments regarding file-locks to log.c. (check-in: 9d51c3b754 user: dan tags: wal) | |
Changes
Changes to src/log.c.
︙ | ︙ | |||
198 199 200 201 202 203 204 205 206 207 208 209 210 211 | ** logLockRegion() function. */ #define LOG_REGION_A 0x01 #define LOG_REGION_B 0x02 #define LOG_REGION_C 0x04 #define LOG_REGION_D 0x08 /* ** A single instance of this structure is allocated as part of each ** connection to a database log. All structures associated with the ** same log file are linked together into a list using LogLock.pNext ** starting at LogSummary.pLock. ** ** The mLock field of the structure describes the locks (if any) | > > > > > > > > | 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 | ** logLockRegion() function. */ #define LOG_REGION_A 0x01 #define LOG_REGION_B 0x02 #define LOG_REGION_C 0x04 #define LOG_REGION_D 0x08 /* ** Values for the third parameter to logLockRegion(). */ #define LOG_UNLOCK 0 /* Unlock a range of bytes */ #define LOG_RDLOCK 1 /* Put a SHARED lock on a range of bytes */ #define LOG_WRLOCK 2 /* Put an EXCLUSIVE lock on a byte-range */ #define LOG_WRLOCKW 3 /* Block on EXCLUSIVE lock on a byte-range */ /* ** A single instance of this structure is allocated as part of each ** connection to a database log. All structures associated with the ** same log file are linked together into a list using LogLock.pNext ** starting at LogSummary.pLock. ** ** The mLock field of the structure describes the locks (if any) |
︙ | ︙ | |||
221 222 223 224 225 226 227 228 229 230 231 232 233 234 | struct Log { LogSummary *pSummary; /* Log file summary data */ sqlite3_vfs *pVfs; /* The VFS used to create pFd */ sqlite3_file *pFd; /* File handle for log file */ int isLocked; /* Non-zero if a snapshot is held open */ int isWriteLocked; /* True if this is the writer connection */ LogSummaryHdr hdr; /* Log summary header for current snapshot */ LogLock lock; /* Lock held by this connection (if any) */ }; /* ** This structure is used to implement an iterator that iterates through | > | 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 | struct Log { LogSummary *pSummary; /* Log file summary data */ sqlite3_vfs *pVfs; /* The VFS used to create pFd */ sqlite3_file *pFd; /* File handle for log file */ int isLocked; /* Non-zero if a snapshot is held open */ int isWriteLocked; /* True if this is the writer connection */ u32 iCallback; /* Value to pass to log callback (or 0) */ LogSummaryHdr hdr; /* Log summary header for current snapshot */ LogLock lock; /* Lock held by this connection (if any) */ }; /* ** This structure is used to implement an iterator that iterates through |
︙ | ︙ | |||
643 644 645 646 647 648 649 | finished: logSummaryWriteHdr(pSummary, &hdr); return rc; } /* | | > < < < < < | > > > > > | | | | | | | | < | | | 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 | finished: logSummaryWriteHdr(pSummary, &hdr); return rc; } /* ** Place, modify or remove a lock on the log-summary file associated ** with pSummary. */ static int logLockFd( LogSummary *pSummary, /* The log-summary object to lock */ int iStart, /* First byte to lock */ int nByte, /* Number of bytes to lock */ int op /* LOG_UNLOCK, RDLOCK, WRLOCK or WRLOCKW */ ){ int aType[4] = { F_UNLCK, /* LOG_UNLOCK */ F_RDLCK, /* LOG_RDLOCK */ F_WRLCK, /* LOG_WRLOCK */ F_WRLCK /* LOG_WRLOCKW */ }; int aOp[4] = { F_SETLK, /* LOG_UNLOCK */ F_SETLK, /* LOG_RDLOCK */ F_SETLK, /* LOG_WRLOCK */ F_SETLKW /* LOG_WRLOCKW */ }; struct flock f; /* Locking operation */ int rc; /* Value returned by fcntl() */ assert( ArraySize(aType)==ArraySize(aOp) ); assert( op>=0 && op<ArraySize(aType) ); memset(&f, 0, sizeof(f)); f.l_type = aType[op]; f.l_whence = SEEK_SET; |
︙ | ︙ | |||
812 813 814 815 816 817 818 819 820 821 822 823 824 825 826 827 828 | } pLog->lock.mLock = mNew; sqlite3_mutex_leave(pSummary->mutex); return SQLITE_OK; } static int logLockDMH(LogSummary *pSummary, int eLock){ assert( eLock==LOG_RDLOCK || eLock==LOG_WRLOCK ); return logLockFd(pSummary, LOG_LOCK_DMH, 1, eLock); } static int logLockMutex(LogSummary *pSummary, int eLock){ assert( eLock==LOG_WRLOCKW || eLock==LOG_UNLOCK ); logLockFd(pSummary, LOG_LOCK_MUTEX, 1, eLock); return SQLITE_OK; } | > > > > > > > > > > > < < | 821 822 823 824 825 826 827 828 829 830 831 832 833 834 835 836 837 838 839 840 841 842 843 844 845 846 847 848 849 850 851 852 853 854 855 | } pLog->lock.mLock = mNew; sqlite3_mutex_leave(pSummary->mutex); return SQLITE_OK; } /* ** Lock the DMH region, either with an EXCLUSIVE or SHARED lock. This ** function is never called with LOG_UNLOCK - the only way the DMH region ** is every completely unlocked is by by closing the file descriptor. */ static int logLockDMH(LogSummary *pSummary, int eLock){ assert( sqlite3_mutex_held(pSummary->mutex) ); assert( eLock==LOG_RDLOCK || eLock==LOG_WRLOCK ); return logLockFd(pSummary, LOG_LOCK_DMH, 1, eLock); } /* ** Lock (or unlock) the MUTEX region. It is always locked using an ** EXCLUSIVE, blocking lock. */ static int logLockMutex(LogSummary *pSummary, int eLock){ assert( sqlite3_mutex_held(pSummary->mutex) ); assert( eLock==LOG_WRLOCKW || eLock==LOG_UNLOCK ); logLockFd(pSummary, LOG_LOCK_MUTEX, 1, eLock); return SQLITE_OK; } /* ** This function intializes the connection to the log-summary identified ** by struct pSummary. */ static int logSummaryInit( LogSummary *pSummary, /* Log summary object to initialize */ |
︙ | ︙ | |||
876 877 878 879 880 881 882 | */ rc = logLockDMH(pSummary, LOG_WRLOCK); if( rc==SQLITE_OK ){ memset(pSummary->aData, 0, (LOGSUMMARY_HDR_NFIELD+2)*sizeof(u32) ); } rc = logLockDMH(pSummary, LOG_RDLOCK); if( rc!=SQLITE_OK ){ | | | 894 895 896 897 898 899 900 901 902 903 904 905 906 907 908 | */ rc = logLockDMH(pSummary, LOG_WRLOCK); if( rc==SQLITE_OK ){ memset(pSummary->aData, 0, (LOGSUMMARY_HDR_NFIELD+2)*sizeof(u32) ); } rc = logLockDMH(pSummary, LOG_RDLOCK); if( rc!=SQLITE_OK ){ rc = SQLITE_IOERR; } out: logLockMutex(pSummary, LOG_UNLOCK); return rc; } |
︙ | ︙ | |||
1457 1458 1459 1460 1461 1462 1463 | return SQLITE_OK; } /* ** Set *pPgno to the size of the database file (or zero, if unknown). */ | | | 1475 1476 1477 1478 1479 1480 1481 1482 1483 1484 1485 1486 1487 1488 1489 | return SQLITE_OK; } /* ** Set *pPgno to the size of the database file (or zero, if unknown). */ void sqlite3LogDbsize(Log *pLog, Pgno *pPgno){ assert( pLog->isLocked ); *pPgno = pLog->hdr.nPage; } /* ** This function returns SQLITE_OK if the caller may write to the database. ** Otherwise, if the caller is operating on a snapshot that has already |
︙ | ︙ | |||
1642 1643 1644 1645 1646 1647 1648 1649 1650 | pLog->hdr.iCheck1 = aCksum[0]; pLog->hdr.iCheck2 = aCksum[1]; /* If this is a commit, update the log-summary header too. */ if( isCommit && SQLITE_OK==(rc = logEnterMutex(pLog)) ){ logSummaryWriteHdr(pLog->pSummary, &pLog->hdr); logLeaveMutex(pLog); } | > | | 1660 1661 1662 1663 1664 1665 1666 1667 1668 1669 1670 1671 1672 1673 1674 1675 1676 1677 | pLog->hdr.iCheck1 = aCksum[0]; pLog->hdr.iCheck2 = aCksum[1]; /* If this is a commit, update the log-summary header too. */ if( isCommit && SQLITE_OK==(rc = logEnterMutex(pLog)) ){ logSummaryWriteHdr(pLog->pSummary, &pLog->hdr); logLeaveMutex(pLog); pLog->iCallback = iFrame; } return rc; } /* ** Checkpoint the database: ** ** 1. Wait for an EXCLUSIVE lock on regions B and C. ** 2. Wait for an EXCLUSIVE lock on region A. |
︙ | ︙ | |||
1693 1694 1695 1696 1697 1698 1699 1700 | rc = logCheckpoint(pLog, pFd, sync_flags, zBuf); } /* Release the locks. */ logLockRegion(pLog, LOG_REGION_A|LOG_REGION_B|LOG_REGION_C, LOG_UNLOCK); return rc; } | > > > > > > > > > | 1712 1713 1714 1715 1716 1717 1718 1719 1720 1721 1722 1723 1724 1725 1726 1727 1728 | rc = logCheckpoint(pLog, pFd, sync_flags, zBuf); } /* Release the locks. */ logLockRegion(pLog, LOG_REGION_A|LOG_REGION_B|LOG_REGION_C, LOG_UNLOCK); return rc; } int sqlite3LogCallback(Log *pLog){ u32 ret = 0; if( pLog ){ ret = pLog->iCallback; pLog->iCallback = 0; } return (int)ret; } |
Changes to src/log.h.
︙ | ︙ | |||
28 29 30 31 32 33 34 | /* Used by readers to open (lock) and close (unlock) a snapshot. */ int sqlite3LogOpenSnapshot(Log *pLog, int *); void sqlite3LogCloseSnapshot(Log *pLog); /* Read a page from the log, if it is present. */ int sqlite3LogRead(Log *pLog, Pgno pgno, int *pInLog, u8 *pOut); | | > > > | 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 53 54 55 56 | /* Used by readers to open (lock) and close (unlock) a snapshot. */ int sqlite3LogOpenSnapshot(Log *pLog, int *); void sqlite3LogCloseSnapshot(Log *pLog); /* Read a page from the log, if it is present. */ int sqlite3LogRead(Log *pLog, Pgno pgno, int *pInLog, u8 *pOut); void sqlite3LogDbsize(Log *pLog, Pgno *pPgno); /* Obtain or release the WRITER lock. */ int sqlite3LogWriteLock(Log *pLog, int op); /* Write a frame or frames to the log. */ int sqlite3LogFrames(Log *pLog, int, PgHdr *, Pgno, int, int); /* Copy pages from the log to the database file */ int sqlite3LogCheckpoint( Log *pLog, /* Log connection */ sqlite3_file *pFd, /* File descriptor open on db file */ int sync_flags, /* Flags to sync db file with (or 0) */ u8 *zBuf, /* Temporary buffer to use */ int (*xBusyHandler)(void *), /* Pointer to busy-handler function */ void *pBusyHandlerArg /* Argument to pass to xBusyHandler */ ); /* Return the value to pass to a log callback. Or 0 for no callback. */ int sqlite3LogCallback(Log *pLog); #endif /* _LOG_H_ */ |
Changes to src/main.c.
︙ | ︙ | |||
1181 1182 1183 1184 1185 1186 1187 1188 1189 1190 1191 1192 1193 1194 | sqlite3_mutex_enter(db->mutex); pRet = db->pRollbackArg; db->xRollbackCallback = xCallback; db->pRollbackArg = pArg; sqlite3_mutex_leave(db->mutex); return pRet; } /* ** This function returns true if main-memory should be used instead of ** a temporary file for transient pager files and statement journals. ** The value returned depends on the value of db->temp_store (runtime ** parameter) and the compile time value of SQLITE_TEMP_STORE. The ** following table describes the relationship between these two values | > > > > > > > > > > > > > > > > > > | 1181 1182 1183 1184 1185 1186 1187 1188 1189 1190 1191 1192 1193 1194 1195 1196 1197 1198 1199 1200 1201 1202 1203 1204 1205 1206 1207 1208 1209 1210 1211 1212 | sqlite3_mutex_enter(db->mutex); pRet = db->pRollbackArg; db->xRollbackCallback = xCallback; db->pRollbackArg = pArg; sqlite3_mutex_leave(db->mutex); return pRet; } /* ** Register a callback to be invoked each time a transaction is written ** into the write-ahead-log by this database connection. */ void *sqlite3_log_hook( sqlite3 *db, /* Attach the hook to this db handle */ int(*xCallback)(void *, sqlite3*, const char*, int), void *pArg /* First argument passed to xCallback() */ ){ void *pRet; sqlite3_mutex_enter(db->mutex); pRet = db->pLogArg; db->xLogCallback = xCallback; db->pLogArg = pArg; sqlite3_mutex_leave(db->mutex); return pRet; } /* ** This function returns true if main-memory should be used instead of ** a temporary file for transient pager files and statement journals. ** The value returned depends on the value of db->temp_store (runtime ** parameter) and the compile time value of SQLITE_TEMP_STORE. The ** following table describes the relationship between these two values |
︙ | ︙ |
Changes to src/pager.c.
︙ | ︙ | |||
2682 2683 2684 2685 2686 2687 2688 | if( pPager->dbSizeValid ){ nPage = pPager->dbSize; }else{ int rc; /* Error returned by OsFileSize() */ i64 n = 0; /* File size in bytes returned by OsFileSize() */ if( pagerUseLog(pPager) ){ | | | 2682 2683 2684 2685 2686 2687 2688 2689 2690 2691 2692 2693 2694 2695 2696 | if( pPager->dbSizeValid ){ nPage = pPager->dbSize; }else{ int rc; /* Error returned by OsFileSize() */ i64 n = 0; /* File size in bytes returned by OsFileSize() */ if( pagerUseLog(pPager) ){ sqlite3LogDbsize(pPager->pLog, &nPage); } if( nPage==0 ){ assert( isOpen(pPager->fd) || pPager->tempFile ); if( isOpen(pPager->fd) ){ if( SQLITE_OK!=(rc = sqlite3OsFileSize(pPager->fd, &n)) ){ pager_error(pPager, rc); |
︙ | ︙ | |||
5704 5705 5706 5707 5708 5709 5710 5711 5712 | rc = sqlite3LogCheckpoint(pPager->pLog, pPager->fd, (pPager->noSync ? 0 : pPager->sync_flags), zBuf, pPager->xBusyHandler, pPager->pBusyHandlerArg ); } return rc; } #endif /* SQLITE_OMIT_DISKIO */ | > > > > | 5704 5705 5706 5707 5708 5709 5710 5711 5712 5713 5714 5715 5716 | rc = sqlite3LogCheckpoint(pPager->pLog, pPager->fd, (pPager->noSync ? 0 : pPager->sync_flags), zBuf, pPager->xBusyHandler, pPager->pBusyHandlerArg ); } return rc; } int sqlite3PagerLogCallback(Pager *pPager){ return sqlite3LogCallback(pPager->pLog); } #endif /* SQLITE_OMIT_DISKIO */ |
Changes to src/pager.h.
︙ | ︙ | |||
129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 | int sqlite3PagerCommitPhaseOne(Pager*,const char *zMaster, int); int sqlite3PagerSync(Pager *pPager); int sqlite3PagerCommitPhaseTwo(Pager*); int sqlite3PagerRollback(Pager*); int sqlite3PagerOpenSavepoint(Pager *pPager, int n); int sqlite3PagerSavepoint(Pager *pPager, int op, int iSavepoint); int sqlite3PagerSharedLock(Pager *pPager); int sqlite3PagerCheckpoint(Pager *pPager); /* Functions used to query pager state and configuration. */ u8 sqlite3PagerIsreadonly(Pager*); int sqlite3PagerRefcount(Pager*); int sqlite3PagerMemUsed(Pager*); const char *sqlite3PagerFilename(Pager*); const sqlite3_vfs *sqlite3PagerVfs(Pager*); | > > | 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 | int sqlite3PagerCommitPhaseOne(Pager*,const char *zMaster, int); int sqlite3PagerSync(Pager *pPager); int sqlite3PagerCommitPhaseTwo(Pager*); int sqlite3PagerRollback(Pager*); int sqlite3PagerOpenSavepoint(Pager *pPager, int n); int sqlite3PagerSavepoint(Pager *pPager, int op, int iSavepoint); int sqlite3PagerSharedLock(Pager *pPager); int sqlite3PagerCheckpoint(Pager *pPager); int sqlite3PagerLogCallback(Pager *pPager); /* Functions used to query pager state and configuration. */ u8 sqlite3PagerIsreadonly(Pager*); int sqlite3PagerRefcount(Pager*); int sqlite3PagerMemUsed(Pager*); const char *sqlite3PagerFilename(Pager*); const sqlite3_vfs *sqlite3PagerVfs(Pager*); |
︙ | ︙ |
Changes to src/sqlite.h.in.
︙ | ︙ | |||
5721 5722 5723 5724 5725 5726 5727 5728 5729 5730 5731 5732 5733 5734 | ** To avoid deadlocks and other threading problems, the sqlite3_log() routine ** will not use dynamically allocated memory. The log message is stored in ** a fixed-length buffer on the stack. If the log message is longer than ** a few hundred characters, it will be truncated to the length of the ** buffer. */ void sqlite3_log(int iErrCode, const char *zFormat, ...); /* ** Undo the hack that converts floating point types to integer for ** builds on processors without floating point support. */ #ifdef SQLITE_OMIT_FLOATING_POINT # undef double | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 5721 5722 5723 5724 5725 5726 5727 5728 5729 5730 5731 5732 5733 5734 5735 5736 5737 5738 5739 5740 5741 5742 5743 5744 5745 5746 5747 5748 5749 5750 5751 5752 5753 5754 5755 5756 5757 5758 5759 5760 5761 5762 5763 5764 5765 5766 5767 5768 | ** To avoid deadlocks and other threading problems, the sqlite3_log() routine ** will not use dynamically allocated memory. The log message is stored in ** a fixed-length buffer on the stack. If the log message is longer than ** a few hundred characters, it will be truncated to the length of the ** buffer. */ void sqlite3_log(int iErrCode, const char *zFormat, ...); /* ** Experimental WAL callback interface. ** ** The [sqlite3_log_hook()] function is used to register a callback that ** will be invoked each time a database connection commits data to a ** write-ahead-log (i.e. whenever a transaction is committed in ** journal_mode=WAL mode). ** ** The callback is invoked by SQLite after the commit has taken place and ** the associated write-lock on the database released, so the implementation ** may read, write or checkpoint the database as required. ** ** The first parameter passed to the callback function when it is invoked ** is a copy of the third parameter passed to sqlite3_log_hook() when ** registering the callback. The second is a copy of the database handle. ** The third parameter is the name of the database that was written to - ** either "main" or the name of an ATTACHed database. The fourth parameter ** is the number of pages currently in the log file, including those that ** were just committed. ** ** If an invocation of the callback function returns non-zero, then a ** checkpoint is automatically run on the database. If zero is returned, ** no special action is taken. ** ** A single database handle may have at most a single log callback ** registered at one time. Calling [sqlite3_log_hook()] replaces any ** previously registered log callback. */ void *sqlite3_log_hook( sqlite3*, int(*)(void *,sqlite3*,const char*,int), void* ); /* ** Undo the hack that converts floating point types to integer for ** builds on processors without floating point support. */ #ifdef SQLITE_OMIT_FLOATING_POINT # undef double |
︙ | ︙ |
Changes to src/sqliteInt.h.
︙ | ︙ | |||
819 820 821 822 823 824 825 826 827 828 829 830 831 832 | void *pProfileArg; /* Argument to profile function */ void *pCommitArg; /* Argument to xCommitCallback() */ int (*xCommitCallback)(void*); /* Invoked at every commit. */ void *pRollbackArg; /* Argument to xRollbackCallback() */ void (*xRollbackCallback)(void*); /* Invoked at every commit. */ void *pUpdateArg; void (*xUpdateCallback)(void*,int, const char*,const char*,sqlite_int64); void(*xCollNeeded)(void*,sqlite3*,int eTextRep,const char*); void(*xCollNeeded16)(void*,sqlite3*,int eTextRep,const void*); void *pCollNeededArg; sqlite3_value *pErr; /* Most recent error message */ char *zErrMsg; /* Most recent error message (UTF-8 encoded) */ char *zErrMsg16; /* Most recent error message (UTF-16 encoded) */ union { | > > | 819 820 821 822 823 824 825 826 827 828 829 830 831 832 833 834 | void *pProfileArg; /* Argument to profile function */ void *pCommitArg; /* Argument to xCommitCallback() */ int (*xCommitCallback)(void*); /* Invoked at every commit. */ void *pRollbackArg; /* Argument to xRollbackCallback() */ void (*xRollbackCallback)(void*); /* Invoked at every commit. */ void *pUpdateArg; void (*xUpdateCallback)(void*,int, const char*,const char*,sqlite_int64); int (*xLogCallback)(void *, sqlite3 *, const char *, u32); void *pLogArg; void(*xCollNeeded)(void*,sqlite3*,int eTextRep,const char*); void(*xCollNeeded16)(void*,sqlite3*,int eTextRep,const void*); void *pCollNeededArg; sqlite3_value *pErr; /* Most recent error message */ char *zErrMsg; /* Most recent error message (UTF-8 encoded) */ char *zErrMsg16; /* Most recent error message (UTF-16 encoded) */ union { |
︙ | ︙ |
Changes to src/tclsqlite.c.
︙ | ︙ | |||
119 120 121 122 123 124 125 126 127 128 129 130 131 132 | char *zProgress; /* The progress callback routine */ char *zAuth; /* The authorization callback routine */ int disableAuth; /* Disable the authorizer if it exists */ char *zNull; /* Text to substitute for an SQL NULL value */ SqlFunc *pFunc; /* List of SQL functions */ Tcl_Obj *pUpdateHook; /* Update hook script (if any) */ Tcl_Obj *pRollbackHook; /* Rollback hook script (if any) */ Tcl_Obj *pUnlockNotify; /* Unlock notify script (if any) */ SqlCollate *pCollate; /* List of SQL collation functions */ int rc; /* Return code of most recent sqlite3_exec() */ Tcl_Obj *pCollateNeeded; /* Collation needed script */ SqlPreparedStmt *stmtList; /* List of prepared statements*/ SqlPreparedStmt *stmtLast; /* Last statement in the list */ int maxStmt; /* The next maximum number of stmtList */ | > | 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 | char *zProgress; /* The progress callback routine */ char *zAuth; /* The authorization callback routine */ int disableAuth; /* Disable the authorizer if it exists */ char *zNull; /* Text to substitute for an SQL NULL value */ SqlFunc *pFunc; /* List of SQL functions */ Tcl_Obj *pUpdateHook; /* Update hook script (if any) */ Tcl_Obj *pRollbackHook; /* Rollback hook script (if any) */ Tcl_Obj *pLogHook; /* WAL hook script (if any) */ Tcl_Obj *pUnlockNotify; /* Unlock notify script (if any) */ SqlCollate *pCollate; /* List of SQL collation functions */ int rc; /* Return code of most recent sqlite3_exec() */ Tcl_Obj *pCollateNeeded; /* Collation needed script */ SqlPreparedStmt *stmtList; /* List of prepared statements*/ SqlPreparedStmt *stmtLast; /* Last statement in the list */ int maxStmt; /* The next maximum number of stmtList */ |
︙ | ︙ | |||
481 482 483 484 485 486 487 488 489 490 491 492 493 494 | } if( pDb->pUpdateHook ){ Tcl_DecrRefCount(pDb->pUpdateHook); } if( pDb->pRollbackHook ){ Tcl_DecrRefCount(pDb->pRollbackHook); } if( pDb->pCollateNeeded ){ Tcl_DecrRefCount(pDb->pCollateNeeded); } Tcl_Free((char*)pDb); } /* | > > > | 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 | } if( pDb->pUpdateHook ){ Tcl_DecrRefCount(pDb->pUpdateHook); } if( pDb->pRollbackHook ){ Tcl_DecrRefCount(pDb->pRollbackHook); } if( pDb->pLogHook ){ Tcl_DecrRefCount(pDb->pLogHook); } if( pDb->pCollateNeeded ){ Tcl_DecrRefCount(pDb->pCollateNeeded); } Tcl_Free((char*)pDb); } /* |
︙ | ︙ | |||
584 585 586 587 588 589 590 591 592 593 594 595 596 597 | static void DbRollbackHandler(void *clientData){ SqliteDb *pDb = (SqliteDb*)clientData; assert(pDb->pRollbackHook); if( TCL_OK!=Tcl_EvalObjEx(pDb->interp, pDb->pRollbackHook, 0) ){ Tcl_BackgroundError(pDb->interp); } } #if defined(SQLITE_TEST) && defined(SQLITE_ENABLE_UNLOCK_NOTIFY) static void setTestUnlockNotifyVars(Tcl_Interp *interp, int iArg, int nArg){ char zBuf[64]; sprintf(zBuf, "%d", iArg); Tcl_SetVar(interp, "sqlite_unlock_notify_arg", zBuf, TCL_GLOBAL_ONLY); sprintf(zBuf, "%d", nArg); | > > > > > > > > > > > > > > > > > > > > > > > > > > | 588 589 590 591 592 593 594 595 596 597 598 599 600 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 | static void DbRollbackHandler(void *clientData){ SqliteDb *pDb = (SqliteDb*)clientData; assert(pDb->pRollbackHook); if( TCL_OK!=Tcl_EvalObjEx(pDb->interp, pDb->pRollbackHook, 0) ){ Tcl_BackgroundError(pDb->interp); } } static int DbLogHandler( void *clientData, sqlite3 *db, const char *zDb, int nEntry ){ int ret = 0; Tcl_Obj *p; SqliteDb *pDb = (SqliteDb*)clientData; Tcl_Interp *interp = pDb->interp; assert(pDb->pLogHook); p = Tcl_DuplicateObj(pDb->pLogHook); Tcl_IncrRefCount(p); Tcl_ListObjAppendElement(interp, p, Tcl_NewStringObj(zDb, -1)); Tcl_ListObjAppendElement(interp, p, Tcl_NewIntObj(nEntry)); if( TCL_OK!=Tcl_EvalObjEx(interp, p, 0) || TCL_OK!=Tcl_GetIntFromObj(interp, Tcl_GetObjResult(interp), &ret) ){ Tcl_BackgroundError(interp); } Tcl_DecrRefCount(p); return ret; } #if defined(SQLITE_TEST) && defined(SQLITE_ENABLE_UNLOCK_NOTIFY) static void setTestUnlockNotifyVars(Tcl_Interp *interp, int iArg, int nArg){ char zBuf[64]; sprintf(zBuf, "%d", iArg); Tcl_SetVar(interp, "sqlite_unlock_notify_arg", zBuf, TCL_GLOBAL_ONLY); sprintf(zBuf, "%d", nArg); |
︙ | ︙ | |||
1536 1537 1538 1539 1540 1541 1542 | static const char *DB_strs[] = { "authorizer", "backup", "busy", "cache", "changes", "close", "collate", "collation_needed", "commit_hook", "complete", "copy", "enable_load_extension", "errorcode", "eval", "exists", "function", "incrblob", "interrupt", | | | | | | | | | | | | | | 1566 1567 1568 1569 1570 1571 1572 1573 1574 1575 1576 1577 1578 1579 1580 1581 1582 1583 1584 1585 1586 1587 1588 1589 1590 1591 1592 1593 1594 1595 1596 1597 1598 1599 | static const char *DB_strs[] = { "authorizer", "backup", "busy", "cache", "changes", "close", "collate", "collation_needed", "commit_hook", "complete", "copy", "enable_load_extension", "errorcode", "eval", "exists", "function", "incrblob", "interrupt", "last_insert_rowid", "log_hook", "nullvalue", "onecolumn", "profile", "progress", "rekey", "restore", "rollback_hook", "status", "timeout", "total_changes", "trace", "transaction", "unlock_notify", "update_hook", "version", 0 }; enum DB_enum { DB_AUTHORIZER, DB_BACKUP, DB_BUSY, DB_CACHE, DB_CHANGES, DB_CLOSE, DB_COLLATE, DB_COLLATION_NEEDED, DB_COMMIT_HOOK, DB_COMPLETE, DB_COPY, DB_ENABLE_LOAD_EXTENSION, DB_ERRORCODE, DB_EVAL, DB_EXISTS, DB_FUNCTION, DB_INCRBLOB, DB_INTERRUPT, DB_LAST_INSERT_ROWID, DB_LOG_HOOK, DB_NULLVALUE, DB_ONECOLUMN, DB_PROFILE, DB_PROGRESS, DB_REKEY, DB_RESTORE, DB_ROLLBACK_HOOK, DB_STATUS, DB_TIMEOUT, DB_TOTAL_CHANGES, DB_TRACE, DB_TRANSACTION, DB_UNLOCK_NOTIFY, DB_UPDATE_HOOK, DB_VERSION }; /* don't leave trailing commas on DB_enum, it confuses the AIX xlc compiler */ if( objc<2 ){ Tcl_WrongNumArgs(interp, 1, objv, "SUBCOMMAND ..."); return TCL_ERROR; } |
︙ | ︙ | |||
2726 2727 2728 2729 2730 2731 2732 2733 2734 2735 2736 2737 2738 2739 2740 2741 2742 2743 2744 2745 2746 2747 2748 2749 2750 2751 | } } #endif break; } /* ** $db update_hook ?script? ** $db rollback_hook ?script? */ case DB_UPDATE_HOOK: case DB_ROLLBACK_HOOK: { /* set ppHook to point at pUpdateHook or pRollbackHook, depending on ** whether [$db update_hook] or [$db rollback_hook] was invoked. */ Tcl_Obj **ppHook; if( choice==DB_UPDATE_HOOK ){ ppHook = &pDb->pUpdateHook; }else{ ppHook = &pDb->pRollbackHook; } if( objc!=2 && objc!=3 ){ Tcl_WrongNumArgs(interp, 2, objv, "?SCRIPT?"); return TCL_ERROR; | > > > > | 2756 2757 2758 2759 2760 2761 2762 2763 2764 2765 2766 2767 2768 2769 2770 2771 2772 2773 2774 2775 2776 2777 2778 2779 2780 2781 2782 2783 2784 2785 | } } #endif break; } /* ** $db log_hook ?script? ** $db update_hook ?script? ** $db rollback_hook ?script? */ case DB_LOG_HOOK: case DB_UPDATE_HOOK: case DB_ROLLBACK_HOOK: { /* set ppHook to point at pUpdateHook or pRollbackHook, depending on ** whether [$db update_hook] or [$db rollback_hook] was invoked. */ Tcl_Obj **ppHook; if( choice==DB_UPDATE_HOOK ){ ppHook = &pDb->pUpdateHook; }else if( choice==DB_LOG_HOOK ){ ppHook = &pDb->pLogHook; }else{ ppHook = &pDb->pRollbackHook; } if( objc!=2 && objc!=3 ){ Tcl_WrongNumArgs(interp, 2, objv, "?SCRIPT?"); return TCL_ERROR; |
︙ | ︙ | |||
2763 2764 2765 2766 2767 2768 2769 2770 2771 2772 2773 2774 2775 2776 | *ppHook = objv[2]; Tcl_IncrRefCount(*ppHook); } } sqlite3_update_hook(pDb->db, (pDb->pUpdateHook?DbUpdateHandler:0), pDb); sqlite3_rollback_hook(pDb->db,(pDb->pRollbackHook?DbRollbackHandler:0),pDb); break; } /* $db version ** ** Return the version string for this database. | > | 2797 2798 2799 2800 2801 2802 2803 2804 2805 2806 2807 2808 2809 2810 2811 | *ppHook = objv[2]; Tcl_IncrRefCount(*ppHook); } } sqlite3_update_hook(pDb->db, (pDb->pUpdateHook?DbUpdateHandler:0), pDb); sqlite3_rollback_hook(pDb->db,(pDb->pRollbackHook?DbRollbackHandler:0),pDb); sqlite3_log_hook(pDb->db,(pDb->pLogHook?DbLogHandler:0),pDb); break; } /* $db version ** ** Return the version string for this database. |
︙ | ︙ |
Changes to src/vdbeapi.c.
︙ | ︙ | |||
301 302 303 304 305 306 307 308 309 310 311 312 313 314 | /* An SQLITE_NOMEM error. */ void sqlite3_result_error_nomem(sqlite3_context *pCtx){ assert( sqlite3_mutex_held(pCtx->s.db->mutex) ); sqlite3VdbeMemSetNull(&pCtx->s); pCtx->isError = SQLITE_NOMEM; pCtx->s.db->mallocFailed = 1; } /* ** Execute the statement pStmt, either until a row of data is ready, the ** statement is completely executed or an error occurs. ** ** This routine implements the bulk of the logic behind the sqlite_step() ** API. The only thing omitted is the automatic recompile if a | > > > > > > > > > > > > > > > > > | 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 | /* An SQLITE_NOMEM error. */ void sqlite3_result_error_nomem(sqlite3_context *pCtx){ assert( sqlite3_mutex_held(pCtx->s.db->mutex) ); sqlite3VdbeMemSetNull(&pCtx->s); pCtx->isError = SQLITE_NOMEM; pCtx->s.db->mallocFailed = 1; } static int doLogCallbacks(sqlite3 *db){ int i; int rc = SQLITE_OK; for(i=0; i<db->nDb; i++){ Btree *pBt = db->aDb[i].pBt; if( pBt ){ int nEntry = sqlite3PagerLogCallback(sqlite3BtreePager(pBt)); if( db->xLogCallback && nEntry>0 && rc==SQLITE_OK && db->xLogCallback(db->pLogArg, db, db->aDb[i].zName, nEntry) ){ rc = sqlite3PagerCheckpoint(sqlite3BtreePager(pBt)); } } } return rc; } /* ** Execute the statement pStmt, either until a row of data is ready, the ** statement is completely executed or an error occurs. ** ** This routine implements the bulk of the logic behind the sqlite_step() ** API. The only thing omitted is the automatic recompile if a |
︙ | ︙ | |||
382 383 384 385 386 387 388 389 390 391 392 393 394 395 | sqlite3OsCurrentTime(db->pVfs, &rNow); elapseTime = (u64)((rNow - (int)rNow)*3600.0*24.0*1000000000.0); elapseTime -= p->startTime; db->xProfile(db->pProfileArg, p->zSql, elapseTime); } #endif db->errCode = rc; if( SQLITE_NOMEM==sqlite3ApiExit(p->db, p->rc) ){ p->rc = SQLITE_NOMEM; } end_of_step: /* At this point local variable rc holds the value that should be | > > > > > > > > | 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 | sqlite3OsCurrentTime(db->pVfs, &rNow); elapseTime = (u64)((rNow - (int)rNow)*3600.0*24.0*1000000000.0); elapseTime -= p->startTime; db->xProfile(db->pProfileArg, p->zSql, elapseTime); } #endif if( rc==SQLITE_DONE ){ assert( p->rc==SQLITE_OK ); p->rc = doLogCallbacks(db); if( p->rc!=SQLITE_OK ){ rc = SQLITE_ERROR; } } db->errCode = rc; if( SQLITE_NOMEM==sqlite3ApiExit(p->db, p->rc) ){ p->rc = SQLITE_NOMEM; } end_of_step: /* At this point local variable rc holds the value that should be |
︙ | ︙ |
Changes to src/vdbeaux.c.
︙ | ︙ | |||
1647 1648 1649 1650 1651 1652 1653 | /* This loop determines (a) if the commit hook should be invoked and ** (b) how many database files have open write transactions, not ** including the temp database. (b) is important because if more than ** one database file has an open write transaction, a master journal ** file is required for an atomic commit. */ | | | 1647 1648 1649 1650 1651 1652 1653 1654 1655 1656 1657 1658 1659 1660 1661 | /* This loop determines (a) if the commit hook should be invoked and ** (b) how many database files have open write transactions, not ** including the temp database. (b) is important because if more than ** one database file has an open write transaction, a master journal ** file is required for an atomic commit. */ for(i=0; i<db->nDb; i++){ Btree *pBt = db->aDb[i].pBt; if( sqlite3BtreeIsInTrans(pBt) ){ needXcommit = 1; if( i!=1 ) nTrans++; } } |
︙ | ︙ |
Changes to test/tclsqlite.test.
︙ | ︙ | |||
31 32 33 34 35 36 37 | set v [catch {sqlite3 bogus} msg] regsub {really_sqlite3} $msg {sqlite3} msg lappend v $msg } [list 1 "wrong # args: should be \"$r\""] do_test tcl-1.2 { set v [catch {db bogus} msg] lappend v $msg | | | 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 | set v [catch {sqlite3 bogus} msg] regsub {really_sqlite3} $msg {sqlite3} msg lappend v $msg } [list 1 "wrong # args: should be \"$r\""] do_test tcl-1.2 { set v [catch {db bogus} msg] lappend v $msg } {1 {bad option "bogus": must be authorizer, backup, busy, cache, changes, close, collate, collation_needed, commit_hook, complete, copy, enable_load_extension, errorcode, eval, exists, function, incrblob, interrupt, last_insert_rowid, log_hook, nullvalue, onecolumn, profile, progress, rekey, restore, rollback_hook, status, timeout, total_changes, trace, transaction, unlock_notify, update_hook, or version}} do_test tcl-1.2.1 { set v [catch {db cache bogus} msg] lappend v $msg } {1 {bad option "bogus": must be flush or size}} do_test tcl-1.2.2 { set v [catch {db cache} msg] lappend v $msg |
︙ | ︙ |
Added test/walhook.test.
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 1 2 3 4 5 6 7 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 34 35 36 37 38 39 40 41 42 43 44 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 | # 2010 April 19 # # 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. # #*********************************************************************** # This file implements regression tests for SQLite library. The # focus of this file is testing the operation of the library in # "PRAGMA journal_mode=WAL" mode. # set testdir [file dirname $argv0] source $testdir/tester.tcl proc sqlite3_wal {args} { eval sqlite3 $args [lindex $args 0] eval { PRAGMA journal_mode = wal; PRAGMA synchronous = normal; PRAGMA page_size = 1024; } } sqlite3_wal db test.db db log_hook log_hook set ::log_hook [list] proc log_hook {zDb nEntry} { lappend ::log_hook $zDb $nEntry return 0 } do_test walhook-1.1 { execsql { CREATE TABLE t1(i PRIMARY KEY, j) } set ::log_hook } {main 3} do_test walhook-1.2 { set ::log_hook [list] execsql { INSERT INTO t1 VALUES(1, 'one') } set ::log_hook } {main 5} do_test walhook-1.3 { proc log_hook {args} { return 1 } execsql { INSERT INTO t1 VALUES(2, 'two') } file size test.db } [expr 3*1024] do_test walhook-1.4 { proc log_hook {zDb nEntry} { execsql { PRAGMA checkpoint } return 0 } execsql { CREATE TABLE t2(a, b) } file size test.db } [expr 4*1024] do_test walhook-1.5 { sqlite3_wal db2 test.db proc log_hook {zDb nEntry} { execsql { PRAGMA checkpoint } db2 return 0 } execsql { CREATE TABLE t3(a PRIMARY KEY, b) } file size test.db } [expr 6*1024] catch { db2 close } catch { db close } finish_test |