Skip to content

Commit 05f7643

Browse files
author
D. Richard Hipp
committed
Add the SQLITE_FCNTL_FILESTAT file control and the sqlite_filestat() SQL
function.
2 parents c12170e + dd44410 commit 05f7643

5 files changed

Lines changed: 290 additions & 5 deletions

File tree

src/func.c

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3083,6 +3083,56 @@ static void percentValue(sqlite3_context *pCtx){
30833083
/****** End of percentile family of functions ******/
30843084
#endif /* SQLITE_ENABLE_PERCENTILE */
30853085

3086+
#if defined(SQLITE_DEBUG) || defined(SQLITE_ENABLE_FILESTAT)
3087+
/*
3088+
** Implementation of sqlite_filestat(SCHEMA).
3089+
**
3090+
** Return JSON text that describes low-level debug/diagnostic information
3091+
** about the sqlite3_file object associated with SCHEMA.
3092+
*/
3093+
static void filestatFunc(
3094+
sqlite3_context *context,
3095+
int argc,
3096+
sqlite3_value **argv
3097+
){
3098+
sqlite3 *db = sqlite3_context_db_handle(context);
3099+
const char *zDbName;
3100+
sqlite3_str *pStr;
3101+
Btree *pBtree;
3102+
3103+
zDbName = (const char*)sqlite3_value_text(argv[0]);
3104+
pBtree = sqlite3DbNameToBtree(db, zDbName);
3105+
if( pBtree ){
3106+
Pager *pPager;
3107+
sqlite3_file *fd;
3108+
int rc;
3109+
sqlite3BtreeEnter(pBtree);
3110+
pPager = sqlite3BtreePager(pBtree);
3111+
assert( pPager!=0 );
3112+
fd = sqlite3PagerFile(pPager);
3113+
pStr = sqlite3_str_new(db);
3114+
if( pStr==0 ){
3115+
sqlite3_result_error_nomem(context);
3116+
}else{
3117+
sqlite3_str_append(pStr, "{\"db\":", 6);
3118+
rc = sqlite3OsFileControl(fd, SQLITE_FCNTL_FILESTAT, pStr);
3119+
if( rc ) sqlite3_str_append(pStr, "null", 4);
3120+
fd = sqlite3PagerJrnlFile(pPager);
3121+
if( fd && fd->pMethods!=0 ){
3122+
sqlite3_str_appendall(pStr, ",\"journal\":");
3123+
rc = sqlite3OsFileControl(fd, SQLITE_FCNTL_FILESTAT, pStr);
3124+
if( rc ) sqlite3_str_append(pStr, "null", 4);
3125+
}
3126+
sqlite3_str_append(pStr, "}", 1);
3127+
sqlite3_result_text(context, sqlite3_str_finish(pStr), -1,
3128+
sqlite3_free);
3129+
}
3130+
sqlite3BtreeLeave(pBtree);
3131+
}else{
3132+
sqlite3_result_text(context, "{}", 2, SQLITE_STATIC);
3133+
}
3134+
}
3135+
#endif /* SQLITE_DEBUG || SQLITE_ENABLE_FILESTAT */
30863136

30873137
#ifdef SQLITE_DEBUG
30883138
/*
@@ -3241,6 +3291,9 @@ void sqlite3RegisterBuiltinFunctions(void){
32413291
INLINE_FUNC(likely, 1, INLINEFUNC_unlikely, SQLITE_FUNC_UNLIKELY),
32423292
#ifdef SQLITE_ENABLE_OFFSET_SQL_FUNC
32433293
INLINE_FUNC(sqlite_offset, 1, INLINEFUNC_sqlite_offset, 0 ),
3294+
#endif
3295+
#if defined(SQLITE_DEBUG) || defined(SQLITE_ENABLE_FILESTAT)
3296+
FUNCTION(sqlite_filestat, 1, 0, 0, filestatFunc ),
32443297
#endif
32453298
FUNCTION(ltrim, 1, 1, 0, trimFunc ),
32463299
FUNCTION(ltrim, 2, 1, 0, trimFunc ),

src/os_unix.c

Lines changed: 197 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -596,6 +596,119 @@ static struct unix_syscall {
596596
}; /* End of the overrideable system calls */
597597

598598

599+
#if defined(SQLITE_DEBUG) || defined(SQLITE_ENABLE_FILESTAT)
600+
/*
601+
** Extract Posix Advisory Locking information about file description fd
602+
** from the /proc/PID/fdinfo/FD pseudo-file. Fill the string buffer a[16]
603+
** with characters to indicate which SQLite-relevant locks are held.
604+
** a[16] will be a 15-character zero-terminated string with the following
605+
** schema:
606+
**
607+
** AAA/B.DDD.DDDDD
608+
**
609+
** Each of character A-D will be "w" or "r" or "-" to indicate either a
610+
** write-lock, a read-lock, or no-lock, respectively. The "." and "/"
611+
** characters are delimiters intended to make the string more easily
612+
** readable by humans. Here are the meaning of the specific letters:
613+
**
614+
** AAA -> The main database locks. PENDING_BYTE, RESERVED_BYTE,
615+
** and SHARED_FIRST, respectively.
616+
**
617+
** B -> The deadman switch lock. Offset 128 of the -shm file.
618+
**
619+
** CCC -> WAL locks: WRITE, CKPT, RECOVER
620+
**
621+
** DDDDD -> WAL read-locks 0 through 5
622+
**
623+
** Note that elements before the "/" apply to the main database file and
624+
** elements after the "/" apply to the -shm file in WAL mode.
625+
**
626+
** Here is another way of thinking about the meaning of the result string:
627+
**
628+
** AAA/B.CCC.DDDDD
629+
** ||| | ||| \___/
630+
** PENDING--'|| | ||| `----- READ 0-5
631+
** RESERVED--'| | ||`---- RECOVER
632+
** SHARED ----' | |`----- CKPT
633+
** DMS ------' `------ WRITE
634+
**
635+
** Return SQLITE_OK on success and SQLITE_ERROR_UNABLE if the /proc
636+
** pseudo-filesystem is unavailable.
637+
*/
638+
static int unixPosixAdvisoryLocks(
639+
int fd, /* The file descriptor to analyze */
640+
char a[16] /* Write a text description of PALs here */
641+
){
642+
int in;
643+
ssize_t n;
644+
char *p, *pNext, *x;
645+
char z[2000];
646+
647+
/* 1 */
648+
/* 012 4 678 01234 */
649+
memcpy(a, "---/-.---.-----", 16);
650+
sqlite3_snprintf(sizeof(z), z, "/proc/%d/fdinfo/%d", getpid(), fd);
651+
in = osOpen(z, O_RDONLY, 0);
652+
if( in<0 ){
653+
return SQLITE_ERROR_UNABLE;
654+
}
655+
n = osRead(in, z, sizeof(z)-1);
656+
osClose(in);
657+
if( n<=0 ) return SQLITE_ERROR_UNABLE;
658+
z[n] = 0;
659+
660+
/* We are looking for lines that begin with "lock:\t". Examples:
661+
**
662+
** lock: 1: POSIX ADVISORY READ 494716 08:02:5277597 1073741826 1073742335
663+
** lock: 1: POSIX ADVISORY WRITE 494716 08:02:5282282 120 120
664+
** lock: 2: POSIX ADVISORY READ 494716 08:02:5282282 123 123
665+
** lock: 3: POSIX ADVISORY READ 494716 08:02:5282282 128 128
666+
*/
667+
pNext = strstr(z, "lock:\t");
668+
while( pNext ){
669+
char cType = 0;
670+
sqlite3_int64 iFirst, iLast;
671+
p = pNext+6;
672+
pNext = strstr(p, "lock:\t");
673+
if( pNext ) pNext[-1] = 0;
674+
if( (x = strstr(p, " READ "))!=0 ){
675+
cType = 'r';
676+
x += 6;
677+
}else if( (x = strstr(p, " WRITE "))!=0 ){
678+
cType = 'w';
679+
x += 7;
680+
}else{
681+
continue;
682+
}
683+
x = strrchr(x, ' ');
684+
if( x==0 ) continue;
685+
iLast = strtoll(x+1, 0, 10);
686+
*x = 0;
687+
x = strrchr(p, ' ');
688+
if( x==0 ) continue;
689+
iFirst = strtoll(x+1, 0, 10);
690+
if( iLast>=PENDING_BYTE ){
691+
if( iFirst<=PENDING_BYTE && iLast>=PENDING_BYTE ) a[0] = cType;
692+
if( iFirst<=PENDING_BYTE+1 && iLast>=PENDING_BYTE+1 ) a[1] = cType;
693+
if( iFirst<=PENDING_BYTE+2 && iLast>=PENDING_BYTE+510 ) a[2] = cType;
694+
}else if( iLast<=128 ){
695+
if( iFirst<=128 && iLast>=128 ) a[4] = cType;
696+
if( iFirst<=120 && iLast>=120 ) a[6] = cType;
697+
if( iFirst<=121 && iLast>=121 ) a[7] = cType;
698+
if( iFirst<=122 && iLast>=122 ) a[8] = cType;
699+
if( iFirst<=123 && iLast>=123 ) a[10] = cType;
700+
if( iFirst<=124 && iLast>=124 ) a[11] = cType;
701+
if( iFirst<=125 && iLast>=125 ) a[12] = cType;
702+
if( iFirst<=126 && iLast>=126 ) a[13] = cType;
703+
if( iFirst<=127 && iLast>=127 ) a[14] = cType;
704+
}
705+
}
706+
return SQLITE_OK;
707+
}
708+
#else
709+
# define unixPosixAdvisoryLocks(A,B) SQLITE_ERROR_UNABLE
710+
#endif /* SQLITE_DEBUG || SQLITE_ENABLE_FILESTAT */
711+
599712
/*
600713
** On some systems, calls to fchown() will trigger a message in a security
601714
** log if they come from non-root processes. So avoid calling fchown() if
@@ -4002,6 +4115,10 @@ static int unixGetTempname(int nBuf, char *zBuf);
40024115
#if !defined(SQLITE_WASI) && !defined(SQLITE_OMIT_WAL)
40034116
static int unixFcntlExternalReader(unixFile*, int*);
40044117
#endif
4118+
#if defined(SQLITE_DEBUG) || defined(SQLITE_ENABLE_FILESTAT)
4119+
static void unixDescribeShm(sqlite3_str*,unixShm*);
4120+
#endif
4121+
40054122

40064123
/*
40074124
** Information and control of an open file handle.
@@ -4144,6 +4261,66 @@ static int unixFileControl(sqlite3_file *id, int op, void *pArg){
41444261
return SQLITE_OK;
41454262
#endif
41464263
}
4264+
4265+
#if defined(SQLITE_DEBUG) || defined(SQLITE_ENABLE_FILESTAT)
4266+
case SQLITE_FCNTL_FILESTAT: {
4267+
sqlite3_str *pStr = (sqlite3_str*)pArg;
4268+
char aLck[16];
4269+
unixInodeInfo *pInode;
4270+
static const char *azLock[] = { "SHARED", "RESERVED",
4271+
"PENDING", "EXCLUSIVE" };
4272+
sqlite3_str_appendf(pStr, "{\"h\":%d", pFile->h);
4273+
sqlite3_str_appendf(pStr, ",\"vfs\":\"%s\"", pFile->pVfs->zName);
4274+
if( pFile->eFileLock ){
4275+
sqlite3_str_appendf(pStr, ",\"eFileLock\":\"%s\"",
4276+
azLock[pFile->eFileLock-1]);
4277+
if( unixPosixAdvisoryLocks(pFile->h, aLck)==SQLITE_OK ){
4278+
sqlite3_str_appendf(pStr, ",\"pal\":\"%s\"", aLck);
4279+
}
4280+
}
4281+
unixEnterMutex();
4282+
if( pFile->pShm ){
4283+
sqlite3_str_appendall(pStr, ",\"shm\":");
4284+
unixDescribeShm(pStr, pFile->pShm);
4285+
}
4286+
#if SQLITE_MAX_MMAP_SIZE>0
4287+
if( pFile->mmapSize ){
4288+
sqlite3_str_appendf(pStr, ",\"mmapSize\":%lld", pFile->mmapSize);
4289+
sqlite3_str_appendf(pStr, ",\"nFetchOut\":%d", pFile->nFetchOut);
4290+
}
4291+
#endif
4292+
if( (pInode = pFile->pInode)!=0 ){
4293+
sqlite3_str_appendf(pStr, ",\"inode\":{\"nRef\":%d",pInode->nRef);
4294+
sqlite3_mutex_enter(pInode->pLockMutex);
4295+
sqlite3_str_appendf(pStr, ",\"nShared\":%d", pInode->nShared);
4296+
if( pInode->eFileLock ){
4297+
sqlite3_str_appendf(pStr, ",\"eFileLock\":\"%s\"",
4298+
azLock[pInode->eFileLock-1]);
4299+
}
4300+
if( pInode->pUnused ){
4301+
char cSep = '[';
4302+
UnixUnusedFd *pUFd = pFile->pInode->pUnused;
4303+
sqlite3_str_appendall(pStr, ",\"unusedFd\":");
4304+
while( pUFd ){
4305+
sqlite3_str_appendf(pStr, "%c{\"fd\":%d,\"flags\":%d",
4306+
cSep, pUFd->fd, pUFd->flags);
4307+
cSep = ',';
4308+
if( unixPosixAdvisoryLocks(pUFd->fd, aLck)==SQLITE_OK ){
4309+
sqlite3_str_appendf(pStr, ",\"pal\":\"%s\"", aLck);
4310+
}
4311+
sqlite3_str_append(pStr, "}", 1);
4312+
pUFd = pUFd->pNext;
4313+
}
4314+
sqlite3_str_append(pStr, "]", 1);
4315+
}
4316+
sqlite3_mutex_leave(pInode->pLockMutex);
4317+
sqlite3_str_append(pStr, "}", 1);
4318+
}
4319+
unixLeaveMutex();
4320+
sqlite3_str_append(pStr, "}", 1);
4321+
return SQLITE_OK;
4322+
}
4323+
#endif /* SQLITE_DEBUG || SQLITE_ENABLE_FILESTAT */
41474324
}
41484325
return SQLITE_NOTFOUND;
41494326
}
@@ -4410,6 +4587,26 @@ struct unixShm {
44104587
#define UNIX_SHM_BASE ((22+SQLITE_SHM_NLOCK)*4) /* first lock byte */
44114588
#define UNIX_SHM_DMS (UNIX_SHM_BASE+SQLITE_SHM_NLOCK) /* deadman switch */
44124589

4590+
#if defined(SQLITE_DEBUG) || defined(SQLITE_ENABLE_FILESTAT)
4591+
/*
4592+
** Describe the pShm object using JSON. Used for diagnostics only.
4593+
*/
4594+
static void unixDescribeShm(sqlite3_str *pStr, unixShm *pShm){
4595+
unixShmNode *pNode = pShm->pShmNode;
4596+
char aLck[16];
4597+
sqlite3_str_appendf(pStr, "{\"h\":%d", pNode->hShm);
4598+
assert( unixMutexHeld() );
4599+
sqlite3_str_appendf(pStr, ",\"nRef\":%d", pNode->nRef);
4600+
sqlite3_str_appendf(pStr, ",\"id\":%d", pShm->id);
4601+
sqlite3_str_appendf(pStr, ",\"sharedMask\":%d", pShm->sharedMask);
4602+
sqlite3_str_appendf(pStr, ",\"exclMask\":%d", pShm->exclMask);
4603+
if( unixPosixAdvisoryLocks(pNode->hShm, aLck)==SQLITE_OK ){
4604+
sqlite3_str_appendf(pStr, ",\"pal\":\"%s\"", aLck);
4605+
}
4606+
sqlite3_str_append(pStr, "}", 1);
4607+
}
4608+
#endif /* SQLITE_DEBUG || SQLITE_ENABLE_FILESTAT */
4609+
44134610
/*
44144611
** Use F_GETLK to check whether or not there are any readers with open
44154612
** wal-mode transactions in other processes on database file pFile. If

src/os_win.c

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3953,6 +3953,28 @@ static int winFileControl(sqlite3_file *id, int op, void *pArg){
39533953
}
39543954
#endif /* SQLITE_ENABLE_SETLK_TIMEOUT */
39553955

3956+
#if defined(SQLITE_DEBUG) || defined(SQLITE_ENABLE_FILESTAT)
3957+
case SQLITE_FCNTL_FILESTAT: {
3958+
sqlite3_str *pStr = (sqlite3_str*)pArg;
3959+
sqlite3_str_appendf(pStr, "{\"h\":%llu", (sqlite3_uint64)pFile->h);
3960+
sqlite3_str_appendf(pStr, ",\"vfs\":\"%s\"", pFile->pVfs->zName);
3961+
if( pFile->locktype ){
3962+
static const char *azLock[] = { "SHARED", "RESERVED",
3963+
"PENDING", "EXCLUSIVE" };
3964+
sqlite3_str_appendf(pStr, ",\"locktype\":\"%s\"",
3965+
azLock[pFile->locktype-1]);
3966+
}
3967+
#if SQLITE_MAX_MMAP_SIZE>0
3968+
if( pFile->mmapSize ){
3969+
sqlite3_str_appendf(pStr, ",\"mmapSize\":%lld", pFile->mmapSize);
3970+
sqlite3_str_appendf(pStr, ",\"nFetchOut\":%d", pFile->nFetchOut);
3971+
}
3972+
#endif
3973+
sqlite3_str_append(pStr, "}", 1);
3974+
return SQLITE_OK;
3975+
}
3976+
#endif /* SQLITE_DEBUG || SQLITE_ENABLE_FILESTAT */
3977+
39563978
}
39573979
OSTRACE(("FCNTL file=%p, rc=SQLITE_NOTFOUND\n", pFile->h));
39583980
return SQLITE_NOTFOUND;

