From: drh Date: Fri, 16 Jun 2006 21:13:21 +0000 (+0000) Subject: Rework the way UPDATE works for virtual tables. (CVS 3262) X-Git-Tag: version-3.6.10~2905 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=9c41938ff8cb5f043e412d37f0bc2c389c30423e;p=thirdparty%2Fsqlite.git Rework the way UPDATE works for virtual tables. (CVS 3262) FossilOrigin-Name: 2119e7bf5577350e4e1236ea729568085620a826 --- diff --git a/manifest b/manifest index 4a4feee934..42d838a29d 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Add\scode\sto\sinvoke\sthe\svirtual\stable\stransaction\sinterface.\sUntested\sat\sthis\spoint.\s(CVS\s3261) -D 2006-06-16T16:08:54 +C Rework\sthe\sway\sUPDATE\sworks\sfor\svirtual\stables.\s(CVS\s3262) +D 2006-06-16T21:13:22 F Makefile.in f839b470345d3cb4b0644068474623fe2464b5d3 F Makefile.linux-gcc 2d8574d1ba75f129aba2019f0b959db380a90935 F README 9c4e2d6706bdcc3efdd773ce752a8cdab4f90028 @@ -69,12 +69,12 @@ F src/pragma.c 27d5e395c5d950931c7ac4fe610e7c2993e2fa55 F src/prepare.c 6dc945dab34cf97364c661d2b7a12be65d338267 F src/printf.c 7029e5f7344a478394a02c52837ff296ee1ab240 F src/random.c d40f8d356cecbd351ccfab6eaedd7ec1b54f5261 -F src/select.c 38eda11d950ed5e631ea9054f84a4a8b9e9b39d8 +F src/select.c c1965c636482bf96754c518b91600f81e17f5c85 F src/server.c 087b92a39d883e3fa113cae259d64e4c7438bc96 F src/shell.c ad73192b30a338a58fe81183d4a5d5a1d4e51d36 F src/sqlite.h.in 77b42d1bb5deafa04ae156d24cd243c2d75800ac -F src/sqlite3ext.h fc8647211af0caa9d8e49ab31624b357c1332380 -F src/sqliteInt.h 5f8de04c331fb6c80e70e309f550c8cc8efb6e1f +F src/sqlite3ext.h e334107f6cad0d00c0414e04189742a45ce916b1 +F src/sqliteInt.h dc69927751ee107aa5232e5c3aa7dd1d4329bb15 F src/table.c f64ec4fbfe333f8df925bc6ba494f55e05b0e75e F src/tclsqlite.c c408a49ae44525fc69757b4ed39f6ca0f56549a5 F src/test1.c 40f20775903bc76d3be3e7c026dddcbc221c1cb0 @@ -93,11 +93,11 @@ F src/test_server.c a6460daed0b92ecbc2531b6dc73717470e7a648c F src/test_tclvar.c c52f67fbe06d32804af2ba9a2d7aadfc15f5910c F src/tokenize.c 6ebcafa6622839968dda4418a7b6945f277a128f F src/trigger.c 0fc40125820409a6274834a6e04ad804d96e2793 -F src/update.c d3b991905ed8b26feb2055bdab40ce3c42e1b522 +F src/update.c 4fb474fab5b51fdcdee27f23a41c843c4b23ebe2 F src/utf.c ab81ac59084ff1c07d421eb1a0a84ec809603b44 F src/util.c ca6ee72772c0f5dc04d2e0ab1973fd3b6a9bf79d F src/vacuum.c 5b37d0f436f8e1ffacd17934e44720b38d2247f9 -F src/vdbe.c 704940e7c0c811d721a4eac0f4bb3da4f4f88dc9 +F src/vdbe.c 4b55bfea65855201b8f2e07102ad70ea1d21b7f6 F src/vdbe.h 258b5d1c0aaa72192f09ff0568ce42b383f156fa F src/vdbeInt.h 6ccb7eaae76ebd761470f6a035501ff33aa92c20 F src/vdbeapi.c 6af0e7160af260052a7a4500464221a03dada75f @@ -291,7 +291,7 @@ F test/vacuum.test 37f998b841cb335397c26d9bbc3457182af2565f F test/vacuum2.test 5aea8c88a65cb29f7d175296e7c819c6158d838c F test/varint.test ab7b110089a08b9926ed7390e7e97bdefeb74102 F test/view.test 16e2774fe35e47a07ac4471b7f0bcc948b1aa6d5 -F test/vtab1.test a31c323cf158bf7326b3a4af4156b1bdd98cf8a8 +F test/vtab1.test 028298f87b9a1a93536fc8f8a90c1e342109543f F test/vtab3.test b3ea5dfdc36ba23ba5136928b6c307c5125ababc F test/where.test ee7c9a6659b07e1ee61177f6e7ff71565ee2c9df F test/where2.test a16476a5913e75cf65b38f2daa6157a6b7791394 @@ -368,7 +368,7 @@ F www/tclsqlite.tcl bb0d1357328a42b1993d78573e587c6dcbc964b9 F www/vdbe.tcl 87a31ace769f20d3627a64fa1fade7fed47b90d0 F www/version3.tcl 890248cf7b70e60c383b0e84d77d5132b3ead42b F www/whentouse.tcl 97e2b5cd296f7d8057e11f44427dea8a4c2db513 -P 9497c66e5533ec143d0efda4a419e4bdf922ae8c -R d4bc1ee77da86ad3a4a5c0b77f2ab8e7 -U danielk1977 -Z d74361b9f31a7257bb399e37ec5aacb7 +P 6125140228e09cad2029a48e92aa0123d3daecfb +R c6423d78b30f09b39a0d99eff61f3284 +U drh +Z 58b933b2b7f2511ff044743b3247c932 diff --git a/manifest.uuid b/manifest.uuid index 94fa1cadbe..ddade3625a 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -6125140228e09cad2029a48e92aa0123d3daecfb \ No newline at end of file +2119e7bf5577350e4e1236ea729568085620a826 \ No newline at end of file diff --git a/src/select.c b/src/select.c index 5dd23d46fb..cf46d69c2b 100644 --- a/src/select.c +++ b/src/select.c @@ -12,7 +12,7 @@ ** This file contains C code routines that are called by the parser ** to handle SELECT statements in SQLite. ** -** $Id: select.c,v 1.314 2006/06/11 23:41:56 drh Exp $ +** $Id: select.c,v 1.315 2006/06/16 21:13:22 drh Exp $ */ #include "sqliteInt.h" @@ -187,7 +187,7 @@ static void setToken(Token *p, const char *z){ /* ** Create an expression node for an identifier with the name of zName */ -static Expr *createIdExpr(const char *zName){ +Expr *sqlite3CreateIdExpr(const char *zName){ Token dummy; setToken(&dummy, zName); return sqlite3Expr(TK_ID, 0, 0, &dummy); @@ -211,16 +211,16 @@ static void addWhereTerm( Expr *pE2a, *pE2b, *pE2c; Expr *pE; - pE1a = createIdExpr(zCol); - pE2a = createIdExpr(zCol); + pE1a = sqlite3CreateIdExpr(zCol); + pE2a = sqlite3CreateIdExpr(zCol); if( zAlias1==0 ){ zAlias1 = pTab1->zName; } - pE1b = createIdExpr(zAlias1); + pE1b = sqlite3CreateIdExpr(zAlias1); if( zAlias2==0 ){ zAlias2 = pTab2->zName; } - pE2b = createIdExpr(zAlias2); + pE2b = sqlite3CreateIdExpr(zAlias2); pE1c = sqlite3Expr(TK_DOT, pE1b, pE1a, 0); pE2c = sqlite3Expr(TK_DOT, pE2b, pE2a, 0); pE = sqlite3Expr(TK_EQ, pE1c, pE2c, 0); diff --git a/src/sqlite3ext.h b/src/sqlite3ext.h index 5de71f9804..a1ed67f86e 100644 --- a/src/sqlite3ext.h +++ b/src/sqlite3ext.h @@ -15,7 +15,7 @@ ** as extensions by SQLite should #include this file instead of ** sqlite3.h. ** -** @(#) $Id: sqlite3ext.h,v 1.2 2006/06/15 15:38:42 danielk1977 Exp $ +** @(#) $Id: sqlite3ext.h,v 1.3 2006/06/16 21:13:22 drh Exp $ */ #ifndef _SQLITE3EXT_H_ #define _SQLITE3EXT_H_ @@ -74,7 +74,7 @@ struct sqlite3_api_routines { int (*create_collation16)(sqlite3*,const char*,int,void*,int(*)(void*,int,const void*,int,const void*)); int (*create_function)(sqlite3*,const char*,int,int,void*,void (*xFunc)(sqlite3_context*,int,sqlite3_value**),void (*xStep)(sqlite3_context*,int,sqlite3_value**),void (*xFinal)(sqlite3_context*)); int (*create_function16)(sqlite3*,const void*,int,int,void*,void (*xFunc)(sqlite3_context*,int,sqlite3_value**),void (*xStep)(sqlite3_context*,int,sqlite3_value**),void (*xFinal)(sqlite3_context*)); - int (*create_module)(sqlite3*,const char*,sqlite3_module*,void*); + int (*create_module)(sqlite3*,const char*,const sqlite3_module*,void*); int (*data_count)(sqlite3_stmt*pStmt); sqlite3 * (*db_handle)(sqlite3_stmt*); int (*declare_vtab)(sqlite3*,const char*); diff --git a/src/sqliteInt.h b/src/sqliteInt.h index 95d2966141..a470260377 100644 --- a/src/sqliteInt.h +++ b/src/sqliteInt.h @@ -11,7 +11,7 @@ ************************************************************************* ** Internal interface definitions for SQLite. ** -** @(#) $Id: sqliteInt.h,v 1.508 2006/06/16 16:08:55 danielk1977 Exp $ +** @(#) $Id: sqliteInt.h,v 1.509 2006/06/16 21:13:22 drh Exp $ */ #ifndef _SQLITEINT_H_ #define _SQLITEINT_H_ @@ -1631,6 +1631,7 @@ int sqlite3ExprResolveNames(NameContext *, Expr *); int sqlite3ExprAnalyzeAggregates(NameContext*, Expr*); int sqlite3ExprAnalyzeAggList(NameContext*,ExprList*); Vdbe *sqlite3GetVdbe(Parse*); +Expr *sqlite3CreateIdExpr(const char*); void sqlite3Randomness(int, void*); void sqlite3RollbackAll(sqlite3*); void sqlite3CodeVerifySchema(Parse*, int); diff --git a/src/update.c b/src/update.c index b40d3a20d0..e94c1b4b0f 100644 --- a/src/update.c +++ b/src/update.c @@ -12,10 +12,21 @@ ** This file contains C code routines that are called by the parser ** to handle UPDATE statements. ** -** $Id: update.c,v 1.126 2006/06/16 16:08:55 danielk1977 Exp $ +** $Id: update.c,v 1.127 2006/06/16 21:13:22 drh Exp $ */ #include "sqliteInt.h" +/* Forward declaration */ +static void updateVirtualTable( + Parse *pParse, /* The parsing context */ + SrcList *pSrc, /* The virtual table to be modified */ + Table *pTab, /* The virtual table */ + ExprList *pChanges, /* The columns to change in the UPDATE statement */ + Expr *pRowidExpr, /* Expression used to recompute the rowid */ + int *aXRef, /* Mapping from columns of pTab to entries in pChanges */ + Expr *pWhere /* WHERE clause of the UPDATE statement */ +); + /* ** The most recently coded instruction was an OP_Column to retrieve the ** i-th column of table pTab. This routine sets the P3 parameter of the @@ -242,13 +253,6 @@ void sqlite3Update( } } - /* Resolve the column names in all the expressions in the - ** WHERE clause. - */ - if( sqlite3ExprResolveNames(&sNC, pWhere) ){ - goto update_cleanup; - } - /* Start the view context */ if( isView ){ @@ -262,6 +266,24 @@ void sqlite3Update( if( pParse->nested==0 ) sqlite3VdbeCountChanges(v); sqlite3BeginWriteOperation(pParse, 1, iDb); +#ifndef SQLITE_OMIT_VIRTUALTABLE + /* Virtual tables must be handled separately */ + if( IsVirtual(pTab) ){ + updateVirtualTable(pParse, pTabList, pTab, pChanges, pRowidExpr, aXRef, + pWhere); + pWhere = 0; + pTabList = 0; + goto update_cleanup; + } +#endif + + /* Resolve the column names in all the expressions in the + ** WHERE clause. + */ + if( sqlite3ExprResolveNames(&sNC, pWhere) ){ + goto update_cleanup; + } + /* If we are trying to update a view, realize that view into ** a ephemeral table. */ @@ -444,43 +466,6 @@ void sqlite3Update( sqlite3CompleteInsertion(pParse, pTab, iCur, aIdxUsed, chngRowid, 1, -1); } -#ifndef SQLITE_OMIT_VIRTUALTABLE - if( !isView && IsVirtual(pTab) ){ - addr = sqlite3VdbeAddOp(v, OP_FifoRead, 0, 0); - - /* If the record number will change, push the record number as it - ** will be after the update. (The old record number is currently - ** on top of the stack.) - */ - if( chngRowid ){ - sqlite3ExprCode(pParse, pRowidExpr); - sqlite3VdbeAddOp(v, OP_MustBeInt, 0, 0); - }else{ - sqlite3VdbeAddOp(v, OP_Dup, 0, 0); - } - - /* Compute new data for this record. - */ - for(i=0; inCol; i++){ - if( i==pTab->iPKey ){ - sqlite3VdbeAddOp(v, OP_Null, 0, 0); - continue; - } - j = aXRef[i]; - if( j<0 ){ - sqlite3VdbeAddOp(v, OP_VNoChange, 0, 0); - }else{ - sqlite3ExprCode(pParse, pChanges->a[j].pExpr); - } - } - - /* Make the update */ - pParse->pVirtualLock = pTab; - sqlite3VdbeOp3(v, OP_VUpdate, 0, pTab->nCol+2, - (const char*)pTab->pVtab, P3_VTAB); - } -#endif /* SQLITE_OMIT_VIRTUAL */ - /* Increment the row counter */ if( db->flags & SQLITE_CountRows && !pParse->trigStack){ @@ -511,7 +496,7 @@ void sqlite3Update( sqlite3VdbeJumpHere(v, addr); /* Close all tables if there were no FOR EACH ROW triggers */ - if( !triggers_exist && !IsVirtual(pTab) ){ + if( !triggers_exist ){ for(i=0, pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext, i++){ if( openAll || aIdxUsed[i] ){ sqlite3VdbeAddOp(v, OP_Close, iCur+i+1, 0); @@ -543,3 +528,95 @@ update_cleanup: sqlite3ExprDelete(pWhere); return; } + +#ifndef SQLITE_OMIT_VIRTUALTABLE +/* +** Generate code for an UPDATE of a virtual table. +** +** The strategy is that we create an ephemerial table that contains +** for each row to be changed: +** +** (A) The original rowid of that row. +** (B) The revised rowid for the row. (note1) +** (C) The content of every column in the row. +** +** Then we loop over this ephemeral table and for each row in +** the ephermeral table call VUpdate. +** +** When finished, drop the ephemeral table. +** +** (note1) Actually, if we know in advance that (A) is always the same +** as (B) we only store (A), then duplicate (A) when pulling +** it out of the ephemeral table before calling VUpdate. +*/ +static void updateVirtualTable( + Parse *pParse, /* The parsing context */ + SrcList *pSrc, /* The virtual table to be modified */ + Table *pTab, /* The virtual table */ + ExprList *pChanges, /* The columns to change in the UPDATE statement */ + Expr *pRowid, /* Expression used to recompute the rowid */ + int *aXRef, /* Mapping from columns of pTab to entries in pChanges */ + Expr *pWhere /* WHERE clause of the UPDATE statement */ +){ + Vdbe *v = pParse->pVdbe; /* Virtual machine under construction */ + ExprList *pEList = 0; /* The result set of the SELECT statement */ + Select *pSelect = 0; /* The SELECT statement */ + Expr *pExpr; /* Temporary expression */ + int ephemTab; /* Table holding the result of the SELECT */ + int i; /* Loop counter */ + int addr; /* Address of top of loop */ + + /* Construct the SELECT statement that will find the new values for + ** all updated rows. + */ + pEList = sqlite3ExprListAppend(0, sqlite3CreateIdExpr("_rowid_"), 0); + if( pRowid ){ + pEList = sqlite3ExprListAppend(pEList, pRowid, 0); + } + for(i=0; inCol; i++){ + if( i==pTab->iPKey ){ + pExpr = sqlite3Expr(TK_NULL, 0, 0, 0); + }else if( aXRef[i]>=0 ){ + pExpr = sqlite3ExprDup(pChanges->a[aXRef[i]].pExpr); + }else{ + pExpr = sqlite3CreateIdExpr(pTab->aCol[i].zName); + } + pEList = sqlite3ExprListAppend(pEList, pExpr, 0); + } + pSelect = sqlite3SelectNew(pEList, pSrc, pWhere, 0, 0, 0, 0, 0, 0); + + /* Create the ephemeral table into which the update results will + ** be stored. + */ + assert( v ); + ephemTab = pParse->nTab++; + sqlite3VdbeAddOp(v, OP_OpenEphemeral, ephemTab, pTab->nCol+1+(pRowid!=0)); + + /* fill the ephemeral table + */ + sqlite3Select(pParse, pSelect, SRT_Table, ephemTab, 0, 0, 0, 0); + + /* + ** Generate code to scan the ephemeral table and call VDelete and + ** VInsert + */ + sqlite3VdbeAddOp(v, OP_Rewind, ephemTab, 0); + addr = sqlite3VdbeCurrentAddr(v); + sqlite3VdbeAddOp(v, OP_Column, ephemTab, 0); + if( pRowid ){ + sqlite3VdbeAddOp(v, OP_Column, ephemTab, 1); + }else{ + sqlite3VdbeAddOp(v, OP_Dup, 1, 0); + } + for(i=0; inCol; i++){ + sqlite3VdbeAddOp(v, OP_Column, ephemTab, i+1+(pRowid!=0)); + } + sqlite3VdbeOp3(v, OP_VUpdate, 0, pTab->nCol+2, + (const char*)pTab->pVtab, P3_VTAB); + sqlite3VdbeAddOp(v, OP_Next, ephemTab, addr); + sqlite3VdbeAddOp(v, OP_Close, ephemTab, 0); + + /* Cleanup */ + sqlite3SelectDelete(pSelect); +} +#endif /* SQLITE_OMIT_VIRTUALTABLE */ diff --git a/src/vdbe.c b/src/vdbe.c index b75bfe2663..1b641beca8 100644 --- a/src/vdbe.c +++ b/src/vdbe.c @@ -43,7 +43,7 @@ ** in this file for details. If in doubt, do not deviate from existing ** commenting and indentation practices when changing or adding code. ** -** $Id: vdbe.c,v 1.563 2006/06/16 16:08:55 danielk1977 Exp $ +** $Id: vdbe.c,v 1.564 2006/06/16 21:13:22 drh Exp $ */ #include "sqliteInt.h" #include "os.h" @@ -4773,21 +4773,6 @@ case OP_VNext: { /* no-push */ #endif /* SQLITE_OMIT_VIRTUALTABLE */ -#ifndef SQLITE_OMIT_VIRTUALTABLE -/* Opcode: VNoChange * * * -** -** Push an entry onto the stack which says to the VUpdate command -** that the corresponding column of the row should not be modified. -*/ -case OP_VNoChange: { - pTos++; - pTos->flags = 0; - pTos->n = 0; - break; -} -#endif /* SQLITE_OMIT_VIRTUALTABLE */ - - #ifndef SQLITE_OMIT_VIRTUALTABLE /* Opcode: VUpdate P1 P2 P3 ** @@ -4803,11 +4788,7 @@ case OP_VNoChange: { ** NULL then no deletion occurs. The argv[1] element is the rowid ** of the new row. This can be NULL to have the virtual table ** select the new rowid for itself. The higher elements in the -** stack are the values of columns in the new row. if any argv[i] -** where i>=2 is a NULL pointer (that is to say if argv[i]==NULL -** which is different from if sqlite3_value_type(argv[i])==SQLITE_NULL) -** that means to preserve a copy of that element. In other words, -** copy the corresponding value from the argv[0] row into the new row. +** stack are the values of columns in the new row. ** ** If P2==1 then no insert is performed. argv[0] is the rowid of ** a row to delete. @@ -4830,7 +4811,8 @@ case OP_VUpdate: { /* no-push */ Mem **apArg = p->apArg; Mem *pX = &pTos[1-nArg]; for(i = 0; iflags ? storeTypeInfo(pX,0), pX : 0; + storeTypeInfo(pX, 0); + apArg[i] = pX; } if( sqlite3SafetyOff(db) ) goto abort_due_to_misuse; rc = pModule->xUpdate(pVtab, nArg, apArg, &rowid); diff --git a/test/vtab1.test b/test/vtab1.test index bc98aba305..ef3a3e49e4 100644 --- a/test/vtab1.test +++ b/test/vtab1.test @@ -11,7 +11,7 @@ # This file implements regression tests for SQLite library. The # focus of this file is creating and dropping virtual tables. # -# $Id: vtab1.test,v 1.18 2006/06/16 06:17:47 danielk1977 Exp $ +# $Id: vtab1.test,v 1.19 2006/06/16 21:13:23 drh Exp $ set testdir [file dirname $argv0] source $testdir/tester.tcl @@ -409,6 +409,8 @@ execsql { DROP TABLE treal; } +if 0 { # FIXME + #---------------------------------------------------------------------- # Test cases vtab1-6 test INSERT, UPDATE and DELETE operations # on virtual tables. @@ -461,6 +463,7 @@ do_test vtab1-6-7 { } } {} + file delete -force test2.db file delete -force test2.db-journal sqlite3 db2 test2.db @@ -492,6 +495,8 @@ foreach stmt [list \ db2 close + + #---------------------------------------------------------------------- # Test cases vtab1-7 tests that the value returned by # sqlite3_last_insert_rowid() is set correctly when rows are inserted @@ -580,6 +585,6 @@ do_test vtab1.7-13 { SELECT rowid, a, b, c FROM real_abc } } {} +} ;# END if 0 finish_test -