SQLite

Check-in [daea156e24]
Login

Many hyperlinks are disabled.
Use anonymous login to enable hyperlinks.

Overview
Comment:Enhancements to the DELETE command (CVS 194)
Downloads: Tarball | ZIP archive
Timelines: family | ancestors | descendants | both | trunk
Files: files | file ages | folders
SHA1: daea156e2430762e683ff5460f9f8bb3204ae168
User & Date: drh 2001-03-20 22:05:00.000
Context
2001-03-20
22:15
Version 1.0.27 (CVS 476) (check-in: 833291c227 user: drh tags: trunk)
22:05
Enhancements to the DELETE command (CVS 194) (check-in: daea156e24 user: drh tags: trunk)
13:00
Version 1.0.26 (CVS 477) (check-in: 99f9ea412f user: drh tags: trunk)
Changes
Unified Diff Ignore Whitespace Patch
Changes to VERSION.
1
1.0.26
|
1
1.0.27
Changes to src/dbbe.c.
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
** sqlite and the code that does the actually reading and writing
** of information to the disk.
**
** This file uses GDBM as the database backend.  It should be
** 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 $
*/
#include "sqliteInt.h"
#include <unistd.h>

/*
** This routine opens a new database.  It looks at the first
** few characters of the database name to try to determine what







|







26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
** sqlite and the code that does the actually reading and writing
** of information to the disk.
**
** This file uses GDBM as the database backend.  It should be
** relatively simple to convert to a different database such
** as NDBM, SDBM, or BerkeleyDB.
**
** $Id: dbbe.c,v 1.25 2001/03/20 22:05:00 drh Exp $
*/
#include "sqliteInt.h"
#include <unistd.h>

/*
** This routine opens a new database.  It looks at the first
** few characters of the database name to try to determine what
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
  if( strncmp(zName, "memory:", 7)==0 ){
    extern Dbbe *sqliteMemOpen(const char*,int,int,char**);
    return sqliteMemOpen(&zName[7], writeFlag, createFlag, pzErrMsg);
  }
  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
** index.  Space to hold the filename is obtained from
** sqliteMalloc() and must be freed by the calling function.
**
** zDir is the name of the directory in which the file should







<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<







60
61
62
63
64
65
66






















































































67
68
69
70
71
72
73
  if( strncmp(zName, "memory:", 7)==0 ){
    extern Dbbe *sqliteMemOpen(const char*,int,int,char**);
    return sqliteMemOpen(&zName[7], writeFlag, createFlag, pzErrMsg);
  }
  return sqliteGdbmOpen(zName, writeFlag, createFlag, pzErrMsg);
}























































































/*
** 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
** sqliteMalloc() and must be freed by the calling function.
**
** zDir is the name of the directory in which the file should
Changes to src/dbbe.h.
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
** This file defines the interface to the database backend (Dbbe).
**
** The database backend is designed to be as general as possible
** so that it can easily be replaced by a different backend.
** 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 $
*/
#ifndef _SQLITE_DBBE_H_
#define _SQLITE_DBBE_H_
#include <stdio.h>

/*
** The database backend supports two opaque structures.  A Dbbe is







|







24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
** This file defines the interface to the database backend (Dbbe).
**
** The database backend is designed to be as general as possible
** so that it can easily be replaced by a different backend.
** This library was originally designed to support the following
** backends: GDBM, NDBM, SDBM, Berkeley DB.
**
** $Id: dbbe.h,v 1.11 2001/03/20 22:05:00 drh Exp $
*/
#ifndef _SQLITE_DBBE_H_
#define _SQLITE_DBBE_H_
#include <stdio.h>

/*
** The database backend supports two opaque structures.  A Dbbe is
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
  /* Write an entry into a table.  If another entry already exists with
  ** the same key, the old entry is discarded first.
  */
  int (*Put)(DbbeCursor*, int nKey, char *pKey, int nData, char *pData);

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

/*
** This is the structure returned by sqliteDbbeOpen().  It contains
** information common to all the different backend drivers.
**
** The information in this structure (with the exception the method
** pointers in the Dbbe.x field) is intended to be visible to
** the backend drivers only.  Users should not access or modify
** this structure in any way other than the read the method pointers
** in Dbbe.x.
*/
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 */
};

