aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authordrh <drh@noemail.net>2001-03-20 22:05:00 +0000
committerdrh <drh@noemail.net>2001-03-20 22:05:00 +0000
commit0353cedda4851e4d653b70421ed7445f62a2688a (patch)
tree1457f2c35c37f81a537a0ab5d29dd31388889d7e /src
parent8721f004853741ab3496cdb7aecef9ec57256bb6 (diff)
downloadsqlite-0353cedda4851e4d653b70421ed7445f62a2688a.tar.gz
sqlite-0353cedda4851e4d653b70421ed7445f62a2688a.zip
Enhancements to the DELETE command (CVS 194)
FossilOrigin-Name: daea156e2430762e683ff5460f9f8bb3204ae168
Diffstat (limited to 'src')
-rw-r--r--src/dbbe.c88
-rw-r--r--src/dbbe.h13
-rw-r--r--src/dbbegdbm.c14
-rw-r--r--src/dbbemem.c30
-rw-r--r--src/delete.c81
-rw-r--r--src/sqliteInt.h5
-rw-r--r--src/vdbe.c105
7 files changed, 125 insertions, 211 deletions
diff --git a/src/dbbe.c b/src/dbbe.c
index da99eb9e5..f637f5388 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 <unistd.h>
@@ -65,92 +65,6 @@ Dbbe *sqliteDbbeOpen(
}
/*
-** 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
** index. Space to hold the filename is obtained from
diff --git a/src/dbbe.h b/src/dbbe.h
index 09166469d..6a386adf9 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 eb4d482e7..777c7dace 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 <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);
}
@@ -542,15 +541,6 @@ static int sqliteGdbmDelete(DbbeCursor *pCursr, int nKey, char *pKey){
}
/*
-** 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 7d03a5fae..6bf1cc0f2 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 <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);
}
@@ -717,31 +716,6 @@ static int sqliteMemDelete(DbbeCursor *pCursr, int nKey, char *pKey){
}
/*
-** 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 d5e5a3b01..c4da28622 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;
+ 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);
+ }
+ }
- /* Remember the key of every item to be deleted.
+ /* The usual case: There is a WHERE clause so we have to scan through
+ ** the table an pick which records to delete.
*/
- sqliteVdbeAddOp(v, OP_ListWrite, 0, 0, 0, 0);
+ 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;
- /* End the database scan loop.
- */
- sqliteWhereEnd(pWInfo);
+ /* Remember the key of every item to be deleted.
+ */
+ sqliteVdbeAddOp(v, OP_ListWrite, 0, 0, 0, 0);
- /* 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);
+ /* 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){
- int j;
+ 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);
- for(j=0; j<pIdx->nColumn; j++){
- sqliteVdbeAddOp(v, OP_Field, base, pIdx->aiColumn[j], 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_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);
}
- 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 22f49b405..371eafabd 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 324730f70..1e852f397 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 <unistd.h>
@@ -168,6 +168,20 @@ struct SetElem {
};
/*
+** 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
*/
struct Vdbe {
@@ -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 */
@@ -686,6 +700,17 @@ static int hardNeedStack(Vdbe *p, int N){
}
/*
+** Delete a keylist
+*/
+static void KeylistFree(Keylist *p){
+ while( p ){
+ Keylist *pNext = p->pNext;
+ sqliteFree(p);
+ p = pNext;
+ }
+}
+
+/*
** Clean up the VM after execution.
**
** This routine will automatically close any cursors, list, and/or
@@ -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;
}