SQLite

Check-in [54d60c68dc]
Login

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

Overview
Comment:Added the "memory:" driver (CVS 158)
Downloads: Tarball | ZIP archive
Timelines: family | ancestors | descendants | both | trunk
Files: files | file ages | folders
SHA1: 54d60c68dc83410e911b828a680772541c44e9df
User & Date: drh 2000-10-19 14:10:08.000
Context
2000-10-19
14:10
Added the "memory:" driver (CVS 1707) (check-in: e9236833d9 user: drh tags: trunk)
14:10
Added the "memory:" driver (CVS 158) (check-in: 54d60c68dc user: drh tags: trunk)
02:00
Version 1.0.13 (CVS 490) (check-in: b9c84fa579 user: drh tags: trunk)
Changes
Unified Diff Ignore Whitespace Patch
Changes to Makefile.in.
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59

60
61
62
63
64
65
66

# The library that programs using readline() must link against.
#
LIBREADLINE = @TARGET_READLINE_LIBS@

# Object files for the SQLite library.
#
LIBOBJ = build.o dbbe.o dbbegdbm.o delete.o expr.o insert.o \
         main.o parse.o printf.o select.o table.o tokenize.o update.o \
         util.o vdbe.o where.o tclsqlite.o

# All of the source code files.
#
SRC = \
  $(TOP)/src/build.c \
  $(TOP)/src/dbbe.c \
  $(TOP)/src/dbbegdbm.c \

  $(TOP)/src/dbbe.h \
  $(TOP)/src/dbbemem.c \
  $(TOP)/src/delete.c \
  $(TOP)/src/expr.c \
  $(TOP)/src/insert.c \
  $(TOP)/src/main.c \
  $(TOP)/src/parse.y \







|









>







43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67

# The library that programs using readline() must link against.
#
LIBREADLINE = @TARGET_READLINE_LIBS@

# Object files for the SQLite library.
#
LIBOBJ = build.o dbbe.o dbbegdbm.o dbbemem.o delete.o expr.o insert.o \
         main.o parse.o printf.o select.o table.o tokenize.o update.o \
         util.o vdbe.o where.o tclsqlite.o

# All of the source code files.
#
SRC = \
  $(TOP)/src/build.c \
  $(TOP)/src/dbbe.c \
  $(TOP)/src/dbbegdbm.c \
  $(TOP)/src/dbbemem.c \
  $(TOP)/src/dbbe.h \
  $(TOP)/src/dbbemem.c \
  $(TOP)/src/delete.c \
  $(TOP)/src/expr.c \
  $(TOP)/src/insert.c \
  $(TOP)/src/main.c \
  $(TOP)/src/parse.y \
118
119
120
121
122
123
124



125
126
127
128
129
130
131

dbbe.o:	$(TOP)/src/dbbe.c $(HDR)
	$(TCC) $(GDBM_FLAGS) -c $(TOP)/src/dbbe.c

dbbegdbm.o:	$(TOP)/src/dbbegdbm.c $(HDR)
	$(TCC) $(GDBM_FLAGS) -c $(TOP)/src/dbbegdbm.c




main.o:	$(TOP)/src/main.c $(HDR)
	$(TCC) $(GDBM_FLAGS) -c $(TOP)/src/main.c

parse.o:	parse.c $(HDR)
	$(TCC) $(GDBM_FLAGS) -c parse.c

parse.h:	parse.c







>
>
>







119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135

dbbe.o:	$(TOP)/src/dbbe.c $(HDR)
	$(TCC) $(GDBM_FLAGS) -c $(TOP)/src/dbbe.c

dbbegdbm.o:	$(TOP)/src/dbbegdbm.c $(HDR)
	$(TCC) $(GDBM_FLAGS) -c $(TOP)/src/dbbegdbm.c

dbbemem.o:	$(TOP)/src/dbbemem.c $(HDR)
	$(TCC) $(GDBM_FLAGS) -c $(TOP)/src/dbbemem.c

main.o:	$(TOP)/src/main.c $(HDR)
	$(TCC) $(GDBM_FLAGS) -c $(TOP)/src/main.c

parse.o:	parse.c $(HDR)
	$(TCC) $(GDBM_FLAGS) -c parse.c

parse.h:	parse.c
Changes to VERSION.
1
1.0.13
|
1
1.0.14
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.20 2000/10/19 01:49:02 drh Exp $
*/
#include "sqliteInt.h"

/*
** This routine opens a new database.  It looks at the first
** few characters of the database name to try to determine what
** kind of database to open.  If the first characters are "gdbm:",







|







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.21 2000/10/19 14:10:09 drh Exp $
*/
#include "sqliteInt.h"

/*
** This routine opens a new database.  It looks at the first
** few characters of the database name to try to determine what
** kind of database to open.  If the first characters are "gdbm:",
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
  int createFlag,        /* True to create database if it doesn't exist */
  char **pzErrMsg        /* Write error messages (if any) here */
){
  extern Dbbe *sqliteGdbmOpen(const char*,int,int,char**);
  if( strncmp(zName, "gdbm:", 5)==0 ){
    return sqliteGdbmOpen(&zName[5], writeFlag, createFlag, pzErrMsg);
  }
#if 0
  if( strncmp(zName, "memory:", 7)==0 ){
    extern Dbbe *sqliteMemOpen(const char*,int,int,char**);
    return sqliteMemOpen(&zName[7], writeFlag, createFlag, pzErrMsg);
  }
#endif
  return sqliteGdbmOpen(zName, writeFlag, createFlag, pzErrMsg);
}







<




<


52
53
54
55
56
57
58

59
60
61
62

63
64
  int createFlag,        /* True to create database if it doesn't exist */
  char **pzErrMsg        /* Write error messages (if any) here */
){
  extern Dbbe *sqliteGdbmOpen(const char*,int,int,char**);
  if( strncmp(zName, "gdbm:", 5)==0 ){
    return sqliteGdbmOpen(&zName[5], writeFlag, createFlag, pzErrMsg);
  }

  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);
}
Changes to src/dbbemem.c.
1
2
3
4
5
6
7
8
9
/*
** Copyright (c) 1999, 2000 D. Richard Hipp
**
** This program is free software; you can redistribute it and/or
** modify it under the terms of the GNU General Public
** License as published by the Free Software Foundation; either
** version 2 of the License, or (at your option) any later version.
**
** This program is distributed in the hope that it will be useful,

|







1
2
3
4
5
6
7
8
9
/*
** Copyright (c) 2000 D. Richard Hipp
**
** This program is free software; you can redistribute it and/or
** modify it under the terms of the GNU General Public
** License as published by the Free Software Foundation; either
** version 2 of the License, or (at your option) any later version.
**
** This program is distributed in the hope that it will be useful,
22
23
24
25
26
27
28
29
30
31
32
33
34
35



36
37
















































































































































































































































































































38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
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
**
*************************************************************************
** 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 implements a backend that constructs a database in
** memory using hash tables.  Nothing is ever read or written
** to disk.  Everything is forgotten when the program exits.
**
** $Id: dbbemem.c,v 1.1 2000/10/11 19:28:52 drh Exp $
*/
#include "sqliteInt.h"



#include <time.h>

















































































































































































































































































































/*
** The following structure holds the current state of the RC4 algorithm.
** We use RC4 as a random number generator.  Each call to RC4 gives
** a random 8-bit number.
**
** Nothing in this file or anywhere else in SQLite does any kind of
** encryption.  The RC4 algorithm is being used as a PRNG (pseudo-random
** number generator) not as an encryption device.
*/
struct rc4 {
  int i, j;
  int s[256];
};

/*
** Key or data is stored as an instance of the following
*/
typedef struct datum {
  void *dptr;    /* The data */
  int dsize;     /* Number of bytes of data */
  void *kptr;    /* The key */
  int ksize;     /* Number of bytes of key */
  datum *pHash;  /* Next datum with the same hash */
}

/*
** Information about each open database table is an instance of this 
** structure.  There will only be one such structure for each
** table.  If the VDBE opens the same table twice (as will happen
** for a self-join, for example) then two DbbeCursor structures are
** created but there is only a single BeFile structure with an
** nRef of 2.
*/
typedef struct BeFile BeFile;
struct BeFile {
  char *zName;             /* Name of the table */
  BeFile *pNext, *pPrev;   /* Next and previous on list of all tables */
  BeFile *pHash;           /* Next table with same hash on zName */
  int nRef;                /* Number of cursor that have this file open */  
  int delOnClose;          /* Delete when the last cursor closes this file */
  int nRec;                /* Number of entries in the hash table */
  int nHash;               /* Number of slots in the hash table */
  datum **aHash;           /* The hash table */
};

/*
** The complete database is an instance of the following structure.
*/
struct Dbbe {
  BeFile *pOpen;    /* List of open tables */
  int nTemp;         /* Number of temporary files created */
  FILE **apTemp;     /* Space to hold temporary file pointers */
  char **azTemp;     /* Names of the temporary files */
  struct rc4 rc4;    /* The random number generator */
  BeFile aHash[331]; /* Hash table of tables */
};

/*
** An cursor into a database file is an instance of the following structure.
** There can only be a single BeFile structure for each disk file, but
** there can be multiple DbbeCursor structures.  Each DbbeCursor represents
** a cursor pointing to a particular part of the open BeFile.  The
** BeFile.nRef field hold a count of the number of DbbeCursor structures
** associated with the same disk file.
*/
struct DbbeCursor {
  Dbbe *pBe;         /* The database of which this record is a part */
  BeFile *pFile;     /* The database file for this table */
  datum *pRec;       /* Most recently used key and data */
  int h;             /* Hash of pRec */
  int needRewind;    /* Next key should be the first */
  int readPending;   /* The fetch hasn't actually been done yet */
};