#endif /* defined(_SQLITE_DBBE_H_) */







<
<
<
<
<
<














|
|
<



146
147
148
149
150
151
152






153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168

169
170
171
  /* Write an entry into a table.  If another entry already exists with
  ** the same key, the old entry is discarded first.
  */
  int (*Put)(DbbeCursor*, int nKey, char *pKey, int nData, char *pData);

  /* Remove an entry from the table */
  int (*Delete)(DbbeCursor*, int nKey, char *pKey);






};

/*
** This is the structure returned by sqliteDbbeOpen().  It contains
** information common to all the different backend drivers.
**
** The information in this structure (with the exception the method
** pointers in the Dbbe.x field) is intended to be visible to
** the backend drivers only.  Users should not access or modify
** this structure in any way other than the read the method pointers
** in Dbbe.x.
*/
struct Dbbe {
  struct DbbeMethods *x; /* Backend-specific methods for database access */
  /* There used to be other information here, but it has since
  ** been removed.  */

};

#endif /* defined(_SQLITE_DBBE_H_) */
Changes to src/dbbegdbm.c.
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
** sqlite and the code that does the actually reading and writing
** of information to the disk.
**
** This file uses GDBM as the database backend.  It should be
** 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 $
*/
#include "sqliteInt.h"
#include <gdbm.h>
#include <sys/stat.h>
#include <unistd.h>
#include <ctype.h>
#include <time.h>







|







26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
** sqlite and the code that does the actually reading and writing
** of information to the disk.
**
** This file uses GDBM as the database backend.  It should be
** relatively simple to convert to a different database such
** as NDBM, SDBM, or BerkeleyDB.
**
** $Id: dbbegdbm.c,v 1.4 2001/03/20 22:05:00 drh Exp $
*/
#include "sqliteInt.h"
#include <gdbm.h>
#include <sys/stat.h>
#include <unistd.h>
#include <ctype.h>
#include <time.h>
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
  BeFile *pFile, *pNext;
  for(pFile=pBe->pOpen; pFile; pFile=pNext){
    pNext = pFile->pNext;
    gdbm_close(pFile->dbf);
    memset(pFile, 0, sizeof(*pFile));   
    sqliteFree(pFile);
  }
  sqliteDbbeCloseAllTempFiles(pDbbe);
  memset(pBe, 0, sizeof(*pBe));
  sqliteFree(pBe);
}

/*
** 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







<







106
107
108
109
110
111
112

113
114
115
116
117
118
119
  BeFile *pFile, *pNext;
  for(pFile=pBe->pOpen; pFile; pFile=pNext){
    pNext = pFile->pNext;
    gdbm_close(pFile->dbf);
    memset(pFile, 0, sizeof(*pFile));   
    sqliteFree(pFile);
  }

  memset(pBe, 0, sizeof(*pBe));
  sqliteFree(pBe);
}

/*
** 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
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
  key.dsize = nKey;
  key.dptr = pKey;
  rc = gdbm_delete(pCursr->pFile->dbf, key);
  if( rc ) rc = SQLITE_ERROR;
  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.
*/
static struct DbbeMethods gdbmMethods = {
  /* n         Close */   sqliteGdbmClose,
  /*      OpenCursor */   sqliteGdbmOpenCursor,







<
<
<
<
<
<
<
<
<







536
537
538
539
540
541
542









543
544
545
546
547
548
549
  key.dsize = nKey;
  key.dptr = pKey;
  rc = gdbm_delete(pCursr->pFile->dbf, key);
  if( rc ) rc = SQLITE_ERROR;
  return rc;
}










