From aecc04d64269c8e798e98b1f049f2b974763fd92 Mon Sep 17 00:00:00 2001 From: dan Date: Fri, 2 Apr 2021 19:55:48 +0000 Subject: [PATCH] Add experimental SQLITE_FCNTL_EXTERNAL_READER file control. FossilOrigin-Name: e16da5af822ef31d7e05992403cf9787fbb3d9abb0b5283aba55ea07e1830a72 --- manifest | 19 +++++----- manifest.uuid | 2 +- src/os_unix.c | 39 +++++++++++++++++++++ src/sqlite.h.in | 15 ++++++++ src/test1.c | 37 ++++++++++++++++++++ test/external_reader.test | 74 +++++++++++++++++++++++++++++++++++++++ 6 files changed, 176 insertions(+), 10 deletions(-) create mode 100644 test/external_reader.test diff --git a/manifest b/manifest index eb22676dbf..cc229d23fc 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Fix\sa\sproblem\swith\sthe\sgeopoly_bbox()\sfunction. -D 2021-04-02T18:59:13.666 +C Add\sexperimental\sSQLITE_FCNTL_EXTERNAL_READER\sfile\scontrol. +D 2021-04-02T19:55:48.856 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724 @@ -526,7 +526,7 @@ F src/os.c 2d6e646370b1aa78942c68d7edf124e518963adf4a90bce87f365a5a5495529a F src/os.h 48388821692e87da174ea198bf96b1b2d9d83be5dfc908f673ee21fafbe0d432 F src/os_common.h b2f4707a603e36811d9b1a13278bffd757857b85 F src/os_setup.h 0dbaea40a7d36bf311613d31342e0b99e2536586 -F src/os_unix.c 7a9eab7b11f552ab91ead980086b312c7e3b871efdee8c0c072b682bbec6592e +F src/os_unix.c b5b7475bd1a8f1b83b6173a81f4fe50f9e077ccbacb62ce2fe7a5cb89916bce1 F src/os_win.c 77d39873836f1831a9b0b91894fec45ab0e9ca8e067dc8c549e1d1eca1566fe9 F src/os_win.h 7b073010f1451abe501be30d12f6bc599824944a F src/pager.c 970691daea03f9f15e34de671bd8675c1e136232b529e21bfd36d4dba6d41753 @@ -544,7 +544,7 @@ F src/resolve.c fc136d935f19966747663bed605ad7f06f84f9fe7bf7bf79e9bf844ef5c7556d F src/rowset.c ba9515a922af32abe1f7d39406b9d35730ed65efab9443dc5702693b60854c92 F src/select.c 2d7a1f3d9f5970962b407cb4b38416fedc1a3b55efd010b40af375088653b6ce F src/shell.c.in dcce260883836c9b58847505fbccce8d5546af925046f7dacd9443e922ece036 -F src/sqlite.h.in 3426a080ea1f222a73e3bd91e7eacbd30570a0117c03d42c6dde606f33e5e318 +F src/sqlite.h.in 18ec33e32001721fd4e9c4705a24a85dff04956ac2c0a21775058884ba845b09 F src/sqlite3.rc 5121c9e10c3964d5755191c80dd1180c122fc3a8 F src/sqlite3ext.h 61b38c073d5e1e96a3d45271b257aef27d0d13da2bea5347692ae579475cd95e F src/sqliteInt.h c5cfae5891a6e643116f66f63769bcffeba89ca51f6278732da710eb0cf092b6 @@ -552,7 +552,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 f5262c95b95aeb2b580101dc4657e7c0df5b25a5a9b7c456e2d3f463cef83fa9 +F src/test1.c 2100f4c28bae21ce83a9a0c5ec6827efd0e15d11b93b569b614daa5654b3fcf6 F src/test2.c 3efb99ab7f1fc8d154933e02ae1378bac9637da5 F src/test3.c 61798bb0d38b915067a8c8e03f5a534b431181f802659a6616f9b4ff7d872644 F src/test4.c 7c4420e01c577b5c4add2cb03119743b1a357543d347773b9e717195ea967159 @@ -888,6 +888,7 @@ F test/existsfault.test 72a0036c1424d9204d49f4d976c3277a1b8bb2eed3c67aa124ba2df2 F test/expr.test 26cd01e8485bc48c8aa6a1add598e9ce1e706b4eb4f3f554e0b0223022e8c2cf F test/expr2.test c27327ae9c017a7ff6280123f67aff496f912da74d78c888926d68b46ec75fd8 F test/extension01.test 00d13cec817f331a687a243e0e5a2d87b0e358c9 +F test/external_reader.test c7d34694f1b25c32d866f56ac80c1e29edddc42b4ef90cad589263ffac2cde0c F test/extraquick.test cb254400bd42bfb777ff675356aabf3287978f79 F test/fallocate.test 37a62e396a68eeede8f8d2ecf23573a80faceb630788d314d0a073d862616717 F test/filectrl.test 6e871c2d35dead1d9a88e176e8d2ca094fec6bb3 @@ -1911,7 +1912,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 fd4ea3f626b6e4957d56c2369be711895735cfc37cfde65650a6682ad5a3eb1a -R 6950494a507d409a31ba9db8450c7ab5 -U drh -Z 19246c5f3c2f1a430773bc8dc29b5ece +P f3a2eb979f1003e8249e613b34afd345f157c0d54b4f05ea0db230ef70e71351 +R 7052bf13a0b7767c57dc2a61770c6de9 +U dan +Z 52db8ae5f9c9b6085061c878477364c6 diff --git a/manifest.uuid b/manifest.uuid index 81c794e664..90bf8371f0 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -f3a2eb979f1003e8249e613b34afd345f157c0d54b4f05ea0db230ef70e71351 \ No newline at end of file +e16da5af822ef31d7e05992403cf9787fbb3d9abb0b5283aba55ea07e1830a72 \ No newline at end of file diff --git a/src/os_unix.c b/src/os_unix.c index e3cfe35cd2..adc89f5a7c 100644 --- a/src/os_unix.c +++ b/src/os_unix.c @@ -3951,6 +3951,7 @@ static void unixModeBit(unixFile *pFile, unsigned char mask, int *pArg){ /* Forward declaration */ static int unixGetTempname(int nBuf, char *zBuf); +static int unixFcntlExternalReader(unixFile*, int*); /* ** Information and control of an open file handle. @@ -4067,6 +4068,10 @@ static int unixFileControl(sqlite3_file *id, int op, void *pArg){ return proxyFileControl(id,op,pArg); } #endif /* SQLITE_ENABLE_LOCKING_STYLE && defined(__APPLE__) */ + + case SQLITE_FCNTL_EXTERNAL_READER: { + return unixFcntlExternalReader((unixFile*)id, (int*)pArg); + } } return SQLITE_NOTFOUND; } @@ -4312,6 +4317,40 @@ 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 */ +/* +** Use F_GETLK to check whether or not there are any readers with open +** wal-mode transactions in other processes on database file pFile. If +** no error occurs, return SQLITE_OK and set (*piOut) to 1 if there are +** such transactions, or 0 otherwise. If an error occurs, return an +** SQLite error code. The final value of *piOut is undefined in this +** case. +*/ +static int unixFcntlExternalReader(unixFile *pFile, int *piOut){ + int rc = SQLITE_OK; + *piOut = 0; + if( pFile->pShm){ + unixShmNode *pShmNode = pFile->pShm->pShmNode; + struct flock f; + + memset(&f, 0, sizeof(f)); + f.l_type = F_WRLCK; + f.l_whence = SEEK_SET; + f.l_start = UNIX_SHM_BASE + 3; + f.l_len = SQLITE_SHM_NLOCK - 3; + + sqlite3_mutex_enter(pShmNode->pShmMutex); + if( osFcntl(pShmNode->hShm, F_GETLK, &f)<0 ){ + rc = SQLITE_IOERR_LOCK; + }else{ + *piOut = (f.l_type!=F_UNLCK); + } + sqlite3_mutex_leave(pShmNode->pShmMutex); + } + + return rc; +} + + /* ** Apply posix advisory locks for all bytes from ofst through ofst+n-1. ** diff --git a/src/sqlite.h.in b/src/sqlite.h.in index c018767d13..9085dc882a 100644 --- a/src/sqlite.h.in +++ b/src/sqlite.h.in @@ -1128,6 +1128,19 @@ struct sqlite3_io_methods { ** file to the database file, but before the *-shm file is updated to ** record the fact that the pages have been checkpointed. ** +** +**
  • [[SQLITE_FCNTL_EXTERNAL_READER]] +** The EXPERIMENTAL [SQLITE_FCNTL_EXTERNAL_READER] opcode is used to detect +** whether or not there is a database client in another process with a wal-mode +** transaction open on the database or not. It is only available on unix.The +** (void*) argument passed with this file-control should be a pointer to a +** value of type (int). The integer value is set to 1 if the database is a wal +** mode database and there exists at least one client in another process that +** currently has an SQL transaction open on the database. It is set to 0 if +** the database is not a wal-mode db, or if there is no such connection in any +** other process. This opcode cannot be used to detect transactions opened +** by clients within the current process, only within other processes. +** */ #define SQLITE_FCNTL_LOCKSTATE 1 #define SQLITE_FCNTL_GET_LOCKPROXYFILE 2 @@ -1168,6 +1181,8 @@ struct sqlite3_io_methods { #define SQLITE_FCNTL_RESERVE_BYTES 38 #define SQLITE_FCNTL_CKPT_START 39 +#define SQLITE_FCNTL_EXTERNAL_READER 40 + /* deprecated names */ #define SQLITE_GET_LOCKPROXYFILE SQLITE_FCNTL_GET_LOCKPROXYFILE #define SQLITE_SET_LOCKPROXYFILE SQLITE_FCNTL_SET_LOCKPROXYFILE diff --git a/src/test1.c b/src/test1.c index 7d13da3733..7cb56d3be9 100644 --- a/src/test1.c +++ b/src/test1.c @@ -6514,6 +6514,42 @@ static int SQLITE_TCLAPI file_control_tempfilename( return TCL_OK; } +/* +** tclcmd: file_control_external_reader DB ?AUXDB? +** +** Return a string that is a temporary filename +*/ +static int SQLITE_TCLAPI file_control_external_reader( + 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 *zName = "main"; + int iRes = 0; + int rc = SQLITE_OK; + + if( objc!=2 && objc!=3 ){ + Tcl_AppendResult(interp, "wrong # args: should be \"", + Tcl_GetStringFromObj(objv[0], 0), " DB ?AUXDB?", 0); + return TCL_ERROR; + } + if( getDbPointer(interp, Tcl_GetString(objv[1]), &db) ){ + return TCL_ERROR; + } + if( objc==3 ){ + zName = Tcl_GetString(objv[2]); + } + rc = sqlite3_file_control(db, zName, SQLITE_FCNTL_EXTERNAL_READER, &iRes); + if( rc!=SQLITE_OK ){ + Tcl_SetResult(interp, (char *)t1ErrorName(rc), TCL_STATIC); + return TCL_ERROR; + } + Tcl_SetObjResult(interp, Tcl_NewIntObj(iRes)); + return TCL_OK; +} + /* ** tclcmd: sqlite3_vfs_list @@ -8452,6 +8488,7 @@ int Sqlitetest1_Init(Tcl_Interp *interp){ { "file_control_vfsname", file_control_vfsname, 0 }, { "file_control_reservebytes", file_control_reservebytes, 0 }, { "file_control_tempfilename", file_control_tempfilename, 0 }, + { "file_control_external_reader", file_control_external_reader, 0 }, { "sqlite3_vfs_list", vfs_list, 0 }, { "sqlite3_create_function_v2", test_create_function_v2, 0 }, diff --git a/test/external_reader.test b/test/external_reader.test new file mode 100644 index 0000000000..5d293981c5 --- /dev/null +++ b/test/external_reader.test @@ -0,0 +1,74 @@ +# 2021 April 2 +# +# 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. +# +#*********************************************************************** +# + +set testdir [file dirname $argv0] +source $testdir/tester.tcl +source $testdir/lock_common.tcl +set testprefix external_reader + +ifcapable !wal { + finish_test + return +} +if {$::tcl_platform(platform)!="unix"} { + finish_test + return +} + +do_multiclient_test tn { + + set bExternal 1 + if {[info commands db3]!=""} { set bExternal 0 } + + do_test 1.$tn.0 { + sql1 { + PRAGMA journal_mode = wal; + CREATE TABLE t1(a, b); + INSERT INTO t1 VALUES(1, 2); + } + } {wal} + + do_test 1.$tn.1 { + sql2 { SELECT * FROM t1 } + } {1 2} + + do_test 1.$tn.2 { + code1 { + file_control_external_reader db + } + } {0} + + do_test 1.$tn.3 { + sql2 { + BEGIN; + SELECT * FROM t1; + } + } {1 2} + + do_test 1.$tn.4 { + code1 { + file_control_external_reader db + } + } $bExternal + + do_test 1.$tn.5 { + sql2 { COMMIT } + } {} + + do_test 1.$tn.6 { + code1 { file_control_external_reader db } + } 0 + +} + + +finish_test