/*
** Initialize the RC4 PRNG.  "seed" is a pointer to some random
** data used to initialize the PRNG.  
*/
static void rc4init(struct rc4 *p, char *seed, int seedlen){







|
<
<

|


>
>
>


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















|
<
<
<
<
<
<
<
<
|
<
<
<
<
<
<
<

|
|
|
<
<
<
<
<
<
<
<
|
<
<
<
<
<




<




|

|
|



|
|
|
<

<







22
23
24
25
26
27
28
29


30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
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
160
161
162
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
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358








359







360
361
362
363








364





365
366
367
368

369
370
371
372
373
374
375
376
377
378
379
380
381
382

383

384
385
386
387
388
389
390
**
*************************************************************************
** 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 talbe as the database backend. 


**
** $Id: dbbemem.c,v 1.2 2000/10/19 14:10:09 drh Exp $
*/
#include "sqliteInt.h"
#include <sys/stat.h>
#include <unistd.h>
#include <ctype.h>
#include <time.h>


typedef struct Array Array;
typedef struct ArrayElem ArrayElem;
typedef struct Datum Datum;

/* A complete associative array is an instance of the following structure.
** The internals of this structure are intended to be opaque -- client
** code should not attempt to access or modify the fields of this structure
** directly.  Change this structure only by using the routines below.
** However, many of the "procedures" and "functions" for modifying and
** accessing this structure are really macros, so we can't really make
** this structure opaque.
*/
struct Array {
  int count;               /* Number of entries in the array */
  ArrayElem *first;        /* The first element of the array */
  int htsize;              /* Number of buckets in the hash table */
  struct _Array_ht {         /* the hash table */
    int count;               /* Number of entries with this hash */
    ArrayElem *chain;        /* Pointer to first entry with this hash */
  } *ht;
};

/*
** An instance of the following structure stores a single key or
** data element.
*/
struct Datum {
  int n;
  void *p;
};

/* Each element in the associative array is an instance of the following 
** structure.  All elements are stored on a single doubly-linked list.
**
** Again, this structure is intended to be opaque, but it can't really
** be opaque because it is used by macros.
*/
struct ArrayElem {
  ArrayElem *next, *prev;  /* Next and previous elements in the array */
  Datum key, data;         /* Key and data for this element */
};

/* Some routines are so simple that they can be implemented as macros
** These are given first. */

/* Return the number of entries in the array */
#define ArrayCount(X)    ((X)->count)

/* Return a pointer to the first element of the array */
#define ArrayFirst(X)    ((X)->first)

/* Return a pointer to the next (or previous) element of the array */
#define ArrayNext(X)     ((X)->next)
#define ArrayPrev(X)     ((X)->prev)

/* Return TRUE if the element given is the last element in the array */
#define ArrayIsLast(X)   ((X)->next==0)
#define ArrayIsFirst(X)  ((X)->prev==0)

/* Return the data or key for an element of the array */
#define ArrayData(X)     ((X)->data.p)
#define ArrayDataSize(X) ((X)->data.n)
#define ArrayKey(X)      ((X)->key.p)
#define ArrayKeySize(X)  ((X)->key.n)

/* Turn bulk memory into an associative array object by initializing the
** fields of the Array structure.
*/
static void ArrayInit(Array *new){
  new->first = 0;
  new->count = 0;
  new->htsize = 0;
  new->ht = 0;
}

/* Remove all entries from an associative array.  Reclaim all memory.
** This is the opposite of ArrayInit().
*/
static void ArrayClear(Array *array){
  ArrayElem *elem;         /* For looping over all elements of the array */

  elem = array->first;
  array->first = 0;
  array->count = 0;
  if( array->ht ) sqliteFree(array->ht);
  array->ht = 0;
  array->htsize = 0;
  while( elem ){
    ArrayElem *next_elem = elem->next;
    sqliteFree(elem);
    elem = next_elem;
  }
}

/*
** Generate a hash from an N-byte key
*/
static int ArrayHash(Datum d){
  int h = 0;
  while( d.n-- > 0 ){
    h = (h<<9) ^ (h<<3) ^ h ^ *(((char*)d.p)++);
  }
  if( h<0 ) h = -h; 
  return h;
}

/* Resize the hash table for a Array array
*/
static void ArrayRehash(Array *array, int new_size){
  struct _Array_ht *new_ht;       /* The new hash table */
  ArrayElem *elem, *next_elem;    /* For looping over existing elements */
  int i;                          /* Loop counter */
  ArrayElem *x;                   /* Element being copied to new hash table */

  new_ht = sqliteMalloc( new_size*sizeof(struct _Array_ht) );
  if( array->ht ) sqliteFree(array->ht);
  array->ht = new_ht;
  array->htsize = new_size;
  for(i=new_size-1; i>=0; i--){ 
    new_ht[i].count = 0;
    new_ht[i].chain = 0;
  }
  for(elem=array->first, array->first=0; elem; elem = next_elem){
    int h = ArrayHash(elem->key) & (new_size-1);
    next_elem = elem->next;
    x = new_ht[h].chain;
    if( x ){
      elem->next = x;
      elem->prev = x->prev;
      if( x->prev ) x->prev->next = elem;
      else          array->first = elem;
      x->prev = elem;
    }else{
      elem->next = array->first;
      if( array->first ) array->first->prev = elem;
      elem->prev = 0;
      array->first = elem;
    }
    new_ht[h].chain = elem;
    new_ht[h].count++;
  }
}

/* This function (for internal use only) locates an element in an
** array that matches the given key.  The hash for this key has
** already been computed and is passed as the 3rd parameter.
*/
static ArrayElem *ArrayFindElementGivenHash(
  const Array *array,    /* The array to be searched */
  const Datum key,       /* The key we are searching for */
  int h                  /* The hash for this key. */
){
  ArrayElem *elem;                /* Used to loop thru the element list */
  int count;                      /* Number of elements left to test */

  if( array->count ){
    elem = array->ht[h].chain;
    count = array->ht[h].count;
    while( count-- && elem ){
      if( elem->key.n==key.n && memcmp(elem->key.p,key.p,key.n)==0 ){ 
        return elem;
      }
      elem = elem->next;
    }
  }
  return 0;
}


/* Attempt to locate an element of the associative array with a key
** that matches "key".  Return the ArrayElement if found and NULL if
** if no match.
*/
static ArrayElem *ArrayFindElement(const Array *array, Datum key){
  int h;             /* A hash on key */
  if( array->count==0 ) return 0;
  h = ArrayHash(key);
  return ArrayFindElementGivenHash(array, key, h & (array->htsize-1));
}

/* Remove a single entry from the array given a pointer to that
** element and a hash on the element's key.
*/
static void ArrayRemoveElementGivenHash(
  Array *array,        /* The array containing "elem" */
  ArrayElem* elem,     /* The element to be removed from the array */
  int h                /* Hash value for the element */
){
  if( elem->prev ){
    elem->prev->next = elem->next; 
  }else{
    array->first = elem->next;
  }
  if( elem->next ){
    elem->next->prev = elem->prev;
  }
  if( array->ht[h].chain==elem ){
    array->ht[h].chain = elem->next;
  }
  array->ht[h].count--;
  if( array->ht[h].count<=0 ){
    array->ht[h].chain = 0;
  }
  sqliteFree( elem );
  array->count--;
}

/* Attempt to locate an element of the associative array with a key
** that matches "key".  Return the data for this element if it is
** found, or NULL if no match is found.
*/
static Datum ArrayFind(const Array *array, Datum key){
  int h;             /* A hash on key */
  ArrayElem *elem;   /* The element that matches key */
  static Datum nil = {0, 0};

  if( array->count==0 ) return nil;
  h = ArrayHash(key);
  elem = ArrayFindElementGivenHash(array, key, h & (array->htsize-1));
  return elem ? elem->data : nil;
}

/* Insert an element into the array.  The key will be "key" and
** the data will be "data".
**
** If no array element exists with a matching key, then a new
** array element is created.  The key is copied into the new element.
** But only a pointer to the data is stored.  NULL is returned.
**
** If another element already exists with the same key, then the
** new data replaces the old data and the old data is returned.
** The key is not copied in this instance.
**
** If the "data" parameter to this function is NULL, then the
** element corresponding to "key" is removed from the array.
*/
static Datum ArrayInsert(Array *array, Datum key, Datum data){
  int hraw;              /* Raw hash value of the key */
  int h;                 /* the hash of the key modulo hash table size */
  ArrayElem *elem;       /* Used to loop thru the element list */
  ArrayElem *new_elem;   /* New element added to the array */
  Datum rv;              /* Return value */
  static Datum nil = {0, 0};

  hraw = ArrayHash(key);
  h = hraw & (array->htsize-1);
  elem = ArrayFindElementGivenHash(array,key,h);
  if( elem ){
    Datum old_data = elem->data;
    if( data.p==0 ){
      ArrayRemoveElementGivenHash(array,elem,h);
    }else{
      elem->data = data;
    }
    return old_data;
  }
  if( data.p==0 ) return nil;
  new_elem = (ArrayElem*)sqliteMalloc( sizeof(ArrayElem) + key.n );
  if( new_elem==0 ) return nil;
  new_elem->key.n = key.n;
  new_elem->key.p = (void*)&new_elem[1];
  memcpy(new_elem->key.p, key.p, key.n);
  array->count++;
  if( array->htsize==0 ) ArrayRehash(array,4);
  if( array->count > array->htsize ){
    ArrayRehash(array,array->htsize*2);
  }
  h = hraw & (array->htsize-1);
  elem = array->ht[h].chain;
  if( elem ){
    new_elem->next = elem;
    new_elem->prev = elem->prev;
    if( elem->prev ){ elem->prev->next = new_elem; }
    else            { array->first = new_elem; }
    elem->prev = new_elem;
  }else{
    new_elem->next = array->first;
    new_elem->prev = 0;
    if( array->first ){ array->first->prev = new_elem; }
    array->first = new_elem;
  }
  array->ht[h].count++;
  array->ht[h].chain = new_elem;
  new_elem->data = data;
  rv.p = 0;
  rv.n = 0;
  return rv;
}

/*
** Information about each open database table is an instance of this 
** structure.  There will only be one such structure for each
** table.  If the VDBE opens the same table twice (as will happen
** for a self-join, for example) then two DbbeCursor structures are
** created but there is only a single MTable structure.
*/
typedef struct MTable MTable;
struct MTable {
  char *zName;            /* Name of the table */
  int delOnClose;         /* Delete when closing */
  Array data;             /* The data in this stable */
};

/*
** The following structure holds the current state of the RC4 algorithm.
** We use RC4 as a random number generator.  Each call to RC4 gives
** a random 8-bit number.
**
** Nothing in this file or anywhere else in SQLite does any kind of
** encryption.  The RC4 algorithm is being used as a PRNG (pseudo-random
** number generator) not as an encryption device.
*/
struct rc4 {
  int i, j;
  int s[256];
};

/*
** The following structure contains all information used by GDBM








** database driver.  This is a subclass of the Dbbe structure.







*/
typedef struct Dbbex Dbbex;
struct Dbbex {
  Dbbe dbbe;         /* The base class */








  Array tables;      /* All tables of the database */





  int nTemp;         /* Number of temporary files created */
  FILE **apTemp;     /* Space to hold temporary file pointers */
  char **azTemp;     /* Names of the temporary files */
  struct rc4 rc4;    /* The random number generator */

};

/*
** An cursor into a database file is an instance of the following structure.
** There can only be a single MTable structure for each disk file, but
** there can be multiple DbbeCursor structures.  Each DbbeCursor represents
** a cursor pointing to a particular part of the open MTable.  The
** MTable.nRef field hold a count of the number of DbbeCursor structures
** associated with the same disk file.
*/
struct DbbeCursor {
  Dbbex *pBe;        /* The database of which this record is a part */
  MTable *pTble;     /* The database file for this table */
  ArrayElem *elem;   /* Most recently accessed record */

  int needRewind;    /* Next key should be the first */

};

/*
** Initialize the RC4 PRNG.  "seed" is a pointer to some random
** data used to initialize the PRNG.  
*/
static void rc4init(struct rc4 *p, char *seed, int seedlen){
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
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191

192
193
194
195
196
197
198
199
200
201

202
203

204
205
206
207

208
209
210
211
212
213
214
  p->s[p->i] = p->s[p->j];
  p->s[p->j] = t;
  t = p->s[p->i] + p->s[p->j];
  return t & 0xff;
}

/*
** 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.
**
** If successful, a pointer to the Dbbe structure is returned.
** If there are errors, an appropriate error message is left
** in *pzErrMsg and NULL is returned.
*/
Dbbe *sqliteDbbeOpen(
  const char *zName,     /* The name of the database */
  int writeFlag,         /* True if we will be writing to the database */
  int createFlag,        /* True to create database if it doesn't exist */
  char **pzErrMsg        /* Write error messages (if any) here */
){
  Dbbe *pNew;
  time_t now;

  pNew = sqliteMalloc(sizeof(Dbbe));
  if( pNew==0 ){
    sqliteSetString(pzErrMsg, "out of memory", 0);
    return 0;
  }
  pNew->pOpen = 0;
  time(&now);
  rc4init(&pNew->rc4, (char*)&now, sizeof(now));
  return pNew;
}

/*
** Free all of the memory associated with a single BeFile structure.
** It is assumed that this BeFile structure has already been unlinked
** from its database.
*/
static void sqliteDbbeFreeFile(BeFile *pFile){
  int i;
  for(i=0; i<pFile->nHash; i++){
    datum *pDatum, *pNextDatum;
    for(pDatum = pFile->aHash[i]; pDatum; pDatum=pNextDatum){
      pNextDatum = pDatum->pHash;
      sqliteFree(pDatum->dptr);
      sqliteFree(pDatum);
    }
  }

  sqliteFree(pFile->zName);
  sqliteFree(pFile->aHash);
  memset(pFile, 0, sizeof(*pFile));   
  sqliteFree(pFile);
}

/*
** Completely shutdown the given database.  Close all files.  Free all memory.
*/
void sqliteDbbeClose(Dbbe *pBe){

  BeFile *pFile, *pNext;
  int i;

  for(pFile=pBe->pOpen; pFile; pFile=pNext){
    pNext = pFile->pNext;
    sqliteDbbeFreeFile(pFile);
  }

  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;







<
<
<
|
<
<
<

<
<
<
<
<
<
<
<
|
<
<
<
<
|
<
<
<
<
<
<

|
<
<

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





|
>
|

>
|
|
|

>







416
417
418
419
420
421
422



423



424








425




426






427
428


429
430
431
432



433
434
435

436
437



438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
  p->s[p->i] = p->s[p->j];
  p->s[p->j] = t;
  t = p->s[p->i] + p->s[p->j];
  return t & 0xff;
}

/*



** Forward declaration



*/








static void sqliteMemCloseCursor(DbbeCursor *pCursr);











/*
** Erase all the memory of an MTable


*/
static void deleteMTable(MTable *p){
  ArrayElem *i;
  for(i=ArrayFirst(&p->data); i; i=ArrayNext(i)){



    void *data = ArrayData(i);
    sqliteFree(data);
  }

  ArrayClear(&p->data);
  sqliteFree(p);



}

/*
** Completely shutdown the given database.  Close all files.  Free all memory.
*/
static void sqliteMemClose(Dbbe *pDbbe){
  Dbbex *pBe = (Dbbex*)pDbbe;
  MTable *pTble, *pNext;
  int i;
  ArrayElem *j, *k;
  for(j=ArrayFirst(&pBe->tables); j; j=ArrayNext(j)){
    pTble = ArrayData(j);
    deleteMTable(pTble);
  }
  ArrayClear(&pBe->tables);
  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;
239
240
241
242
243
244
245

246



247
248
249
250
251
252
253

254
255
256
257
258
259
260
261
262

263
264
265
266
267
268
269
270
271
272
273
274
275
    int c = rc4byte(pRc4) % (sizeof(zRandomChars) - 1);
    zBuf[j++] = zRandomChars[c];
  }
  zBuf[j] = 0;
}