/*
** This variable contains pointers to all of the access methods
** used to implement the GDBM backend.
*/
static struct DbbeMethods gdbmMethods = {
  /* n         Close */   sqliteGdbmClose,
  /*      OpenCursor */   sqliteGdbmOpenCursor,
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
  /*       KeyLength */   sqliteGdbmKeyLength,
  /*      DataLength */   sqliteGdbmDataLength,
  /*         NextKey */   sqliteGdbmNextKey,
  /*          Rewind */   sqliteGdbmRewind,
  /*             New */   sqliteGdbmNew,
  /*             Put */   sqliteGdbmPut,
  /*          Delete */   sqliteGdbmDelete,
  /*    OpenTempFile */   sqliteGdbmOpenTempFile,
  /*   CloseTempFile */   sqliteDbbeCloseTempFile
};


/*
** This routine opens a new database.  For the GDBM driver
** implemented here, the database name is the name of the directory
** containing all the files of the database.







<
<







559
560
561
562
563
564
565


566
567
568
569
570
571
572
  /*       KeyLength */   sqliteGdbmKeyLength,
  /*      DataLength */   sqliteGdbmDataLength,
  /*         NextKey */   sqliteGdbmNextKey,
  /*          Rewind */   sqliteGdbmRewind,
  /*             New */   sqliteGdbmNew,
  /*             Put */   sqliteGdbmPut,
  /*          Delete */   sqliteGdbmDelete,


};


/*
** This routine opens a new database.  For the GDBM driver
** implemented here, the database name is the name of the directory
** containing all the files of the database.
Changes to src/dbbemem.c.
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
** This file contains code to implement the database backend (DBBE)
** for sqlite.  The database backend is the interface between
** sqlite and the code that does the actually reading and writing
** of information to the disk.
**
** 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 $
*/
#include "sqliteInt.h"
#include <sys/stat.h>
#include <unistd.h>
#include <ctype.h>
#include <time.h>








|







24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
** This file contains code to implement the database backend (DBBE)
** for sqlite.  The database backend is the interface between
** sqlite and the code that does the actually reading and writing
** of information to the disk.
**
** This file uses an in-memory hash table as the database backend. 
**
** $Id: dbbemem.c,v 1.11 2001/03/20 22:05:00 drh Exp $
*/
#include "sqliteInt.h"
#include <sys/stat.h>
#include <unistd.h>
#include <ctype.h>
#include <time.h>

395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
  MTable *pTble;
  ArrayElem *j;
  for(j=ArrayFirst(&pBe->tables); j; j=ArrayNext(j)){
    pTble = ArrayData(j);
    deleteMTable(pTble);
  }
  ArrayClear(&pBe->tables);
  sqliteDbbeCloseAllTempFiles(pDbbe);
  memset(pBe, 0, sizeof(*pBe));
  sqliteFree(pBe);
}

/*
** Translate the name of an SQL table (or index) into its
** canonical name.







<







395
396
397
398
399
400
401

402
403
404
405
406
407
408
  MTable *pTble;
  ArrayElem *j;
  for(j=ArrayFirst(&pBe->tables); j; j=ArrayNext(j)){
    pTble = ArrayData(j);
    deleteMTable(pTble);
  }
  ArrayClear(&pBe->tables);

  memset(pBe, 0, sizeof(*pBe));
  sqliteFree(pBe);
}

/*
** Translate the name of an SQL table (or index) into its
** canonical name.
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
  data = ArrayInsert(&pCursr->pTble->data, key, data);
  if( data.p ){
    sqliteFree(data.p);
  }
  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.
*/
static struct DbbeMethods memoryMethods = {
  /* n         Close */   sqliteMemClose,
  /*      OpenCursor */   sqliteMemOpenCursor,







<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<







711
712
713
714
715
716
717

























718
719
720
721
722
723
724
  data = ArrayInsert(&pCursr->pTble->data, key, data);
  if( data.p ){
    sqliteFree(data.p);
  }
  return SQLITE_OK;
}


























/*
** This variable contains pointers to all of the access methods
** used to implement the MEMORY backend.
*/
static struct DbbeMethods memoryMethods = {
  /* n         Close */   sqliteMemClose,
  /*      OpenCursor */   sqliteMemOpenCursor,
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
  /*       KeyLength */   sqliteMemKeyLength,
  /*      DataLength */   sqliteMemDataLength,
  /*         NextKey */   sqliteMemNextKey,
  /*          Rewind */   sqliteMemRewind,
  /*             New */   sqliteMemNew,
  /*             Put */   sqliteMemPut,
  /*          Delete */   sqliteMemDelete,
  /*    OpenTempFile */   sqliteMemOpenTempFile,
  /*   CloseTempFile */   sqliteDbbeCloseTempFile
};

