Add the SQLITE_FCNTL_DATA_VERSION file control

FossilOrigin-Name: a5087c5c87ad65f92e3bc96bbc84afb43faf10ab6b9ed3ba16304b5c60ad069f
This commit is contained in:
drh 2018-07-18 19:09:07 +00:00
parent 378a2da91e
commit ea99a31c59
8 changed files with 183 additions and 15 deletions

View File

@ -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

View File

@ -1 +1 @@
4c70ea5b0e2a512c6a46ac1f14397720e8c8556875701e78c30b19f850c24f55
a5087c5c87ad65f92e3bc96bbc84afb43faf10ab6b9ed3ba16304b5c60ad069f

View 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);
}

View 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;
}

View 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;
}

View 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

View 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 },

87
test/dataversion1.test Normal file
View File

@ -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