-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
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
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
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
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
-6125140228e09cad2029a48e92aa0123d3daecfb
\ No newline at end of file
+2119e7bf5577350e4e1236ea729568085620a826
\ No newline at end of file
** 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"
/*
** 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);
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);
** 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_
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*);
*************************************************************************
** 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_
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);
** 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
}
}
- /* 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 ){
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.
*/
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; i<pTab->nCol; 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){
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);
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; i<pTab->nCol; 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; i<pTab->nCol; 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 */
** 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"
#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
**
** 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.
Mem **apArg = p->apArg;
Mem *pX = &pTos[1-nArg];
for(i = 0; i<nArg; i++, pX++){
- apArg[i] = pX->flags ? 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);
# 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
DROP TABLE treal;
}
+if 0 { # FIXME
+
#----------------------------------------------------------------------
# Test cases vtab1-6 test INSERT, UPDATE and DELETE operations
# on virtual tables.
}
} {}
+
file delete -force test2.db
file delete -force test2.db-journal
sqlite3 db2 test2.db
db2 close
+
+
#----------------------------------------------------------------------
# Test cases vtab1-7 tests that the value returned by
# sqlite3_last_insert_rowid() is set correctly when rows are inserted
SELECT rowid, a, b, c FROM real_abc
}
} {}
+} ;# END if 0
finish_test
-