/*
** This routine opens a new database.  For the GDBM driver
** implemented here, the database name is the name of the directory
** containing all the files of the database.
**







<
<







734
735
736
737
738
739
740


741
742
743
744
745
746
747
  /*       KeyLength */   sqliteMemKeyLength,
  /*      DataLength */   sqliteMemDataLength,
  /*         NextKey */   sqliteMemNextKey,
  /*          Rewind */   sqliteMemRewind,
  /*             New */   sqliteMemNew,
  /*             Put */   sqliteMemPut,
  /*          Delete */   sqliteMemDelete,


};

/*
** This routine opens a new database.  For the GDBM driver
** implemented here, the database name is the name of the directory
** containing all the files of the database.
**
Changes to src/delete.c.
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
**   drh@hwaci.com
**   http://www.hwaci.com/drh/
**
*************************************************************************
** 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 $
*/
#include "sqliteInt.h"

/*
** Process a DELETE FROM statement.
*/
void sqliteDeleteFrom(







|







20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
**   drh@hwaci.com
**   http://www.hwaci.com/drh/
**
*************************************************************************
** This file contains C code routines that are called by the parser
** to handle DELETE FROM statements.
**
** $Id: delete.c,v 1.8 2001/03/20 22:05:00 drh Exp $
*/
#include "sqliteInt.h"

/*
** Process a DELETE FROM statement.
*/
void sqliteDeleteFrom(
81
82
83
84
85
86
87














88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129

130
131
132
133
134
135
  }

  /* Begin generating code.
  */
  v = sqliteGetVdbe(pParse);
  if( v==0 ) goto delete_from_cleanup;















  /* 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);
  sqliteExprDelete(pWhere);
  return;
}







>
>
>
>
>
>
>
>
>
>
>
>
>
>
|
|
|
|
|

|
|
|

|
|
|

|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
>






81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
  }

  /* Begin generating code.
  */
  v = sqliteGetVdbe(pParse);
  if( v==0 ) goto delete_from_cleanup;

  /* Special case: A DELETE without a WHERE clause deletes everything.
  ** It is easier just to deleted the database files directly.
  */
  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);
    }
  }

  /* 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);
  sqliteExprDelete(pWhere);
  return;
}
Changes to src/sqliteInt.h.
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
** Author contact information:
**   drh@hwaci.com
**   http://www.hwaci.com/drh/
**
*************************************************************************
** Internal interface definitions for SQLite.
**
** @(#) $Id: sqliteInt.h,v 1.36 2001/01/20 19:52:50 drh Exp $
*/
#include "sqlite.h"
#include "dbbe.h"
#include "vdbe.h"
#include "parse.h"
#include <gdbm.h>
#include <stdio.h>







|







19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
** Author contact information:
**   drh@hwaci.com
**   http://www.hwaci.com/drh/
**
*************************************************************************
** Internal interface definitions for SQLite.
**
** @(#) $Id: sqliteInt.h,v 1.37 2001/03/20 22:05:00 drh Exp $
*/
#include "sqlite.h"
#include "dbbe.h"
#include "vdbe.h"
#include "parse.h"
#include <gdbm.h>
#include <stdio.h>
423
424
425
426
427
428
429
430
431
432
433
void sqliteExprResolveInSelect(Parse*, Expr*);
int sqliteExprAnalyzeAggregates(Parse*, Expr*);
void sqliteParseInfoReset(Parse*);
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*);







<
<
<

423
424
425
426
427
428
429



430
void sqliteExprResolveInSelect(Parse*, Expr*);
int sqliteExprAnalyzeAggregates(Parse*, Expr*);
void sqliteParseInfoReset(Parse*);
Vdbe *sqliteGetVdbe(Parse*);
int sqliteRandomByte(void);
int sqliteRandomInteger(void);
void sqliteRandomName(char*,char*);



