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:
@@ -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
911
ext/misc/tmstmpvfs.c
Normal 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) */
|
||||
@@ -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(
|
||||
|
||||
@@ -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>),
|
||||
|
||||
4
main.mk
4
main.mk
@@ -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)
|
||||
|
||||
27
manifest
27
manifest
@@ -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.
|
||||
|
||||
@@ -1 +1 @@
|
||||
9cec3b67d95c145c968c560e8b3d2c33896b4b5b6c86c61350263764bb616c43
|
||||
6e5f1fbaec775db5c5e90b29cfa0820c9f4d560b36c063b504ef55a6c3a65bdc
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
249
tool/showdb.c
249
tool/showdb.c
@@ -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
135
tool/showtmlog.c
Normal 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;
|
||||
}
|
||||
Reference in New Issue
Block a user