/*

** Hash a NULL-terminated string.



*/
static int sqliteStrHash(const char *z){
  int h = 0;
  while( *z ){
    h = (h<<3) ^ h ^ *(z++);
  }
  if( h<0 ) h = -h;

  return h;
}

/*
** Locate a file in a database
*/
static BeFile *sqliteDbbeFindFile(Dbbe *pBe, const char *zFile){
  int h;
  BeFile *pFile;


  h = sqliteStrHash(zFile) % (sizeof(pBe->aHash)/sizeof(pBe->aHash[0]));
  for(pFile=pBe->aHash[h]; pFile; pFile=pFile->pHash){
    if( strcmp(pFile->zName, zFile)==0 ) break;
  }
  return pFile;
}

/*
** Open a new table cursor.  Write a pointer to the corresponding
** DbbeCursor structure into *ppCursr.  Return an integer success
** code:
**







>
|
>
>
>

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

|







484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499



500
501

502

503




504
505



506
507
508
509
510
511
512
513
514
    int c = rc4byte(pRc4) % (sizeof(zRandomChars) - 1);
    zBuf[j++] = zRandomChars[c];
  }
  zBuf[j] = 0;
}

/*
** Translate the name of an SQL table (or index) into its
** canonical name.
** 
** Space to hold the canonical name is obtained from
** sqliteMalloc() and must be freed by the calling function.
*/
static char *sqliteNameOfTable(const char *zTable){
  char *zNew = 0;
  int i, c;



  sqliteSetString(&zNew, zTable, 0);
  if( zNew==0 ) return 0;

  for(i=0; (c = zNew[i])!=0; i++){

    if( isupper(c) ){




      zNew[i] = tolower(c);
    }



  }
  return zNew;
}

/*
** Open a new table cursor.  Write a pointer to the corresponding
** DbbeCursor structure into *ppCursr.  Return an integer success
** code:
**
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302

303
304
305
306

307
308
309
310
311

312

313

314
315
316
317
318
319
320
321
322
323
324
325
326
327


328
329


330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393


394


395
396



397





398

399
















400

401
402
403
404
405
406

407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549

550
551
552
553
554
555
556
557
558
559
560
561

562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583


584
585
586
587


588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636

637
638
639
640
641
642
643

644
645
646
647
648
649
650
651
652
653
654
655


656
657
658
659
660
661
662
663
664
665
666
667
668
669

670
671
672
673

674
675
676
677
678
679
680
681
682

683
684
685
686
687
688
689
690
691
692
693
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

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
751
752
753
754
755

756
757
758
759
760
761
762
763
764
765
766


















































**                       (This can happen if a SELECT callback tries to
**                       do an UPDATE or DELETE.)
**
** If zTable is 0 or "", then a temporary database file is created and
** a cursor to that temporary file is opened.  The temporary file
** will be deleted from the disk when it is closed.
*/
int sqliteDbbeOpenCursor(
  Dbbe *pBe,              /* The database the table belongs to */
  const char *zTable,     /* The SQL name of the file to be opened */
  int writeable,          /* True to open for writing */
  DbbeCursor **ppCursr    /* Write the resulting table pointer here */
){
  char *zFile;            /* Name of the table file */
  DbbeCursor *pCursr;     /* The new table cursor */

  BeFile *pFile;          /* The underlying data file for this table */
  int rc = SQLITE_OK;     /* Return value */
  int rw_mask;            /* Permissions mask for opening a table */
  int mode;               /* Mode for opening a table */


  *ppCursr = 0;
  pCursr = sqliteMalloc( sizeof(*pCursr) );
  if( pCursr==0 ) return SQLITE_NOMEM;
  if( zTable ){

    zFile = sqliteStrDup(zTable);

    pFile = sqliteDbbeFindFile(zFile);

  }else{
    pFile = 0;
    zFile = 0;
  }
  if( pFile==0 ){
    pFile = sqliteMalloc( sizeof(*pFile) );
    if( pFile==0 ){
      sqliteFree(zFile);
      return SQLITE_NOMEM;
    }
    if( zFile ){
      if( !writeable || pBe->write ){
        pFile->dbf = gdbm_open(zFile, 0, rw_mask, mode, 0);
      }else{


        pFile->dbf = 0;
      }


    }else{
      int limit;
      struct rc4 *pRc4;
      char zRandom[50];
      pRc4 = &pBe->rc4;
      zFile = 0;
      limit = 5;
      while( 1 ){
        randomName(&pBe->rc4, zRandom, "_temp_table_");
        sqliteFree(zFile);
        zFile = sqliteStrDup(zRandom);
        if( sqliteDbbeFindFile(pBe, zFile)==0 ) break;
      }
      pFile->delOnClose = 1;
    }
    pFile->zName = zFile;
    pFile->nRef = 1;
    pFile->pPrev = 0;
    if( pBe->pOpen ){
      pBe->pOpen->pPrev = pFile;
    }
    pFile->pNext = pBe->pOpen;
    pBe->pOpen = pFile;
  }else{
    sqliteFree(zFile);
    pFile->nRef++;
  }
  pCursr->pBe = pBe;
  pCursr->pFile = pFile;
  pCursr->readPending = 0;
  pCursr->needRewind = 1;
  if( rc!=SQLITE_OK ){
    sqliteDbbeCloseCursor(pCursr);
    *ppCursr = 0;
  }else{
    *ppCursr = pCursr;
  }
  return rc;
}

