diff options
author | drh <drh@noemail.net> | 2001-03-20 22:05:00 +0000 |
---|---|---|
committer | drh <drh@noemail.net> | 2001-03-20 22:05:00 +0000 |
commit | 0353cedda4851e4d653b70421ed7445f62a2688a (patch) | |
tree | 1457f2c35c37f81a537a0ab5d29dd31388889d7e /src | |
parent | 8721f004853741ab3496cdb7aecef9ec57256bb6 (diff) | |
download | sqlite-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.c | 88 | ||||
-rw-r--r-- | src/dbbe.h | 13 | ||||
-rw-r--r-- | src/dbbegdbm.c | 14 | ||||
-rw-r--r-- | src/dbbemem.c | 30 | ||||
-rw-r--r-- | src/delete.c | 81 | ||||
-rw-r--r-- | src/sqliteInt.h | 5 | ||||
-rw-r--r-- | src/vdbe.c | 105 |
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; } |