]> git.ipfire.org Git - thirdparty/sqlite.git/commitdiff
Add the SQLITE_FCNTL_DATA_VERSION file control
authordrh <drh@noemail.net>
Wed, 18 Jul 2018 19:09:07 +0000 (19:09 +0000)
committerdrh <drh@noemail.net>
Wed, 18 Jul 2018 19:09:07 +0000 (19:09 +0000)
FossilOrigin-Name: a5087c5c87ad65f92e3bc96bbc84afb43faf10ab6b9ed3ba16304b5c60ad069f

manifest
manifest.uuid
src/main.c
src/pager.c
src/shell.c.in
src/sqlite.h.in
src/test1.c
test/dataversion1.test [new file with mode: 0644]

index 150614f6383fdecacdf01e5eaa79598714e2b82d..31443dc45afb17c239b6004361d336265b88680b 100644 (file)
--- 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
index f477a0b101292ef14f6bf63a4619f8f39bf7589a..0d7d2629fabcfc0c425acb77b67b554bbc213080 100644 (file)
@@ -1 +1 @@
-4c70ea5b0e2a512c6a46ac1f14397720e8c8556875701e78c30b19f850c24f55
\ No newline at end of file
+a5087c5c87ad65f92e3bc96bbc84afb43faf10ab6b9ed3ba16304b5c60ad069f
\ No newline at end of file
index e0b1c23ad240c0bfb46a0f677793d78abe45347d..9c10cab6ae2bfa90d20724f9ee6aea145a190b89 100644 (file)
@@ -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);
     }
index 65591632ab7cd9ba706a735d44fa7bf8bce98d21..4794c5f73f2ed35cbe9f979bc1bab6f98d416b90 100644 (file)
@@ -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;
 }
 
index 66c0c5b67e26c83cf09ce243827eadf30c10fab5..ab1ec6af71447c9f8f7a380af1702ff5306ee5d8 100644 (file)
@@ -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;
 }
 
index 22c5339bdcc4cb7db6aa311902cbd55177db0af4..4e371360583c89622757a307cc7fd98f75855ca0 100644 (file)
@@ -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.
+**
+** <li>[[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.
 ** </ul>
 */
 #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*);
 ** <li> the [count_changes pragma]
 ** <li> the [changes() SQL function]
 ** <li> the [data_version pragma]
+** <li> the [SQLITE_FCNTL_DATA_VERSION] [file control]
 ** </ul>
 */
 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
index 127650537d3dedffc410545658a85259308a7b78..9ffc2e2a8246032ed62c54325eb86421f2d0b8a0 100644 (file)
@@ -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 (file)
index 0000000..55f7aad
--- /dev/null
@@ -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