char *sqliteDbbeNameToFile(const char*,const char*,const char*);
Changes to src/vdbe.c.
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
** inplicit conversion from one type to the other occurs as necessary.
** 
** Most of the code in this file is taken up by the sqliteVdbeExec()
** function which does the work of interpreting a VDBE program.
** 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 $
*/
#include "sqliteInt.h"
#include <unistd.h>
#include <ctype.h>

/*
** SQL is translated into a sequence of instructions to be







|







37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
** inplicit conversion from one type to the other occurs as necessary.
** 
** Most of the code in this file is taken up by the sqliteVdbeExec()
** function which does the work of interpreting a VDBE program.
** But other routines are also provided to help in building up
** a program instruction by instruction.
**
** $Id: vdbe.c,v 1.53 2001/03/20 22:05:00 drh Exp $
*/
#include "sqliteInt.h"
#include <unistd.h>
#include <ctype.h>

/*
** SQL is translated into a sequence of instructions to be
163
164
165
166
167
168
169














170
171
172
173
174
175
176
};
struct SetElem {
  SetElem *pHash;        /* Next element with the same hash on zKey */
  SetElem *pNext;        /* Next element in a list of them all */
  char zKey[1];          /* Value of this key */
};















/*
** An instance of the virtual machine
*/
struct Vdbe {
  sqlite *db;         /* The whole database */
  Dbbe *pBe;          /* Opaque context structure used by DB backend */
  FILE *trace;        /* Write an execution trace here, if not NULL */







>
>
>
>
>
>
>
>
>
>
>
>
>
>







163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
};
struct SetElem {
  SetElem *pHash;        /* Next element with the same hash on zKey */
  SetElem *pNext;        /* Next element in a list of them all */
  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
*/
struct Vdbe {
  sqlite *db;         /* The whole database */
  Dbbe *pBe;          /* Opaque context structure used by DB backend */
  FILE *trace;        /* Write an execution trace here, if not NULL */
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
  int nStackAlloc;    /* Size of the stack */
  Stack *aStack;      /* The operand stack, except string values */
  char **zStack;      /* Text or binary values of the stack */
  char **azColName;   /* Becomes the 4th parameter to callbacks */
  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 */
  int nSort;          /* Number of slots in apSort[] */
  Sorter **apSort;    /* An open sorter list */
  FILE *pFile;        /* At most one open file handler */
  int nField;         /* Number of file fields */
  char **azField;     /* Data for each file field */
  char *zLine;        /* A single line from the input file */
  int nLineAlloc;     /* Number of spaces allocated for zLine */







|







198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
  int nStackAlloc;    /* Size of the stack */
  Stack *aStack;      /* The operand stack, except string values */
  char **zStack;      /* Text or binary values of the stack */
  char **azColName;   /* Becomes the 4th parameter to callbacks */
  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[] */
  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 */
  int nField;         /* Number of file fields */
  char **azField;     /* Data for each file field */
  char *zLine;        /* A single line from the input file */
  int nLineAlloc;     /* Number of spaces allocated for zLine */
680
681
682
683
684
685
686











687
688
689
690
691
692
693
    for(i=oldAlloc; i<p->nStackAlloc; i++){
      p->zStack[i] = 0;
      p->aStack[i].flags = 0;
    }
  }
  return 0;
}












/*
** Clean up the VM after execution.
**
** This routine will automatically close any cursors, list, and/or
** sorters that were left open.
*/







>
>
>
>
>
>
>
>
>
>
>







694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
    for(i=oldAlloc; i<p->nStackAlloc; i++){
      p->zStack[i] = 0;
      p->aStack[i].flags = 0;
    }
  }
  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.
