Compare commits

...

3 Commits

Author SHA1 Message Date
dan 922703138e Add tests for virtual table shmlock.
FossilOrigin-Name: 1c6a8442735e9f6117d7a8f1d88630958ec8fdb25a75cc392af1e8511a5fc56d
2020-09-02 16:14:17 +00:00
dan 148920e52a Fix reporting on locks from external processes. Various other fixes.
FossilOrigin-Name: 459de23e869c79357221100e81d2c8e0b812ed3260e58d7d7acb3419876f41a3
2020-09-02 15:26:28 +00:00
dan b7ae3b541c Add file-controls and a vtab frontend for querying the unix VFS for the currently held shm-locks. Still some issues.
FossilOrigin-Name: ef10e1b386d7a9028eab64e1f4802e3d6b2df11c6723130a29e5effc8dcb2e80
2020-09-01 20:56:28 +00:00
8 changed files with 690 additions and 12 deletions
+352
View File
@@ -0,0 +1,352 @@
/*
** 2020-09-02
**
** 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 contains the implementation of the "shmlock" eponymous
** virtual table module. The schema of which is effectively:
**
** CREATE TABLE shmlock(
** connid TEXT,
** lock TEXT,
** locktype TEXT,
** mxFrame INTEGER,
** dbname HIDDEN
** );
**
** This virtual table may be used to query for the locks held on the main or
** any attached database that is in wal mode. If the database is not in
** wal mode, zero rows are returned. Otherwise, one row is returned for
** each lock held on the database by a connection in either the current
** process, as well as - to the extent possible - one row for each lock held
** by another process. On unix, "to the extent possible" means that if
** two or more external processes hold a SHARED wal-mode lock on the same
** locking-slot, only one of them is reported on.
**
** To query for locks on the main database, either of:
**
** SELECT * FROM shmlock;
** SELECT * FROM shmlock('main');
**
** To query for locks on the attached database named 'aux':
**
** SELECT * FROM shmlock('aux');
**
** The non-HIDDEN columns of each row may be interpreted as follows:
**
** connid:
** String identifying the connection. For a local connection lock, this is
** either the string representation of an internal pointer value, or else a
** string configured using the file-control SQLITE_FCNTL_SHMLOCK_NAME.
** For a lock from an external process, the id takes the form "pid.123",
** where 123 is the process-id of the lock holder.
**
** lock:
** A text value representing the particular wal mode lock held. One of
** "WRITE", "CHECKPOINT", "RECOVER" or "READ(n)", where n is between 0 and
** 4, inclusive.
**
** locktype:
** 'R' for a shared (reader) lock, or 'W' for an exclusive (writer) lock.
**
** mxframe:
** When the lock is a shared lock held on a READ(n) slot, the integer value
** of the corresponding read-mark slot in shared-memory.
**
** This module currently only works with the "unix" VFS.
**
*/
#if !defined(SQLITEINT_H)
#include "sqlite3ext.h"
#endif
SQLITE_EXTENSION_INIT1
#include <string.h>
#include <assert.h>
/* shmlock_vtab is a subclass of sqlite3_vtab which is
** underlying representation of the virtual table
*/
typedef struct shmlock_vtab shmlock_vtab;
struct shmlock_vtab {
sqlite3_vtab base; /* Base class - must be first */
sqlite3 *db;
};
/* shmlock_cursor is a subclass of sqlite3_vtab_cursor which will
** serve as the underlying representation of a cursor that scans
** over rows of the result
*/
typedef struct shmlock_cursor shmlock_cursor;
struct shmlock_cursor {
sqlite3_vtab_cursor base; /* Base class - must be first */
char *zFcntl;
int iFcntl;
char *aSpace; /* Space to use for dequoted strings */
const char *azCol[4];
sqlite3_int64 iRowid;
};
/*
** Create a new shmlock_vtab object.
*/
static int shmlockConnect(
sqlite3 *db,
void *pAux,
int argc, const char *const*argv,
sqlite3_vtab **ppVtab,
char **pzErr
){
shmlock_vtab *pNew;
int rc;
rc = sqlite3_declare_vtab(db,
"CREATE TABLE shmlock("
"connid TEXT, "
"lock TEXT, "
"locktype TEXT, "
"mxFrame INTEGER,"
"dbname HIDDEN"
");"
);
if( rc==SQLITE_OK ){
pNew = sqlite3_malloc( sizeof(*pNew) );
*ppVtab = (sqlite3_vtab*)pNew;
if( pNew==0 ) return SQLITE_NOMEM;
memset(pNew, 0, sizeof(*pNew));
pNew->db = db;
}
return rc;
}
/*
** This method is the destructor for shmlock_vtab objects.
*/
static int shmlockDisconnect(sqlite3_vtab *pVtab){
shmlock_vtab *p = (shmlock_vtab*)pVtab;
sqlite3_free(p);
return SQLITE_OK;
}
/*
** Constructor for a new shmlock_cursor object.
*/
static int shmlockOpen(sqlite3_vtab *p, sqlite3_vtab_cursor **ppCursor){
shmlock_cursor *pCur;
pCur = sqlite3_malloc( sizeof(*pCur) );
if( pCur==0 ) return SQLITE_NOMEM;
memset(pCur, 0, sizeof(*pCur));
*ppCursor = &pCur->base;
return SQLITE_OK;
}
/*
** Destructor for a shmlock_cursor.
*/
static int shmlockClose(sqlite3_vtab_cursor *cur){
shmlock_cursor *pCur = (shmlock_cursor*)cur;
sqlite3_free(pCur->zFcntl);
sqlite3_free(pCur->aSpace);
sqlite3_free(pCur);
return SQLITE_OK;
}
/*
** Advance a shmlock_cursor to its next row of output.
*/
static int shmlockNext(sqlite3_vtab_cursor *cur){
shmlock_cursor *pCur = (shmlock_cursor*)cur;
int ii = pCur->iFcntl;
const char *z = pCur->zFcntl;
char *a = pCur->aSpace;
int iCol;
memset(pCur->azCol, 0, sizeof(char*)*4);
if( z[ii]=='\0' ) return SQLITE_OK;;
for(iCol=0; iCol<4; iCol++){
if( z[ii]!='\'' ) return SQLITE_ERROR;
pCur->azCol[iCol] = a;
ii++;
while( z[ii] ){
if( z[ii]=='\'' ){
ii++;
if( z[ii]!='\'' ) break;
}
*a++ = z[ii++];
}
*a++ = '\0';
while( z[ii]==' ' ) ii++;
if( z[ii]=='\0' ) break;
if( z[ii]=='\n' ){
ii++;
break;
}
}
pCur->iFcntl = ii;
pCur->iRowid++;
return SQLITE_OK;
}
/*
** Return values of columns for the row at which the shmlock_cursor
** is currently pointing.
*/
static int shmlockColumn(
sqlite3_vtab_cursor *cur, /* The cursor */
sqlite3_context *ctx, /* First argument to sqlite3_result_...() */
int i /* Which column to return */
){
shmlock_cursor *pCur = (shmlock_cursor*)cur;
if( i<=3 && pCur->azCol[i] ){
sqlite3_result_text(ctx, pCur->azCol[i], -1, SQLITE_TRANSIENT);
}
return SQLITE_OK;
}
/*
** Return the rowid for the current row. In this implementation, the
** rowid is the same as the output value.
*/
static int shmlockRowid(sqlite3_vtab_cursor *cur, sqlite_int64 *pRowid){
shmlock_cursor *pCur = (shmlock_cursor*)cur;
*pRowid = pCur->iRowid;
return SQLITE_OK;
}
/*
** Return TRUE if the cursor has been moved off of the last
** row of output.
*/
static int shmlockEof(sqlite3_vtab_cursor *cur){
shmlock_cursor *pCur = (shmlock_cursor*)cur;
return pCur->azCol[1]==0;
}
/*
** This method is called to "rewind" the shmlock_cursor object back
** to the first row of output. This method is always called at least
** once prior to any call to shmlockColumn() or shmlockRowid() or
** shmlockEof().
*/
static int shmlockFilter(
sqlite3_vtab_cursor *pVtabCursor,
int idxNum, const char *idxStr,
int argc, sqlite3_value **argv
){
int rc;
shmlock_cursor *pCur = (shmlock_cursor *)pVtabCursor;
shmlock_vtab *pTab = (shmlock_vtab*)pVtabCursor->pVtab;
const char *zDb = "main";
if( idxNum==1 ){
assert( argc==1 );
zDb = (const char*)sqlite3_value_text(argv[0]);
}
sqlite3_free(pCur->aSpace);
pCur->zFcntl = 0;
pCur->aSpace = 0;
rc = sqlite3_file_control(
pTab->db, zDb, SQLITE_FCNTL_SHMLOCK_GET, (void*)&pCur->zFcntl
);
pCur->iRowid = 1;
pCur->iFcntl = 0;
if( rc==SQLITE_NOTFOUND ){
rc = SQLITE_OK;
}
if( pCur->zFcntl ){
int n = strlen(pCur->zFcntl);
pCur->aSpace = sqlite3_malloc(n);
if( pCur->aSpace==0 ){
rc = SQLITE_NOMEM;
}else{
rc = shmlockNext(pVtabCursor);
}
}
return rc;
}
/*
** SQLite will invoke this method one or more times while planning a query
** that uses the virtual table. This routine needs to create
** a query plan for each invocation and compute an estimated cost for that
** plan.
*/
static int shmlockBestIndex(
sqlite3_vtab *tab,
sqlite3_index_info *pIdxInfo
){
int ii;
/* Search for a dbname=? constraint. If one is found, set idxNum=1 and
** pass the ? as the only argument to xFilter. Otherwise, leave idxNum=0
** and pass no arguments to xFilter. */
for(ii=0; ii<pIdxInfo->nConstraint; ii++){
struct sqlite3_index_constraint *p = &pIdxInfo->aConstraint[ii];
if( p->usable && p->op==SQLITE_INDEX_CONSTRAINT_EQ && p->iColumn==4 ){
pIdxInfo->aConstraintUsage[ii].argvIndex = 1;
pIdxInfo->aConstraintUsage[ii].omit = 1;
pIdxInfo->idxNum = 1;
break;
}
}
pIdxInfo->estimatedCost = (double)10;
pIdxInfo->estimatedRows = 10;
return SQLITE_OK;
}
/*
** This following structure defines all the methods for the
** virtual table.
*/
static sqlite3_module shmlockModule = {
/* iVersion */ 0,
/* xCreate */ 0,
/* xConnect */ shmlockConnect,
/* xBestIndex */ shmlockBestIndex,
/* xDisconnect */ shmlockDisconnect,
/* xDestroy */ 0,
/* xOpen */ shmlockOpen,
/* xClose */ shmlockClose,
/* xFilter */ shmlockFilter,
/* xNext */ shmlockNext,
/* xEof */ shmlockEof,
/* xColumn */ shmlockColumn,
/* xRowid */ shmlockRowid,
/* xUpdate */ 0,
/* xBegin */ 0,
/* xSync */ 0,
/* xCommit */ 0,
/* xRollback */ 0,
/* xFindMethod */ 0,
/* xRename */ 0,
/* xSavepoint */ 0,
/* xRelease */ 0,
/* xRollbackTo */ 0,
/* xShadowName */ 0
};
#ifdef _WIN32
__declspec(dllexport)
#endif
int sqlite3_shmlockvtab_init(
sqlite3 *db,
char **pzErrMsg,
const sqlite3_api_routines *pApi
){
int rc = SQLITE_OK;
SQLITE_EXTENSION_INIT2(pApi);
rc = sqlite3_create_module(db, "shmlock", &shmlockModule, 0);
return rc;
}
+1
View File
@@ -377,6 +377,7 @@ TESTSRC += \
$(TOP)/ext/misc/regexp.c \
$(TOP)/ext/misc/remember.c \
$(TOP)/ext/misc/series.c \
$(TOP)/ext/misc/shmlockvtab.c \
$(TOP)/ext/misc/spellfix.c \
$(TOP)/ext/misc/totype.c \
$(TOP)/ext/misc/unionvtab.c \
+12 -10
View File
@@ -1,5 +1,5 @@
C In\sthe\sLemon\soutput,\sadd\sa\sprefix\scomment\sthat\sexplains\sthat\sthe\soutput\sfile\nis\sautomatically\sgenerated\sand\sshows\sthe\sname\sof\sthe\ssource\sfile.
D 2020-09-01T12:26:55.781
C Add\stests\sfor\svirtual\stable\sshmlock.
D 2020-09-02T16:14:17.011
F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1
F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea
F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724
@@ -317,6 +317,7 @@ F ext/misc/scrub.c 2a44b0d44c69584c0580ad2553f6290a307a49df4668941d2812135bfb96a
F ext/misc/series.c 4057dda3579b38ff88b2d3b13b4dd92dbd9d6f90dac2b55c19b0a8ed87ee4959
F ext/misc/sha1.c c8f2253c8792ffab9517695ea7d88c079f0395a5505eefef5c8198fe184ed5ac
F ext/misc/shathree.c 135b7c145db4a09b1650c3e7aff9cb538763a9a361e834c015dd1aaf8d5c9a00
F ext/misc/shmlockvtab.c d218fd9eeacd8406b72cb12d4b80409734415350d3c1dfc3b504ce10cb01f072
F ext/misc/showauth.c 732578f0fe4ce42d577e1c86dc89dd14a006ab52
F ext/misc/spellfix.c 94df9bbfa514a563c1484f684a2df3d128a2f7209a84ca3ca100c68a0163e29f
F ext/misc/sqlar.c 0ace5d3c10fe736dc584bf1159a36b8e2e60fab309d310cd8a0eecd9036621b6
@@ -457,7 +458,7 @@ F ext/userauth/userauth.c 7f00cded7dcaa5d47f54539b290a43d2e59f4b1eb5f447545fa865
F install-sh 9d4de14ab9fb0facae2f48780b874848cbf2f895 x
F ltmain.sh 3ff0879076df340d2e23ae905484d8c15d5fdea8
F magic.txt 8273bf49ba3b0c8559cb2774495390c31fd61c60
F main.mk b1cd0bc6aedad7ebb667b7f74f835f932f60ee33be2a5c3051fd93eb465f5c75
F main.mk 9702e0f4d4c530f3516fc062255c0874e5044000bfb81a5210507399ca2054ac
F mkso.sh fd21c06b063bb16a5d25deea1752c2da6ac3ed83
F mptest/config01.test 3c6adcbc50b991866855f1977ff172eb6d901271
F mptest/config02.test 4415dfe36c48785f751e16e32c20b077c28ae504
@@ -519,7 +520,7 @@ F src/os.c 80e4cf3e5da06be03ca641661e331ce60eeeeabf0d7354dbb1c0e166d0eedbbe
F src/os.h 48388821692e87da174ea198bf96b1b2d9d83be5dfc908f673ee21fafbe0d432
F src/os_common.h b2f4707a603e36811d9b1a13278bffd757857b85
F src/os_setup.h 0dbaea40a7d36bf311613d31342e0b99e2536586
F src/os_unix.c d707ed2867a2fb32101469327acf3274165d9935e9ab9e27bdab0c1a7d661be7
F src/os_unix.c 695560d6427b8d45d1bb0595c4ec9ce944d0b1cbb8bc97d9345141301abf28d2
F src/os_win.c a2149ff0a85c1c3f9cc102a46c673ce87e992396ba3411bfb53db66813b32f1d
F src/os_win.h 7b073010f1451abe501be30d12f6bc599824944a
F src/pager.c 3700a1c55427a3d4168ad1f1b8a8b0cb9ace1d107e4506e30a8f1e66d8a1195e
@@ -537,7 +538,7 @@ F src/resolve.c 97b91fb25d86881ff20c9ad2ad98412c6c1bb5f7d6c9bb044db250cbc9cfcd4b
F src/rowset.c ba9515a922af32abe1f7d39406b9d35730ed65efab9443dc5702693b60854c92
F src/select.c 233e884d7da6601486c7b93aedb97fd29302ae5c03742d0e0eccb4790638bb77
F src/shell.c.in 9bae0c8397e7b592fb404678c4c1fc7944d9dc798a928d1eb40bcd608c33d21b
F src/sqlite.h.in 473a79ff2c5c6d54a09af88206ea4d02c4b74558f7d29315a4ede05da8eb8732
F src/sqlite.h.in 7c248cbaedb64e39071cda07e7ff7ab47a4bbebc279ccb1676f47b353a89360d
F src/sqlite3.rc 5121c9e10c3964d5755191c80dd1180c122fc3a8
F src/sqlite3ext.h 2d1af80082edffd71c6f96f70ad1ce6a4fb46615ad10291fc77fe0dea9ff0197
F src/sqliteInt.h d8d69318b1ba3906d4860da1cd1c6b3650b81c9595e5bc360c6469a1e54e09e1
@@ -545,7 +546,7 @@ F src/sqliteLimit.h d7323ffea5208c6af2734574bae933ca8ed2ab728083caa117c9738581a3
F src/status.c 4b8bc2a6905163a38b739854a35b826c737333fab5b1f8e03fa7eb9a4799c4c1
F src/table.c 0f141b58a16de7e2fbe81c308379e7279f4c6b50eb08efeec5892794a0ba30d1
F src/tclsqlite.c 986b6391f02cd9b53c1d688be55899f6ffddeb8e8014cd83c1b73ff912579a71
F src/test1.c 9e52fb797bf74fa327295df38881aa3ade0824bfb0c14abd0719e555b169fd55
F src/test1.c 1cfa55932b81c8f3980f6983b3f77a7836c2f8e53da1d39d7e831444e714f8a3
F src/test2.c 3efb99ab7f1fc8d154933e02ae1378bac9637da5
F src/test3.c 61798bb0d38b915067a8c8e03f5a534b431181f802659a6616f9b4ff7d872644
F src/test4.c 7c4420e01c577b5c4add2cb03119743b1a357543d347773b9e717195ea967159
@@ -1353,6 +1354,7 @@ F test/shell6.test 1ceb51b2678c472ba6cf1e5da96679ce8347889fe2c3bf93a0e0fa73f00b0
F test/shell7.test 115132f66d0463417f408562cc2cf534f6bbc6d83a6d50f0072a9eb171bae97f
F test/shell8.test 96be02ea0c21f05b24c1883d7b711a1fa8525a68ab7b636aacf6057876941013
F test/shmlock.test 3dbf017d34ab0c60abe6a44e447d3552154bd0c87b41eaf5ceacd408dd13fda5
F test/shmlockvtab.test b5f78b3de4bf111c173fd494ef1d962333f6afd4d6ccbfbef0425cf71aed3178
F test/shortread1.test bb591ef20f0fd9ed26d0d12e80eee6d7ac8897a3
F test/show_speedtest1_rtree.tcl 32e6c5f073d7426148a6936a0408f4b5b169aba5
F test/shrink.test 1b4330b1fd9e818c04726d45cb28db73087535ce
@@ -1879,7 +1881,7 @@ F vsixtest/vsixtest.tcl 6a9a6ab600c25a91a7acc6293828957a386a8a93
F vsixtest/vsixtest.vcxproj.data 2ed517e100c66dc455b492e1a33350c1b20fbcdc
F vsixtest/vsixtest.vcxproj.filters 37e51ffedcdb064aad6ff33b6148725226cd608e
F vsixtest/vsixtest_TemporaryKey.pfx e5b1b036facdb453873e7084e1cae9102ccc67a0
P 84d54eb35716174195ee7e5ac846f47308e5dbb0056e8ff568daa133860bab74
R 7f45e5b59f1c52d29dee23ebb0c2337a
U drh
Z 0e6fd8fbc683d31bd259b25e22ecc736
P 459de23e869c79357221100e81d2c8e0b812ed3260e58d7d7acb3419876f41a3
R 637719ed77f67752632996ec57ddfff5
U dan
Z 75180f503b9702867928ecc1ca309dda
+1 -1
View File
@@ -1 +1 @@
d34caf3bb63d0512ea116a8c8c8343b76aa39441bd4b3e98231747a705b91d54
1c6a8442735e9f6117d7a8f1d88630958ec8fdb25a75cc392af1e8511a5fc56d
+124 -1
View File
@@ -229,6 +229,7 @@ struct unixFile {
const char *zPath; /* Name of the file */
unixShm *pShm; /* Shared memory segment information */
int szChunk; /* Configured by FCNTL_CHUNK_SIZE */
char *zShmlockName;
#if SQLITE_MAX_MMAP_SIZE>0
int nFetchOut; /* Number of outstanding xFetch refs */
sqlite3_int64 mmapSize; /* Usable size of mapping at pMapRegion */
@@ -2112,6 +2113,7 @@ static int closeUnixFile(sqlite3_file *id){
OSTRACE(("CLOSE %-3d\n", pFile->h));
OpenCounter(-1);
sqlite3_free(pFile->pPreallocatedUnused);
sqlite3_free(pFile->zShmlockName);
memset(pFile, 0, sizeof(unixFile));
return SQLITE_OK;
}
@@ -3928,8 +3930,10 @@ static void unixModeBit(unixFile *pFile, unsigned char mask, int *pArg){
}
}
/* Forward declaration */
/* Forward declarations used by file-control implementations */
static int unixGetTempname(int nBuf, char *zBuf);
static int unixFcntlShmlockGet(unixFile *pFile, char **pzOut);
static int unixFcntlShmlockName(unixFile *pFile, const char *zName);
/*
** Information and control of an open file handle.
@@ -4046,6 +4050,16 @@ static int unixFileControl(sqlite3_file *id, int op, void *pArg){
return proxyFileControl(id,op,pArg);
}
#endif /* SQLITE_ENABLE_LOCKING_STYLE && defined(__APPLE__) */
#ifndef SQLITE_OMIT_WAL
case SQLITE_FCNTL_SHMLOCK_GET: {
char **pzOut = (char**)pArg;
return unixFcntlShmlockGet((unixFile*)id, pzOut);
}
case SQLITE_FCNTL_SHMLOCK_NAME: {
char *zName = (char*)pArg;
return unixFcntlShmlockName((unixFile*)id, zName);
}
#endif
}
return SQLITE_NOTFOUND;
}
@@ -4283,6 +4297,7 @@ struct unixShm {
u8 id; /* Id of this connection within its unixShmNode */
u16 sharedMask; /* Mask of shared locks held */
u16 exclMask; /* Mask of exclusive locks held */
const char *zShmlockName; /* Name to use for FCNTL_SHMLOCK_GET */
};
/*
@@ -4291,6 +4306,113 @@ struct unixShm {
#define UNIX_SHM_BASE ((22+SQLITE_SHM_NLOCK)*4) /* first lock byte */
#define UNIX_SHM_DMS (UNIX_SHM_BASE+SQLITE_SHM_NLOCK) /* deadman switch */
/*
** This is the implementation of the SQLITE_FCNTL_SHMLOCK_NAME file-control.
*/
static int unixFcntlShmlockName(unixFile *pFile, const char *zName){
int rc = SQLITE_OK;
assert( !pFile->pShm || pFile->pShm->zShmlockName==pFile->zShmlockName );
if( pFile->pShm ){
sqlite3_mutex_enter(pFile->pShm->pShmNode->pShmMutex);
}
sqlite3_free(pFile->zShmlockName);
pFile->zShmlockName = sqlite3_mprintf("%s", zName);
if( pFile->zShmlockName==0 ){
rc = SQLITE_NOMEM;
}
if( pFile->pShm ){
pFile->pShm->zShmlockName = pFile->zShmlockName;
sqlite3_mutex_leave(pFile->pShm->pShmNode->pShmMutex);
}
return rc;
}
/*
** This is the implementation of the SQLITE_FCNTL_SHMLOCK_GET file-control.
*/
static int unixFcntlShmlockGet(unixFile *pFile, char **pzOut){
int rc = SQLITE_OK;
unixShm *p = pFile->pShm;
const char *aLockName[] = {
"WRITE", "CHECKPOINT", "RECOVER",
"READ(0)", "READ(1)", "READ(2)", "READ(3)", "READ(4)"
};
assert( ArraySize(aLockName)==SQLITE_SHM_NLOCK );
if( p ){
sqlite3_str *pStr = sqlite3_str_new(0);
if( pStr==0 ){
rc = SQLITE_NOMEM_BKPT;
}else{
int nRow;
int i;
u32 aReadMark[5];
char *zOut = 0;
unixShm *pX = 0;
unixShmNode *pShmNode = p->pShmNode;
sqlite3_mutex_enter(pShmNode->pShmMutex);
memcpy(aReadMark, &(pShmNode->apRegion[0])[100], sizeof(aReadMark));
for(pX=pShmNode->pFirst; pX; pX=pX->pNext){
if( pX->exclMask|pX->sharedMask ){
char zBuf[32];
const char *zName = pX->zShmlockName;
if( zName==0 ){
sqlite3_snprintf(sizeof(zBuf), zBuf, "%p", (void*)pX);
zName = zBuf;
}
assert( (pX->exclMask & pX->sharedMask)==0 );
for(i=0; i<SQLITE_SHM_NLOCK; i++){
if( (1<<i) & (pX->exclMask|pX->sharedMask) ){
int bWrite = (1<<i) & pX->exclMask;
if( bWrite==0 && i>=3 ){
sqlite3_str_appendf(pStr, "%Q %Q 'R' '%d'\n",
zName, aLockName[i], aReadMark[i-3]
);
}else{
sqlite3_str_appendf(pStr, "%Q %Q %Q\n",
zName, aLockName[i], bWrite ? "W" : "R"
);
}
nRow++;
}
}
}
}
for(i=0; i<SQLITE_SHM_NLOCK; i++){
if( pShmNode->aLock[i]>=0 ){
struct flock lock;
lock.l_whence = SEEK_SET;
lock.l_start = UNIX_SHM_BASE+i;
lock.l_len = 1;
lock.l_type = F_WRLCK;
if( osFcntl(pShmNode->hShm, F_GETLK, &lock) ){
rc = SQLITE_IOERR_LOCK;
}else if( lock.l_type!=F_UNLCK ){
if( lock.l_type==F_RDLCK && i>=3 ){
sqlite3_str_appendf(pStr, "'pid.%d' %Q 'R' '%d'\n",
lock.l_pid, aLockName[i], aReadMark[i-3]
);
}else{
sqlite3_str_appendf(pStr, "'pid.%d' %Q %Q\n",
lock.l_pid, aLockName[i], lock.l_type==F_WRLCK ? "W" : "R"
);
}
nRow++;
}
}
}
sqlite3_mutex_leave(pShmNode->pShmMutex);
if( rc!=SQLITE_OK ){
sqlite3_free(sqlite3_str_finish(pStr));
}else{
rc = sqlite3_str_errcode(pStr);
zOut = sqlite3_str_finish(pStr);
}
*pzOut = zOut;
}
}
return rc;
}
/*
** Apply posix advisory locks for all bytes from ofst through ofst+n-1.
**
@@ -4629,6 +4751,7 @@ static int unixOpenSharedMemory(unixFile *pDbFd){
** pShmNode->pShmMutex.
*/
sqlite3_mutex_enter(pShmNode->pShmMutex);
p->zShmlockName = pDbFd->zShmlockName;
p->pNext = pShmNode->pFirst;
pShmNode->pFirst = p;
sqlite3_mutex_leave(pShmNode->pShmMutex);
+21
View File
@@ -1126,6 +1126,24 @@ struct sqlite3_io_methods {
** in wal mode after the client has finished copying pages from the wal
** file to the database file, but before the *-shm file is updated to
** record the fact that the pages have been checkpointed.
**
** <li>[[SQLITE_FCNTL_SHMLOCK_GET]]
** This file-control is currently only supported by the "unix" VFS. The
** (void*) argument passed with this file-control must actually be
** a (char**) pointer. If the database is in wal-mode, the target of the
** pointer is set to point to a human readable description of all the
** wal-mode locks held on the database, by the current and external
** processes. It is the responsibility of the caller to eventually free
** this string using sqlite3_free(). See also the virtual table module in
** file "shmlockvtab.c" of the source distribution.
** </ul>
**
** <li>[[SQLITE_FCNTL_SHMLOCK_NAME]]
** This file-control is currently only supported by the "unix" VFS. The
** (void*) argument passed with this file-control should point to a
** nul-terminated string. A copy of this string will be used in reports
** returned by future calls to SQLITE_FCNTL_SHMLOCK_GET within the current
** process to identify this connection.
** </ul>
*/
#define SQLITE_FCNTL_LOCKSTATE 1
@@ -1167,6 +1185,9 @@ struct sqlite3_io_methods {
#define SQLITE_FCNTL_RESERVE_BYTES 38
#define SQLITE_FCNTL_CKPT_START 39
#define SQLITE_FCNTL_SHMLOCK_GET 40
#define SQLITE_FCNTL_SHMLOCK_NAME 41
/* deprecated names */
#define SQLITE_GET_LOCKPROXYFILE SQLITE_FCNTL_GET_LOCKPROXYFILE
#define SQLITE_SET_LOCKPROXYFILE SQLITE_FCNTL_SET_LOCKPROXYFILE
+32
View File
@@ -6271,6 +6271,35 @@ static int SQLITE_TCLAPI file_control_tempfilename(
return TCL_OK;
}
/*
** tclcmd: file_control_shmlock_name DB DBNAME SHMLOCKNAME
**
** Return a string that is a temporary filename
*/
static int SQLITE_TCLAPI file_control_shmlock_name(
ClientData clientData, /* Pointer to sqlite3_enable_XXX function */
Tcl_Interp *interp, /* The TCL interpreter that invoked this command */
int objc, /* Number of arguments */
Tcl_Obj *CONST objv[] /* Command arguments */
){
sqlite3 *db;
const char *zDbName = "main";
char *zName = 0;
if( objc!=4 ){
Tcl_WrongNumArgs(interp, 1, objv, "DB DBNAME SHMLOCKNAME");
return TCL_ERROR;
}
if( getDbPointer(interp, Tcl_GetString(objv[1]), &db) ){
return TCL_ERROR;
}
zDbName = Tcl_GetString(objv[2]);
zName = Tcl_GetString(objv[3]);
sqlite3_file_control(db, zDbName, SQLITE_FCNTL_SHMLOCK_NAME, (void*)zName);
return TCL_OK;
}
/*
** tclcmd: sqlite3_vfs_list
@@ -7292,6 +7321,7 @@ static int SQLITE_TCLAPI tclLoadStaticExtensionCmd(
extern int sqlite3_regexp_init(sqlite3*,char**,const sqlite3_api_routines*);
extern int sqlite3_remember_init(sqlite3*,char**,const sqlite3_api_routines*);
extern int sqlite3_series_init(sqlite3*,char**,const sqlite3_api_routines*);
extern int sqlite3_shmlockvtab_init(sqlite3*,char**,const sqlite3_api_routines*);
extern int sqlite3_spellfix_init(sqlite3*,char**,const sqlite3_api_routines*);
extern int sqlite3_totype_init(sqlite3*,char**,const sqlite3_api_routines*);
extern int sqlite3_wholenumber_init(sqlite3*,char**,const sqlite3_api_routines*);
@@ -7321,6 +7351,7 @@ static int SQLITE_TCLAPI tclLoadStaticExtensionCmd(
{ "regexp", sqlite3_regexp_init },
{ "remember", sqlite3_remember_init },
{ "series", sqlite3_series_init },
{ "shmlockvtab", sqlite3_shmlockvtab_init },
{ "spellfix", sqlite3_spellfix_init },
{ "totype", sqlite3_totype_init },
{ "unionvtab", sqlite3_unionvtab_init },
@@ -8161,6 +8192,7 @@ int Sqlitetest1_Init(Tcl_Interp *interp){
{ "file_control_powersafe_overwrite",file_control_powersafe_overwrite,0},
{ "file_control_vfsname", file_control_vfsname, 0 },
{ "file_control_tempfilename", file_control_tempfilename, 0 },
{ "file_control_shmlock_name", file_control_shmlock_name, 0 },
{ "sqlite3_vfs_list", vfs_list, 0 },
{ "sqlite3_create_function_v2", test_create_function_v2, 0 },
+147
View File
@@ -0,0 +1,147 @@
# 2015-08-19
#
# The author disclaims copyright to this source code. In place of
# a legal notice, here is a blessing:
#
# May you do good and not evil.
# May you find forgiveness for yourself and forgive others.
# May you share freely, never taking more than you give.
#
#***********************************************************************
#
# This file implements tests for table-valued-functions implemented using
# eponymous virtual tables.
#
set testdir [file dirname $argv0]
source $testdir/tester.tcl
source $testdir/lock_common.tcl
set testprefix shmlockvtab
ifcapable !vtab||!wal {
finish_test
return
}
load_static_extension db shmlockvtab
do_execsql_test 1.0 {
SELECT * FROM shmlock
}
do_execsql_test 1.1 {
CREATE TABLE t1(a, b);
PRAGMA journal_mode = wal;
SELECT * FROM t1;
SELECT lock, locktype, mxframe FROM shmlock;
} {
wal
READ(0) R 0
}
do_execsql_test 1.2 {
BEGIN EXCLUSIVE;
SELECT lock, locktype, coalesce(mxframe, 'NULL') FROM shmlock;
} {
WRITE W NULL
READ(0) R 0
}
do_execsql_test 1.3 {
INSERT INTO t1 VALUES(3, 4);
COMMIT;
SELECT lock, locktype, mxframe FROM shmlock;
} {
READ(1) R 1
}
do_execsql_test 1.4 {
INSERT INTO t1 VALUES(5, 6);
INSERT INTO t1 VALUES(8, 7);
SELECT lock, locktype, mxframe FROM shmlock;
} {
READ(1) R 3
}
do_test 1.5 {
execsql { SELECT connid FROM shmlock }
} {/[0123456789ABCDEF]*/}
do_test 1.6 {
file_control_shmlock_name db main thisdb
execsql { SELECT * FROM shmlock }
} {thisdb READ(1) R 3}
do_test 1.7 {
sqlite3 db2 test.db
load_static_extension db2 shmlockvtab
file_control_shmlock_name db2 main thisdb2
execsql { SELECT * FROM shmlock } db2
} {thisdb2 READ(1) R 3}
forcedelete test.db2
do_execsql_test 1.8 {
ATTACH 'test.db2' AS aux;
SELECT * FROM shmlock('aux');
} {}
do_execsql_test 1.9 {
SELECT * FROM shmlock('main');
} {thisdb READ(1) R 3}
do_test 1.10 {
file_control_shmlock_name db aux auxdb
execsql {
PRAGMA aux.journal_mode = wal;
CREATE TABLE aux.t1(x);
}
} {wal}
do_execsql_test 1.11 {
SELECT * FROM shmlock('aux');
} {}
do_execsql_test 1.12 {
BEGIN EXCLUSIVE;
INSERT INTO aux.t1 VALUES('val');
SELECT * FROM shmlock('aux');
} {
auxdb WRITE W {}
auxdb READ(1) R 2
}
file_control_shmlock_name db aux {Fred's database}
do_execsql_test 1.13 {
SELECT * FROM shmlock('aux');
} {
{Fred's database} WRITE W {}
{Fred's database} READ(1) R 2
}
do_multiclient_test tn {
code1 { load_static_extension db shmlockvtab }
do_test 2.$tn.0 {
sql1 {
CREATE TABLE t1(a, b);
PRAGMA journal_mode = wal;
INSERT INTO t1 VALUES('A', 'B');
}
} {wal}
do_test 2.$tn.1 {
sql2 {
BEGIN EXCLUSIVE;
SELECT * FROM t1
}
sql1 {
SELECT lock, locktype, mxframe FROM shmlock WHERE lock='WRITE'
}
} {WRITE W {}}
do_test 2.$tn.2 {
sql1 { SELECT lock, locktype, mxframe FROM shmlock ORDER BY 1 }
} {READ(1) R 1 READ(1) R 1 WRITE W {}}
}
finish_test