src/shell.c.in

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -5308,6 +5308,7 @@ static const char *(azHelp[]) = {
53085308
" --maxsize N Maximum size for --hexdb or --deserialized database",
53095309
#endif
53105310
" --new Initialize FILE to an empty database",
5311+
" --normal FILE is an ordinary SQLite database",
53115312
" --nofollow Do not follow symbolic links",
53125313
" --readonly Open FILE readonly",
53135314
" --zip FILE is a ZIP archive",
@@ -5649,15 +5650,15 @@ static int session_filter(void *pCtx, const char *zTab){
56495650
** Otherwise, assume an ordinary database regardless of the filename if
56505651
** the type cannot be determined from content.
56515652
*/
5652-
int deduceDatabaseType(const char *zName, int dfltZip){
5653+
int deduceDatabaseType(const char *zName, int dfltZip, int openFlags){
56535654
FILE *f;
56545655
size_t n;
56555656
sqlite3 *db = 0;
56565657
sqlite3_stmt *pStmt = 0;
56575658
int rc = SHELL_OPEN_UNSPEC;
56585659
char zBuf[100];
56595660
if( access(zName,0)!=0 ) goto database_type_by_name;
5660-
if( sqlite3_open_v2(zName, &db, SQLITE_OPEN_READONLY, 0)==SQLITE_OK
5661+
if( sqlite3_open_v2(zName, &db, openFlags, 0)==SQLITE_OK
56615662
&& sqlite3_prepare_v2(db,"SELECT count(*) FROM sqlite_schema",-1,&pStmt,0)
56625663
==SQLITE_OK
56635664
&& sqlite3_step(pStmt)==SQLITE_ROW
@@ -5866,7 +5867,7 @@ static void open_db(ShellState *p, int openFlags){
58665867
p->openMode = SHELL_OPEN_NORMAL;
58675868
}else{
58685869
p->openMode = (u8)deduceDatabaseType(zDbFilename,
5869-
(openFlags & OPEN_DB_ZIPFILE)!=0);
5870+
(openFlags & OPEN_DB_ZIPFILE)!=0, p->openFlags);
58705871
}
58715872
}
58725873
if( (p->openFlags & (SQLITE_OPEN_READONLY|SQLITE_OPEN_READWRITE))==0 ){
@@ -8247,7 +8248,7 @@ static int arDotCommand(
82478248
cmd.out = pState->out;
82488249
cmd.db = pState->db;
82498250
if( cmd.zFile ){
8250-
eDbType = deduceDatabaseType(cmd.zFile, 1);
8251+
eDbType = deduceDatabaseType(cmd.zFile, 1, 0);
82518252
}else{
82528253
eDbType = pState->openMode;
82538254
}
@@ -10349,6 +10350,8 @@ static int do_meta_command(char *zLine, ShellState *p){
1034910350
openMode = SHELL_OPEN_DESERIALIZE;
1035010351
}else if( optionMatch(z, "hexdb") ){
1035110352
openMode = SHELL_OPEN_HEXDB;
10353+
}else if( optionMatch(z, "normal") ){
10354+
openMode = SHELL_OPEN_NORMAL;
1035210355
}else if( optionMatch(z, "maxsize") && iName+1<nArg ){
1035310356
p->szMax = integerValue(azArg[++iName]);
1035410357
#endif /* SQLITE_OMIT_DESERIALIZE */

0 commit comments

Comments
 (0)