**
** This routine will automatically close any cursors, list, and/or
** sorters that were left open.
*/
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
      sqliteFree(p->aMem[i].z);
    }
  }
  sqliteFree(p->aMem);
  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;
    }
  }
  sqliteFree(p->apList);
  p->apList = 0;
  p->nList = 0;
  for(i=0; i<p->nSort; i++){
    Sorter *pSorter;
    while( (pSorter = p->apSort[i])!=0 ){







|
<
|
<







735
736
737
738
739
740
741
742

743

744
745
746
747
748
749
750
      sqliteFree(p->aMem[i].z);
    }
  }
  sqliteFree(p->aMem);
  p->aMem = 0;
  p->nMem = 0;
  for(i=0; i<p->nList; i++){
    KeylistFree(p->apList[i]);

    p->apList[i] = 0;

  }
  sqliteFree(p->apList);
  p->apList = 0;
  p->nList = 0;
  for(i=0; i<p->nSort; i++){
    Sorter *pSorter;
    while( (pSorter = p->apSort[i])!=0 ){
2427
2428
2429
2430
2431
2432
2433
2434

2435
2436
2437
2438
2439
2440
2441
2442
2443
2444
2445
2446
2447
2448
2449
2450
2451
2452
2453
2454
2455
2456
2457
2458
2459
2460
2461
2462
2463
2464
2465

2466
2467
2468






2469


2470
2471
2472
2473
2474
2475
2476
2477
2478
2479
2480
2481
2482
2483
2484
2485
2486
2487
2488
2489
2490
2491
2492
2493
2494
2495
2496
2497
2498
2499

2500
2501
2502





2503
2504
2505
2506
2507




2508
2509
2510
2511
2512
2513
2514
2515
2516
2517
2518
2519
2520
2521
2522
2523
2524
2525
2526
2527
2528
2529
2530
2531
      case OP_Reorganize: {
        pBex->ReorganizeTable(pBe, pOp->p3);
        break;
      }

      /* 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
      ** in its place.
      */
      case OP_ListOpen: {
        int i = pOp->p1;
        VERIFY( if( i<0 ) goto bad_instruction; )
        if( i>=p->nList ){
          int j;
          p->apList = sqliteRealloc( p->apList, (i+1)*sizeof(FILE*) );
          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);
        }
        break;
      }

      /* Opcode: ListWrite P1 * *
      **
      ** Write the integer on the top of the stack
      ** into the temporary storage file P1.
      */
      case OP_ListWrite: {
        int i = pOp->p1;

        VERIFY( if( i<0 ) 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]);
        }
        break;
      }

      /* Opcode: ListRewind P1 * *
      **
      ** Rewind the temporary buffer P1 back to the beginning.
      */
      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]);
        }
        break;
      }

      /* Opcode: ListRead P1 P2 *
      **
      ** Attempt to read an integer from temporary storage buffer P1
      ** and push it onto the stack.  If the storage buffer is empty, 
      ** push nothing but instead jump to P2.
      */
      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 ){





          p->tos++;
          if( NeedStack(p, p->tos) ) goto no_mem;
          aStack[p->tos].i = val;
          aStack[p->tos].flags = STK_Int;
          zStack[p->tos] = 0;




        }else{
          pc = pOp->p2 - 1;
        }
        break;
      }

      /* Opcode: ListClose P1 * *
      **
      ** Close the temporary storage buffer and discard its contents.
      */
      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;
        }
        break;
      }

      /* Opcode: SortOpen P1 * *
      **
      ** Create a new sorter with index P1
      */







|
>
|
|
|







|




|
<
|
<
<







|



>
|

|
>
>
>
>
>
>
|
>
>
|
|
|
<
<










|
<
<












>
|
|
|
>
>
>
>
>


|


>
>
>
>













|
|
|
<







2450
2451
2452
2453
2454
2455
2456
2457
2458
2459
2460
2461
2462
2463
2464
2465
2466
2467
2468
2469
2470
2471
2472
2473
2474

2475


2476
2477
2478
2479
2480
2481
2482
2483
2484
2485
2486
2487
2488
2489
2490
2491
2492
2493
2494
2495
2496
2497
2498
2499
2500
2501
2502


2503
2504
2505
2506
2507
2508
2509
2510
2511
2512
2513


2514
2515
2516
2517
2518
2519
2520
2521
2522
2523
2524
2525
2526
2527
2528
2529
2530
2531
2532
2533
2534
2535
2536
2537
2538
2539
2540
2541
2542
2543
2544
2545
2546
2547
2548
2549
2550
2551
2552
2553
2554
2555
2556
2557
2558
2559

