Enhancements to the DELETE command (CVS 194)
FossilOrigin-Name: daea156e2430762e683ff5460f9f8bb3204ae168
This commit is contained in:
parent
8721f00485
commit
0353cedda4
30
manifest
30
manifest
@ -1,20 +1,20 @@
|
||||
C Version\s1.0.26\s(CVS\s477)
|
||||
D 2001-03-20T13:00:00
|
||||
C Enhancements\sto\sthe\sDELETE\scommand\s(CVS\s194)
|
||||
D 2001-03-20T22:05:00
|
||||
F COPYRIGHT 74a8a6531a42e124df07ab5599aad63870fa0bd4
|
||||
F Makefile.in 7efa81e2985b45ba73db27d55b70cc927f5abfd7
|
||||
F README 51f6a4e7408b34afa5bc1c0485f61b6a4efb6958
|
||||
F VERSION 900bbde4128ea2f66efdd32cae849187ea790e77
|
||||
F VERSION dee81fa1259d8cbb43230e982a9392f11f89859d
|
||||
F configure 3dc1edb9dcf60215e31ff72b447935ab62211442 x
|
||||
F configure.in d892ca33db7e88a055519ce2f36dcb11020e8fff
|
||||
F doc/lemon.html e233a3e97a779c7a87e1bc4528c664a58e49dd47
|
||||
F doc/report1.txt 734cbae63b1310cc643fe5e9e3da1ab55a79b99e
|
||||
F src/TODO 38a68a489e56e9fd4a96263e0ff9404a47368ad4
|
||||
F src/build.c 7aa5879bf58ea6bbff22c26c59d1130021fa6ca4
|
||||
F src/dbbe.c 1b3b8b5ded9e58d10d94d268cd12343b6212dbd1
|
||||
F src/dbbe.h 0435a36906a839cce062608f51bd9d3e79878fec
|
||||
F src/dbbegdbm.c 5bfcb1b4ee47a98c5eae83041e9716cd3233fd0e
|
||||
F src/dbbemem.c e0ffaae47c22dd8d73362a4fae2f2b4f074fd079
|
||||
F src/delete.c b83dc815f83220a82df13f1f1f479187d305fe6a
|
||||
F src/dbbe.c b178f0959f6bac5ef8a109484c1571053f31abe5
|
||||
F src/dbbe.h 57d6debb99f6d7a91364b73cf3f0b129058ebbb3
|
||||
F src/dbbegdbm.c 84b2bf31d220f1de1cb0507ea051d30128fd681b
|
||||
F src/dbbemem.c d0fb86f1e8a52cdb195dc91d07f43765cf48bb95
|
||||
F src/delete.c 7aa9dcb86d5e98c3eb9dee00a459e0ef9b73fbe3
|
||||
F src/ex/README b745b00acce2d892f60c40111dacdfc48e0c1c7a
|
||||
F src/ex/db.c f1419ae6c93e40b5ac6e39fe7efd95d868e6f9d7
|
||||
F src/ex/db.h 3f2933ee20c147fe494835786e4c6f3a562def4e
|
||||
@ -33,20 +33,20 @@ F src/select.c faac634ef0c717bc82ca112a4531a257886f2c7a
|
||||
F src/shell.c 441e20913cde0bb71281f4027623c623530241cd
|
||||
F src/shell.tcl 27ecbd63dd88396ad16d81ab44f73e6c0ea9d20e
|
||||
F src/sqlite.h.in 7c1a53f020418d89d13ed2fe9c477ff54540755d
|
||||
F src/sqliteInt.h fd513fa6b7ac94919f85ebfa183aaa194284ce16
|
||||
F src/sqliteInt.h 9887d207b98362392668410a11c59b3e334f51a1
|
||||
F src/table.c 5be76051a8ed6f6bfa641f4adc52529efa34fbf9
|
||||
F src/tclsqlite.c 2a925e1835c348f07dd17c87d95ae9a577833407
|
||||
F src/test.file 55ca6286e3e4f4fba5d0448333fa99fc5a404a73
|
||||
F src/tokenize.c c7ad428f38e56342eb2025320480b5ae9ece1b90
|
||||
F src/update.c 8365b3922ea098330d1e20862d6e64911e4e03d0
|
||||
F src/util.c f4573201fc2b581dbf601c53787349310b7da150
|
||||
F src/vdbe.c e8254ae39d2f42a3f85b168b67a493ae206f4bf4
|
||||
F src/vdbe.c aa14a8aef0229fd5cfa32c3957dc627555f42be8
|
||||
F src/vdbe.h 031b7dd7d6f94c51dc37cdf26efe43d1619bb672
|
||||
F src/where.c 478fde7c930969ca428de2d80b137959d25ee2fb
|
||||
F test/all.test 15cac2f6b2d4c55bf896212aff3cc9d6597b0490
|
||||
F test/copy.test b77a1214bd7756f2849d5c4fa6e715c0ff0c34eb
|
||||
F test/dbbe.test 03a6940807f8a1e7538897b4c802a7937677e053
|
||||
F test/delete.test e3e082a9dc764e3c259a8d175b31cd648e7c58f7
|
||||
F test/delete.test 50b9b1f06c843d591741dba7869433a105360dbf
|
||||
F test/expr.test 48273bf48a15d226c35829f702af4254c0ff6795
|
||||
F test/func.test 02aed8845b98bde1043dda97455de1d37238ebb3
|
||||
F test/in.test 2c560c0f55fb777029fd9bb5378f2997582aa603
|
||||
@ -80,7 +80,7 @@ F www/arch.fig 4f246003b7da23bd63b8b0af0618afb4ee3055c8
|
||||
F www/arch.png 8dae0766d42ed3de9ed013c1341a5792bcf633e6
|
||||
F www/arch.tcl a40380c1fe0080c43e6cc5c20ed70731511b06be
|
||||
F www/c_interface.tcl 11be2d5826eb7d6efd629751d3b483c1ed78ba14
|
||||
F www/changes.tcl 6216d3a09f4cde137e635ab9aedda63adef086c0
|
||||
F www/changes.tcl e01a5257038fafe0302b629d1b278789409029d2
|
||||
F www/crosscompile.tcl c99efacb3aefaa550c6e80d91b240f55eb9fd33e
|
||||
F www/dynload.tcl 02eb8273aa78cfa9070dd4501dca937fb22b466c
|
||||
F www/fileformat.tcl cfb7fba80b7275555281ba2f256c00734bcdd1c9
|
||||
@ -91,7 +91,7 @@ F www/opcode.tcl cb3a1abf8b7b9be9f3a228d097d6bf8b742c2b6f
|
||||
F www/sqlite.tcl cb0d23d8f061a80543928755ec7775da6e4f362f
|
||||
F www/tclsqlite.tcl 06f81c401f79a04f2c5ebfb97e7c176225c0aef2
|
||||
F www/vdbe.tcl 0c8aaa529dd216ccbf7daaabd80985e413d5f9ad
|
||||
P 126fbcb7a8f52a0cc01c8346150a187dbabe96d5
|
||||
R daa25a3d54c3808b11e465e636487674
|
||||
P 99f9ea412f87cfb518cfb34f0e187fd1baf56761
|
||||
R e053fcd301655951022cbe2ddba947b1
|
||||
U drh
|
||||
Z a8bc1022041cda3a4a5da0367e128d28
|
||||
Z 61cc14fc5b8ff57148f02f9ffc944cce
|
||||
|
@ -1 +1 @@
|
||||
99f9ea412f87cfb518cfb34f0e187fd1baf56761
|
||||
daea156e2430762e683ff5460f9f8bb3204ae168
|
88
src/dbbe.c
88
src/dbbe.c
@ -30,7 +30,7 @@
|
||||
** relatively simple to convert to a different database such
|
||||
** as NDBM, SDBM, or BerkeleyDB.
|
||||
**
|
||||
** $Id: dbbe.c,v 1.24 2001/03/20 12:55:14 drh Exp $
|
||||
** $Id: dbbe.c,v 1.25 2001/03/20 22:05:00 drh Exp $
|
||||
*/
|
||||
#include "sqliteInt.h"
|
||||
#include <unistd.h>
|
||||
@ -64,92 +64,6 @@ Dbbe *sqliteDbbeOpen(
|
||||
return sqliteGdbmOpen(zName, writeFlag, createFlag, pzErrMsg);
|
||||
}
|
||||
|
||||
/*
|
||||
** Open a temporary file. The file should be deleted when closed.
|
||||
**
|
||||
** Note that we can't use the old Unix trick of opening the file
|
||||
** and then immediately unlinking the file. That works great
|
||||
** under Unix, but fails when we try to port to Windows.
|
||||
*/
|
||||
int sqliteDbbeOpenTempFile(const char *zDir, Dbbe *pBe, FILE **ppFile){
|
||||
char *zFile; /* Full name of the temporary file */
|
||||
char zBuf[50]; /* Base name of the temporary file */
|
||||
int i; /* Loop counter */
|
||||
int limit; /* Prevent an infinite loop */
|
||||
int rc = SQLITE_OK; /* Value returned by this function */
|
||||
|
||||
for(i=0; i<pBe->nTemp; i++){
|
||||
if( pBe->apTemp[i]==0 ) break;
|
||||
}
|
||||
if( i>=pBe->nTemp ){
|
||||
pBe->nTemp++;
|
||||
pBe->apTemp = sqliteRealloc(pBe->apTemp, pBe->nTemp*sizeof(FILE*) );
|
||||
pBe->azTemp = sqliteRealloc(pBe->azTemp, pBe->nTemp*sizeof(char*) );
|
||||
}
|
||||
if( pBe->apTemp==0 ){
|
||||
*ppFile = 0;
|
||||
return SQLITE_NOMEM;
|
||||
}
|
||||
limit = 4;
|
||||
zFile = 0;
|
||||
do{
|
||||
sqliteRandomName(zBuf, "/_temp_file_");
|
||||
sqliteFree(zFile);
|
||||
zFile = 0;
|
||||
sqliteSetString(&zFile, zDir, zBuf, 0);
|
||||
}while( access(zFile,0)==0 && limit-- >= 0 );
|
||||
#if OS_WIN
|
||||
*ppFile = pBe->apTemp[i] = fopen(zFile, "w+b");
|
||||
#else
|
||||
*ppFile = pBe->apTemp[i] = fopen(zFile, "w+");
|
||||
#endif
|
||||
if( pBe->apTemp[i]==0 ){
|
||||
rc = SQLITE_ERROR;
|
||||
sqliteFree(zFile);
|
||||
pBe->azTemp[i] = 0;
|
||||
}else{
|
||||
pBe->azTemp[i] = zFile;
|
||||
}
|
||||
return rc;
|
||||
}
|
||||
|
||||
/*
|
||||
** Close a temporary file opened using sqliteGdbmOpenTempFile()
|
||||
*/
|
||||
void sqliteDbbeCloseTempFile(Dbbe *pBe, FILE *f){
|
||||
int i;
|
||||
for(i=0; i<pBe->nTemp; i++){
|
||||
if( pBe->apTemp[i]==f ){
|
||||
unlink(pBe->azTemp[i]);
|
||||
sqliteFree(pBe->azTemp[i]);
|
||||
pBe->apTemp[i] = 0;
|
||||
pBe->azTemp[i] = 0;
|
||||
break;
|
||||
}
|
||||
}
|
||||
fclose(f);
|
||||
}
|
||||
|
||||
/*
|
||||
** Close all temporary files that happen to still be open.
|
||||
** This routine is called when the database is being closed.
|
||||
*/
|
||||
void sqliteDbbeCloseAllTempFiles(Dbbe *pBe){
|
||||
int i;
|
||||
for(i=0; i<pBe->nTemp; i++){
|
||||
if( pBe->apTemp[i]!=0 ){
|
||||
unlink(pBe->azTemp[i]);
|
||||
fclose(pBe->apTemp[i]);
|
||||
sqliteFree(pBe->azTemp[i]);
|
||||
pBe->apTemp[i] = 0;
|
||||
pBe->azTemp[i] = 0;
|
||||
break;
|
||||
}
|
||||
}
|
||||
sqliteFree(pBe->azTemp);
|
||||
sqliteFree(pBe->apTemp);
|
||||
}
|
||||
|
||||
/*
|
||||
** Translate the name of an SQL table (or index) into the name
|
||||
** of a file that holds the key/data pairs for that table or
|
||||
|
13
src/dbbe.h
13
src/dbbe.h
@ -28,7 +28,7 @@
|
||||
** This library was originally designed to support the following
|
||||
** backends: GDBM, NDBM, SDBM, Berkeley DB.
|
||||
**
|
||||
** $Id: dbbe.h,v 1.10 2001/01/15 22:51:10 drh Exp $
|
||||
** $Id: dbbe.h,v 1.11 2001/03/20 22:05:00 drh Exp $
|
||||
*/
|
||||
#ifndef _SQLITE_DBBE_H_
|
||||
#define _SQLITE_DBBE_H_
|
||||
@ -150,12 +150,6 @@ struct DbbeMethods {
|
||||
|
||||
/* Remove an entry from the table */
|
||||
int (*Delete)(DbbeCursor*, int nKey, char *pKey);
|
||||
|
||||
/* Open a file suitable for temporary storage */
|
||||
int (*OpenTempFile)(Dbbe*, FILE**);
|
||||
|
||||
/* Close a temporary file */
|
||||
void (*CloseTempFile)(Dbbe *, FILE *);
|
||||
};
|
||||
|
||||
/*
|
||||
@ -170,9 +164,8 @@ struct DbbeMethods {
|
||||
*/
|
||||
struct Dbbe {
|
||||
struct DbbeMethods *x; /* Backend-specific methods for database access */
|
||||
int nTemp; /* Number of temporary files created */
|
||||
FILE **apTemp; /* Space to hold temporary file pointers */
|
||||
char **azTemp; /* Names of the temporary files */
|
||||
/* There used to be other information here, but it has since
|
||||
** been removed. */
|
||||
};
|
||||
|
||||
#endif /* defined(_SQLITE_DBBE_H_) */
|
||||
|
@ -30,7 +30,7 @@
|
||||
** relatively simple to convert to a different database such
|
||||
** as NDBM, SDBM, or BerkeleyDB.
|
||||
**
|
||||
** $Id: dbbegdbm.c,v 1.3 2001/01/15 22:51:10 drh Exp $
|
||||
** $Id: dbbegdbm.c,v 1.4 2001/03/20 22:05:00 drh Exp $
|
||||
*/
|
||||
#include "sqliteInt.h"
|
||||
#include <gdbm.h>
|
||||
@ -110,7 +110,6 @@ static void sqliteGdbmClose(Dbbe *pDbbe){
|
||||
memset(pFile, 0, sizeof(*pFile));
|
||||
sqliteFree(pFile);
|
||||
}
|
||||
sqliteDbbeCloseAllTempFiles(pDbbe);
|
||||
memset(pBe, 0, sizeof(*pBe));
|
||||
sqliteFree(pBe);
|
||||
}
|
||||
@ -541,15 +540,6 @@ static int sqliteGdbmDelete(DbbeCursor *pCursr, int nKey, char *pKey){
|
||||
return rc;
|
||||
}
|
||||
|
||||
/*
|
||||
** Open a temporary file. The file is located in the same directory
|
||||
** as the rest of the database.
|
||||
*/
|
||||
static int sqliteGdbmOpenTempFile(Dbbe *pDbbe, FILE **ppFile){
|
||||
Dbbex *pBe = (Dbbex*)pDbbe;
|
||||
return sqliteDbbeOpenTempFile(pBe->zDir, pDbbe, ppFile);
|
||||
}
|
||||
|
||||
/*
|
||||
** This variable contains pointers to all of the access methods
|
||||
** used to implement the GDBM backend.
|
||||
@ -573,8 +563,6 @@ static struct DbbeMethods gdbmMethods = {
|
||||
/* New */ sqliteGdbmNew,
|
||||
/* Put */ sqliteGdbmPut,
|
||||
/* Delete */ sqliteGdbmDelete,
|
||||
/* OpenTempFile */ sqliteGdbmOpenTempFile,
|
||||
/* CloseTempFile */ sqliteDbbeCloseTempFile
|
||||
};
|
||||
|
||||
|
||||
|
@ -28,7 +28,7 @@
|
||||
**
|
||||
** This file uses an in-memory hash table as the database backend.
|
||||
**
|
||||
** $Id: dbbemem.c,v 1.10 2001/03/20 12:57:57 drh Exp $
|
||||
** $Id: dbbemem.c,v 1.11 2001/03/20 22:05:00 drh Exp $
|
||||
*/
|
||||
#include "sqliteInt.h"
|
||||
#include <sys/stat.h>
|
||||
@ -399,7 +399,6 @@ static void sqliteMemClose(Dbbe *pDbbe){
|
||||
deleteMTable(pTble);
|
||||
}
|
||||
ArrayClear(&pBe->tables);
|
||||
sqliteDbbeCloseAllTempFiles(pDbbe);
|
||||
memset(pBe, 0, sizeof(*pBe));
|
||||
sqliteFree(pBe);
|
||||
}
|
||||
@ -716,31 +715,6 @@ static int sqliteMemDelete(DbbeCursor *pCursr, int nKey, char *pKey){
|
||||
return SQLITE_OK;
|
||||
}
|
||||
|
||||
/*
|
||||
** Open a temporary file. The file is located in the current working
|
||||
** directory.
|
||||
*/
|
||||
static int sqliteMemOpenTempFile(Dbbe *pDbbe, FILE **ppFile){
|
||||
#if OS_UNIX
|
||||
const char *zTemps[] = { "/usr/tmp", "/var/tmp", "/tmp", "/temp", 0};
|
||||
#endif
|
||||
#if OS_WIN
|
||||
const char *zTemps[] = { "/temp", "c:/temp", "c:", "d:", "e:", 0};
|
||||
#endif
|
||||
const char *zDir;
|
||||
int i;
|
||||
struct stat statbuf;
|
||||
for(i=0; zTemps[i]; i++){
|
||||
zDir = zTemps[i];
|
||||
if( stat(zDir, &statbuf)==0 && S_ISDIR(statbuf.st_mode)
|
||||
&& access(zDir, W_OK|X_OK)==0 ){
|
||||
break;
|
||||
}
|
||||
}
|
||||
if( zDir==0 ) zDir = ".";
|
||||
return sqliteDbbeOpenTempFile(zDir, pDbbe, ppFile);
|
||||
}
|
||||
|
||||
/*
|
||||
** This variable contains pointers to all of the access methods
|
||||
** used to implement the MEMORY backend.
|
||||
@ -764,8 +738,6 @@ static struct DbbeMethods memoryMethods = {
|
||||
/* New */ sqliteMemNew,
|
||||
/* Put */ sqliteMemPut,
|
||||
/* Delete */ sqliteMemDelete,
|
||||
/* OpenTempFile */ sqliteMemOpenTempFile,
|
||||
/* CloseTempFile */ sqliteDbbeCloseTempFile
|
||||
};
|
||||
|
||||
/*
|
||||
|
95
src/delete.c
95
src/delete.c
@ -24,7 +24,7 @@
|
||||
** This file contains C code routines that are called by the parser
|
||||
** to handle DELETE FROM statements.
|
||||
**
|
||||
** $Id: delete.c,v 1.7 2001/01/15 22:51:10 drh Exp $
|
||||
** $Id: delete.c,v 1.8 2001/03/20 22:05:00 drh Exp $
|
||||
*/
|
||||
#include "sqliteInt.h"
|
||||
|
||||
@ -85,48 +85,63 @@ void sqliteDeleteFrom(
|
||||
v = sqliteGetVdbe(pParse);
|
||||
if( v==0 ) goto delete_from_cleanup;
|
||||
|
||||
/* Begin the database scan
|
||||
/* Special case: A DELETE without a WHERE clause deletes everything.
|
||||
** It is easier just to deleted the database files directly.
|
||||
*/
|
||||
sqliteVdbeAddOp(v, OP_ListOpen, 0, 0, 0, 0);
|
||||
pWInfo = sqliteWhereBegin(pParse, pTabList, pWhere, 1);
|
||||
if( pWInfo==0 ) goto delete_from_cleanup;
|
||||
|
||||
/* Remember the key of every item to be deleted.
|
||||
*/
|
||||
sqliteVdbeAddOp(v, OP_ListWrite, 0, 0, 0, 0);
|
||||
|
||||
/* End the database scan loop.
|
||||
*/
|
||||
sqliteWhereEnd(pWInfo);
|
||||
|
||||
/* Delete every item whose key was written to the list during the
|
||||
** database scan. We have to delete items after the scan is complete
|
||||
** because deleting an item can change the scan order.
|
||||
*/
|
||||
base = pParse->nTab;
|
||||
sqliteVdbeAddOp(v, OP_ListRewind, 0, 0, 0, 0);
|
||||
sqliteVdbeAddOp(v, OP_OpenTbl, base, 1, pTab->zName, 0);
|
||||
for(i=1, pIdx=pTab->pIndex; pIdx; i++, pIdx=pIdx->pNext){
|
||||
sqliteVdbeAddOp(v, OP_OpenIdx, base+i, 1, pIdx->zName, 0);
|
||||
}
|
||||
end = sqliteVdbeMakeLabel(v);
|
||||
addr = sqliteVdbeAddOp(v, OP_ListRead, 0, end, 0, 0);
|
||||
if( pTab->pIndex ){
|
||||
sqliteVdbeAddOp(v, OP_Dup, 0, 0, 0, 0);
|
||||
sqliteVdbeAddOp(v, OP_Fetch, base, 0, 0, 0);
|
||||
for(i=1, pIdx=pTab->pIndex; pIdx; i++, pIdx=pIdx->pNext){
|
||||
int j;
|
||||
sqliteVdbeAddOp(v, OP_Dup, 0, 0, 0, 0);
|
||||
for(j=0; j<pIdx->nColumn; j++){
|
||||
sqliteVdbeAddOp(v, OP_Field, base, pIdx->aiColumn[j], 0, 0);
|
||||
}
|
||||
sqliteVdbeAddOp(v, OP_MakeKey, pIdx->nColumn, 0, 0, 0);
|
||||
sqliteVdbeAddOp(v, OP_DeleteIdx, base+i, 0, 0, 0);
|
||||
if( pWhere==0 ){
|
||||
sqliteVdbeAddOp(v, OP_Destroy, 0, 0, pTab->zName, 0);
|
||||
for(pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext){
|
||||
sqliteVdbeAddOp(v, OP_Destroy, 0, 0, pIdx->zName, 0);
|
||||
}
|
||||
}
|
||||
sqliteVdbeAddOp(v, OP_Delete, base, 0, 0, 0);
|
||||
sqliteVdbeAddOp(v, OP_Goto, 0, addr, 0, 0);
|
||||
sqliteVdbeAddOp(v, OP_ListClose, 0, 0, 0, end);
|
||||
|
||||
/* The usual case: There is a WHERE clause so we have to scan through
|
||||
** the table an pick which records to delete.
|
||||
*/
|
||||
else{
|
||||
/* Begin the database scan
|
||||
*/
|
||||
sqliteVdbeAddOp(v, OP_ListOpen, 0, 0, 0, 0);
|
||||
pWInfo = sqliteWhereBegin(pParse, pTabList, pWhere, 1);
|
||||
if( pWInfo==0 ) goto delete_from_cleanup;
|
||||
|
||||
/* Remember the key of every item to be deleted.
|
||||
*/
|
||||
sqliteVdbeAddOp(v, OP_ListWrite, 0, 0, 0, 0);
|
||||
|
||||
/* End the database scan loop.
|
||||
*/
|
||||
sqliteWhereEnd(pWInfo);
|
||||
|
||||
/* Delete every item whose key was written to the list during the
|
||||
** database scan. We have to delete items after the scan is complete
|
||||
** because deleting an item can change the scan order.
|
||||
*/
|
||||
base = pParse->nTab;
|
||||
sqliteVdbeAddOp(v, OP_ListRewind, 0, 0, 0, 0);
|
||||
sqliteVdbeAddOp(v, OP_OpenTbl, base, 1, pTab->zName, 0);
|
||||
for(i=1, pIdx=pTab->pIndex; pIdx; i++, pIdx=pIdx->pNext){
|
||||
sqliteVdbeAddOp(v, OP_OpenIdx, base+i, 1, pIdx->zName, 0);
|
||||
}
|
||||
end = sqliteVdbeMakeLabel(v);
|
||||
addr = sqliteVdbeAddOp(v, OP_ListRead, 0, end, 0, 0);
|
||||
if( pTab->pIndex ){
|
||||
sqliteVdbeAddOp(v, OP_Dup, 0, 0, 0, 0);
|
||||
sqliteVdbeAddOp(v, OP_Fetch, base, 0, 0, 0);
|
||||
for(i=1, pIdx=pTab->pIndex; pIdx; i++, pIdx=pIdx->pNext){
|
||||
int j;
|
||||
sqliteVdbeAddOp(v, OP_Dup, 0, 0, 0, 0);
|
||||
for(j=0; j<pIdx->nColumn; j++){
|
||||
sqliteVdbeAddOp(v, OP_Field, base, pIdx->aiColumn[j], 0, 0);
|
||||
}
|
||||
sqliteVdbeAddOp(v, OP_MakeKey, pIdx->nColumn, 0, 0, 0);
|
||||
sqliteVdbeAddOp(v, OP_DeleteIdx, base+i, 0, 0, 0);
|
||||
}
|
||||
}
|
||||
sqliteVdbeAddOp(v, OP_Delete, base, 0, 0, 0);
|
||||
sqliteVdbeAddOp(v, OP_Goto, 0, addr, 0, 0);
|
||||
sqliteVdbeAddOp(v, OP_ListClose, 0, 0, 0, end);
|
||||
}
|
||||
|
||||
delete_from_cleanup:
|
||||
sqliteIdListDelete(pTabList);
|
||||
|
@ -23,7 +23,7 @@
|
||||
*************************************************************************
|
||||
** Internal interface definitions for SQLite.
|
||||
**
|
||||
** @(#) $Id: sqliteInt.h,v 1.36 2001/01/20 19:52:50 drh Exp $
|
||||
** @(#) $Id: sqliteInt.h,v 1.37 2001/03/20 22:05:00 drh Exp $
|
||||
*/
|
||||
#include "sqlite.h"
|
||||
#include "dbbe.h"
|
||||
@ -427,7 +427,4 @@ Vdbe *sqliteGetVdbe(Parse*);
|
||||
int sqliteRandomByte(void);
|
||||
int sqliteRandomInteger(void);
|
||||
void sqliteRandomName(char*,char*);
|
||||
int sqliteDbbeOpenTempFile(const char*, Dbbe*, FILE**);
|
||||
void sqliteDbbeCloseTempFile(Dbbe*, FILE*);
|
||||
void sqliteDbbeCloseAllTempFiles(Dbbe*);
|
||||
char *sqliteDbbeNameToFile(const char*,const char*,const char*);
|
||||
|
105
src/vdbe.c
105
src/vdbe.c
@ -41,7 +41,7 @@
|
||||
** But other routines are also provided to help in building up
|
||||
** a program instruction by instruction.
|
||||
**
|
||||
** $Id: vdbe.c,v 1.52 2001/02/19 23:23:39 drh Exp $
|
||||
** $Id: vdbe.c,v 1.53 2001/03/20 22:05:00 drh Exp $
|
||||
*/
|
||||
#include "sqliteInt.h"
|
||||
#include <unistd.h>
|
||||
@ -167,6 +167,20 @@ struct SetElem {
|
||||
char zKey[1]; /* Value of this key */
|
||||
};
|
||||
|
||||
/*
|
||||
** A Keylist is a bunch of keys into a table. The keylist can
|
||||
** grow without bound. The keylist stores the keys of database
|
||||
** records that need to be deleted.
|
||||
*/
|
||||
typedef struct Keylist Keylist;
|
||||
struct Keylist {
|
||||
int nKey; /* Number of slots in aKey[] */
|
||||
int nUsed; /* Next unwritten slot in aKey[] */
|
||||
int nRead; /* Next unread slot in aKey[] */
|
||||
Keylist *pNext; /* Next block of keys */
|
||||
int aKey[1]; /* One or more keys. Extra space allocated as needed */
|
||||
};
|
||||
|
||||
/*
|
||||
** An instance of the virtual machine
|
||||
*/
|
||||
@ -188,7 +202,7 @@ struct Vdbe {
|
||||
int nCursor; /* Number of slots in aCsr[] */
|
||||
Cursor *aCsr; /* On element of this array for each open cursor */
|
||||
int nList; /* Number of slots in apList[] */
|
||||
FILE **apList; /* An open file for each list */
|
||||
Keylist **apList; /* For each Keylist */
|
||||
int nSort; /* Number of slots in apSort[] */
|
||||
Sorter **apSort; /* An open sorter list */
|
||||
FILE *pFile; /* At most one open file handler */
|
||||
@ -685,6 +699,17 @@ static int hardNeedStack(Vdbe *p, int N){
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
** Delete a keylist
|
||||
*/
|
||||
static void KeylistFree(Keylist *p){
|
||||
while( p ){
|
||||
Keylist *pNext = p->pNext;
|
||||
sqliteFree(p);
|
||||
p = pNext;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
** Clean up the VM after execution.
|
||||
**
|
||||
@ -714,10 +739,8 @@ static void Cleanup(Vdbe *p){
|
||||
p->aMem = 0;
|
||||
p->nMem = 0;
|
||||
for(i=0; i<p->nList; i++){
|
||||
if( p->apList[i] ){
|
||||
p->pBe->x->CloseTempFile(p->pBe, p->apList[i]);
|
||||
p->apList[i] = 0;
|
||||
}
|
||||
KeylistFree(p->apList[i]);
|
||||
p->apList[i] = 0;
|
||||
}
|
||||
sqliteFree(p->apList);
|
||||
p->apList = 0;
|
||||
@ -2431,10 +2454,11 @@ int sqliteVdbeExec(
|
||||
|
||||
/* Opcode: ListOpen P1 * *
|
||||
**
|
||||
** Open a file used for temporary storage of integer table keys. P1
|
||||
** will server as a handle to this temporary file for future
|
||||
** interactions. If another temporary file with the P1 handle is
|
||||
** already opened, the prior file is closed and a new one opened
|
||||
** Open a "List" structure used for temporary storage of integer
|
||||
** table keys. P1
|
||||
** will server as a handle to this list for future
|
||||
** interactions. If another list with the P1 handle is
|
||||
** already opened, the prior list is closed and a new one opened
|
||||
** in its place.
|
||||
*/
|
||||
case OP_ListOpen: {
|
||||
@ -2442,16 +2466,13 @@ int sqliteVdbeExec(
|
||||
VERIFY( if( i<0 ) goto bad_instruction; )
|
||||
if( i>=p->nList ){
|
||||
int j;
|
||||
p->apList = sqliteRealloc( p->apList, (i+1)*sizeof(FILE*) );
|
||||
p->apList = sqliteRealloc( p->apList, (i+1)*sizeof(Keylist*) );
|
||||
if( p->apList==0 ){ p->nList = 0; goto no_mem; }
|
||||
for(j=p->nList; j<=i; j++) p->apList[j] = 0;
|
||||
p->nList = i+1;
|
||||
}else if( p->apList[i] ){
|
||||
pBex->CloseTempFile(pBe, p->apList[i]);
|
||||
}
|
||||
rc = pBex->OpenTempFile(pBe, &p->apList[i]);
|
||||
if( rc!=SQLITE_OK ){
|
||||
sqliteSetString(pzErrMsg, "unable to open a temporary file", 0);
|
||||
KeylistFree(p->apList[i]);
|
||||
p->apList[i] = 0;
|
||||
}
|
||||
break;
|
||||
}
|
||||
@ -2459,19 +2480,26 @@ int sqliteVdbeExec(
|
||||
/* Opcode: ListWrite P1 * *
|
||||
**
|
||||
** Write the integer on the top of the stack
|
||||
** into the temporary storage file P1.
|
||||
** into the temporary storage list P1.
|
||||
*/
|
||||
case OP_ListWrite: {
|
||||
int i = pOp->p1;
|
||||
VERIFY( if( i<0 ) goto bad_instruction; )
|
||||
Keylist *pKeylist;
|
||||
VERIFY( if( i<0 || i>=p->nList ) goto bad_instruction; )
|
||||
VERIFY( if( p->tos<0 ) goto not_enough_stack; )
|
||||
if( VERIFY( i<p->nList && ) p->apList[i]!=0 ){
|
||||
int val;
|
||||
Integerify(p, p->tos);
|
||||
val = aStack[p->tos].i;
|
||||
POPSTACK;
|
||||
fwrite(&val, sizeof(int), 1, p->apList[i]);
|
||||
pKeylist = p->apList[i];
|
||||
if( pKeylist==0 || pKeylist->nUsed>=pKeylist->nKey ){
|
||||
pKeylist = sqliteMalloc( sizeof(Keylist)+999*sizeof(int) );
|
||||
if( pKeylist==0 ) goto no_mem;
|
||||
pKeylist->nKey = 1000;
|
||||
pKeylist->nRead = 0;
|
||||
pKeylist->nUsed = 0;
|
||||
pKeylist->pNext = p->apList[i];
|
||||
p->apList[i] = pKeylist;
|
||||
}
|
||||
Integerify(p, p->tos);
|
||||
pKeylist->aKey[pKeylist->nUsed++] = aStack[p->tos].i;
|
||||
POPSTACK;
|
||||
break;
|
||||
}
|
||||
|
||||
@ -2482,9 +2510,7 @@ int sqliteVdbeExec(
|
||||
case OP_ListRewind: {
|
||||
int i = pOp->p1;
|
||||
VERIFY( if( i<0 ) goto bad_instruction; )
|
||||
if( VERIFY( i<p->nList && ) p->apList[i]!=0 ){
|
||||
rewind(p->apList[i]);
|
||||
}
|
||||
/* This is now a no-op */
|
||||
break;
|
||||
}
|
||||
|
||||
@ -2497,14 +2523,24 @@ int sqliteVdbeExec(
|
||||
case OP_ListRead: {
|
||||
int i = pOp->p1;
|
||||
int val, amt;
|
||||
VERIFY(if( i<0 || i>=p->nList || p->apList[i]==0 )goto bad_instruction;)
|
||||
amt = fread(&val, sizeof(int), 1, p->apList[i]);
|
||||
if( amt==1 ){
|
||||
Keylist *pKeylist;
|
||||
VERIFY(if( i<0 || i>=p->nList ) goto bad_instruction;)
|
||||
pKeylist = p->apList[i];
|
||||
if( pKeylist!=0 ){
|
||||
VERIFY(
|
||||
if( pKeylist->nRead<0
|
||||
|| pKeylist->nRead>=pKeylist->nUsed
|
||||
|| pKeylist->nRead>=pKeylist->nKey ) goto bad_instruction;
|
||||
)
|
||||
p->tos++;
|
||||
if( NeedStack(p, p->tos) ) goto no_mem;
|
||||
aStack[p->tos].i = val;
|
||||
aStack[p->tos].i = pKeylist->aKey[pKeylist->nRead++];
|
||||
aStack[p->tos].flags = STK_Int;
|
||||
zStack[p->tos] = 0;
|
||||
if( pKeylist->nRead>=pKeylist->nUsed ){
|
||||
p->apList[i] = pKeylist->pNext;
|
||||
sqliteFree(pKeylist);
|
||||
}
|
||||
}else{
|
||||
pc = pOp->p2 - 1;
|
||||
}
|
||||
@ -2518,10 +2554,9 @@ int sqliteVdbeExec(
|
||||
case OP_ListClose: {
|
||||
int i = pOp->p1;
|
||||
VERIFY( if( i<0 ) goto bad_instruction; )
|
||||
if( VERIFY( i<p->nList && ) p->apList[i]!=0 ){
|
||||
pBex->CloseTempFile(pBe, p->apList[i]);
|
||||
p->apList[i] = 0;
|
||||
}
|
||||
VERIFY( if( i>=p->nList ) goto bad_instruction; )
|
||||
KeylistFree(p->apList[i]);
|
||||
p->apList[i] = 0;
|
||||
break;
|
||||
}
|
||||
|
||||
|
@ -23,7 +23,7 @@
|
||||
# This file implements regression tests for SQLite library. The
|
||||
# focus of this file is testing the DELETE FROM statement.
|
||||
#
|
||||
# $Id: delete.test,v 1.7 2001/03/20 12:55:14 drh Exp $
|
||||
# $Id: delete.test,v 1.8 2001/03/20 22:05:00 drh Exp $
|
||||
|
||||
set testdir [file dirname $argv0]
|
||||
source $testdir/tester.tcl
|
||||
@ -118,4 +118,56 @@ do_test delete-5.7 {
|
||||
execsql {SELECT f1 FROM table1 ORDER BY f1}
|
||||
} {48}
|
||||
|
||||
# Delete large quantities of data. We want to test the List overflow
|
||||
# mechanism in the vdbe.
|
||||
#
|
||||
do_test delete-6.1 {
|
||||
set fd [open data1.txt w]
|
||||
for {set i 1} {$i<=3000} {incr i} {
|
||||
puts $fd "[expr {$i}]\t[expr {$i*$i}]"
|
||||
}
|
||||
close $fd
|
||||
execsql {DELETE FROM table1}
|
||||
execsql {COPY table1 FROM 'data1.txt'}
|
||||
execsql {DELETE FROM table2}
|
||||
execsql {COPY table2 FROM 'data1.txt'}
|
||||
file delete data1.txt
|
||||
execsql {SELECT count(*) FROM table1}
|
||||
} {3000}
|
||||
do_test delete-6.2 {
|
||||
execsql {SELECT count(*) FROM table2}
|
||||
} {3000}
|
||||
do_test delete-6.3 {
|
||||
execsql {SELECT f1 FROM table1 WHERE f1<10 ORDER BY f1}
|
||||
} {1 2 3 4 5 6 7 8 9}
|
||||
do_test delete-6.4 {
|
||||
execsql {SELECT f1 FROM table2 WHERE f1<10 ORDER BY f1}
|
||||
} {1 2 3 4 5 6 7 8 9}
|
||||
do_test delete-6.5 {
|
||||
execsql {DELETE FROM table1 WHERE f1>7}
|
||||
execsql {SELECT f1 FROM table1 ORDER BY f1}
|
||||
} {1 2 3 4 5 6 7}
|
||||
do_test delete-6.6 {
|
||||
execsql {DELETE FROM table2 WHERE f1>7}
|
||||
execsql {SELECT f1 FROM table2 ORDER BY f1}
|
||||
} {1 2 3 4 5 6 7}
|
||||
do_test delete-6.7 {
|
||||
execsql {DELETE FROM table1}
|
||||
execsql {SELECT f1 FROM table1}
|
||||
} {}
|
||||
do_test delete-6.8 {
|
||||
execsql {INSERT INTO table1 VALUES(2,3)}
|
||||
execsql {SELECT f1 FROM table1}
|
||||
} {2}
|
||||
do_test delete-6.9 {
|
||||
execsql {DELETE FROM table2}
|
||||
execsql {SELECT f1 FROM table2}
|
||||
} {}
|
||||
do_test delete-6.10 {
|
||||
execsql {INSERT INTO table2 VALUES(2,3)}
|
||||
execsql {SELECT f1 FROM table2}
|
||||
} {2}
|
||||
|
||||
|
||||
|
||||
finish_test
|
||||
|
@ -17,6 +17,15 @@ proc chng {date desc} {
|
||||
puts "<DD><P><UL>$desc</UL></P></DD>"
|
||||
}
|
||||
|
||||
chng {2001 Mar 20 (1.0.27)} {
|
||||
<li>When doing DELETE and UPDATE, the library used to write the record
|
||||
numbers of records to be deleted or updated into a temporary file.
|
||||
This is changed so that the record numbers are held in memory.</li>
|
||||
<li>The DELETE command without a WHILE clause just removes the database
|
||||
files from the disk, rather than going through and deleting record
|
||||
by record.</li>
|
||||
}
|
||||
|
||||
chng {2001 Mar 20 (1.0.26)} {
|
||||
<li>A serious bug fixed on Windows. Windows users should upgrade.
|
||||
No impact to Unix.</li>
|
||||
|
Loading…
x
Reference in New Issue
Block a user