diff --git a/manifest b/manifest index 150614f638..31443dc45a 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Fix\sa\sharmless\scompiler\swarning\sin\sthe\spager.\s\sEnhance\sthe\sdocs\sfor\nsqlite3_changes()\sand\ssqlite3_total_changes()\sto\srefer\sto\sthe\sdata_version\npragma. -D 2018-07-18T17:37:51.142 +C Add\sthe\sSQLITE_FCNTL_DATA_VERSION\sfile\scontrol +D 2018-07-18T19:09:07.674 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea F Makefile.in 0a3a6c81e6fcb969ff9106e882f0a08547014ba463cb6beca4c4efaecc924ee6 @@ -461,7 +461,7 @@ F src/in-operator.md 10cd8f4bcd225a32518407c2fb2484089112fd71 F src/insert.c 8613af9c5ba1503bc68f4e9432c6c024e0fdafdc791575c50f380f73ec91189f F src/legacy.c 134ab3e3fae00a0f67a5187981d6935b24b337bcf0f4b3e5c9fa5763da95bf4e F src/loadext.c 6aae5739198d96c51ae6eb97c4a5b1744c22ed7a5a565a5399a717780d48a36b -F src/main.c a67aa346d68608a449aca06a975056c4a5b6c193290e09539efaeb3664ed3707 +F src/main.c 9c8ef68bd3779d493d632946b3a65156b2e636ad2c7a1b7cd1450e87dc0c6a7d F src/malloc.c 07295435093ce354c6d9063ac05a2eeae28bd251d2e63c48b3d67c12c76f7e18 F src/mem0.c 6a55ebe57c46ca1a7d98da93aaa07f99f1059645 F src/mem1.c c12a42539b1ba105e3707d0e628ad70e611040d8f5e38cf942cee30c867083de @@ -484,7 +484,7 @@ F src/os_setup.h 0dbaea40a7d36bf311613d31342e0b99e2536586 F src/os_unix.c c230a7a24766320d8414afd087edcd43e499fb45e86361f6f4f464f343d965a9 F src/os_win.c ac29c25cde4cfb4adacc59cdec4aa45698ca0e29164ea127859585ccd9faa354 F src/os_win.h 7b073010f1451abe501be30d12f6bc599824944a -F src/pager.c c4269e3481aa3cc5325a52b2d2663bf80f0c6eb0b0adfef7691cd6b113619eb9 +F src/pager.c 29c48653efacf4f3a0b0e193515b789a1e9eee7d232816704cef95216906baa3 F src/pager.h c571b064df842ec8f2e90855dead9acf4cbe0d1b2c05afe0ef0d0145f7fd0388 F src/parse.y 3bd43415ea974b9921b0ff2c0bd3e9100f6e501ede0b6d3b90cca2ab6af25485 F src/pcache.c 135ef0bc6fb2e3b7178d49ab5c9176254c8a691832c1bceb1156b2fbdd0869bd @@ -498,8 +498,8 @@ F src/random.c 80f5d666f23feb3e6665a6ce04c7197212a88384 F src/resolve.c 797088662ed61102485e3070ba3b3f7828bd5ef6a588223ba6865d77d52f6cea F src/rowset.c 7b7e7e479212e65b723bf40128c7b36dc5afdfac F src/select.c 2e9661d4424f43ccf595c4a7b4acdf32db523c0f6b31cbd62e6e5a2f43118981 -F src/shell.c.in f1c79c537117ee61317a5ed85cdbcb854998cd690eb34ab803779358a2ace780 -F src/sqlite.h.in 679a25353c90ccd83b16207888704d09bc0e5d5bd6fc1828e7ac653336ee4575 +F src/shell.c.in 239aee5570703fcbf9e6bec176a3ba7704bf327515f9f0dbd2498f00746e82b6 +F src/sqlite.h.in 6203acde410e38d96ffb24e9b6349f87eb80914ba5676466ac5d8478b6b8f144 F src/sqlite3.rc 5121c9e10c3964d5755191c80dd1180c122fc3a8 F src/sqlite3ext.h 9887b27e69c01e79c2cbe74ef73bf01af5b5703d6a7f0a4371e386d7249cb1c7 F src/sqliteInt.h c2ceebe60d1d2e11674b90c8b55fdffd91386ce8d7ae38613fbcc61659b8fcac @@ -507,7 +507,7 @@ F src/sqliteLimit.h 1513bfb7b20378aa0041e7022d04acb73525de35b80b252f1b83fedb4de6 F src/status.c 46e7aec11f79dad50965a5ca5fa9de009f7d6bde08be2156f1538a0a296d4d0e F src/table.c b46ad567748f24a326d9de40e5b9659f96ffff34 F src/tclsqlite.c 916a92de77ec5cbe27818ca194d8cf0c58aa7ad5b87527098f6aa5a6068800ce -F src/test1.c ca6bdbbffcf8322de014570741c0d627e81d441b9e6464cc349538bd899ef2ca +F src/test1.c 335740ddc632c0b54765b6fd373da7f76a397adde3ded3592390dd1e5fb0dd55 F src/test2.c 3efb99ab7f1fc8d154933e02ae1378bac9637da5 F src/test3.c 61798bb0d38b915067a8c8e03f5a534b431181f802659a6616f9b4ff7d872644 F src/test4.c 18ec393bb4d0ad1de729f0b94da7267270f3d8e6 @@ -749,6 +749,7 @@ F test/csv01.test 6e1445b3207d574cff22fc41a8e549dfcf2466ee90546ada97d22a90fa89eb F test/ctime.test 78749e6c9a5f0010d67985be80788f841e3cd2da18114e2ed6010399a7d807f3 F test/cursorhint.test 7bc346788390475e77a345da2b92270d04d35856 F test/cursorhint2.test 0078ae1ded4afcf5eb80d06e3a72b6e1c3f1a646aab26eeb583b0a9ec6f0d56e +F test/dataversion1.test 6e5e86ac681f0782e766ebcb56c019ae001522d114e0e111e5ebf68ccf2a7bb8 F test/date.test 9b73bbeb1b82d9c1f44dec5cf563bf7da58d2373 F test/date2.test 74c234bece1b016e94dd4ef9c8cc7a199a8806c0e2291cab7ba64bace6350b10 F test/dbfuzz.c 73047c920d6210e5912c87cdffd9a1c281d4252e @@ -1749,7 +1750,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 2e0357c2ed30927921cd17129e348a18a6f3fd086d1fc0a42756036b321a084d -R 471d432eb2ed159696aafa5a0bf72e5a +P 4c70ea5b0e2a512c6a46ac1f14397720e8c8556875701e78c30b19f850c24f55 +R 77c9c3d812b42dbbcdaf40ac46fb4612 U drh -Z 5df8cf4ea35c08c158f5d1145417612b +Z 68e40ab59c72a085fa81a9bdc778d1c6 diff --git a/manifest.uuid b/manifest.uuid index f477a0b101..0d7d2629fa 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -4c70ea5b0e2a512c6a46ac1f14397720e8c8556875701e78c30b19f850c24f55 \ No newline at end of file +a5087c5c87ad65f92e3bc96bbc84afb43faf10ab6b9ed3ba16304b5c60ad069f \ No newline at end of file diff --git a/src/main.c b/src/main.c index e0b1c23ad2..9c10cab6ae 100644 --- a/src/main.c +++ b/src/main.c @@ -3711,6 +3711,9 @@ int sqlite3_file_control(sqlite3 *db, const char *zDbName, int op, void *pArg){ }else if( op==SQLITE_FCNTL_JOURNAL_POINTER ){ *(sqlite3_file**)pArg = sqlite3PagerJrnlFile(pPager); rc = SQLITE_OK; + }else if( op==SQLITE_FCNTL_DATA_VERSION ){ + *(unsigned int*)pArg = sqlite3PagerDataVersion(pPager); + rc = SQLITE_OK; }else{ rc = sqlite3OsFileControl(fd, op, pArg); } diff --git a/src/pager.c b/src/pager.c index 65591632ab..4794c5f73f 100644 --- a/src/pager.c +++ b/src/pager.c @@ -1764,7 +1764,6 @@ static void pager_reset(Pager *pPager){ ** Return the pPager->iDataVersion value */ u32 sqlite3PagerDataVersion(Pager *pPager){ - assert( pPager->eState>PAGER_OPEN ); return pPager->iDataVersion; } diff --git a/src/shell.c.in b/src/shell.c.in index 66c0c5b67e..ab1ec6af71 100644 --- a/src/shell.c.in +++ b/src/shell.c.in @@ -4354,6 +4354,7 @@ static int shell_dbinfo_command(ShellState *p, int nArg, char **azArg){ "SELECT total(length(sql)) FROM %s" }, }; int i; + unsigned iDataVersion; char *zSchemaTab; char *zDb = nArg>=2 ? azArg[1] : "main"; sqlite3_stmt *pStmt = 0; @@ -4406,6 +4407,8 @@ static int shell_dbinfo_command(ShellState *p, int nArg, char **azArg){ utf8_printf(p->out, "%-20s %d\n", aQuery[i].zName, val); } sqlite3_free(zSchemaTab); + sqlite3_file_control(p->db, zDb, SQLITE_FCNTL_DATA_VERSION, &iDataVersion); + utf8_printf(p->out, "%-20s %u\n", "data version", iDataVersion); return 0; } diff --git a/src/sqlite.h.in b/src/sqlite.h.in index 22c5339bdc..4e37136058 100644 --- a/src/sqlite.h.in +++ b/src/sqlite.h.in @@ -1073,6 +1073,25 @@ struct sqlite3_io_methods { ** a file lock using the xLock or xShmLock methods of the VFS to wait ** for up to M milliseconds before failing, where M is the single ** unsigned integer parameter. +** +**
  • [[SQLITE_FCNTL_DATA_VERSION]] +** The [SQLITE_FCNTL_DATA_VERSION] opcode is used to detect changes to +** a database file. The argument is a pointer to a 32-bit unsigned integer. +** The "data version" for the pager is written into the pointer. The +** "data version" changes whenever any change occurs to the corresponding +** database file, either through SQL statements on the same database +** connection, or through transactions committed by separate database +** connections possibly in other processes. The [sqlite3_total_changes()] +** interface can be used to find if any database on the connection has changed, +** but that interface response to changes on TEMP as well as MAIN and does +** not provide a mechanism to detect changes to MAIN only. Also, the +** [sqlite3_total_changes()] interface response to internal changes only and +** omits changes made by other database connections. The +** [PRAGMA data_version] command provide a mechanism to detect changes to +** a single attached database that occur due to other database connections, +** but omits changes implemented by the database connection for which it is +** called. This file control is the only mechanism to detect changes that +** happen either internally or externally on a single database. ** */ #define SQLITE_FCNTL_LOCKSTATE 1 @@ -1108,6 +1127,7 @@ struct sqlite3_io_methods { #define SQLITE_FCNTL_COMMIT_ATOMIC_WRITE 32 #define SQLITE_FCNTL_ROLLBACK_ATOMIC_WRITE 33 #define SQLITE_FCNTL_LOCK_TIMEOUT 34 +#define SQLITE_FCNTL_DATA_VERSION 35 /* deprecated names */ #define SQLITE_GET_LOCKPROXYFILE SQLITE_FCNTL_GET_LOCKPROXYFILE @@ -2304,6 +2324,13 @@ int sqlite3_changes(sqlite3*); ** count, but those made as part of REPLACE constraint resolution are ** not. ^Changes to a view that are intercepted by INSTEAD OF triggers ** are not counted. +** +** This the [sqlite3_total_changes(D)] interface only reports the number +** of rows that changed due to SQL statement run against database +** connection D. Any changes by other database connections are ignored. +** To detect changes against a database file from other database +** connections use the [PRAGMA data_version] command or the +** [SQLITE_FCNTL_DATA_VERSION] [file control]. ** ** If a separate thread makes changes on the same database connection ** while [sqlite3_total_changes()] is running then the value @@ -2315,6 +2342,7 @@ int sqlite3_changes(sqlite3*); **
  • the [count_changes pragma] **
  • the [changes() SQL function] **
  • the [data_version pragma] +**
  • the [SQLITE_FCNTL_DATA_VERSION] [file control] ** */ int sqlite3_total_changes(sqlite3*); @@ -7087,6 +7115,7 @@ sqlite3_mutex *sqlite3_db_mutex(sqlite3*); /* ** CAPI3REF: Low-Level Control Of Database Files ** METHOD: sqlite3 +** KEYWORDS: {file control} ** ** ^The [sqlite3_file_control()] interface makes a direct call to the ** xFileControl method for the [sqlite3_io_methods] object associated @@ -7101,11 +7130,18 @@ sqlite3_mutex *sqlite3_db_mutex(sqlite3*); ** the xFileControl method. ^The return value of the xFileControl ** method becomes the return value of this routine. ** +** A few opcodes for [sqlite3_file_control()] are handled directly +** by the SQLite core and never invoke the +** sqlite3_io_methods.xFileControl method. ** ^The [SQLITE_FCNTL_FILE_POINTER] value for the op parameter causes ** a pointer to the underlying [sqlite3_file] object to be written into -** the space pointed to by the 4th parameter. ^The [SQLITE_FCNTL_FILE_POINTER] -** case is a short-circuit path which does not actually invoke the -** underlying sqlite3_io_methods.xFileControl method. +** the space pointed to by the 4th parameter. The +** [SQLITE_FCNTL_JOURNAL_POINTER] works similarly except that it returns +** the [sqlite3_file] object associated with the journal file instead of +** the main database. The [SQLITE_FCNTL_VFS_POINTER] opcode returns +** a pointer to the underlying [sqlite3_vfs] object for the file. +** The [SQLITE_FCNTL_DATA_VERSION] returns the data version counter +** from the pager. ** ** ^If the second parameter (zDbName) does not match the name of any ** open database file, then SQLITE_ERROR is returned. ^This error diff --git a/src/test1.c b/src/test1.c index 127650537d..9ffc2e2a82 100644 --- a/src/test1.c +++ b/src/test1.c @@ -5684,6 +5684,44 @@ static int SQLITE_TCLAPI file_control_lasterrno_test( return TCL_OK; } +/* +** tclcmd: file_control_data_version DB DBNAME +** +** This TCL command runs the sqlite3_file_control with the +** SQLITE_FCNTL_DATA_VERSION opcode, returning the result. +*/ +static int SQLITE_TCLAPI file_control_data_version( + 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 */ +){ + unsigned int iVers; /* data version */ + char *zDb; /* Db name ("main", "temp" etc.) */ + sqlite3 *db; /* Database handle */ + int rc; /* file_control() return code */ + char zBuf[100]; + + if( objc!=3 && objc!=2 ){ + Tcl_WrongNumArgs(interp, 1, objv, "DB [DBNAME]"); + return TCL_ERROR; + } + if( getDbPointer(interp, Tcl_GetString(objv[1]), &db) ){ + return TCL_ERROR; + } + zDb = objc==3 ? Tcl_GetString(objv[2]) : NULL; + + rc = sqlite3_file_control(db, zDb, SQLITE_FCNTL_DATA_VERSION, (void *)&iVers); + if( rc ){ + Tcl_SetResult(interp, (char *)sqlite3ErrName(rc), TCL_STATIC); + return TCL_ERROR; + }else{ + sqlite3_snprintf(sizeof(zBuf),zBuf,"%u",iVers); + Tcl_SetResult(interp, (char *)zBuf, TCL_VOLATILE); + return TCL_OK; + } +} + /* ** tclcmd: file_control_chunksize_test DB DBNAME SIZE ** @@ -7700,6 +7738,7 @@ int Sqlitetest1_Init(Tcl_Interp *interp){ { "file_control_lockproxy_test", file_control_lockproxy_test, 0 }, { "file_control_chunksize_test", file_control_chunksize_test, 0 }, { "file_control_sizehint_test", file_control_sizehint_test, 0 }, + { "file_control_data_version", file_control_data_version, 0 }, #if SQLITE_OS_WIN { "file_control_win32_av_retry", file_control_win32_av_retry, 0 }, { "file_control_win32_get_handle", file_control_win32_get_handle, 0 }, diff --git a/test/dataversion1.test b/test/dataversion1.test new file mode 100644 index 0000000000..55f7aada0c --- /dev/null +++ b/test/dataversion1.test @@ -0,0 +1,87 @@ +# 2018-07-18 +# +# 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. +# +#*********************************************************************** +# +# Test case for SQLITE_FCNTL_DATA_VERSION +# + +set testdir [file dirname $argv0] +source $testdir/tester.tcl + +# Construct a database and get its initial data version +sqlite3 db test.db +do_test dataversion1-100 { + db eval { + CREATE TABLE t1(x); + INSERT INTO t1(x) VALUES(99); + SELECT * FROM t1; + } +} {99} +set dv1 [file_control_data_version db main] + +# The data version does not change by ATTACH or by changes to +# other schemas within the same connection. +# +do_test dataversion1-101 { + db eval { + ATTACH ':memory:' AS aux1; + CREATE TABLE aux1.t2(y); + CREATE TEMP TABLE t3(z); + } + file_control_data_version db main +} $dv1 + +# The data version does change when SQL modifies the table +do_test dataversion1-110 { + db eval { + UPDATE t1 SET x=x+1; + } + set dv2 [file_control_data_version db] + expr {$::dv1==$dv2} +} {0} + +# But the data version is constant if there are changes to other +# schemas +set dv1 [file_control_data_version db main] +do_test dataversion1-120 { + db eval { + UPDATE t2 SET y=y+1; + } + file_control_data_version db +} $dv1 + +# Changes to the database via another connection are not detected +# until there is a read transaction. +# +sqlite3 db2 test.db +do_test dataversion1-130 { + db2 eval { + SELECT * FROM t1 + } +} {100} +do_test dataversion1-131 { + file_control_data_version db +} $dv1 +do_test dataversion1-132 { + db2 eval { + UPDATE t1 SET x=x+1; + } + set dv2 [file_control_data_version db] + expr {$::dv1==$dv2} +} {1} +do_test dataversion1-133 { + db eval {SELECT * FROM t1} + set dv2 [file_control_data_version db] + expr {$::dv1==$dv2} +} {0} + + + +finish_test