1
0
mirror of https://github.com/sqlite/sqlite.git synced 2026-10-28 10:57:26 +03:00

Merge all the latest trunk changes into the reset-reserve branch.

FossilOrigin-Name: 6e5f1fbaec775db5c5e90b29cfa0820c9f4d560b36c063b504ef55a6c3a65bdc
This commit is contained in:
drh
2026-01-08 18:14:29 +00:00
10 changed files with 1302 additions and 68 deletions

View File

@@ -2709,6 +2709,9 @@ showwal.exe: $(TOP)\tool\showwal.c $(SQLITE3C) $(SQLITE3H)
showshm.exe: $(TOP)\tool\showshm.c
$(LTLINK) $(NO_WARN) $(TOP)\tool\showshm.c /link $(LDFLAGS) $(LTLINKOPTS)
showtmlog.exe: $(TOP)\tool\showtmlog.c
$(LTLINK) $(NO_WARN) $(TOP)\tool\showtmlog.c /link $(LDFLAGS) $(LTLINKOPTS)
index_usage.exe: $(TOP)\tool\index_usage.c $(SQLITE3C) $(SQLITE3H)
$(LTLINK) $(NO_WARN) -DSQLITE_THREADSAFE=0 -DSQLITE_OMIT_LOAD_EXTENSION \
$(TOP)\tool\index_usage.c $(SQLITE3C) /link $(LDFLAGS) $(LTLINKOPTS)

911
ext/misc/tmstmpvfs.c Normal file
View File