/*
** Unlink a file from the database
*/
static void sqliteDbbeUnlinkFile(Dbbe *pBe, BeFile *pFile){
  int h = sqliteStrHash(pFile->zName) % 
             (sizeof(pBe->aHash)/sizeof(pBe->aHash[0])));
  if( pBe->aHash[h]==pFile ){
    pBe->aHash[h] = pFile->pHash;
  }else{
    BeFile *pProbe;
    for(pProbe=pBe->aHash[h]; pProbe; pProbe=pProbe->pHash){
      if( pProbe->pHash==pFile ){
        pProbe->pHash = pFile->pHash;
        break;
      }
    }
  }
}

/*
** Drop a table from the database.  The file that corresponds
** to this table is deleted.
*/
void sqliteDbbeDropTable(Dbbe *pBe, const char *zTable){


  File *pFile;



  pFile = sqliteDbbeFindFile(pBe, zTable);



  if( pFile ){





    sqliteDbbeUnlinkFile(pFile);

    sqliteDbbeFreeFile(pFile);
















  }

}

/*
** Reorganize a table to reduce search times and disk usage.
*/
int sqliteDbbeReorganizeTable(Dbbe *pBe, const char *zTable){

  return SQLITE_OK;
}

/*
** Close a cursor previously opened by sqliteDbbeOpenCursor().
**
** There can be multiple cursors pointing to the same open file.
** The underlying file is not closed until all cursors have been
** closed.  This routine decrements the BeFile.nref field of the
** underlying file and closes the file when nref reaches 0.
*/
void sqliteDbbeCloseCursor(DbbeCursor *pCursr){
  BeFile *pFile;
  Dbbe *pBe;
  if( pCursr==0 ) return;
  pFile = pCursr->pFile;
  pBe = pCursr->pBe;
  pFile->nRef--;
  if( pFile->nRef<=0 ){
    if( pFile->pPrev ){
      pFile->pPrev->pNext = pFile->pNext;
    }else{
      pBe->pOpen = pFile->pNext;
    }
    if( pFile->pNext ){
      pFile->pNext->pPrev = pFile->pPrev;
    }
    if( pFile->delOnClose ){
      sqliteDbbeUnlinkFile(pFile);
      sqliteDbbeFreeFile(pFile);
    }
  }
  memset(pCursr, 0, sizeof(*pCursr));
  sqliteFree(pCursr);
}

/*
** Compute a hash on binary data
*/
static int sqliteBinaryHash(int n, char *z){
  int h = 0;
  while( n-- ){
    h = (h<<9) ^ (h<<3) ^ h ^ *(z++);
  }
  if( h<0 ) h = -h;
  return h;
}

/*
** Resize the hash table
*/
static void sqliteDbbeRehash(BeFile *pFile, int nHash){
  int i, h;
  datum *pRec, *pNextRec;
  datum **aHash;

  if( nHash<1 ) return;
  aHash = sqliteMalloc( sizeof(aHash[0])*nHash );
  if( aHash==0 ) return;
  for(i=0; i<pFile->nHash; i++){
    for(pRec=pFile->aHash[i]; pRec; pRec=pNextRec){
      pNextRec = pRec->pHash;
      h = sqliteBinaryHash(pRec->ksize, pRec->kptr) % nHash;
      pRec->pHash = aHash[h];
      aHash[h] = pRec;
    }
  }
  sqliteFree(pFile->aHash);
  pFile->aHash = aHash;
  pFile->nHash = nHash;
}

/*
** Locate a datum in a file.  Create it if it isn't already there and
** the createFlag is set.
*/
static datum **sqliteDbbeLookup(
  BeFile *pFile,      /* Where to look */
  int nKey,           /* The size of the key */
  char *pKey,         /* The key */
  int *pH,            /* Write the hash line here */
  int createFlag      /* Create a new entry if this is true */
){
  int h;
  datum **ppRec = 0;
  datum *pNew;
  if( pFile->nHash>0 ){
    h = sqliteBinaryHash(nKey, pKey) % pFile->nHash;
    ppRec = &pFile->aHash[h];
    while( *ppRec ){
      if( (**ppRec).ksize==nKey && memcpy((**ppRec).kptr, pKey, nKey)==0 ){
        if( *pH ) *pH = h;
        return ppRec;
      }
    }
  }
  if( createFlag==0 ) return 0;
  if( (pFile->nRec + 1) > pFile->nHash*2 ){
    int nHash = (pFile->nRec + 1)*4;
    if( nHash<51 ) nHash = 51;
    sqliteDbbeRehash(pFile, nHash);
    if( pFile->nHash==0 ) return 0;
  }
  h = sqliteBinaryHash(nKey, pKey) % pFile->nHash;
  pNew = sqliteMalloc( sizeof(*pNew) + nKey );
  if( pNew==0 ) return 0;
  pNew->kptr = (void*)&pNew[1];
  pNew->ksize = nKey;
  memcpy(pNew->kptr, pkey, nKey);
  pNew->pHash = pFile->aHash[h];
  pFile->aHash[h] = pNew;
  pNew->dsize = 0;
  pNew->dptr = 0;
  pFile->nRec++;
  if( pH ) *pH = h;
  return &pFile->aHash[h];
}

/*
** Fetch a single record from an open cursor.  Return 1 on success
** and 0 on failure.
*/
int sqliteDbbeFetch(DbbeCursor *pCursr, int nKey, char *pKey){
  datum **ppRec;
  ppRec = sqliteDbbeLookup(pCursr->pFile, nKey, pKey, &pCursr->h, 0);
  if( ppRec ){
    pCursr->pRec = *ppRec;
  }
  return pCursr->pRec!=0;
}

/*
** Return 1 if the given key is already in the table.  Return 0
** if it is not.
*/
int sqliteDbbeTest(DbbeCursor *pCursr, int nKey, char *pKey){
  return sqliteDbbeFetch(pCursr, nKey, pKey);
}

/*
** Copy bytes from the current key or data into a buffer supplied by
** the calling function.  Return the number of bytes copied.
*/

int sqliteDbbeCopyKey(DbbeCursor *pCursr, int offset, int size, char *zBuf){
  int n;
  datum *pRec;
  if( (pRec = pCursor->pRec)==0 || offset>=pRec->ksize ) return 0;
  if( offset+size>pRec->ksize ){
    n = pRec->ksize - offset;
  }else{
    n = size;
  }
  memcpy(zBuf, pRec->kptr[offset], n);
  return n;
}

int sqliteDbbeCopyData(DbbeCursor *pCursr, int offset, int size, char *zBuf){
  int n;
  datum *pRec;
  if( (pRec = pCursr->pRec)==0 || offset>=pRec->dsize ) return 0;
  if( offset+size>pRec->dsize ){
    n = pRec->dsize - offset;
  }else{
    n = size;
  }
  memcpy(zBuf, &pRec->dptr[offset], n);
  return n;
}

/*
** Return a pointer to bytes from the key or data.  The data returned
** is ephemeral.
*/
char *sqliteDbbeReadKey(DbbeCursor *pCursr, int offset){
  datum *pRec;
  if( (pRec = pCursr->pRec)==0 || offset<0 || offset>=pRec->ksize ) return "";
  return &pRec->kptr[offset];
}


char *sqliteDbbeReadData(DbbeCursor *pCursr, int offset){
  datum *pRec;
  if( (pRec = pCursr->pRec)==0 || offset<0 || offset>=pRec->dsize ) return "";
  return &pRec->dptr[offset];


}

/*
** Return the total number of bytes in either data or key.
*/
int sqliteDbbeKeyLength(DbbeCursor *pCursr){
  return pCursr->pRec ? pCursor->pRec->ksize : 0;
}
int sqliteDbbeDataLength(DbbeCursor *pCursr){
  return pCursr->pRec ? pCursor->pRec->dsize : 0;
}

/*
** Make is so that the next call to sqliteNextKey() finds the first
** key of the table.
*/
int sqliteDbbeRewind(DbbeCursor *pCursr){
  pCursr->needRewind = 1;
  return SQLITE_OK;
}

/*
** Read the next key from the table.  Return 1 on success.  Return
** 0 if there are no more keys.
*/
int sqliteDbbeNextKey(DbbeCursor *pCursr){
  int h;
  BeFile *pFile;
  if( pCursr==0 || (pFile = pCursr->pFile)==0 || pFile->nHash==0 ){
    return 0;
  }
  if( pCursr->needRewind ){
    pCursr->pRec = 0;
    pCursr->h = -1;
  }
  if( pCursr->pRec ){
    pCursr->pRec = pCursr->pRec->pHash;
  }
  if( pCursr->pRec==0 ){
    for(h=pCursr->h; h<pFile->nHash && pFile->aHash[h]==0; h++){}
    if( h>=pFile->nHash ){
      pCursr->h = -1;
      return 0;
    }else{
      pCursr->h = h;
      pCursr->pRec = pFile->aHash[h];
      return 1;
    }
  }

}

/*
** Get a new integer key.
*/
int sqliteDbbeNew(DbbeCursor *pCursr){
  int iKey;

  int go = 1;
  int i;
  struct rc4 *pRc4;

  if( pCursr->pFile==0 || pCursr->pFile->dbf==0 ) return 1;
  pRc4 = &pCursr->pBe->rc4;
  while( go ){
    iKey = 0;
    for(i=0; i<4; i++){
      iKey = (iKey<<8) + rc4byte(pRc4);
    }
    if( iKey==0 ) continue;


    go = sqliteDbbeLookup(pCursr->pFile, sizeof(iKey), &iKey, 0, 0)!=0;
  }
  return iKey;
}   

/*
** Write an entry into the table.  Overwrite any prior entry with the
** same key.
*/
int sqliteDbbePut(
  DbbeCursor *pCursr,   /* Write to this cursor */
  int nKey,             /* Size of the key */
  char *pKey,           /* The key */
  int nData,            /* Size of the data */

  char *pData           /* The data */
){
  int rc;
  datum **ppRec, *pRec;

  if( pCursr->pFile==0 ) return SQLITE_ERROR;
  ppRec = sqliteDbbeLookup(pCursr->pFile, nKey, pKey, &pCursr->h, 1);
  if( ppRec==0 ) return SQLITE_NOMEM;
  pRec = *ppRec;
  sqliteFree(pRec->dptr);
  pRec->dptr = sqliteMalloc( nData );
  if( pRec->dptr==0 ) return SQLITE_NOMEM;
  memcpy(pRec->dptr, pData, nData);
  pRec->dsize = nData;

  return SQLITE_OK;
}

