From 0353cedda4851e4d653b70421ed7445f62a2688a Mon Sep 17 00:00:00 2001 From: drh Date: Tue, 20 Mar 2001 22:05:00 +0000 Subject: [PATCH] Enhancements to the DELETE command (CVS 194) FossilOrigin-Name: daea156e2430762e683ff5460f9f8bb3204ae168 --- VERSION | 2 +- manifest | 30 +++++++------- manifest.uuid | 2 +- src/dbbe.c | 88 +-------------------------------------- src/dbbe.h | 13 ++---- src/dbbegdbm.c | 14 +------ src/dbbemem.c | 30 +------------- src/delete.c | 95 ++++++++++++++++++++++++------------------ src/sqliteInt.h | 5 +-- src/vdbe.c | 105 +++++++++++++++++++++++++++++++---------------- test/delete.test | 54 +++++++++++++++++++++++- www/changes.tcl | 9 ++++ 12 files changed, 211 insertions(+), 236 deletions(-) diff --git a/VERSION b/VERSION index 8955a0173e..adb7b04cb2 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -1.0.26 +1.0.27 diff --git a/manifest b/manifest index e092172dc8..c4e9b7cd81 100644 --- a/manifest +++ b/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 diff --git a/manifest.uuid b/manifest.uuid index 97f6034d99..167435563a 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -99f9ea412f87cfb518cfb34f0e187fd1baf56761 \ No newline at end of file +daea156e2430762e683ff5460f9f8bb3204ae168 \ No newline at end of file diff --git a/src/dbbe.c b/src/dbbe.c index da99eb9e5a..f637f5388c 100644 --- a/src/dbbe.c +++ b/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 @@ -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; inTemp; 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; inTemp; 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; inTemp; 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 diff --git a/src/dbbe.h b/src/dbbe.h index 09166469d5..6a386adf9a 100644 --- a/src/dbbe.h +++ b/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_) */ diff --git a/src/dbbegdbm.c b/src/dbbegdbm.c index eb4d482e7f..777c7dacef 100644 --- a/src/dbbegdbm.c +++ b/src/dbbegdbm.c @@ -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 @@ -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 }; diff --git a/src/dbbemem.c b/src/dbbemem.c index 7d03a5faef..6bf1cc0f2f 100644 --- a/src/dbbemem.c +++ b/src/dbbemem.c @@ -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 @@ -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 }; /* diff --git a/src/delete.c b/src/delete.c index d5e5a3b01e..c4da286220 100644 --- a/src/delete.c +++ b/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; jnColumn; 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; jnColumn; 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); diff --git a/src/sqliteInt.h b/src/sqliteInt.h index 22f49b405c..371eafabdd 100644 --- a/src/sqliteInt.h +++ b/src/sqliteInt.h @@ -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*); diff --git a/src/vdbe.c b/src/vdbe.c index 324730f701..1e852f3975 100644 --- a/src/vdbe.c +++ b/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 @@ -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; inList; 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( inList && ) 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( inList && ) 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( inList && ) 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; } diff --git a/test/delete.test b/test/delete.test index d8c6f56185..4587fbf9f7 100644 --- a/test/delete.test +++ b/test/delete.test @@ -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 diff --git a/www/changes.tcl b/www/changes.tcl index c12e98e825..111276c81a 100644 --- a/www/changes.tcl +++ b/www/changes.tcl @@ -17,6 +17,15 @@ proc chng {date desc} { puts "

    $desc

" } +chng {2001 Mar 20 (1.0.27)} { +
  • 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.
  • +
  • The DELETE command without a WHILE clause just removes the database + files from the disk, rather than going through and deleting record + by record.
  • +} + chng {2001 Mar 20 (1.0.26)} {
  • A serious bug fixed on Windows. Windows users should upgrade. No impact to Unix.