@@ -0,0 +1,911 @@
/*
** 2026-01-05
**
** 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 a VFS shim that writes a timestamp and other tracing
** information into 16 byts of reserved space at the end of each page of the
** database file. The additional data is written as the page is added to
** the WAL file for databases in WAL mode, or as the database file itself
** is modified in rollback modes.
**
** The VFS also tries to generate log-files with names of the form:
**
** $(DATABASE)-tmstmp/$(TIME)-$(PID)-$(ID)
**
** Log files are only generated if directory $(DATABASE)-tmstmp exists.
** The name of each log file is the current unix-epoch time in millisecond,
** the process ID, and a random 32-bit value (to disambiguate multiple
** connections from the same process) separated by spaces. The log file
** contains one 16-byte record for various events, such as opening or close
** of the database or WAL file, writes to the WAL file, checkpoints, and
** similar. The logfile is only generated if the connection attempts to
** modify the database. There is a separate log file for each open database
** connection.
**
** COMPILING
**
** To build this extension as a separately loaded shared library or
** DLL, use compiler command-lines similar to the following:
**
** (linux) gcc -fPIC -shared tmstmpvfs.c -o tmstmpvfs.so
** (mac) clang -fPIC -dynamiclib tmstmpvfs.c -o tmstmpvfs.dylib
** (windows) cl tmstmpvfs.c -link -dll -out:tmstmpvfs.dll
**
** You may want to add additional compiler options, of course,
** according to the needs of your project.
**
** LOADING
**
** To load this extension as a shared library, you first have to
** bring up a dummy SQLite database connection to use as the argument
** to the sqlite3_load_extension() API call. Then you invoke the
** sqlite3_load_extension() API and shutdown the dummy database
** connection. All subsequent database connections that are opened
** will include this extension. For example:
**
** sqlite3 *db;
** sqlite3_open(":memory:", &db);
** sqlite3_load_extension(db, "./tmstmpvfs");
** sqlite3_close(db);
**
** Tmstmpvfs is a VFS Shim. When loaded, "tmstmpvfs" becomes the new
** default VFS and it uses the prior default VFS as the next VFS
** down in the stack. This is normally what you want. However, in
** complex situations where multiple VFS shims are being loaded,
** it might be important to ensure that tmstmpvfs is loaded in the
** correct order so that it sequences itself into the default VFS
** Shim stack in the right order.
**
** An application can see if the tmstmpvfs is being used by examining
** the results from SQLITE_FCNTL_VFSNAME (or the .vfsname command in
** the CLI). If the answer include "tmstmp", then this VFS is being
** used.
**
** USING
**
** Open database connections using the sqlite3_open() or
** sqlite3_open_v2() interfaces, as normal. Ordinary database files
** (without a timestamp) will operate normally.
**
** Timestamping only works on databases that have a reserve-bytes
** value of exactly 16. The default value for reserve-bytes is 0.
** Hence, newly created database files will omit the timestamp by
** default. To create a database that includes a timestamp, change
** the reserve-bytes value to 16 by running:
**
** int n = 16;
** sqlite3_file_control(db, 0, SQLITE_FCNTL_RESERVE_BYTES, &n);
**
** If you do this immediately after creating a new database file,
** before anything else has been written into the file, then that
** might be all that you need to do. Otherwise, the API call
** above should be followed by:
**
** sqlite3_exec(db, "VACUUM", 0, 0, 0);
**
** It never hurts to run the VACUUM, even if you don't need it.
**
** From the CLI, use the ".filectrl reserve_bytes 16" command,
** followed by "VACUUM;".
**
** Note that SQLite allows the number of reserve-bytes to be
** increased but not decreased. So if a database file already
** has a reserve-bytes value greater than 16, there is no way to
** activate timestamping on that database, other than to dump
** and restore the database file. Note also that other extensions
** might also make use of the reserve-bytes. Timestamping will
** be incompatible with those other extensions.
**
** IMPLEMENTATION NOTES
**
** The timestamp information is stored in the last 16 bytes of each page.
** This module only operates if the "bytes of reserved space on each page"
** value at offset 20 the SQLite database header is exactly 16. If
** the reserved-space value is not 16, no timestamp information is added
** to database pages. Some, but not all, logfile entries will be made
** still, but the size of the logs will be greatly reduced.
**
** The timestamp layout is as follows:
**
** bytes 0,1 Zero. Reserved for future expansion
** bytes 2-7 Milliseconds since the Unix Epoch
** bytes 8-11 WAL frame number
** bytes 12 0: WAL write 1: WAL txn 2: rollback write
** bytes 13-15 Lower 24 bits of Salt-1
**
** For transactions that occur in rollback mode, only the timestamp
** in bytes 2-7 and byte 12 are non-zero. Byte 12 is set to 2 for
** rollback writes.
**
** LOGGING
**
** An open database connection that attempts to write to the database
** will create a log file if a directory name $(DATABASE)-tmstmp exists.
** The name of the log file is:
**
** $(TIME)-$(PID)-$(RANDOM)
**
** Where TIME is the number of milliseconds since 1970, PID is the process
** ID, and RANDOM is a 32-bit random number. All values are expressed
** in hexadecimal.
**
** The log consists of 16-byte records. Each record consists of five
** unsigned integers:
**
** 1 1 6 4 4 <--- bytes
** op a1 ts a2 a3
**
** The meanings of the a1-a3 values depend on op. ts is the timestamp
** in milliseconds since the unix epoch (1970-01-01 00:00:00).
** Opcodes are defined by the ELOG_* #defines below.
**
** ELOG_OPEN_DB "Open a connection to the database file"
** op = 0x01
** a2 = process-ID
**
** ELOG_OPEN_WAL "Open a connection to the -wal file"
** op = 0x02
** a2 = process-ID
**
** ELOG_WAL_PAGE "New page added to the WAL file"
** op = 0x03
** a1 = 1 if last page of a txn. 0 otherwise.
** a2 = page number in the DB file
** a3 = frame number in the WAL file
**
** ELOG_DB_PAGE "Database page updated using rollback mode"
** op = 0x04
** a2 = page number in the DB file
**
** ELOG_CKPT_START "Start of a checkpoint operation"
** op = 0x05
**
** ELOG_CKPT_PAGE "Page xfer from WAL to database"
** op = 0x06
** a2 = database page number
**
** ELOG_CKPT_END "Start of a checkpoint operation"
** op = 0x07
**
** ELOG_WAL_RESET "WAL file header overwritten"
** op = 0x08
** a3 = Salt1 value
**
** ELOG_CLOSE_WAL "Close the WAL file connection"
** op = 0x0e
**
** ELOG_CLOSE_DB "Close the DB connection"
** op = 0x0f
*/
#if defined(SQLITE_AMALGAMATION) && !defined(SQLITE_TMSTMPVFS_STATIC)
# define SQLITE_TMSTMPVFS_STATIC
#endif
#ifdef SQLITE_TMSTMPVFS_STATIC
# include "sqlite3.h"
#else
# include "sqlite3ext.h"
SQLITE_EXTENSION_INIT1
#endif
#include <string.h>
#include <assert.h>
#include <stdio.h>
/*
** Forward declaration of objects used by this utility
*/
typedef struct sqlite3_vfs TmstmpVfs;
typedef struct TmstmpFile TmstmpFile;
typedef struct TmstmpLog TmstmpLog;
/*
** Bytes of reserved space used by this extension
*/
#define TMSTMP_RESERVE 16
/*
** The magic number used to identify TmstmpFile objects
*/
#define TMSTMP_MAGIC 0x2a87b72d
/*
** Useful datatype abbreviations
*/
#if !defined(SQLITE_AMALGAMATION)
typedef unsigned char u8;
typedef unsigned int u32;
#endif
/*
** Current process id
*/
#if defined(_WIN32)
# include <windows.h>
# define GETPID (u32)GetCurrentProcessId()
#else
# include <unistd.h>
# define GETPID (u32)getpid()
#endif
/* Access to a lower-level VFS that (might) implement dynamic loading,
** access to randomness, etc.
*/
#define ORIGVFS(p) ((sqlite3_vfs*)((p)->pAppData))
#define ORIGFILE(p) ((sqlite3_file*)(((TmstmpFile*)(p))+1))
/* Information for the tmstmp log file. */
struct TmstmpLog {
char *zLogname; /* Log filename */
FILE *log; /* Open log file */
int n; /* Bytes of a[] used */
unsigned char a[16*6]; /* Buffered header for the log */
};
/* An open WAL file */
struct TmstmpFile {
sqlite3_file base; /* IO methods */
u32 uMagic; /* Magic number for sanity checking */
u32 salt1; /* Last WAL salt-1 value */
u32 iFrame; /* Last WAL frame number */
u32 pgno; /* Current page number */
u32 pgsz; /* Size of each page, in bytes */
u8 isWal; /* True if this is a WAL file */
u8 isDb; /* True if this is a DB file */
u8 isCommit; /* Last WAL frame header was a transaction commit */
u8 hasCorrectReserve; /* File has the correct reserve size */
u8 inCkpt; /* True if in a checkpoint */
TmstmpLog *pLog; /* Log file */
TmstmpFile *pPartner; /* DB->WAL or WAL->DB mapping */
sqlite3_int64 iOfst; /* Offset of last WAL frame header */
sqlite3_vfs *pSubVfs; /* Underlying VFS */
};
/*
** Event log opcodes
*/
#define ELOG_OPEN_DB 0x01
#define ELOG_OPEN_WAL 0x02
#define ELOG_WAL_PAGE 0x03
#define ELOG_DB_PAGE 0x04
#define ELOG_CKPT_START 0x05
#define ELOG_CKPT_PAGE 0x06
#define ELOG_CKPT_DONE 0x07
#define ELOG_WAL_RESET 0x08
#define ELOG_CLOSE_WAL 0x0e
#define ELOG_CLOSE_DB 0x0f
/*
** Methods for TmstmpFile
*/
static int tmstmpClose(sqlite3_file*);
static int tmstmpRead(sqlite3_file*, void*, int iAmt, sqlite3_int64 iOfst);
static int tmstmpWrite(sqlite3_file*,const void*,int iAmt, sqlite3_int64 iOfst);
static int tmstmpTruncate(sqlite3_file*, sqlite3_int64 size);
static int tmstmpSync(sqlite3_file*, int flags);
static int tmstmpFileSize(sqlite3_file*, sqlite3_int64 *pSize);
static int tmstmpLock(sqlite3_file*, int);
static int tmstmpUnlock(sqlite3_file*, int);
static int tmstmpCheckReservedLock(sqlite3_file*, int *pResOut);
static int tmstmpFileControl(sqlite3_file*, int op, void *pArg);
static int tmstmpSectorSize(sqlite3_file*);
static int tmstmpDeviceCharacteristics(sqlite3_file*);
static int tmstmpShmMap(sqlite3_file*, int iPg, int pgsz, int, void volatile**);
static int tmstmpShmLock(sqlite3_file*, int offset, int n, int flags);
static void tmstmpShmBarrier(sqlite3_file*);
static int tmstmpShmUnmap(sqlite3_file*, int deleteFlag);
static int tmstmpFetch(sqlite3_file*, sqlite3_int64 iOfst, int iAmt, void **pp);
static int tmstmpUnfetch(sqlite3_file*, sqlite3_int64 iOfst, void *p);
/*
** Methods for TmstmpVfs
*/
static int tmstmpOpen(sqlite3_vfs*, const char *, sqlite3_file*, int , int *);
static int tmstmpDelete(sqlite3_vfs*, const char *zName, int syncDir);
static int tmstmpAccess(sqlite3_vfs*, const char *zName, int flags, int *);
static int tmstmpFullPathname(sqlite3_vfs*, const char *zName, int, char *zOut);
static void *tmstmpDlOpen(sqlite3_vfs*, const char *zFilename);
static void tmstmpDlError(sqlite3_vfs*, int nByte, char *zErrMsg);
static void (*tmstmpDlSym(sqlite3_vfs *pVfs, void *p, const char*zSym))(void);
static void tmstmpDlClose(sqlite3_vfs*, void*);
static int tmstmpRandomness(sqlite3_vfs*, int nByte, char *zOut);
static int tmstmpSleep(sqlite3_vfs*, int microseconds);
static int tmstmpCurrentTime(sqlite3_vfs*, double*);
static int tmstmpGetLastError(sqlite3_vfs*, int, char *);
static int tmstmpCurrentTimeInt64(sqlite3_vfs*, sqlite3_int64*);
static int tmstmpSetSystemCall(sqlite3_vfs*, const char*,sqlite3_syscall_ptr);
static sqlite3_syscall_ptr tmstmpGetSystemCall(sqlite3_vfs*, const char *z);
static const char *tmstmpNextSystemCall(sqlite3_vfs*, const char *zName);
static sqlite3_vfs tmstmp_vfs = {
3, /* iVersion (set when registered) */
0, /* szOsFile (set when registered) */
1024, /* mxPathname */
0, /* pNext */
"tmstmpvfs", /* zName */
0, /* pAppData (set when registered) */
tmstmpOpen, /* xOpen */
tmstmpDelete, /* xDelete */
tmstmpAccess, /* xAccess */
tmstmpFullPathname, /* xFullPathname */
tmstmpDlOpen, /* xDlOpen */
tmstmpDlError, /* xDlError */
tmstmpDlSym, /* xDlSym */
tmstmpDlClose, /* xDlClose */
tmstmpRandomness, /* xRandomness */
tmstmpSleep, /* xSleep */
tmstmpCurrentTime, /* xCurrentTime */
tmstmpGetLastError, /* xGetLastError */
tmstmpCurrentTimeInt64, /* xCurrentTimeInt64 */
tmstmpSetSystemCall, /* xSetSystemCall */
tmstmpGetSystemCall, /* xGetSystemCall */
tmstmpNextSystemCall /* xNextSystemCall */
};
static const sqlite3_io_methods tmstmp_io_methods = {
3, /* iVersion */
tmstmpClose, /* xClose */
tmstmpRead, /* xRead */
tmstmpWrite, /* xWrite */
tmstmpTruncate, /* xTruncate */
tmstmpSync, /* xSync */
tmstmpFileSize, /* xFileSize */
tmstmpLock, /* xLock */
tmstmpUnlock, /* xUnlock */
tmstmpCheckReservedLock, /* xCheckReservedLock */
tmstmpFileControl, /* xFileControl */
tmstmpSectorSize, /* xSectorSize */
tmstmpDeviceCharacteristics, /* xDeviceCharacteristics */
tmstmpShmMap, /* xShmMap */
tmstmpShmLock, /* xShmLock */
tmstmpShmBarrier, /* xShmBarrier */
tmstmpShmUnmap, /* xShmUnmap */
tmstmpFetch, /* xFetch */
tmstmpUnfetch /* xUnfetch */
};
/*
** Write a 6-byte millisecond timestamp into aOut[]
*/
static void tmstmpPutTS(TmstmpFile *p, unsigned char *aOut){
sqlite3_uint64 tm = 0;
p->pSubVfs->xCurrentTimeInt64(p->pSubVfs, (sqlite3_int64*)&tm);
tm -= 210866760000000LL;
aOut[0] = (tm>>40)&0xff;
aOut[1] = (tm>>32)&0xff;
aOut[2] = (tm>>24)&0xff;
aOut[3] = (tm>>16)&0xff;
aOut[4] = (tm>>8)&0xff;
aOut[5] = tm&0xff;
}
/*
** Read a 32-bit big-endian unsigned integer and return it.
*/
static u32 tmstmpGetU32(const unsigned char *a){
return (a[0]<<24) + (a[1]<<16) + (a[2]<<8) + a[3];
}
/* Write a 32-bit integer as big-ending into a[]
*/
static void tmstmpPutU32(u32 v, unsigned char *a){
a[0] = (v>>24) & 0xff;
a[1] = (v>>16) & 0xff;
a[2] = (v>>8) & 0xff;
a[3] = v & 0xff;
}
/* Free a TmstmpLog object */
static void tmstmpLogFree(TmstmpLog *pLog){
if( pLog==0 ) return;
sqlite3_free(pLog->zLogname);
sqlite3_free(pLog);
}
/* Flush log content. Open the file if necessary. Return the
** number of errors. */
static int tmstmpLogFlush(TmstmpFile *p){
TmstmpLog *pLog = p->pLog;
assert( pLog!=0 );
if( pLog->log==0 ){
pLog->log = fopen(pLog->zLogname, "wb");
if( pLog->log==0 ){
tmstmpLogFree(pLog);
p->pLog = 0;
return 1;
}
}
(void)fwrite(pLog->a, pLog->n, 1, pLog->log);
pLog->n = 0;
return 0;
}
/*
** Write a record onto the event log
*/
static void tmstmpEvent(
TmstmpFile *p,
u8 op,
u8 a1,
u32 a2,
u32 a3
){
unsigned char *a;
TmstmpLog *pLog;
if( p->isWal ){
p = p->pPartner;
assert( p!=0 );
assert( p->isDb );
}
pLog = p->pLog;
if( pLog==0 ) return;
if( pLog->n >= (int)sizeof(pLog->a) ){
if( tmstmpLogFlush(p) ) return;
}
a = pLog->a + pLog->n;
a[0] = op;
a[1] = a1;
tmstmpPutTS(p, a+2);
tmstmpPutU32(a2, a+8);
tmstmpPutU32(a3, a+12);
pLog->n += 16;
if( pLog->log || (op>=ELOG_WAL_PAGE && op<=ELOG_WAL_RESET) ){
(void)tmstmpLogFlush(p);
}
}
/*
** Close a connection
*/
static int tmstmpClose(sqlite3_file *pFile){
TmstmpFile *p = (TmstmpFile *)pFile;
if( p->hasCorrectReserve ){
tmstmpEvent(p, p->isDb ? ELOG_CLOSE_DB : ELOG_CLOSE_WAL, 0, 0, 0);
}
tmstmpLogFree(p->pLog);
if( p->pPartner ){
assert( p->pPartner->pPartner==p );
p->pPartner->pPartner = 0;
p->pPartner = 0;
}
pFile = ORIGFILE(pFile);
return pFile->pMethods->xClose(pFile);
}
/*
** Read bytes from a file
*/
static int tmstmpRead(
sqlite3_file *pFile,
void *zBuf,
int iAmt,
sqlite_int64 iOfst
){
int rc;
TmstmpFile *p = (TmstmpFile*)pFile;
pFile = ORIGFILE(pFile);
rc = pFile->pMethods->xRead(pFile, zBuf, iAmt, iOfst);
if( rc!=SQLITE_OK ) return rc;
if( p->isDb
&& iOfst==0
&& iAmt>=100
){
const unsigned char *a = (unsigned char*)zBuf;
p->hasCorrectReserve = (a[20]==TMSTMP_RESERVE);
p->pgsz = (a[16]<<8) + a[17];
if( p->pgsz==1 ) p->pgsz = 65536;
if( p->pPartner ){
p->pPartner->hasCorrectReserve = p->hasCorrectReserve;
p->pPartner->pgsz = p->pgsz;
}
}
return rc;
}
/*
** Write data to a tmstmp-file.
*/
static int tmstmpWrite(
sqlite3_file *pFile,
const void *zBuf,
int iAmt,
sqlite_int64 iOfst
){
TmstmpFile *p = (TmstmpFile*)pFile;
sqlite3_file *pSub = ORIGFILE(pFile);
if( !p->hasCorrectReserve ){
/* The database does not have the correct reserve size. No-op */
}else if( p->isWal ){
/* Writing into a WAL file */
if( iAmt==24 ){
/* A frame header */
u32 x = 0;
p->iFrame = (iOfst - 32)/(p->pgsz+24);
p->pgno = tmstmpGetU32((const u8*)zBuf);
p->salt1 = tmstmpGetU32(((const u8*)zBuf)+8);
memcpy(&x, ((const u8*)zBuf)+4, 4);
p->isCommit = (x!=0);
p->iOfst = iOfst;
}else if( iAmt>=512 && iOfst==p->iOfst+24 ){
unsigned char *s = (unsigned char*)zBuf+iAmt-TMSTMP_RESERVE;
memset(s, 0, TMSTMP_RESERVE);
tmstmpPutTS(p, s+2);
tmstmpPutU32(p->iFrame, s+8);
tmstmpPutU32(p->salt1, s+12);
s[12] = p->isCommit ? 1 : 0;
tmstmpEvent(p, ELOG_WAL_PAGE, s[12], p->pgno, p->iFrame);
}else if( iAmt==32 && iOfst==0 ){
u32 salt1 = tmstmpGetU32(((const u8*)zBuf)+16);
tmstmpEvent(p, ELOG_WAL_RESET, 0, 0, salt1);
}
}else if( p->inCkpt ){
assert( p->pgsz>0 );
tmstmpEvent(p, ELOG_CKPT_PAGE, 0, (iOfst/p->pgsz)+1, 0);
}else if( p->pPartner==0 ){
/* Writing into a database in rollback mode */
unsigned char *s = (unsigned char*)zBuf+iAmt-TMSTMP_RESERVE;
memset(s, 0, TMSTMP_RESERVE);
tmstmpPutTS(p, s+2);
s[12] = 2;
assert( p->pgsz>0 );
tmstmpEvent(p, ELOG_DB_PAGE, 0, (u32)(iOfst/p->pgsz), 0);
}
return pSub->pMethods->xWrite(pSub,zBuf,iAmt,iOfst);
}
/*
** Truncate a tmstmp-file.
*/
static int tmstmpTruncate(sqlite3_file *pFile, sqlite_int64 size){
pFile = ORIGFILE(pFile);
return pFile->pMethods->xTruncate(pFile, size);
}
/*
** Sync a tmstmp-file.
*/
static int tmstmpSync(sqlite3_file *pFile, int flags){
pFile = ORIGFILE(pFile);
return pFile->pMethods->xSync(pFile, flags);
}
/*
** Return the current file-size of a tmstmp-file.
*/
static int tmstmpFileSize(sqlite3_file *pFile, sqlite_int64 *pSize){
TmstmpFile *p = (TmstmpFile *)pFile;
pFile = ORIGFILE(p);
return pFile->pMethods->xFileSize(pFile, pSize);
}
/*
** Lock a tmstmp-file.
*/
static int tmstmpLock(sqlite3_file *pFile, int eLock){
pFile = ORIGFILE(pFile);
return pFile->pMethods->xLock(pFile, eLock);
}
/*
** Unlock a tmstmp-file.
*/
static int tmstmpUnlock(sqlite3_file *pFile, int eLock){
pFile = ORIGFILE(pFile);
return pFile->pMethods->xUnlock(pFile, eLock);
}
/*
** Check if another file-handle holds a RESERVED lock on a tmstmp-file.
*/
static int tmstmpCheckReservedLock(sqlite3_file *pFile, int *pResOut){
pFile = ORIGFILE(pFile);
return pFile->pMethods->xCheckReservedLock(pFile, pResOut);
}
/*
** File control method. For custom operations on a tmstmp-file.
*/
static int tmstmpFileControl(sqlite3_file *pFile, int op, void *pArg){
int rc;
TmstmpFile *p = (TmstmpFile*)pFile;
pFile = ORIGFILE(pFile);
rc = pFile->pMethods->xFileControl(pFile, op, pArg);
switch( op ){
case SQLITE_FCNTL_VFSNAME: {
if( p->hasCorrectReserve && rc==SQLITE_OK ){
*(char**)pArg = sqlite3_mprintf("tmstmp/%z", *(char**)pArg);
}
break;
}
case SQLITE_FCNTL_CKPT_START: {
p->inCkpt = 1;
assert( p->isDb );
assert( p->pPartner!=0 );
p->pPartner->inCkpt = 1;
if( p->hasCorrectReserve ){
tmstmpEvent(p, ELOG_CKPT_START, 0, 0, 0);
}
rc = SQLITE_OK;
break;
}
case SQLITE_FCNTL_CKPT_DONE: {
p->inCkpt = 0;
assert( p->isDb );
assert( p->pPartner!=0 );
p->pPartner->inCkpt = 0;
if( p->hasCorrectReserve ){
tmstmpEvent(p, ELOG_CKPT_DONE, 0, 0, 0);
}
rc = SQLITE_OK;
break;
}
}
return rc;
}
/*
** Return the sector-size in bytes for a tmstmp-file.
*/
static int tmstmpSectorSize(sqlite3_file *pFile){
pFile = ORIGFILE(pFile);
return pFile->pMethods->xSectorSize(pFile);
}
/*
** Return the device characteristic flags supported by a tmstmp-file.
*/
static int tmstmpDeviceCharacteristics(sqlite3_file *pFile){
int devchar = 0;
pFile = ORIGFILE(pFile);
devchar = pFile->pMethods->xDeviceCharacteristics(pFile);
return (devchar & ~SQLITE_IOCAP_SUBPAGE_READ);
}
/* Create a shared memory file mapping */
static int tmstmpShmMap(
sqlite3_file *pFile,
int iPg,
int pgsz,
int bExtend,
void volatile **pp
){
pFile = ORIGFILE(pFile);
return pFile->pMethods->xShmMap(pFile,iPg,pgsz,bExtend,pp);
}
/* Perform locking on a shared-memory segment */
static int tmstmpShmLock(sqlite3_file *pFile, int offset, int n, int flags){
pFile = ORIGFILE(pFile);
return pFile->pMethods->xShmLock(pFile,offset,n,flags);
}
/* Memory barrier operation on shared memory */
static void tmstmpShmBarrier(sqlite3_file *pFile){
pFile = ORIGFILE(pFile);
pFile->pMethods->xShmBarrier(pFile);
}
/* Unmap a shared memory segment */
static int tmstmpShmUnmap(sqlite3_file *pFile, int deleteFlag){
pFile = ORIGFILE(pFile);
return pFile->pMethods->xShmUnmap(pFile,deleteFlag);
}
/* Fetch a page of a memory-mapped file */
static int tmstmpFetch(
sqlite3_file *pFile,
sqlite3_int64 iOfst,
int iAmt,
void **pp
){
pFile = ORIGFILE(pFile);
return pFile->pMethods->xFetch(pFile, iOfst, iAmt, pp);
}
/* Release a memory-mapped page */
static int tmstmpUnfetch(sqlite3_file *pFile, sqlite3_int64 iOfst, void *pPage){
pFile = ORIGFILE(pFile);
return pFile->pMethods->xUnfetch(pFile, iOfst, pPage);
}
/*
** Open a tmstmp file handle.
*/
static int tmstmpOpen(
sqlite3_vfs *pVfs,
const char *zName,
sqlite3_file *pFile,
int flags,
int *pOutFlags
){
TmstmpFile *p, *pDb;
sqlite3_file *pSubFile;
sqlite3_vfs *pSubVfs;
int rc;
pSubVfs = ORIGVFS(pVfs);
if( (flags & (SQLITE_OPEN_MAIN_DB|SQLITE_OPEN_WAL))==0 ){
/* If the file is not a persistent database or a WAL file, then
** bypass the timestamp logic all together */
return pSubVfs->xOpen(pSubVfs, zName, pFile, flags, pOutFlags);
}
if( (flags & SQLITE_OPEN_WAL)!=0 ){
pDb = (TmstmpFile*)sqlite3_database_file_object(zName);
if( pDb==0
|| pDb->uMagic!=TMSTMP_MAGIC
|| !pDb->isDb
|| pDb->pPartner!=0
){
return pSubVfs->xOpen(pSubVfs, zName, pFile, flags, pOutFlags);
}
}else{
pDb = 0;
}
p = (TmstmpFile*)pFile;
memset(p, 0, sizeof(*p));
pSubFile = ORIGFILE(pFile);
pFile->pMethods = &tmstmp_io_methods;
p->pSubVfs = pSubVfs;
p->uMagic = TMSTMP_MAGIC;
rc = pSubVfs->xOpen(pSubVfs, zName, pSubFile, flags, pOutFlags);
if( rc ) goto tmstmp_open_done;
if( pDb!=0 ){
p->isWal = 1;
p->pPartner = pDb;
pDb->pPartner = p;
}else{
sqlite3_uint64 r1;
u32 r2;
u32 pid;
TmstmpLog *pLog;
p->isDb = 1;
r1 = 0;
pLog = sqlite3_malloc64( sizeof(TmstmpLog) );
if( pLog==0 ){
return SQLITE_NOMEM;
}
memset(pLog, 0, sizeof(pLog[0]));
p->pLog = pLog;
p->pSubVfs->xCurrentTimeInt64(p->pSubVfs, (sqlite3_int64*)&r1);
r1 -= 210866760000000LL;
sqlite3_randomness(sizeof(r2), &r2);
pid = GETPID;
pLog->zLogname = sqlite3_mprintf("%s-tmstmp/%llx-%08x-%08x",
zName, r1, pid, r2);
}
tmstmpEvent(p, p->isWal ? ELOG_OPEN_WAL : ELOG_OPEN_DB, 0, GETPID, 0);
tmstmp_open_done:
if( rc ) pFile->pMethods = 0;
return rc;
}
/*
** All VFS interfaces other than xOpen are passed down into the Sub-VFS.
*/
static int tmstmpDelete(sqlite3_vfs *p, const char *zName, int syncDir){
sqlite3_vfs *pSub = ORIGVFS(p);
return pSub->xDelete(pSub,zName,syncDir);
}
static int tmstmpAccess(sqlite3_vfs *p, const char *zName, int flags, int *pR){
sqlite3_vfs *pSub = ORIGVFS(p);
return pSub->xAccess(pSub,zName,flags,pR);
}
static int tmstmpFullPathname(sqlite3_vfs*p,const char *zName,int n,char *zOut){
sqlite3_vfs *pSub = ORIGVFS(p);
return pSub->xFullPathname(pSub,zName,n,zOut);
}
static void *tmstmpDlOpen(sqlite3_vfs *p, const char *zFilename){
sqlite3_vfs *pSub = ORIGVFS(p);
return pSub->xDlOpen(pSub,zFilename);
}
static void tmstmpDlError(sqlite3_vfs *p, int nByte, char *zErrMsg){
sqlite3_vfs *pSub = ORIGVFS(p);
return pSub->xDlError(pSub,nByte,zErrMsg);
}
static void(*tmstmpDlSym(sqlite3_vfs *p, void *pDl, const char *zSym))(void){
sqlite3_vfs *pSub = ORIGVFS(p);
return pSub->xDlSym(pSub,pDl,zSym);
}
static void tmstmpDlClose(sqlite3_vfs *p, void *pDl){
sqlite3_vfs *pSub = ORIGVFS(p);
return pSub->xDlClose(pSub,pDl);
}
static int tmstmpRandomness(sqlite3_vfs *p, int nByte, char *zOut){
sqlite3_vfs *pSub = ORIGVFS(p);
return pSub->xRandomness(pSub,nByte,zOut);
}
static int tmstmpSleep(sqlite3_vfs *p, int microseconds){
sqlite3_vfs *pSub = ORIGVFS(p);
return pSub->xSleep(pSub,microseconds);
}
static int tmstmpCurrentTime(sqlite3_vfs *p, double *prNow){
sqlite3_vfs *pSub = ORIGVFS(p);
return pSub->xCurrentTime(pSub,prNow);
}
static int tmstmpGetLastError(sqlite3_vfs *p, int a, char *b){
sqlite3_vfs *pSub = ORIGVFS(p);
return pSub->xGetLastError(pSub,a,b);
}
static int tmstmpCurrentTimeInt64(sqlite3_vfs *p, sqlite3_int64 *piNow){
sqlite3_vfs *pSub = ORIGVFS(p);
return pSub->xCurrentTimeInt64(pSub,piNow);
}
static int tmstmpSetSystemCall(sqlite3_vfs *p, const char *zName,
sqlite3_syscall_ptr x){
sqlite3_vfs *pSub = ORIGVFS(p);
return pSub->xSetSystemCall(pSub,zName,x);
}
static sqlite3_syscall_ptr tmstmpGetSystemCall(sqlite3_vfs *p, const char *z){
sqlite3_vfs *pSub = ORIGVFS(p);
return pSub->xGetSystemCall(pSub,z);
}
static const char *tmstmpNextSystemCall(sqlite3_vfs *p, const char *zName){
sqlite3_vfs *pSub = ORIGVFS(p);
return pSub->xNextSystemCall(pSub,zName);
}
/*
** Register the tmstmp VFS as the default VFS for the system.
*/
static int tmstmpRegisterVfs(void){
int rc = SQLITE_OK;
sqlite3_vfs *pOrig = sqlite3_vfs_find(0);
if( pOrig==0 ) return SQLITE_ERROR;
if( pOrig==&tmstmp_vfs ) return SQLITE_OK;
tmstmp_vfs.iVersion = pOrig->iVersion;
tmstmp_vfs.pAppData = pOrig;
tmstmp_vfs.szOsFile = pOrig->szOsFile + sizeof(TmstmpFile);
rc = sqlite3_vfs_register(&tmstmp_vfs, 1);
return rc;
}
#if defined(SQLITE_TMSTMPVFS_STATIC)
/* This variant of the initializer runs when the extension is
** statically linked.
*/
int sqlite3_register_tmstmpvfs(const char *NotUsed){
(void)NotUsed;
return tmstmpRegisterVfs();
}
int sqlite3_unregister_tmstmpvfs(void){
if( sqlite3_vfs_find("tmstmpvfs") ){
sqlite3_vfs_unregister(&tmstmp_vfs);
}
return SQLITE_OK;
}
#endif /* defined(SQLITE_TMSTMPVFS_STATIC */
#if !defined(SQLITE_TMSTMPVFS_STATIC)
/* This variant of the initializer function is used when the
** extension is shared library to be loaded at run-time.
*/
#ifdef _WIN32
__declspec(dllexport)
#endif
/*
** This routine is called by sqlite3_load_extension() when the
** extension is first loaded.
***/
int sqlite3_tmstmpvfs_init(
sqlite3 *db,
char **pzErrMsg,
const sqlite3_api_routines *pApi
){
int rc;
SQLITE_EXTENSION_INIT2(pApi);
(void)pzErrMsg; /* not used */
(void)db; /* not used */
rc = tmstmpRegisterVfs();
if( rc==SQLITE_OK ) rc = SQLITE_OK_LOAD_PERMANENTLY;
return rc;
}
#endif /* !defined(SQLITE_TMSTMPVFS_STATIC) */