/*
** Remove an entry from a table, if the entry exists.
*/
int sqliteDbbeDelete(DbbeCursor *pCursr, int nKey, char *pKey){
  datum **ppRec, *pRec;
  ppRec = sqliteDbbeLookcup(pCursr->pFile, nKey, pKey, 0, 0);
  if( ppRec ){
    pRec = *ppRec;
    *ppRec = pRec->pNext;
    if( pCursr->pRec==pRec ){
      pCursr->pRec = 0;

      pCursr->h = -1;
    }
    sqliteFree(pRec->dptr);

    sqliteFree(pRec);
    pCursr->pFile->nRec--;
  }
  return SQLITE_OK;
}

/*
** 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(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{
    randomName(&pBe->rc4, zBuf, "/_temp_file_");
    sqliteFree(zFile);
    zFile = 0;
    sqliteSetString(&zFile, pBe->zDir, zBuf, 0);
  }while( access(zFile,0)==0 && limit-- >= 0 );
  *ppFile = pBe->apTemp[i] = fopen(zFile, "w+");
  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 sqliteDbbeOpenTempFile()
*/
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);
}

























































|
|




<

>
|



>





>
|
>
|
>

|
|

|
|
|
|


|
|
|
|
>
>
|
<
>
>

<
<
<
<
|
<
<
<
<
<
<
<
|

<
|
<
<
<
<
<
<

|
<


|
<

<
<
<
<
|
<




<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
|


|
>
>
|
>
>

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

>





|
>



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




|
|
|
|
|
<
|






|
|






>
|

|
|
|
|



|


>
|

|
|
|
|



|







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





|
|

|
|






|








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





|

>




<







>
>
|








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






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











|
|




>










|



|

|
|
|
|
|
|


|


|





|

|

>











>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
527
528
529
530
531
532
533
534
535
536
537
538
539

540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573

574
575
576




577







578
579

580






581
582

583
584
585

586




587

588
589
590
591



















592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642



















































































































643
644
645
646
647
648
649
650
651

652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
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
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
751
752
753

754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773

774
775
776
777

778

779
780

781

782




783
784
785
786
787
788
789
790
791


792
793

794
795
796


797
798

799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
**                       (This can happen if a SELECT callback tries to
**                       do an UPDATE or DELETE.)
**
** If zTable is 0 or "", then a temporary database file is created and
** a cursor to that temporary file is opened.  The temporary file
** will be deleted from the disk when it is closed.
*/
static int sqliteMemOpenCursor(
  Dbbe *pDbbe,            /* The database the table belongs to */
  const char *zTable,     /* The SQL name of the file to be opened */
  int writeable,          /* True to open for writing */
  DbbeCursor **ppCursr    /* Write the resulting table pointer here */
){

  DbbeCursor *pCursr;     /* The new table cursor */
  char *zName;            /* Canonical table name */
  MTable *pTble;          /* The underlying data file for this table */
  int rc = SQLITE_OK;     /* Return value */
  int rw_mask;            /* Permissions mask for opening a table */
  int mode;               /* Mode for opening a table */
  Dbbex *pBe = (Dbbex*)pDbbe;

  *ppCursr = 0;
  pCursr = sqliteMalloc( sizeof(*pCursr) );
  if( pCursr==0 ) return SQLITE_NOMEM;
  if( zTable ){
    Datum key;
    zName = sqliteNameOfTable(zTable);
    key.p = zName;
    key.n = strlen(zName);
    pTble = ArrayFind(&pBe->tables, key).p;
  }else{
    zName = 0;
    pTble = 0;
  }
  if( pTble==0 ){
    pTble = sqliteMalloc( sizeof(*pTble) );
    if( pTble==0 ){
      sqliteFree(zName);
      return SQLITE_NOMEM;
    }
    if( zName ){
      Datum ins_key, ins_data;
      pTble->zName = zName;
      pTble->delOnClose = 0;
      ins_data.p = pTble;
      ins_data.n = sizeof( *pTble );
      ins_key.p = zName;

      ins_key.n = strlen(zName);
      ArrayInsert(&pBe->tables, ins_key, ins_data);
    }else{




      pTble->zName = 0;







      pTble->delOnClose = 1;
    }

    ArrayInit(&pTble->data);






  }else{
    sqliteFree(zName);

  }
  pCursr->pBe = pBe;
  pCursr->pTble = pTble;

  pCursr->needRewind = 1;




  *ppCursr = pCursr;

  return rc;
}

/*



















** Drop a table from the database.  The file on the disk that corresponds
** to this table is deleted.
*/
static void sqliteMemDropTable(Dbbe *pDbbe, const char *zTable){
  char *zName;            /* Name of the table file */
  Datum key, data;
  MTable *pTble;
  ArrayElem *i;
  Dbbex *pBe = (Dbbex*)pDbbe;

  zName = sqliteNameOfTable(zTable);
  key.p = zName;
  key.n = strlen(zName);
  pTble = ArrayFind(&pBe->tables, key).p;
  if( pTble ){
    data.p = 0;
    data.n = 0;
    ArrayInsert(&pBe->tables, key, data);
    deleteMTable(pTble);
  }
  sqliteFree(zName);
}

/*
** Close a cursor previously opened by sqliteMemOpenCursor().
**
** There can be multiple cursors pointing to the same open file.
** The underlying file is not closed until all cursors have been
** closed.  This routine decrements the MTable.nref field of the
** underlying file and closes the file when nref reaches 0.
*/
static void sqliteMemCloseCursor(DbbeCursor *pCursr){
  MTable *pTble;
  Dbbex *pBe;
  if( pCursr==0 ) return;
  pTble = pCursr->pTble;
  pBe = pCursr->pBe;
  if( pTble->delOnClose ){
    deleteMTable(pTble);
  }
  sqliteFree(pCursr);
}

/*
** Reorganize a table to reduce search times and disk usage.
*/
static int sqliteMemReorganizeTable(Dbbe *pBe, const char *zTable){
  /* Do nothing */
  return SQLITE_OK;
}




















































































































/*
** Fetch a single record from an open cursor.  Return 1 on success
** and 0 on failure.
*/
static int sqliteMemFetch(DbbeCursor *pCursr, int nKey, char *pKey){
  Datum key;
  key.n = nKey;
  key.p = pKey;
  pCursr->elem = ArrayFindElement(&pCursr->pTble->data, key);

  return pCursr->elem!=0;
}

/*
** Return 1 if the given key is already in the table.  Return 0
** if it is not.
*/
static int sqliteMemTest(DbbeCursor *pCursr, int nKey, char *pKey){
  return sqliteMemFetch(pCursr, nKey, pKey);
}

/*
** Copy bytes from the current key or data into a buffer supplied by
** the calling function.  Return the number of bytes copied.
*/
static
int sqliteMemCopyKey(DbbeCursor *pCursr, int offset, int size, char *zBuf){
  int n;
  if( pCursr->elem==0 ) return 0;
  if( offset>=ArrayKeySize(pCursr->elem) ) return 0;
  if( offset+size>ArrayKeySize(pCursr->elem) ){
    n = ArrayKeySize(pCursr->elem) - offset;
  }else{
    n = size;
  }
  memcpy(zBuf, &((char*)ArrayKey(pCursr->elem))[offset], n);
  return n;
}
static
int sqliteMemCopyData(DbbeCursor *pCursr, int offset, int size, char *zBuf){
  int n;
  if( pCursr->elem==0 ) return 0;
  if( offset>=ArrayDataSize(pCursr->elem) ) return 0;
  if( offset+size>ArrayDataSize(pCursr->elem) ){
    n = ArrayDataSize(pCursr->elem) - offset;
  }else{
    n = size;
  }
  memcpy(zBuf, &((char*)ArrayData(pCursr->elem))[offset], n);
  return n;
}

/*
** Return a pointer to bytes from the key or data.  The data returned
** is ephemeral.
*/
static char *sqliteMemReadKey(DbbeCursor *pCursr, int offset){

  if( pCursr->elem==0 || offset<0 || offset>=ArrayKeySize(pCursr->elem) ){
    return "";
  }
  return &((char*)ArrayKey(pCursr->elem))[offset];
}
static char *sqliteMemReadData(DbbeCursor *pCursr, int offset){

  if( pCursr->elem==0 || offset<0 || offset>=ArrayDataSize(pCursr->elem) ){
    return "";
  }
  return &((char*)ArrayData(pCursr->elem))[offset];
}

/*
** Return the total number of bytes in either data or key.
*/
static int sqliteMemKeyLength(DbbeCursor *pCursr){
  return pCursr->elem ? ArrayKeySize(pCursr->elem) : 0;
}
static int sqliteMemDataLength(DbbeCursor *pCursr){
  return pCursr->elem ? ArrayDataSize(pCursr->elem) : 0;
}

/*
** Make is so that the next call to sqliteNextKey() finds the first
** key of the table.
*/
static int sqliteMemRewind(DbbeCursor *pCursr){
  pCursr->needRewind = 1;
  return SQLITE_OK;
}

/*
** Read the next key from the table.  Return 1 on success.  Return
** 0 if there are no more keys.
*/
static int sqliteMemNextKey(DbbeCursor *pCursr){





  if( pCursr->needRewind || pCursr->elem==0 ){

    pCursr->elem = ArrayFirst(&pCursr->pTble->data);







    pCursr->needRewind = 0;

  }else{
    pCursr->elem = ArrayNext(pCursr->elem);


  }

  return pCursr->elem!=0;
}

/*
** Get a new integer key.
*/
static int sqliteMemNew(DbbeCursor *pCursr){
  int iKey;
  Datum key;
  int go = 1;
  int i;
  struct rc4 *pRc4;


  pRc4 = &pCursr->pBe->rc4;
  while( go ){
    iKey = 0;
    for(i=0; i<4; i++){
      iKey = (iKey<<8) + rc4byte(pRc4);
    }
    if( iKey==0 ) continue;
    key.p = (char*)&iKey;
    key.n = 4;
    go = ArrayFindElement(&pCursr->pTble->data, key)!=0;
  }
  return iKey;
}   

/*
** Write an entry into the table.  Overwrite any prior entry with the
** same key.
*/
static int
sqliteMemPut(DbbeCursor *pCursr, int nKey,char *pKey, int nData, char *pData){

  Datum data, key;
  data.n = nData;
  data.p = sqliteMalloc( data.n );
  memcpy(data.p, pData, data.n);

  key.n = nKey;

  key.p = pKey;
  data = ArrayInsert(&pCursr->pTble->data, key, data);

  if( data.p ){

    sqliteFree(data.p);




  }
  return SQLITE_OK;
}

