Compare commits
3 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 922703138e | |||
| 148920e52a | |||
| b7ae3b541c |
@@ -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;
|
||||
}
|
||||
@@ -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 \
|
||||
|
||||
@@ -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
@@ -1 +1 @@
|
||||
d34caf3bb63d0512ea116a8c8c8343b76aa39441bd4b3e98231747a705b91d54
|
||||
1c6a8442735e9f6117d7a8f1d88630958ec8fdb25a75cc392af1e8511a5fc56d
|
||||
+124
-1
@@ -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);
|
||||
|
||||
@@ -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
@@ -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 },
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user