2560
2561
2562
2563
2564
2565
2566
      case OP_Reorganize: {
        pBex->ReorganizeTable(pBe, pOp->p3);
        break;
      }

      /* Opcode: ListOpen P1 * *
      **
      ** 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: {
        int i = pOp->p1;
        VERIFY( if( i<0 ) goto bad_instruction; )
        if( i>=p->nList ){
          int j;
          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] ){
          KeylistFree(p->apList[i]);

          p->apList[i] = 0;


        }
        break;
      }

      /* Opcode: ListWrite P1 * *
      **
      ** Write the integer on the top of the stack
      ** into the temporary storage list P1.
      */
      case OP_ListWrite: {
        int i = pOp->p1;
        Keylist *pKeylist;
        VERIFY( if( i<0 || i>=p->nList ) goto bad_instruction; )
        VERIFY( if( p->tos<0 ) goto not_enough_stack; )
        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;
      }

      /* Opcode: ListRewind P1 * *
      **
      ** Rewind the temporary buffer P1 back to the beginning.
      */
      case OP_ListRewind: {
        int i = pOp->p1;
        VERIFY( if( i<0 ) goto bad_instruction; )
        /* This is now a no-op */


        break;
      }

      /* Opcode: ListRead P1 P2 *
      **
      ** Attempt to read an integer from temporary storage buffer P1
      ** and push it onto the stack.  If the storage buffer is empty, 
      ** push nothing but instead jump to P2.
      */
      case OP_ListRead: {
        int i = pOp->p1;
        int val, amt;
        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 = 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;
        }
        break;
      }

      /* Opcode: ListClose P1 * *
      **
      ** Close the temporary storage buffer and discard its contents.
      */
      case OP_ListClose: {
        int i = pOp->p1;
        VERIFY( if( i<0 ) goto bad_instruction; )
        VERIFY( if( i>=p->nList ) goto bad_instruction; )
        KeylistFree(p->apList[i]);
        p->apList[i] = 0;

        break;
      }

      /* Opcode: SortOpen P1 * *
      **
      ** Create a new sorter with index P1
      */
Changes to test/delete.test.
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
#   drh@hwaci.com
#   http://www.hwaci.com/drh/
#
#***********************************************************************
# 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 $

set testdir [file dirname $argv0]
source $testdir/tester.tcl

# Try to delete from a non-existant table.
#
do_test delete-1.1 {







|







19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
#   drh@hwaci.com
#   http://www.hwaci.com/drh/
#
#***********************************************************************
# This file implements regression tests for SQLite library.  The
# focus of this file is testing the DELETE FROM statement.
#
# $Id: delete.test,v 1.8 2001/03/20 22:05:00 drh Exp $

set testdir [file dirname $argv0]
source $testdir/tester.tcl

# Try to delete from a non-existant table.
#
do_test delete-1.1 {
113
114
115
116
117
118
119
120




















































121
  }
  execsql {SELECT f1 FROM table1 ORDER BY f1}
} {42 44 47 48 50}
do_test delete-5.7 {
  execsql "DELETE FROM table1 WHERE f1!=48"
  execsql {SELECT f1 FROM table1 ORDER BY f1}
} {48}





















































finish_test








>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>

113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
  }
  execsql {SELECT f1 FROM table1 ORDER BY f1}
} {42 44 47 48 50}
do_test delete-5.7 {
  execsql "DELETE FROM table1 WHERE f1!=48"
  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
Changes to www/changes.tcl.
12
13
14
15
16
17
18









19
20
21
22
23
24
25
}


proc chng {date desc} {
  puts "<DT><B>$date</B></DT>"
  puts "<DD><P><UL>$desc</UL></P></DD>"
}










chng {2001 Mar 20 (1.0.26)} {
<li>A serious bug fixed on Windows.  Windows users should upgrade.
    No impact to Unix.</li>
}

chng {2001 Mar 15 (1.0.25)} {







>
>
>
>
>
>
>
>
>







12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
}


proc chng {date desc} {
  puts "<DT><B>$date</B></DT>"
  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>
}

chng {2001 Mar 15 (1.0.25)} {