View File

@@ -118,7 +118,7 @@
argv.push("--vfs", vfs);
log2('',"Using VFS:",vfs);
if('kvvfs' === vfs){
//forceSize = 10
forceSize = 5
/* --size > 2 is too big for local/session storage
as of mid-2025, but kvvfs v2 (late 2025)
lets us use transient storage. */;
@@ -135,7 +135,7 @@
'size'
].forEach(function(k){
const v = urlParams.get(k);
if(v) argv.push('--'+k, urlParams[k]);
if(v && +v) argv.push('--'+k, urlParams[k]);
});
}
argv.push(

View File

@@ -12,7 +12,7 @@
</head>
<body>
<h1 id='color-target'>sqlite3 tester #1: Worker thread (@bitness@-bit WASM)</h1>
<div>Variants (32-bit):
<div>Variants:
conventional UI thread:
(<a href='tester1.html' target='tester1.html'>32-bit</a>,
<a href='tester1-64bit.html' target='tester1-64bit.html'>64-bit</a>),

View File

@@ -2018,6 +2018,10 @@ showshm$(T.exe): $(TOP)/tool/showshm.c
$(T.link) -o $@ $(TOP)/tool/showshm.c $(LDFLAGS.configure)
xbin: showshm$(T.exe)
showtmlog$(T.exe): $(TOP)/tool/showtmlog.c
$(T.link) -o $@ $(TOP)/tool/showtmlog.c $(LDFLAGS.configure)
xbin: showshm$(T.exe)
index_usage$(T.exe): $(TOP)/tool/index_usage.c sqlite3.o
$(T.link) $(SHELL_OPT) -o $@ $(TOP)/tool/index_usage.c sqlite3.o \
$(LDFLAGS.libsqlite3)

View File

@@ -1,12 +1,12 @@
C Enhance\sthe\sVACUUM\sINTO\scommand\ssuch\sthat\sif\sthe\starget\sfile\sis\sa\sURI\nwith\sa\sreserve=N\squery\sparameter,\sthen\sthe\sreserve_bytes\svalue\sis\sset\sto\nN\sprovided\sthat\sN\sis\swithin\sthe\sallowed\srange.\s\sThis\scan\seven\sreduce\sthe\nsize\sof\sthe\sreserve,\sall\sthe\sway\sto\szero.\s\sThis\sprovides\sa\sway\sto\sremove\nreserve\sfrom\sa\sdatabase\swithout\shaving\sto\sdo\sa\scomplete\sdump/restore.
D 2026-01-07T21:19:42.469
C Merge\sall\sthe\slatest\strunk\schanges\sinto\sthe\sreset-reserve\sbranch.
D 2026-01-08T18:14:29.822
F .fossil-settings/binary-glob 61195414528fb3ea9693577e1980230d78a1f8b0a54c78cf1b9b24d0a409ed6a x
F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1
F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea
F LICENSE.md 6bc480fc673fb4acbc4094e77edb326267dd460162d7723c7f30bee2d3d9e97d
F Makefile.in 3ce07126d7e87c7464301482e161fdae6a51d0a2aa06b200b8f0000ef4d6163b
F Makefile.linux-generic bd3e3cacd369821a6241d4ea1967395c962dfe3057e38cb0a435cee0e8b789d0
F Makefile.msc 48c5be34727fd835c675eb9a6ba21900d66088fcec81210cb2e134927aa0fa4f
F Makefile.msc 1fa179beafd6d438b8479146cae77fb1724dd35b330b09dbfebd8a2f0823c62a
F README.md 3fa51fc7ababc32edd175ae8b2986c86d5ea120c1cb1e57c7f7849492d1405ec
F VERSION 74672bfd4c7826c0fc6f84762488a707c52e7d2d94af42ccb0edcc6c74311c41
F art/icon-243x273.gif 9750b734f82fdb3dc43127753d5e6fbf3b62c9f4e136c2fbf573b2f57ea87af5
@@ -402,6 +402,7 @@ F ext/misc/sqlite3_stdio.h 27a4ecea47e61bc9574ccdf2806f468afe23af2f95028c9b689bf
F ext/misc/stmt.c b090086cd6bd6281c21271d38d576eeffe662f0e6b67536352ce32bbaa438321
F ext/misc/stmtrand.c 59cffa5d8e158943ff1ce078956d8e208e8c04e67307e8f249dece2436dcb7fc
F ext/misc/templatevtab.c 10f15b165b95423ddef593bc5dcb915ec4eb5e0f1066d585e5435a368b8bc22b
F ext/misc/tmstmpvfs.c 95994e33526581b75bebb320628d220d86c0734807d9780b4f1804e6c7248df2
F ext/misc/totype.c ba11aac3c0b52c685bd25aa4e0f80c41c624fb1cc5ab763250e09ddc762bc3a8
F ext/misc/uint.c 327afc166058acf566f33a15bf47c869d2d3564612644d9ff81a23efc8b36039
F ext/misc/unionvtab.c 716d385256d5fb4beea31b0efede640807e423e85c9784d21d22f0cce010a785
@@ -640,13 +641,13 @@ F ext/wasm/speedtest1-wasmfs.html 0e9d335a9b5b5fafe6e1bc8dc0f0ca7e22e6eb916682a2
F ext/wasm/speedtest1-wasmfs.mjs 60dd5842f6d2a70a6d0bef12633a11491bde6984aff75a37c2040980d8cbf36a
F ext/wasm/speedtest1-worker.html 068d4190f304fa1c34e6501a1b3a4c32fe8d8dac93c2d0f53d667a1cb386eedc
F ext/wasm/speedtest1-worker.js 8acad67bfd6aeeb799bd5ae007ea32af85a082a287d8877c5a10adf4bd7efd89
F ext/wasm/speedtest1.html 856ddb26af6f21c228614614ccff73abc1f8bd877447c1c8da15cfabba5615c2
F ext/wasm/speedtest1.html f32c66997eb0b036c4546e6302cd0673157912661df0b290ab65816f713feac6
F ext/wasm/split-speedtest1-script.sh a3e271938d4d14ee49105eb05567c6a69ba4c1f1293583ad5af0cd3a3779e205 x
F ext/wasm/sql/000-mandelbrot.sql 775337a4b80938ac8146aedf88808282f04d02d983d82675bd63d9c2d97a15f0
F ext/wasm/sql/001-sudoku.sql 35b7cb7239ba5d5f193bc05ec379bcf66891bce6f2a5b3879f2f78d0917299b5
F ext/wasm/test-opfs-vfs.html 1f2d672f3f3fce810dfd48a8d56914aba22e45c6834e262555e685bce3da8c3f
F ext/wasm/test-opfs-vfs.js 1618670e466f424aa289859fe0ec8ded223e42e9e69b5c851f809baaaca1a00c
F ext/wasm/tester1-worker.c-pp.html 0e432ec2c0d99cd470484337066e8d27e7aee4641d97115338f7d962bf7b081a
F ext/wasm/tester1-worker.c-pp.html d0032241d0b24d996cf1c4dd0dde364189693af9b5c986e48af7d3d720fcd244
F ext/wasm/tester1.c-pp.html 52d88fe2c6f21a046030a36410b4839b632f4424028197a45a3d5669ea724ddb
F ext/wasm/tester1.c-pp.js 7bc90f6e3d133c735fad05d5409915bd1389f4b5d6ce7c5daca33856669e706b
F ext/wasm/tests/opfs/concurrency/index.html 657578a6e9ce1e9b8be951549ed93a6a471f4520a99e5b545928668f4285fb5e
@@ -658,7 +659,7 @@ F ext/wasm/tests/opfs/sahpool/index.html be736567fd92d3ecb9754c145755037cbbd2bca
F ext/wasm/tests/opfs/sahpool/sahpool-pausing.js f264925cfc82155de38cecb3d204c36e0f6991460fff0cb7c15079454679a4e2
F ext/wasm/tests/opfs/sahpool/sahpool-worker.js bd25a43fc2ab2d1bafd8f2854ad3943ef673f7c3be03e95ecf1612ff6e8e2a61
F magic.txt 5ade0bc977aa135e79e3faaea894d5671b26107cc91e70783aa7dc83f22f3ba0
F main.mk ba9ac943f81c80cd2d7b502759e60d23a988be74cc7b4b1575419932fe2afcb0
F main.mk 9393d5982db60f26e72c5af24a8c11cf39374ff5e695fadb5a4e7376f28150c6
F make.bat a136fd0b1c93e89854a86d5f4edcf0386d211e5d5ec2434480f6eea436c7420c
F mptest/config01.test 3c6adcbc50b991866855f1977ff172eb6d901271
F mptest/config02.test 4415dfe36c48785f751e16e32c20b077c28ae504
@@ -737,7 +738,7 @@ F src/random.c 606b00941a1d7dd09c381d3279a058d771f406c5213c9932bbd93d5587be4b9c
F src/resolve.c 47aa7fdc9ec4c19b103ac5e79d7887d30119b5675309facf5eed1118391c868b
F src/rowset.c 8432130e6c344b3401a8874c3cb49fefe6873fec593294de077afea2dce5ec97
F src/select.c 4d45a04431db072040d6625ee21c1dc483c9b2b64a5ab419f4a4e05aabed1204
F src/shell.c.in 587ec295ced5204e7a172cd090a470776ca58c2875ee7cc18194f18fa0d3ff2a
F src/shell.c.in 3bc4c6aa962bdf950dc144c2138eb4bc734bf7e305f2ac42dbda5a83c4a4c660
F src/sqlite.h.in 476f3efeb5dd26ad94dcbce262ca7eb9d042d797a92d624059c67ef37d5b3ab4
F src/sqlite3.rc 015537e6ac1eec6c7050e17b616c2ffe6f70fca241835a84a4f0d5937383c479
F src/sqlite3ext.h 5d5330f5f8461f5ce74960436ddcfa53ecd09c2b8b23901e22ae38aec3243998
@@ -2158,11 +2159,12 @@ F tool/pagesig.c f98909b4168d9cac11a2de7f031adea0e2f3131faa7515a72807c03ec58eafe
F tool/replace.tcl 511c61acfe563dfb58675efb4628bb158a13d48ff8322123ac447e9d25a82d9a
F tool/restore_jrnl.tcl 1079ecba47cc82fa82115b81c1f68097ab1f956f357ee8da5fc4b2589af6bd98
F tool/rollback-test.c 9fc98427d1e23e84429d7e6d07d9094fbdec65a5
F tool/showdb.c 3956d71e5193162609a60e8c9edfcf09274c00cfea2b1d221261427adb2b5cca
F tool/showdb.c db1fba9149d29bc4d9fe2826b881d66da980a5f01e0c0858244e93f18d388321
F tool/showjournal.c 5bad7ae8784a43d2b270d953060423b8bd480818
F tool/showlocks.c 9cc5e66d4ebbf2d194f39db2527ece92077e86ae627ddd233ee48e16e8142564
F tool/showshm.c a0ab6ec32dd1f11218ca2a4018f8fb875b59414801ab8ceed8b2e69b7b45a809
F tool/showstat4.c b706fcbc4cd1a6e4a73ac32549afc4b460479d650402d64b23e8d813516e8de4
F tool/showtmlog.c 2ee3cfe698f64256419b2c2beacd504f24b020d6e2a91115f872a28ede188aa1
F tool/showwal.c 11eca547980a066b081f512636151233350ac679f29ecf4ebfce7f4530230b3d
F tool/soak1.tcl a3892082ed1079671565c044e93b55c3c7f38829aedf53cc597c65d23ffdaddf
F tool/spaceanal.tcl 1f83962090a6b60e1d7bf92495d643e622bef9fe82ea3f2d22350dcbce9a12d0
@@ -2189,11 +2191,8 @@ F tool/warnings-clang.sh bbf6a1e685e534c92ec2bfba5b1745f34fb6f0bc2a362850723a9ee
F tool/warnings.sh d924598cf2f55a4ecbc2aeb055c10bd5f48114793e7ba25f9585435da29e7e98
F tool/win/sqlite.vsix deb315d026cc8400325c5863eef847784a219a2f
F tool/winmain.c 00c8fb88e365c9017db14c73d3c78af62194d9644feaf60e220ab0f411f3604c
P ebedeb6169cc3bc3708130d4061065af600a8851e896fc3eeaf33c8c83fe3b95
R e7bd6852978f7187c19f7af8715f2c9b
T *branch * reset-reserve
T *sym-reset-reserve *
T -sym-trunk *
P 9cec3b67d95c145c968c560e8b3d2c33896b4b5b6c86c61350263764bb616c43 6fafd010de2d9820f77ee6d2a5f0a7ab1260872d09e95b6932c959ef017aaa77
R 9d283fb18177ce4fdf0022b4cfb348c6
U drh
Z 61fb77209c8b97e80b941b8f996389a6
Z b61dc34a2e46333735fdcd406d74e690
# Remove this line to create a well-formed Fossil manifest.

View File

@@ -1 +1 @@
9cec3b67d95c145c968c560e8b3d2c33896b4b5b6c86c61350263764bb616c43
6e5f1fbaec775db5c5e90b29cfa0820c9f4d560b36c063b504ef55a6c3a65bdc

View File

@@ -39,6 +39,10 @@ typedef unsigned short int u16;
** and this build mode rewires the user input subsystem to account for
** that.
*/
#if defined(SQLITE_SHELL_FIDDLE)
# undef SQLITE_OMIT_LOAD_EXTENSION
# define SQLITE_OMIT_LOAD_EXTENSION 1
#endif
/*
** Warning pragmas copied from msvc.h in the core.
@@ -10411,13 +10415,15 @@ static int do_meta_command(const char *zLine, ShellState *p){
|| sqlite3_strlike(zName,"sqlite_temp_schema", '\\')==0;
if( isSchema ){
cli_printf(p->out,
"CREATE TABLE %s (\n"
"CREATE TABLE %ssqlite_schema (\n"
" type text,\n"
" name text,\n"
" tbl_name text,\n"
" rootpage integer,\n"
" sql text\n"
");\n", zName);
");\n",
sqlite3_strlike("sqlite_t%",zName,0)==0 ? "temp." : ""
);
}
}
rc = sqlite3_prepare_v2(p->db, "PRAGMA database_list", -1, &pStmt, 0);
@@ -10477,7 +10483,7 @@ static int do_meta_command(const char *zLine, ShellState *p){
}
}
if( bNoSystemTabs ){
sqlite3_str_appendf(pSql, " name NOT LIKE 'sqlite__%%' ESCALE '_' AND ");
sqlite3_str_appendf(pSql, " name NOT LIKE 'sqlite__%%' ESCAPE '_' AND ");
}
sqlite3_str_appendf(pSql, "sql IS NOT NULL ORDER BY snum, rowid");
if( bDebug ){
@@ -13021,7 +13027,26 @@ int SQLITE_CDECL main(int argc, char **argv){
sqlite3_vfs *pVfs = sqlite3_vfs_find(zVfs);
if( pVfs ){
sqlite3_vfs_register(pVfs, 1);
}else{
}
#if !defined(SQLITE_OMIT_LOAD_EXTENSION)
else if( access(zVfs,0)==0 ){
/* If the VFS name is not the name of an existing VFS, but it is
** the name of a file, then try to load that file as an extension.
** Presumably the extension implements the desired VFS. */
sqlite3 *db = 0;
char *zErr = 0;
sqlite3_open(":memory:", &db);
sqlite3_enable_load_extension(db, 1);
rc = sqlite3_load_extension(db, zVfs, 0, &zErr);
sqlite3_close(db);
if( (rc&0xff)!=SQLITE_OK ){
cli_printf(stderr, "could not load extension VFS \"%s\": %s\n",
zVfs, zErr);
exit(1);
}
}
#endif
else{
cli_printf(stderr,"no such VFS: \"%s\"\n", zVfs);
exit(1);
}