/*
** Remove an entry from a table, if the entry exists.
*/
static int sqliteMemDelete(DbbeCursor *pCursr, int nKey, char *pKey){
  Datum key, data;


  key.n = nKey;
  key.p = pKey;

  data.p = 0;
  data.n = 0;
  data = ArrayInsert(&pCursr->pTble->data, key, data);


  if( data.p ){
    sqliteFree(data.p);

  }
  return SQLITE_OK;
}

/*
** 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.
*/
static int sqliteMemOpenTempFile(Dbbe *pDbbe, FILE **ppTble){
  char *zName;         /* 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 */
  Dbbex *pBe = (Dbbex*)pDbbe;

  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 ){
    *ppTble = 0;
    return SQLITE_NOMEM;
  }
  limit = 4;
  zName = 0;
  do{
    randomName(&pBe->rc4, zBuf, "/tmp/_temp_file_");
    sqliteFree(zName);
    zName = 0;
    sqliteSetString(&zName, zBuf, 0);
  }while( access(zName,0)==0 && limit-- >= 0 );
  *ppTble = pBe->apTemp[i] = fopen(zName, "w+");
  if( pBe->apTemp[i]==0 ){
    rc = SQLITE_ERROR;
    sqliteFree(zName);
    pBe->azTemp[i] = 0;
  }else{
    pBe->azTemp[i] = zName;
  }
  return rc;
}

/*
** Close a temporary file opened using sqliteMemOpenTempFile()
*/
static void sqliteMemCloseTempFile(Dbbe *pDbbe, FILE *f){
  int i;
  Dbbex *pBe = (Dbbex*)pDbbe;
  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);
}


/*
** 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.
**
** If successful, a pointer to the Dbbe structure is returned.
** If there are errors, an appropriate error message is left
** in *pzErrMsg and NULL is returned.
*/
Dbbe *sqliteMemOpen(
  const char *zName,     /* The name of the database */
  int writeFlag,         /* True if we will be writing to the database */
  int createFlag,        /* True to create database if it doesn't exist */
  char **pzErrMsg        /* Write error messages (if any) here */
){
  Dbbex *pNew;
  long now;

  pNew = sqliteMalloc( sizeof(*pNew) );
  if( pNew==0 ){
    sqliteSetString(pzErrMsg, "out of memory", 0);
    return 0;
  }
  ArrayInit(&pNew->tables);
  pNew->dbbe.Close = sqliteMemClose;
  pNew->dbbe.OpenCursor = sqliteMemOpenCursor;
  pNew->dbbe.DropTable = sqliteMemDropTable;
  pNew->dbbe.ReorganizeTable = sqliteMemReorganizeTable;
  pNew->dbbe.CloseCursor = sqliteMemCloseCursor;
  pNew->dbbe.Fetch = sqliteMemFetch;
  pNew->dbbe.Test = sqliteMemTest;
  pNew->dbbe.CopyKey = sqliteMemCopyKey;
  pNew->dbbe.CopyData = sqliteMemCopyData;
  pNew->dbbe.ReadKey = sqliteMemReadKey;
  pNew->dbbe.ReadData = sqliteMemReadData;
  pNew->dbbe.KeyLength = sqliteMemKeyLength;
  pNew->dbbe.DataLength = sqliteMemDataLength;
  pNew->dbbe.NextKey = sqliteMemNextKey;
  pNew->dbbe.Rewind = sqliteMemRewind;
  pNew->dbbe.New = sqliteMemNew;
  pNew->dbbe.Put = sqliteMemPut;
  pNew->dbbe.Delete = sqliteMemDelete;
  pNew->dbbe.OpenTempFile = sqliteMemOpenTempFile;
  pNew->dbbe.CloseTempFile = sqliteMemCloseTempFile;
  time(&now);
  rc4init(&pNew->rc4, (char*)&now, sizeof(now));
  return &pNew->dbbe;
}
Changes to test/all.test.
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38






39
40
41
42
43
44
45
# Author contact information:
#   drh@hwaci.com
#   http://www.hwaci.com/drh/
#
#***********************************************************************
# This file runs all tests.
#
# $Id: all.test,v 1.2 2000/06/02 14:27:23 drh Exp $

set testdir [file dirname $argv0]
source $testdir/tester.tcl
rename finish_test really_finish_test
proc finish_test {} {}

if {[file exists ./sqlite_test_count]} {
  set COUNT [exec cat ./sqlite_test_count]
} else {
  set COUNT 1
}

