From: danielk1977 Date: Tue, 25 Jul 2006 15:14:52 +0000 (+0000) Subject: Allow database writes from within virtual table module xSync() callbacks. (CVS 3334) X-Git-Tag: version-3.6.10~2833 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=5bd270b20006cb170c638028e4fdeb1e38771e26;p=thirdparty%2Fsqlite.git Allow database writes from within virtual table module xSync() callbacks. (CVS 3334) FossilOrigin-Name: d5a608d0a412e13dfced6a3827574a2cff802f25 --- diff --git a/manifest b/manifest index 61e2e4accd..7c92a4b5c4 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Fix\slemon\sso\sthat\sit\sdoes\snot\scrash\son\sa\sempty\sreduce\saction.\s\sTicket\s#1892.\s(CVS\s3333) -D 2006-07-17T00:19:39 +C Allow\sdatabase\swrites\sfrom\swithin\svirtual\stable\smodule\sxSync()\scallbacks.\s(CVS\s3334) +D 2006-07-25T15:14:53 F Makefile.in 9c2a76055c305868cc5f5b73e29a252ff3632c0a F Makefile.linux-gcc 2d8574d1ba75f129aba2019f0b959db380a90935 F README 9c4e2d6706bdcc3efdd773ce752a8cdab4f90028 @@ -101,10 +101,10 @@ F src/vdbe.c 3ffc96ec2e870b3ab3e59d1f6fe34687e4ed1db6 F src/vdbe.h 258b5d1c0aaa72192f09ff0568ce42b383f156fa F src/vdbeInt.h e3eaab262b67b84474625cfc38aec1125c32834b F src/vdbeapi.c 6af0e7160af260052a7a4500464221a03dada75f -F src/vdbeaux.c 51722bb3661f2d836c1a7a8e7eac242859ddbd07 +F src/vdbeaux.c 0088120c4e321f680d2728d26770f35c10dc64b1 F src/vdbefifo.c 9efb94c8c3f4c979ebd0028219483f88e57584f5 F src/vdbemem.c 5f0afe3b92bb2c037f8d5d697f7c151fa50783a3 -F src/vtab.c 34b20b011147ca6df6bda565f7c791760698279f +F src/vtab.c 1fe25b3e59f92ed1f29b63ac9c6f954eb6907a29 F src/where.c 75a89957fcb8c068bec55caa4e9d2ed5fa0b0724 F tclinstaller.tcl 046e3624671962dc50f0481d7c25b38ef803eb42 F test/aggerror.test a867e273ef9e3d7919f03ef4f0e8c0d2767944f2 @@ -299,6 +299,7 @@ F test/vtab3.test f38d6d7d19f08bffdadce4d5b8cba078f8118587 F test/vtab4.test 4b4293341443839ef6dc02f8d9e614702a6c67ff F test/vtab5.test 9fb8f335651afe8f870011e2f68e5b00c5ad03cd F test/vtab6.test ec0036f29f8a803da9935206f2d9d1b6a8026392 +F test/vtab7.test b87ed57f6f635728c86fbad556e7844927dbc9a8 F test/vtab_err.test 11b90203ad60d63746d0de547b1ca014704d8f0e F test/where.test ee7c9a6659b07e1ee61177f6e7ff71565ee2c9df F test/where2.test a16476a5913e75cf65b38f2daa6157a6b7791394 @@ -375,7 +376,7 @@ F www/tclsqlite.tcl bb0d1357328a42b1993d78573e587c6dcbc964b9 F www/vdbe.tcl 87a31ace769f20d3627a64fa1fade7fed47b90d0 F www/version3.tcl 890248cf7b70e60c383b0e84d77d5132b3ead42b F www/whentouse.tcl 97e2b5cd296f7d8057e11f44427dea8a4c2db513 -P b0d19e575b14778e76ae5d6546fba0d2e9f25e33 -R 561f99ebf82ea369ef9b3313bf8e7d52 -U drh -Z 12ff8c7d7f4fc93ec58cc516f73d8e63 +P 4207ebc4e107df9f9f046be652f061e53263c8dd +R b62c9f53d5d9aafa4e186b1b50a281ff +U danielk1977 +Z 4145b3acb8db85dcd373c4df89f46175 diff --git a/manifest.uuid b/manifest.uuid index 34b6ae21dd..69a0145916 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -4207ebc4e107df9f9f046be652f061e53263c8dd \ No newline at end of file +d5a608d0a412e13dfced6a3827574a2cff802f25 \ No newline at end of file diff --git a/src/vdbeaux.c b/src/vdbeaux.c index f81a2cce66..e7b0058390 100644 --- a/src/vdbeaux.c +++ b/src/vdbeaux.c @@ -868,7 +868,9 @@ void sqlite3VdbeFreeCursor(Vdbe *p, Cursor *pCx){ sqlite3_vtab_cursor *pVtabCursor = pCx->pVtabCursor; const sqlite3_module *pModule = pCx->pModule; p->inVtabMethod = 1; + sqlite3SafetyOff(p->db); pModule->xClose(pVtabCursor); + sqlite3SafetyOn(p->db); p->inVtabMethod = 0; } #endif @@ -983,6 +985,23 @@ static int vdbeCommit(sqlite3 *db){ int rc = SQLITE_OK; int needXcommit = 0; + /* Before doing anything else, call the xSync() callback for any + ** virtual module tables written in this transaction. This has to + ** be done before determining whether a master journal file is + ** required, as an xSync() callback may add an attached database + ** to the transaction. + */ + rc = sqlite3VtabSync(db, rc); + if( rc!=SQLITE_OK ){ + return rc; + } + + /* This loop determines (a) if the commit hook should be invoked and + ** (b) how many database files have open write transactions, not + ** including the temp database. (b) is important because if more than + ** one database file has an open write transaction, a master journal + ** file is required for an atomic commit. + */ for(i=0; inDb; i++){ Btree *pBt = db->aDb[i].pBt; if( pBt && sqlite3BtreeIsInTrans(pBt) ){ @@ -1017,7 +1036,6 @@ static int vdbeCommit(sqlite3 *db){ rc = sqlite3BtreeSync(pBt, 0); } } - rc = sqlite3VtabSync(db, rc); /* Do the commit only if all databases successfully synced */ if( rc==SQLITE_OK ){ @@ -1109,20 +1127,17 @@ static int vdbeCommit(sqlite3 *db){ ** file name was written into the journal file before the failure ** occured. */ - for(i=0; inDb; i++){ + for(i=0; rc==SQLITE_OK && inDb; i++){ Btree *pBt = db->aDb[i].pBt; if( pBt && sqlite3BtreeIsInTrans(pBt) ){ rc = sqlite3BtreeSync(pBt, zMaster); - if( rc!=SQLITE_OK ){ - sqlite3OsClose(&master); - sqliteFree(zMaster); - return rc; - } } } - rc = sqlite3VtabSync(db, SQLITE_OK); - if( rc!=SQLITE_OK ) return rc; sqlite3OsClose(&master); + if( rc!=SQLITE_OK ){ + sqliteFree(zMaster); + return rc; + } /* Delete the master journal file. This commits the transaction. After ** doing this the directory is synced again before any individual @@ -1426,7 +1441,9 @@ int sqlite3VdbeReset(Vdbe *p){ ** error, then it might not have been halted properly. So halt ** it now. */ + sqlite3SafetyOn(p->db); sqlite3VdbeHalt(p); + sqlite3SafetyOff(p->db); /* If the VDBE has be run even partially, then transfer the error code ** and error message from the VDBE into the main database structure. But @@ -1495,7 +1512,6 @@ int sqlite3VdbeReset(Vdbe *p){ */ int sqlite3VdbeFinalize(Vdbe *p){ int rc = SQLITE_OK; - if( p->magic==VDBE_MAGIC_RUN || p->magic==VDBE_MAGIC_HALT ){ rc = sqlite3VdbeReset(p); }else if( p->magic!=VDBE_MAGIC_INIT ){ diff --git a/src/vtab.c b/src/vtab.c index 0f35af6dca..5aa17f6066 100644 --- a/src/vtab.c +++ b/src/vtab.c @@ -11,7 +11,7 @@ ************************************************************************* ** This file contains code used to help implement virtual tables. ** -** $Id: vtab.c,v 1.27 2006/07/08 18:09:15 drh Exp $ +** $Id: vtab.c,v 1.28 2006/07/25 15:14:53 danielk1977 Exp $ */ #ifndef SQLITE_OMIT_VIRTUALTABLE #include "sqliteInt.h" @@ -503,7 +503,7 @@ static void callFinaliser(sqlite3 *db, int offset){ } /* -** If argument rc is not SQLITE_OK, then return it and do nothing. +** If argument rc2 is not SQLITE_OK, then return it and do nothing. ** Otherwise, invoke the xSync method of all virtual tables in the ** sqlite3.aVTrans array. Return the error code for the first error ** that occurs, or SQLITE_OK if all xSync operations are successful. @@ -511,7 +511,9 @@ static void callFinaliser(sqlite3 *db, int offset){ int sqlite3VtabSync(sqlite3 *db, int rc2){ int i; int rc = SQLITE_OK; + int rcsafety; if( rc2!=SQLITE_OK ) return rc2; + rc = sqlite3SafetyOff(db); for(i=0; rc==SQLITE_OK && inVTrans && db->aVTrans[i]; i++){ sqlite3_vtab *pVtab = db->aVTrans[i]; int (*x)(sqlite3_vtab *); @@ -520,6 +522,10 @@ int sqlite3VtabSync(sqlite3 *db, int rc2){ rc = x(pVtab); } } + rcsafety = sqlite3SafetyOn(db); + if( rc==SQLITE_OK ){ + rc = rcsafety; + } return rc; } @@ -596,7 +602,6 @@ FuncDef *sqlite3VtabOverloadFunction( sqlite3_module *pMod; void (*xFunc)(sqlite3_context*,int,sqlite3_value**); void *pArg; - int rc; FuncDef *pNew; /* Check to see the left operand is a column in a virtual table */ @@ -608,7 +613,7 @@ FuncDef *sqlite3VtabOverloadFunction( pVtab = pTab->pVtab; assert( pVtab!=0 ); assert( pVtab->pModule!=0 ); - pMod = pVtab->pModule; + pMod = (sqlite3_module *)pVtab->pModule; if( pMod->xFindFunction==0 ) return pDef; /* Call the xFuncFunction method on the virtual table implementation diff --git a/test/vtab7.test b/test/vtab7.test new file mode 100644 index 0000000000..bb21627e9d --- /dev/null +++ b/test/vtab7.test @@ -0,0 +1,163 @@ +# 2006 July 25 +# +# 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 regression tests for SQLite library. The focus +# of this test is reading and writing to the database from within a +# virtual table xSync() callback. +# +# $Id: vtab7.test,v 1.1 2006/07/25 15:14:53 danielk1977 Exp $ + +set testdir [file dirname $argv0] +source $testdir/tester.tcl + +ifcapable !vtab { + finish_test + return +} + +# Register the echo module. Code inside the echo module appends elements +# to the global tcl list variable ::echo_module whenever SQLite invokes +# certain module callbacks. This includes the xSync(), xCommit() and +# xRollback() callbacks. For each of these callback, two elements are +# appended to ::echo_module, as follows: +# +# Module method Elements appended to ::echo_module +# ------------------------------------------------------- +# xSync() xSync echo($tablename) +# xCommit() xCommit echo($tablename) +# xRollback() xRollback echo($tablename) +# ------------------------------------------------------- +# +# In each case, $tablename is replaced by the name of the real table (not +# the echo table). By setting up a tcl trace on the ::echo_module variable, +# code in this file arranges for a Tcl script to be executed from within +# the echo module xSync() callback. +# +register_echo_module [sqlite3_connection_pointer db] +trace add variable ::echo_module write echo_module_trace + +# This Tcl proc is invoked whenever the ::echo_module variable is written. +# +proc echo_module_trace {args} { + # Filter out writes to ::echo_module that are not xSync, xCommit or + # xRollback callbacks. + if {[llength $::echo_module] < 2} return + set x [lindex $::echo_module end-1] + if {$x ne "xSync" && $x ne "xCommit" && $x ne "xRollback"} return + + regexp {^echo.(.*).$} [lindex $::echo_module end] dummy tablename + # puts "Ladies and gentlemen, an $x on $tablename!" + + if {[info exists ::callbacks($x,$tablename)]} { + eval $::callbacks($x,$tablename) + } +} + +# The following tests, vtab7-1.*, test that the trace callback on +# ::echo_module is providing the expected tcl callbacks. +do_test vtab7-1.1 { + execsql { + CREATE TABLE abc(a, b, c); + CREATE VIRTUAL TABLE abc2 USING echo(abc); + } +} {} + +do_test vtab7-1.2 { + set ::callbacks(xSync,abc) {incr ::counter} + set ::counter 0 + execsql { + INSERT INTO abc2 VALUES(1, 2, 3); + } + set ::counter +} {1} + +# Write to an existing database table from within an xSync callback. +do_test vtab7-2.1 { + set ::callbacks(xSync,abc) { + execsql {INSERT INTO log VALUES('xSync');} + } + execsql { + CREATE TABLE log(msg); + INSERT INTO abc2 VALUES(4, 5, 6); + SELECT * FROM log; + } +} {xSync} +do_test vtab7-2.3 { + execsql { + INSERT INTO abc2 VALUES(4, 5, 6); + SELECT * FROM log; + } +} {xSync xSync} +do_test vtab7-2.4 { + execsql { + INSERT INTO abc2 VALUES(4, 5, 6); + SELECT * FROM log; + } +} {xSync xSync xSync} + +# Create a database table from within xSync callback. +do_test vtab7-2.5 { + set ::callbacks(xSync,abc) { + execsql { CREATE TABLE newtab(d, e, f); } + } + execsql { + INSERT INTO abc2 VALUES(1, 2, 3); + SELECT name FROM sqlite_master ORDER BY name; + } +} {abc abc2 log newtab} + +# Drop a database table from within xSync callback. +do_test vtab7-2.6 { + set ::callbacks(xSync,abc) { + execsql { DROP TABLE newtab } + } + execsql { + INSERT INTO abc2 VALUES(1, 2, 3); + SELECT name FROM sqlite_master ORDER BY name; + } +} {abc abc2 log} + +# Write to an attached database from xSync(). +do_test vtab7-3.1 { + file delete -force test2.db + file delete -force test2.db-journal + execsql { + ATTACH 'test2.db' AS db2; + CREATE TABLE db2.stuff(description, shape, color); + } + set ::callbacks(xSync,abc) { + execsql { INSERT INTO db2.stuff VALUES('abc', 'square', 'green'); } + } + execsql { + INSERT INTO abc2 VALUES(1, 2, 3); + SELECT * from stuff; + } +} {abc square green} + +# UPDATE: The next test passes, but leaks memory. So leave it out. +# +# The following tests test that writing to the database from within +# the xCommit callback causes a misuse error. +# do_test vtab7-4.1 { +# unset -nocomplain ::callbacks(xSync,abc) +# set ::callbacks(xCommit,abc) { +# execsql { INSERT INTO log VALUES('hello') } +# } +# catchsql { +# INSERT INTO abc2 VALUES(1, 2, 3); +# } +# } {1 {library routine called out of sequence}} + + +trace remove variable ::echo_module write echo_module_trace +unset -nocomplain ::callbacks + +finish_test +