View File

@@ -8,6 +8,7 @@
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdint.h>
#if !defined(_MSC_VER)
#include <unistd.h>
@@ -28,13 +29,21 @@ typedef sqlite3_uint64 u64; /* unsigned 64-bit */
static struct GlobalData {
i64 pagesize; /* Size of a database page */
i64 usablesize; /* pagesize-nRes */
int dbfd; /* File descriptor for reading the DB */
u32 mxPage; /* Last page number */
u32 nRes; /* Amount of reserve space */
int perLine; /* HEX elements to print per line */
int bRaw; /* True to access db file via OS APIs */
int bCSV; /* CSV output for "pgidx" */
int bTmstmp; /* Interpret tmstmpvfs tags on "pgidx" */
sqlite3_file *pFd; /* File descriptor for non-raw mode */
sqlite3 *pDb; /* Database handle that owns pFd */
} g = {1024, -1, 0, 16, 0, 0, 0};
char **zPageUse; /* Use for each page */
struct TmstmpTag {
unsigned char a[16]; /* tmstmpvfs tag for each page */
} *aPageTag;
} g = {4096, 4096, -1, 0, 0, 16, 0, 0, 0, 0, 0, 0, 0};
/*
** Convert the var-int format into i64. Return the number of bytes
@@ -142,11 +151,12 @@ static void fileClose(){
static unsigned char *fileRead(sqlite3_int64 ofst, int nByte){
unsigned char *aData;
int got;
int rc;
aData = sqlite3_malloc64(32+(i64)nByte);
if( aData==0 ) out_of_memory();
memset(aData, 0, nByte+32);
if( g.bRaw==0 ){
int rc = g.pFd->pMethods->xRead(g.pFd, (void*)aData, nByte, ofst);
rc = g.pFd->pMethods->xRead(g.pFd, (void*)aData, nByte, ofst);
if( rc!=SQLITE_OK && rc!=SQLITE_IOERR_SHORT_READ ){
fprintf(stderr, "error in xRead() - %d\n", rc);
exit(1);
@@ -154,7 +164,21 @@ static unsigned char *fileRead(sqlite3_int64 ofst, int nByte){
}else{
lseek(g.dbfd, (long)ofst, SEEK_SET);
got = read(g.dbfd, aData, nByte);
if( got>0 && got<nByte ) memset(aData+got, 0, nByte-got);
if( got==nByte ){
rc = SQLITE_OK;
}else if( got>0 && got<nByte ){
memset(aData+got, 0, nByte-got);
rc = SQLITE_IOERR_SHORT_READ;
}else{
memset(aData,0,nByte);
rc = SQLITE_IOERR;
}
}
if( g.aPageTag && nByte==(int)g.pagesize ){
unsigned int pgno = (unsigned int)(ofst/g.pagesize) + 1;
if( pgno>0 && pgno<=g.mxPage ){
memcpy(g.aPageTag[pgno].a, &aData[nByte-16], 16);
}
}
return aData;
}
@@ -382,14 +406,14 @@ static i64 localPayload(i64 nPayload, char cType){
i64 nLocal;
if( cType==13 ){
/* Table leaf */
maxLocal = g.pagesize-35;
minLocal = (g.pagesize-12)*32/255-23;
maxLocal = g.usablesize-35;
minLocal = (g.usablesize-12)*32/255-23;
}else{
maxLocal = (g.pagesize-12)*64/255-23;
minLocal = (g.pagesize-12)*32/255-23;
maxLocal = (g.usablesize-12)*64/255-23;
minLocal = (g.usablesize-12)*32/255-23;
}
if( nPayload>maxLocal ){
surplus = minLocal + (nPayload-minLocal)%(g.pagesize-4);
surplus = minLocal + (nPayload-minLocal)%(g.usablesize-4);
if( surplus<=maxLocal ){
nLocal = surplus;
}else{
@@ -751,7 +775,7 @@ static void decode_trunk_page(
print_decode_line(a, 4, 4, "Number of entries on this page");
if( detail ){
n = decodeInt32(&a[4]);
for(i=0; i<n && i<g.pagesize/4; i++){
for(i=0; i<n && i<g.usablesize/4; i++){
u32 x = decodeInt32(&a[8+4*i]);
char zIdx[13];
sprintf(zIdx, "[%d]", i);
@@ -769,11 +793,6 @@ static void decode_trunk_page(
}
}
/*
** A short text comment on the use of each page.
*/
static char **zPageUse;
/*
** Add a comment on the use of a page.
*/
@@ -790,13 +809,13 @@ static void page_usage_msg(u32 pgno, const char *zFormat, ...){
sqlite3_free(zMsg);
return;
}
if( zPageUse[pgno]!=0 ){
if( g.zPageUse[pgno]!=0 ){
printf("ERROR: page %d used multiple times:\n", pgno);
printf("ERROR: previous: %s\n", zPageUse[pgno]);
printf("ERROR: previous: %s\n", g.zPageUse[pgno]);
printf("ERROR: current: %s\n", zMsg);
sqlite3_free(zPageUse[pgno]);
sqlite3_free(g.zPageUse[pgno]);
}
zPageUse[pgno] = zMsg;
g.zPageUse[pgno] = zMsg;
}
/*
@@ -837,7 +856,7 @@ static void page_usage_cell(
while( ovfl && (cnt++)<g.mxPage ){
page_usage_msg(ovfl, "overflow %d from cell %d of page %u",
cnt, cellno, pgno);
a = fileRead((ovfl-1)*(sqlite3_int64)g.pagesize, 4);
a = fileRead((ovfl-1)*(sqlite3_int64)g.pagesize, g.pagesize);
ovfl = decodeInt32(a);
sqlite3_free(a);
}
@@ -916,12 +935,12 @@ static void page_usage_btree(
u32 ofst;
cellidx = cellstart + i*2;
if( cellidx+1 >= g.pagesize ){
if( cellidx+1 >= g.usablesize ){
printf("ERROR: page %d too many cells (%d)\n", pgno, nCell);
break;
}
ofst = a[cellidx]*256 + a[cellidx+1];
if( ofst<cellidx+2 || ofst+4>=g.pagesize ){
if( ofst<cellidx+2 || ofst+4>=g.usablesize ){
printf("ERROR: page %d cell %d out of bounds\n", pgno, i);
continue;
}
@@ -959,9 +978,9 @@ static void page_usage_freelist(u32 pgno){
a = fileRead((pgno-1)*g.pagesize, g.pagesize);
iNext = decodeInt32(a);
n = decodeInt32(a+4);
if( n>(g.pagesize - 8)/4 ){
if( n>(g.usablesize - 8)/4 ){
printf("ERROR: page %d too many freelist entries (%d)\n", pgno, n);
n = (g.pagesize - 8)/4;
n = (g.usablesize - 8)/4;
}
for(i=0; i<n; i++){
int child = decodeInt32(a + (i*4+8));
@@ -990,6 +1009,63 @@ static void page_usage_ptrmap(u8 *a){
}
}
/*
** The six bytes at a[] are a big-endian unsigned integer which is the
** number of milliseconds since 1970. Decode that value into an ISO 8601
** date/time string stored in static space and return a pointer to that
** string.
*/
static const char *decodeTimestamp(const unsigned char *a){
uint64_t ms; /* Milliseconds since 1970 */
uint64_t days; /* Days since 1970-01-01 */
uint64_t sod; /* Start of date specified by ms */
uint64_t z; /* Days since 0000-03-01 */
uint64_t era; /* 400-year era */
int i; /* Loop counter */
int h; /* hour */
int m; /* minute */
int s; /* second */
int f; /* millisecond */
int Y; /* year */
int M; /* month */
int D; /* day */
int y; /* year assuming March is first month */
unsigned int doe; /* day of 400-year era */
unsigned int yoe; /* year of 400-year era */
unsigned int doy; /* day of year */
unsigned int mp; /* month with March==0 */
static char zOut[50]; /* Return results here */
for(ms=0, i=0; i<=5; i++) ms = (ms<<8) + a[i];
if( ms==0 ){
return " ";
}else if( ms>4102444800000LL ){ /* 2100-01-01 */
/* YYYY-MM-DD HH:MM:SS.SSS */
return " (bad date) ";
}
days = ms/86400000;
sod = (ms%86400000)/1000;
f = (int)(ms%1000);
h = sod/3600;
m = (sod%3600)/60;
s = sod%60;
z = days + 719468;
era = z/147097;
doe = (unsigned)(z - era*146097);
yoe = (doe - doe/1460 + doe/36524 - doe/146096)/365;
y = (int)yoe + era*400;
doy = doe - (365*yoe + yoe/4 - yoe/100);
mp = (5*doy + 2)/153;
D = doy - (153*mp + 2)/5 + 1;
M = mp + (mp<10 ? 3 : -9);
Y = y + (M <=2);
snprintf(zOut, sizeof(zOut),
"%04d-%02d-%02d %02d:%02d:%02d.%03d",
Y, M, D, h, m, s, f);
return zOut;
}
/*
** Try to figure out how every page in the database file is being used.
*/
@@ -1010,14 +1086,22 @@ static void page_usage_report(const char *zPrg, const char *zDbName){
/* Open the database file */
db = openDatabase(zPrg, zDbName);
/* Set up global variables zPageUse[] and g.mxPage to record page
/* Set up global variables g.zPageUse[] and g.mxPage to record page
** usages */
zPageUse = sqlite3_malloc64( sizeof(zPageUse[0])*(g.mxPage+1) );
if( zPageUse==0 ) out_of_memory();
memset(zPageUse, 0, sizeof(zPageUse[0])*(g.mxPage+1));
g.zPageUse = sqlite3_malloc64( sizeof(g.zPageUse[0])*(g.mxPage+1) );
if( g.zPageUse==0 ) out_of_memory();
memset(g.zPageUse, 0, sizeof(g.zPageUse[0])*(g.mxPage+1));
/* Discover the usage of each page */
a = fileRead(0, 100);
if( g.bTmstmp && a[20]==16 ){
g.aPageTag = sqlite3_malloc64( sizeof(struct TmstmpTag)*(g.mxPage+1) );
if( g.aPageTag==0 ) out_of_memory();
memset(g.aPageTag, 0, sizeof(struct TmstmpTag)*(g.mxPage+1) );
}else{
g.bTmstmp = 0;
g.aPageTag = 0;
}
page_usage_freelist(decodeInt32(a+32));
page_usage_ptrmap(a);
sqlite3_free(a);
@@ -1042,15 +1126,68 @@ static void page_usage_report(const char *zPrg, const char *zDbName){
sqlite3_close(db);
/* Print the report and free memory used */
for(i=1; i<=g.mxPage; i++){
if( zPageUse[i]==0 ) page_usage_btree(i, -1, 0, 0);
printf("%5u: %s\n", i, zPageUse[i] ? zPageUse[i] : "???");
if( g.bCSV ){
if( g.bTmstmp ){
printf("pgno,tm,frame,flg,salt,parent,child,ovfl,txt\r\n");
}else{
printf("pgno,parent,child,ovfl,txt\r\n");
}
}
for(i=1; i<=g.mxPage; i++){
sqlite3_free(zPageUse[i]);
if( g.zPageUse[i]==0 ){
g.zPageUse[i] = sqlite3_mprintf("???");
if( g.zPageUse[i]==0 ) continue;
}
if( g.bCSV ){
const char *z = g.zPageUse[i];
const char *s;
printf("%u,", i);
if( g.bTmstmp ){
const unsigned char *a = g.aPageTag[i].a;
sqlite3_uint64 tm = 0;
unsigned int x;
int k;
for(k=2; k<=7; k++) tm = (tm<<8)+a[k];
printf("%llu.%03u,", tm/1000, (unsigned int)(tm%1000));
for(x=0, k=8; k<=11; k++) x = (x<<8)+a[k];
printf("%u,", x);
printf("%u,", a[12]);
for(x=0, k=13; k<=15; k++) x = (x<<8)+a[k];
printf("%u,", x);
}
if( (s = strstr(z, " of page "))!=0 ){
printf("%d,", atoi(s+9));
}else if( (s = strstr(z, " of trunk page "))!=0 ){
printf("%d,", atoi(s+15));
}else{
printf("0,");
}
if( (s = strstr(z, "], child "))!=0 ){
printf("%d,", atoi(s+9));
}else if( (s = strstr(z, " from cell "))!=0 ){
printf("%d,", atoi(s+12));
}else{
printf("-1,");
}
if( strncmp(z,"overflow ", 9)==0 ){
printf("%d,", atoi(z+9));
}else{
printf("-1,");
}
printf("\"%s\"\r\n", z);
}else if( g.bTmstmp ){
printf("%5u: %s %s\n", i,
decodeTimestamp(&g.aPageTag[i].a[2]),
g.zPageUse[i]);
}else{
printf("%5u: %s\n", i, g.zPageUse[i]);
}
}
sqlite3_free(zPageUse);
zPageUse = 0;
for(i=1; i<=g.mxPage; i++){
sqlite3_free(g.zPageUse[i]);
}
sqlite3_free(g.zPageUse);
g.zPageUse = 0;
}
/*
@@ -1083,7 +1220,7 @@ static void ptrmap_coverage_report(const char *zDbName){
for(pgno=2; pgno<=g.mxPage; pgno += perPage+1){
printf("%5llu: PTRMAP page covering %llu..%llu\n", pgno,
pgno+1, pgno+perPage);
a = fileRead((pgno-1)*g.pagesize, usable);
a = fileRead((pgno-1)*g.pagesize, g.pagesize);
for(i=0; i+5<=usable; i+=5){
const char *zType;
u32 iFrom = decodeInt32(&a[i+1]);
@@ -1115,7 +1252,7 @@ static void ptrmap_coverage_report(const char *zDbName){
** Check the range validity for a page number. Print an error and
** exit if the page is out of range.
*/
static void checkPageValidity(int iPage){
static void checkPageValidity(unsigned int iPage){
if( iPage<1 || iPage>g.mxPage ){
fprintf(stderr, "Invalid page number %d: valid range is 1..%d\n",
iPage, g.mxPage);
@@ -1130,7 +1267,9 @@ static void usage(const char *argv0){
fprintf(stderr, "Usage %s ?--uri? FILENAME ?args...?\n\n", argv0);
fprintf(stderr,
"switches:\n"
" --csv CSV output for \"pgidx\"\n"
" --raw Read db file directly, bypassing SQLite VFS\n"
" --tmstmp Interpret tmstmpvfs tags\n"
"args:\n"
" dbheader Show database header\n"
" pgidx Index of how each page is used\n"
@@ -1154,14 +1293,28 @@ int main(int argc, char **argv){
char **azArg = argv;
int nArg = argc;
/* Check for the "--uri" or "-uri" switch. */
if( nArg>1 ){
if( sqlite3_stricmp("-raw", azArg[1])==0
|| sqlite3_stricmp("--raw", azArg[1])==0
){
/* Check for the switches. */
while( nArg>1 && azArg[1][0]=='-' ){
const char *z = azArg[1];
if( z[1]=='-' && z[2]!=0 ) z++;
if( sqlite3_stricmp("-raw", z)==0 ){
g.bRaw = 1;
azArg++;
nArg--;
}else
if( strcmp("-csv", z)==0 ){
g.bCSV = 1;
azArg++;
nArg--;
}else
if( strcmp("-tmstmp", z)==0 ){
g.bTmstmp = 1;
azArg++;
nArg--;
}else
{
usage(zPrg);
exit(1);
}
}
@@ -1173,15 +1326,19 @@ int main(int argc, char **argv){
fileOpen(zPrg, azArg[1]);
szFile = fileGetsize();
zPgSz = fileRead(16, 2);
g.pagesize = zPgSz[0]*256 + zPgSz[1]*65536;
if( g.pagesize==0 ) g.pagesize = 1024;
zPgSz = fileRead(0, 24);
g.pagesize = zPgSz[16]*256 + zPgSz[17]*65536;
if( g.pagesize==0 ) g.pagesize = 4096;
g.nRes = zPgSz[20];
g.usablesize = g.pagesize - g.nRes;
sqlite3_free(zPgSz);
printf("Pagesize: %d\n", (int)g.pagesize);
g.mxPage = (u32)((szFile+g.pagesize-1)/g.pagesize);
printf("Available pages: 1..%u\n", g.mxPage);
if( !g.bCSV ){
printf("Pagesize: %d\n", (int)g.pagesize);
if( g.nRes ) printf("Useable-size: %d\n", (int)g.usablesize);
printf("Available pages: 1..%u\n", g.mxPage);
}
if( nArg==2 ){
u32 i;
for(i=1; i<=g.mxPage; i++) print_page(i);

135
tool/showtmlog.c Normal file
View File

@@ -0,0 +1,135 @@
/*
** A utility program to decode tmstmpvfs log files.
*/
#include <stdio.h>
#include <assert.h>
#include <string.h>
#include <stdint.h>
/*
** The six bytes at a[] are a big-endian unsigned integer which is the
** number of milliseconds since 1970. Decode that value into an ISO 8601
** date/time string stored in static space and return a pointer to that
** string.
*/
static const char *decodeTimestamp(const unsigned char *a){
uint64_t ms; /* Milliseconds since 1970 */
uint64_t days; /* Days since 1970-01-01 */
uint64_t sod; /* Start of date specified by ms */
uint64_t z; /* Days since 0000-03-01 */
uint64_t era; /* 400-year era */
int i; /* Loop counter */
int h; /* hour */
int m; /* minute */
int s; /* second */
int f; /* millisecond */
int Y; /* year */
int M; /* month */
int D; /* day */
int y; /* year assuming March is first month */
unsigned int doe; /* day of 400-year era */
unsigned int yoe; /* year of 400-year era */
unsigned int doy; /* day of year */
unsigned int mp; /* month with March==0 */
static char zOut[50]; /* Return results here */
for(ms=0, i=0; i<=5; i++) ms = (ms<<8) + a[i];
if( ms==0 ){
return " ";
}else if( ms>4102444800000LL ){ /* 2100-01-01 */
/* YYYY-MM-DD HH:MM:SS.SSS */
return " (bad date) ";
}
days = ms/86400000;
sod = (ms%86400000)/1000;
f = (int)(ms%1000);
h = sod/3600;
m = (sod%3600)/60;
s = sod%60;
z = days + 719468;
era = z/147097;
doe = (unsigned)(z - era*146097);
yoe = (doe - doe/1460 + doe/36524 - doe/146096)/365;
y = (int)yoe + era*400;
doy = doe - (365*yoe + yoe/4 - yoe/100);
mp = (5*doy + 2)/153;
D = doy - (153*mp + 2)/5 + 1;
M = mp + (mp<10 ? 3 : -9);
Y = y + (M <=2);
snprintf(zOut, sizeof(zOut),
"%04d-%02d-%02d %02d:%02d:%02d.%03d",
Y, M, D, h, m, s, f);
return zOut;
}
int main(int argc, char **argv){
int i,j;
FILE *in;
unsigned int a2, a3;
unsigned char a[16];
for(i=1; i<argc; i++){
in = fopen(argv[i], "rb");
if( in==0 ){
printf("%s: can't open\n", argv[i]);
continue;
}
if( argc>2 ){
printf("*** %s ***\n", argv[i]);
}
while( 16==fread(a, 1, 16, in) ){
printf("%s ", decodeTimestamp(a+2));
for(a2=0, j=8; j<=11; j++) a2 = (a2<<8)+a[j];
for(a3=0, j=12; j<=15; j++) a3 = (a3<<8)+a[j];
switch( a[0] ){
case 0x01: {
printf("open-db pid %u\n", a2);
break;
}
case 0x02: {
printf("open-wal pid %u\n", a2);
break;
}
case 0x03: {
printf("wal-page pgno %-8u frame %-8u%s\n", a2, a3,
a[1]==1 ? " txn" : "");
break;
}
case 0x04: {
printf("db-page pgno %-8u\n", a2);
break;
}
case 0x05: {
printf("ckpt-start\n");
break;
}
case 0x06: {
printf("ckpt-page pgno %-8u\n", a2);
break;
}
case 0x07: {
printf("ckpt-end\n");
break;
}
case 0x08: {
printf("wal-reset salt1 0x%08x\n", a3);
break;
}
case 0x0e: {
printf("close-wal\n");
break;
}
case 0x0f: {
printf("close-db\n");
break;
}
default: {
printf("invalid-record\n");
break;
}
}
}
fclose(in);
}
return 0;
}