for {set Counter 0} {$Counter<$COUNT} {incr Counter} {






  foreach testfile [lsort -dictionary [glob $testdir/*.test]] {
    if {[file tail $testfile]=="all.test"} continue
    source $testfile
  }
}

really_finish_test







|













>
>
>
>
>
>







18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
# Author contact information:
#   drh@hwaci.com
#   http://www.hwaci.com/drh/
#
#***********************************************************************
# This file runs all tests.
#
# $Id: all.test,v 1.3 2000/10/19 14:10:09 drh Exp $

set testdir [file dirname $argv0]
source $testdir/tester.tcl
rename finish_test really_finish_test
proc finish_test {} {}

if {[file exists ./sqlite_test_count]} {
  set COUNT [exec cat ./sqlite_test_count]
} else {
  set COUNT 1
}

for {set Counter 0} {$Counter<$COUNT} {incr Counter} {
  set dbprefix memory:
  foreach testfile [lsort -dictionary [glob $testdir/*.test]] {
    if {[file tail $testfile]=="all.test"} continue
    source $testfile
  }
  set dbprefix gdbm:
  foreach testfile [lsort -dictionary [glob $testdir/*.test]] {
    if {[file tail $testfile]=="all.test"} continue
    source $testfile
  }
}

really_finish_test
Changes to test/index.test.
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41

42
43
44
45
46
47

48
49
50
51
52
53
54
#   drh@hwaci.com
#   http://www.hwaci.com/drh/
#
#***********************************************************************
# This file implements regression tests for SQLite library.  The
# focus of this file is testing the CREATE INDEX statement.
#
# $Id: index.test,v 1.7 2000/08/02 13:47:42 drh Exp $

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

# Create a basic index and verify it is added to sqlite_master
#
do_test index-1.1 {
  execsql {CREATE TABLE test1(f1 int, f2 int, f3 int)}
  execsql {CREATE INDEX index1 ON test1(f1)}
  execsql {SELECT name FROM sqlite_master WHERE type!='meta' ORDER BY name}
} {index1 test1}
do_test index-1.1b {
  execsql {SELECT name, sql, tbl_name, type FROM sqlite_master 
           WHERE name='index1'}
} {index1 {CREATE INDEX index1 ON test1(f1)} test1 index}

do_test index-1.1c {
  db close
  sqlite db testdb
  execsql {SELECT name, sql, tbl_name, type FROM sqlite_master 
           WHERE name='index1'}
} {index1 {CREATE INDEX index1 ON test1(f1)} test1 index}

do_test index-1.1d {
  db close
  sqlite db testdb
  execsql {SELECT name FROM sqlite_master WHERE type!='meta' ORDER BY name}
} {index1 test1}

# Verify that the index dies with the table







|















>






>







19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
#   drh@hwaci.com
#   http://www.hwaci.com/drh/
#
#***********************************************************************
# This file implements regression tests for SQLite library.  The
# focus of this file is testing the CREATE INDEX statement.
#
# $Id: index.test,v 1.8 2000/10/19 14:10:09 drh Exp $

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

# Create a basic index and verify it is added to sqlite_master
#
do_test index-1.1 {
  execsql {CREATE TABLE test1(f1 int, f2 int, f3 int)}
  execsql {CREATE INDEX index1 ON test1(f1)}
  execsql {SELECT name FROM sqlite_master WHERE type!='meta' ORDER BY name}
} {index1 test1}
do_test index-1.1b {
  execsql {SELECT name, sql, tbl_name, type FROM sqlite_master 
           WHERE name='index1'}
} {index1 {CREATE INDEX index1 ON test1(f1)} test1 index}
skipif memory:
do_test index-1.1c {
  db close
  sqlite db testdb
  execsql {SELECT name, sql, tbl_name, type FROM sqlite_master 
           WHERE name='index1'}
} {index1 {CREATE INDEX index1 ON test1(f1)} test1 index}
skipif memory:
do_test index-1.1d {
  db close
  sqlite db testdb
  execsql {SELECT name FROM sqlite_master WHERE type!='meta' ORDER BY name}
} {index1 test1}

# Verify that the index dies with the table
102
103
104
105
106
107
108

109
110
111
112
113
114
115
# Add a single entry to the table.  Verify that files are created
# for every index.
#
set r {}
for {set i 1} {$i<100} {incr i} {
  lappend r testdb/index$i.tbl
}

do_test index-3.2 {
  execsql {INSERT INTO test1 VALUES(1,2,3,4,5)}
  lsort -dictionary [glob testdb/index*.tbl]
} $r

# Verify that all the indices go away when we drop the table.
#







>







104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
# Add a single entry to the table.  Verify that files are created
# for every index.
#
set r {}
for {set i 1} {$i<100} {incr i} {
  lappend r testdb/index$i.tbl
}
skipif memory:
do_test index-3.2 {
  execsql {INSERT INTO test1 VALUES(1,2,3,4,5)}
  lsort -dictionary [glob testdb/index*.tbl]
} $r

# Verify that all the indices go away when we drop the table.
#
219
220
221
222
223
224
225




226
227
228
229
230
231
232
# Create a primary key
#
do_test index-7.1 {
  execsql {CREATE TABLE test1(f1 int, f2 int primary key)}
  for {set i 1} {$i<20} {incr i} {
    execsql "INSERT INTO test1 VALUES($i,[expr {int(pow(2,$i))}])"
  }




  lsort -dictionary [glob testdb/test1*.tbl]
} {testdb/test1.tbl testdb/test1__primary_key.tbl}
do_test index-7.2 {
  execsql {SELECT f1 FROM test1 WHERE f2=65536}
} {16}
do_test index-7.3 {
  set code [execsql {EXPLAIN SELECT f1 FROM test1 WHERE f2=65536}]







>
>
>
>







222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
# Create a primary key
#
do_test index-7.1 {
  execsql {CREATE TABLE test1(f1 int, f2 int primary key)}
  for {set i 1} {$i<20} {incr i} {
    execsql "INSERT INTO test1 VALUES($i,[expr {int(pow(2,$i))}])"
  }
  execsql {SELECT count(*) FROM test1}
} {19}
skipif memory:
do_test index-7.1b {
  lsort -dictionary [glob testdb/test1*.tbl]
} {testdb/test1.tbl testdb/test1__primary_key.tbl}
do_test index-7.2 {
  execsql {SELECT f1 FROM test1 WHERE f2=65536}
} {16}
do_test index-7.3 {
  set code [execsql {EXPLAIN SELECT f1 FROM test1 WHERE f2=65536}]
Changes to test/lock.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 script is database locks.
#
# $Id: lock.test,v 1.3 2000/10/16 22:06:43 drh Exp $



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


# Create a largish table
#







|
>
>







19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
#   drh@hwaci.com
#   http://www.hwaci.com/drh/
#
#***********************************************************************
# This file implements regression tests for SQLite library.  The
# focus of this script is database locks.
#
# $Id: lock.test,v 1.4 2000/10/19 14:10:09 drh Exp $

if {$dbprefix=="gdbm:"} {

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


# Create a largish table
#
95
96
97
98
99
100
101


} {xyz}

catch {exec ps -uax | grep $::lock_pid}
catch {exec kill -HUP $::lock_pid}
catch {exec kill -9 $::lock_pid}

finish_test









>
>
97
98
99
100
101
102
103
104
105
} {xyz}

catch {exec ps -uax | grep $::lock_pid}
catch {exec kill -HUP $::lock_pid}
catch {exec kill -9 $::lock_pid}

finish_test

}
Changes to test/select2.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 SELECT statement.
#
# $Id: select2.test,v 1.8 2000/07/29 13:07:00 drh Exp $

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

# Create a table with some data
#
execsql {CREATE TABLE tbl1(f1 int, f2 int)}







|







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 SELECT statement.
#
# $Id: select2.test,v 1.9 2000/10/19 14:10:09 drh Exp $

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

# Create a table with some data
#
execsql {CREATE TABLE tbl1(f1 int, f2 int)}
100
101
102
103
104
105
106







107
108
109
110
111
112
113
114
115
116
117
118
119



120
121
do_test select2-3.2b {
  execsql {SELECT f1 FROM tbl2 WHERE 1000=f2}
} {500}
do_test select2-3.2c {
  execsql {SELECT f1 FROM tbl2 WHERE f2=1000}
} {500}
do_test select2-3.2d {







  set t1 [lindex [time {execsql {SELECT f1 FROM tbl2 WHERE 1000=f2}} 1] 0]
  set t2 [lindex [time {execsql {SELECT f1 FROM tbl2 WHERE f2=1000}} 1] 0]
  expr {$t1*0.8<$t2 && $t2*0.8<$t1}
} {1}

# Make sure queries run faster with an index than without
#
do_test select2-3.3 {
  set t1 [lindex [time {execsql {SELECT f1 from tbl2 WHERE f2==2000}} 1] 0]
  execsql {DROP INDEX idx1}
  set t2 [lindex [time {execsql {SELECT f1 FROM tbl2 WHERE f2==2000}} 1] 0]
  expr {$t1*10 < $t2}
} {1}




finish_test







>
>
>
>
>
>
>


|










>
>
>


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
do_test select2-3.2b {
  execsql {SELECT f1 FROM tbl2 WHERE 1000=f2}
} {500}
do_test select2-3.2c {
  execsql {SELECT f1 FROM tbl2 WHERE f2=1000}
} {500}
do_test select2-3.2d {
  execsql {SELECT fcnt() FROM tbl2 WHERE 1000=f2}
} {2}
do_test select2-3.2e {
  execsql {SELECT fcnt() FROM tbl2 WHERE f2=1000}
} {2}
testif gdbm:
do_test select2-3.2f {
  set t1 [lindex [time {execsql {SELECT f1 FROM tbl2 WHERE 1000=f2}} 1] 0]
  set t2 [lindex [time {execsql {SELECT f1 FROM tbl2 WHERE f2=1000}} 1] 0]
  expr {$t1*0.7<$t2 && $t2*0.7<$t1}
} {1}

# Make sure queries run faster with an index than without
#
do_test select2-3.3 {
  set t1 [lindex [time {execsql {SELECT f1 from tbl2 WHERE f2==2000}} 1] 0]
  execsql {DROP INDEX idx1}
  set t2 [lindex [time {execsql {SELECT f1 FROM tbl2 WHERE f2==2000}} 1] 0]
  expr {$t1*10 < $t2}
} {1}
do_test select2-3.4 {
  expr {[execsql {SELECT fcnt() FROM tbl2 WHERE f2==2000}]>10}
} {1}

finish_test
Changes to test/table.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 CREATE TABLE statement.
#
# $Id: table.test,v 1.6 2000/08/02 13:47:43 drh Exp $

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

# Create a basic table and verify it is added to sqlite_master
#
do_test table-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 CREATE TABLE statement.
#
# $Id: table.test,v 1.7 2000/10/19 14:10:09 drh Exp $

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

# Create a basic table and verify it is added to sqlite_master
#
do_test table-1.1 {
45
46
47
48
49
50
51



52
53
54
55
56
57
58
59
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
      two text
    )}}

# Verify that both table files exists in the database directory
#
do_test table-1.2 {
  execsql {INSERT INTO test1 VALUES('hi', 'y''all')}



  lsort [glob -nocomplain testdb/*.tbl]
} {testdb/sqlite_master.tbl testdb/test1.tbl}

# Verify the other fields of the sqlite_master file.
#
do_test table-1.3 {
  execsql {SELECT name, tbl_name, type FROM sqlite_master WHERE type!='meta'}
} {test1 test1 table}

# Close and reopen the database.  Verify that everything is
# still the same.
#

do_test table-1.4 {
  db close
  sqlite db testdb
  execsql {SELECT name, tbl_name, type from sqlite_master WHERE type!='meta'}
} {test1 test1 table}

# Drop the database and make sure it disappears.
#
do_test table-1.5 {
  execsql {DROP TABLE test1}
  execsql {SELECT * FROM sqlite_master WHERE type!='meta'}
} {}

# Verify that the file associated with the database is gone.
#

do_test table-1.5 {
  lsort [glob -nocomplain testdb/*.tbl]
} {testdb/sqlite_master.tbl}

# Close and reopen the database.  Verify that the table is
# still gone.
#

do_test table-1.6 {
  db close
  sqlite db testdb
  execsql {SELECT name FROM sqlite_master WHERE type!='meta'}
} {}

# Repeat the above steps, but this time quote the table name.







>
>
>












>















>







>







45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
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
      two text
    )}}

# Verify that both table files exists in the database directory
#
do_test table-1.2 {
  execsql {INSERT INTO test1 VALUES('hi', 'y''all')}
} {}
testif gdbm:
do_test table-1.2b {
  lsort [glob -nocomplain testdb/*.tbl]
} {testdb/sqlite_master.tbl testdb/test1.tbl}

# Verify the other fields of the sqlite_master file.
#
do_test table-1.3 {
  execsql {SELECT name, tbl_name, type FROM sqlite_master WHERE type!='meta'}
} {test1 test1 table}

# Close and reopen the database.  Verify that everything is
# still the same.
#
skipif memory:
do_test table-1.4 {
  db close
  sqlite db testdb
  execsql {SELECT name, tbl_name, type from sqlite_master WHERE type!='meta'}
} {test1 test1 table}

# Drop the database and make sure it disappears.
#
do_test table-1.5 {
  execsql {DROP TABLE test1}
  execsql {SELECT * FROM sqlite_master WHERE type!='meta'}
} {}

# Verify that the file associated with the database is gone.
#
testif gdbm:
do_test table-1.5 {
  lsort [glob -nocomplain testdb/*.tbl]
} {testdb/sqlite_master.tbl}

# Close and reopen the database.  Verify that the table is
# still gone.
#
skipif memory:
do_test table-1.6 {
  db close
  sqlite db testdb
  execsql {SELECT name FROM sqlite_master WHERE type!='meta'}
} {}

# Repeat the above steps, but this time quote the table name.
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
  set v [catch {execsql {CREATE TABLE test2(two text)}} msg]
  lappend v $msg
} {1 {table test2 already exists}}
do_test table-2.1b {
  set v [catch {execsql {CREATE TABLE sqlite_master(two text)}} msg]
  lappend v $msg
} {1 {table sqlite_master already exists}}

do_test table-2.1c {
  db close
  sqlite db testdb
  set v [catch {execsql {CREATE TABLE sqlite_master(two text)}} msg]
  lappend v $msg
} {1 {table sqlite_master already exists}}
do_test table-2.1d {
  execsql {DROP TABLE test2; SELECT name FROM sqlite_master WHERE type!='meta'}
} {}

# Verify that we cannot make a table with the same name as an index
#
do_test table-2.2a {
  execsql {CREATE TABLE test2(one text); CREATE INDEX test3 ON test2(one)}
  set v [catch {execsql {CREATE TABLE test3(two text)}} msg]
  lappend v $msg
} {1 {there is already an index named test3}}

do_test table-2.2b {
  db close
  sqlite db testdb
  set v [catch {execsql {CREATE TABLE test3(two text)}} msg]
  lappend v $msg
} {1 {there is already an index named test3}}
do_test table-2.2c {







>

















>







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
  set v [catch {execsql {CREATE TABLE test2(two text)}} msg]
  lappend v $msg
} {1 {table test2 already exists}}
do_test table-2.1b {
  set v [catch {execsql {CREATE TABLE sqlite_master(two text)}} msg]
  lappend v $msg
} {1 {table sqlite_master already exists}}
skipif memory:
do_test table-2.1c {
  db close
  sqlite db testdb
  set v [catch {execsql {CREATE TABLE sqlite_master(two text)}} msg]
  lappend v $msg
} {1 {table sqlite_master already exists}}
do_test table-2.1d {
  execsql {DROP TABLE test2; SELECT name FROM sqlite_master WHERE type!='meta'}
} {}

# Verify that we cannot make a table with the same name as an index
#
do_test table-2.2a {
  execsql {CREATE TABLE test2(one text); CREATE INDEX test3 ON test2(one)}
  set v [catch {execsql {CREATE TABLE test3(two text)}} msg]
  lappend v $msg
} {1 {there is already an index named test3}}
skipif memory:
do_test table-2.2b {
  db close
  sqlite db testdb
  set v [catch {execsql {CREATE TABLE test3(two text)}} msg]
  lappend v $msg
} {1 {there is already an index named test3}}
do_test table-2.2c {
194
195
196
197
198
199
200

201
202
203
204
205
206
207
  set v [catch {execsql {CREATE TABLE biG(xyz foo)}} msg]
  lappend v $msg
} {1 {table biG already exists}}
do_test table-3.4 {
  set v [catch {execsql {CREATE TABLE bIg(xyz foo)}} msg]
  lappend v $msg
} {1 {table bIg already exists}}

do_test table-3.5 {
  db close
  sqlite db testdb
  set v [catch {execsql {CREATE TABLE Big(xyz foo)}} msg]
  lappend v $msg
} {1 {table Big already exists}}
do_test table-3.6 {







>







202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
  set v [catch {execsql {CREATE TABLE biG(xyz foo)}} msg]
  lappend v $msg
} {1 {table biG already exists}}
do_test table-3.4 {
  set v [catch {execsql {CREATE TABLE bIg(xyz foo)}} msg]
  lappend v $msg
} {1 {table bIg already exists}}
skipif memory:
do_test table-3.5 {
  db close
  sqlite db testdb
  set v [catch {execsql {CREATE TABLE Big(xyz foo)}} msg]
  lappend v $msg
} {1 {table Big already exists}}
do_test table-3.6 {
222
223
224
225
226
227
228

229
230
231
232
233
234
235
      append sql "field$k text,"
    }
    append sql "last_field text)"
    execsql $sql
  }
  execsql {SELECT name FROM sqlite_master WHERE type!='meta' ORDER BY name}
} $r

do_test table-4.1b {
  db close
  sqlite db testdb
  execsql {SELECT name FROM sqlite_master WHERE type!='meta' ORDER BY name}
} $r

# Drop the even number tables







>







231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
      append sql "field$k text,"
    }
    append sql "last_field text)"
    execsql $sql
  }
  execsql {SELECT name FROM sqlite_master WHERE type!='meta' ORDER BY name}
} $r
skipif memory:
do_test table-4.1b {
  db close
  sqlite db testdb
  execsql {SELECT name FROM sqlite_master WHERE type!='meta' ORDER BY name}
} $r

# Drop the even number tables
283
284
285
286
287
288
289

290
291
292
293
294
295
296
  execsql {CREATE TABLE test1(f1 int)}
  execsql {EXPLAIN DROP TABLE test1}
  execsql {SELECT name FROM sqlite_master WHERE type!='meta'}
} {test1}

# Create a table with a goofy name
#

do_test table-6.1 {
  execsql {CREATE TABLE 'Spaces In This Name!'(x int)}
  execsql {INSERT INTO 'spaces in this name!' VALUES(1)}
  set list [glob -nocomplain testdb/spaces*.tbl]
} {testdb/spaces+in+this+name+.tbl}

finish_test







>







293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
  execsql {CREATE TABLE test1(f1 int)}
  execsql {EXPLAIN DROP TABLE test1}
  execsql {SELECT name FROM sqlite_master WHERE type!='meta'}
} {test1}

# Create a table with a goofy name
#
testif gdbm:
do_test table-6.1 {
  execsql {CREATE TABLE 'Spaces In This Name!'(x int)}
  execsql {INSERT INTO 'spaces in this name!' VALUES(1)}
  set list [glob -nocomplain testdb/spaces*.tbl]
} {testdb/spaces+in+this+name+.tbl}

finish_test
Changes to test/tester.tcl.
19
20
21
22
23
24
25
26
27
28
29









30
31





32
33
34
35
36
37
38
39
40
41

42
43
44
45
46




47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71























72
73
74
75
76
77
78
#   drh@hwaci.com
#   http://www.hwaci.com/drh/
#
#***********************************************************************
# This file implements some common TCL routines used for regression
# testing the SQLite library
#
# $Id: tester.tcl,v 1.6 2000/09/21 13:01:37 drh Exp $

# Create a test database
#









file delete -force testdb
file mkdir testdb





sqlite db testdb

# Abort early if this script has been run before.
#
if {[info exists nTest]} return

# Set the test counters to zero
#
set nErr 0
set nTest 0


# Invoke the do_test procedure to run a single test 
#
proc do_test {name cmd expected} {
  global argv nErr nTest




  if {[llength $argv]==0} {
    set go 1
  } else {
    set go 0
    foreach pattern $argv {
      if {[string match $pattern $name]} {
        set go 1
        break
      }
    }
  }
  if {!$go} return
  incr nTest
  puts -nonewline $name...
  flush stdout
  if {[catch {uplevel #0 "$cmd;\n"} result]} {
    puts "\nError: $result"
    incr nErr
  } elseif {[string compare $result $expected]} {
    puts "\nExpected: \[$expected\]\n     Got: \[$result\]"
    incr nErr
  } else {
    puts " Ok"
  }
}
























# Run this routine last
#
proc finish_test {} {
  global nTest nErr
  catch {db close}
  puts "$nErr errors out of $nTest tests"







|



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









>




|
>
>
>
>
|












|











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







19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
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
#   drh@hwaci.com
#   http://www.hwaci.com/drh/
#
#***********************************************************************
# This file implements some common TCL routines used for regression
# testing the SQLite library
#
# $Id: tester.tcl,v 1.7 2000/10/19 14:10:09 drh Exp $

# Create a test database
#
if {![info exists dbprefix]} {
  if {[info exists env(SQLITE_PREFIX)]} {
    set dbprefix $env(SQLITE_PREFIX):
  } else {
    set dbprefix "gdbm:"
  }
}
switch $dbprefix {
  gdbm: {
   file delete -force testdb
   file mkdir testdb
  }
  memory: {
   # do nothing
  }
}
sqlite db ${dbprefix}testdb

# Abort early if this script has been run before.
#
if {[info exists nTest]} return

# Set the test counters to zero
#
set nErr 0
set nTest 0
set skip_test 0

# Invoke the do_test procedure to run a single test 
#
proc do_test {name cmd expected} {
  global argv nErr nTest skip_test
  if {$skip_test} {
    set skip_test 0
    return
  }
  if {[llength $argv]==0} { 
    set go 1
  } else {
    set go 0
    foreach pattern $argv {
      if {[string match $pattern $name]} {
        set go 1
        break
      }
    }
  }
  if {!$go} return
  incr nTest
  puts -nonewline $::dbprefix$name...
  flush stdout
  if {[catch {uplevel #0 "$cmd;\n"} result]} {
    puts "\nError: $result"
    incr nErr
  } elseif {[string compare $result $expected]} {
    puts "\nExpected: \[$expected\]\n     Got: \[$result\]"
    incr nErr
  } else {
    puts " Ok"
  }
}

# Skip a test based on the dbprefix
#
proc skipif {args} {
  foreach a $args {
    if {$::dbprefix==$a} {
      set ::skip_test 1
      return
    }
  }
}

# Run the next test only if the dbprefix is among the listed arguments
#
proc testif {args} {
  foreach a $args {
    if {$::dbprefix==$a} {
      set ::skip_test 0
      return
    }
  }
  set ::skip_test 1
}

# Run this routine last
#
proc finish_test {} {
  global nTest nErr
  catch {db close}
  puts "$nErr errors out of $nTest tests"
Changes to test/vacuum.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 VACUUM statement.
#
# $Id: vacuum.test,v 1.1 2000/06/03 18:06:54 drh Exp $

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

# Try to vacuum a non-existant table.
#
do_test vacuum-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 VACUUM statement.
#
# $Id: vacuum.test,v 1.2 2000/10/19 14:10:10 drh Exp $

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

# Try to vacuum a non-existant table.
#
do_test vacuum-1.1 {
49
50
51
52
53
54
55

56
57
58
59
60
61
62
63
64
65
66

67
68
69
70
71
72
73
execsql {CREATE INDEX index1 ON test1(a)}
execsql {INSERT INTO test1 VALUES(1)}
execsql {INSERT INTO test1 VALUES(1)}
execsql {INSERT INTO test1 VALUES(2)}
execsql {INSERT INTO test1 VALUES(3)}
execsql {INSERT INTO test2 VALUES(4)}


do_test vacuum-1.3 {
  set b1 [file mtime testdb/test1.tbl]
  set b2 [file mtime testdb/test2.tbl]
  set b3 [file mtime testdb/index1.tbl]
  after 1000
  execsql {VACUUM test1}
  set a1 [file mtime testdb/test1.tbl]
  set a2 [file mtime testdb/test2.tbl]
  set a3 [file mtime testdb/index1.tbl]
  expr {$a1>$b1 && $a2==$b2 && $a3==$b3}
} {1}

do_test vacuum-1.4 {
  set b1 [file mtime testdb/test1.tbl]
  set b2 [file mtime testdb/test2.tbl]
  set b3 [file mtime testdb/index1.tbl]
  after 1000
  execsql {VACUUM}
  set a1 [file mtime testdb/test1.tbl]







>











>







49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
execsql {CREATE INDEX index1 ON test1(a)}
execsql {INSERT INTO test1 VALUES(1)}
execsql {INSERT INTO test1 VALUES(1)}
execsql {INSERT INTO test1 VALUES(2)}
execsql {INSERT INTO test1 VALUES(3)}
execsql {INSERT INTO test2 VALUES(4)}

testif gdbm:
do_test vacuum-1.3 {
  set b1 [file mtime testdb/test1.tbl]
  set b2 [file mtime testdb/test2.tbl]
  set b3 [file mtime testdb/index1.tbl]
  after 1000
  execsql {VACUUM test1}
  set a1 [file mtime testdb/test1.tbl]
  set a2 [file mtime testdb/test2.tbl]
  set a3 [file mtime testdb/index1.tbl]
  expr {$a1>$b1 && $a2==$b2 && $a3==$b3}
} {1}
testif gdbm:
do_test vacuum-1.4 {
  set b1 [file mtime testdb/test1.tbl]
  set b2 [file mtime testdb/test2.tbl]
  set b3 [file mtime testdb/index1.tbl]
  after 1000
  execsql {VACUUM}
  set a1 [file mtime testdb/test1.tbl]
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 {2000 Oct 18 (1.0.13)} {
<li>Break out the GDBM driver into a separate file in anticipation
    to added new drivers.</li>
<li>Allow the name of a database to be prefixed by the driver type.
    For now, the only driver type is "gdbm:".<li>
}







>
>
>
>
>







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


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

chng {2000 Oct 19 (1.0.14)} {
<li>Added a "memory:" backend driver that stores its database in an
    in-memory hash table.</li>
}

chng {2000 Oct 18 (1.0.13)} {
<li>Break out the GDBM driver into a separate file in anticipation
    to added new drivers.</li>
<li>Allow the name of a database to be prefixed by the driver type.
    For now, the only driver type is "gdbm:".<li>
}