1
0
mirror of https://github.com/sqlite/sqlite.git synced 2025-11-08 03:22:21 +03:00

Rework the way UPDATE works for virtual tables. (CVS 3262)

FossilOrigin-Name: 2119e7bf5577350e4e1236ea729568085620a826
This commit is contained in:
drh
2006-06-16 21:13:21 +00:00
parent f9e7dda7ef
commit 9c41938ff8
8 changed files with 157 additions and 92 deletions

View File

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

View File

@@ -1 +1 @@
6125140228e09cad2029a48e92aa0123d3daecfb
2119e7bf5577350e4e1236ea729568085620a826

View File

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

View File

@@ -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*);

View File

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

View File

@@ -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; 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){
@@ -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; 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 */

View File

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

View File

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