diff --git a/manifest b/manifest
index 152225a490..79a3d1eb2d 100644
--- a/manifest
+++ b/manifest
@@ -1,5 +1,5 @@
-C :-)\s(CVS\s26)
-D 2000-05-31T18:33:10
+C added\sDISTINCT\son\sselect\s(CVS\s27)
+D 2000-05-31T20:00:52
F COPYRIGHT 74a8a6531a42e124df07ab5599aad63870fa0bd4
F Makefile.in 7ac2fef265940d93a544cb454efa836451559a71
F README 6b5960603c7f8bf42fc022b4b6436f242f238dbb
@@ -7,23 +7,23 @@ F configure 00a5b5c82147a576fa6e82d7c1b0d55c321d6d2c x
F configure.in 6ccfd5fc80517f7cfe605a7fc7e0f62d962a233c
F doc/lemon.html e233a3e97a779c7a87e1bc4528c664a58e49dd47
F src/build.c 03f83e95d46e328a2ac08aace102b142ea38e6d7
-F src/dbbe.c dc9439f839d13e633158808e352056b531f17e1b
-F src/dbbe.h b678e31c32fa252e6fba830ad16ed8978d1521a9
+F src/dbbe.c 80080f5ef2297e54797ee24f5951dab1c39af5d5
+F src/dbbe.h 0147c9f8539d421d6c5558d3e854b78387372fae
F src/delete.c 16ef3418b19be9ab39db836c693970ca7bbff605
F src/expr.c 91970700e3e39b2b725b028c166f588a5bb0c038
F src/insert.c bd34716d0bba5561f6b55101adbf16fa75f872e8
F src/main.c 25cce7bce0eb3ba10bada7c05f4b38dc6dbbc86f
-F src/parse.y 038e0f0fd243b89344c974c5d0552e85c4d27916
-F src/select.c 719ca9605a351b2a3521a692ae8d2936d4832609
+F src/parse.y 16322c46ec117082ef745715f7a4761f2491a0b2
+F src/select.c 25cada7cb2b0b4973b3e17c81ba1b1c887829f71
F src/shell.c c5752d32cdeaa7d548d4f91177b697b023a00381
F src/sqlite.h 2397c17a8f4ca90c09acab0100dc7e2f8f441b69
-F src/sqliteInt.h 81552acdedb0c3b256510a66c0f656d35d2ea2bd
+F src/sqliteInt.h 04f38613a8ecedfdb15b21175acf0fe2ce55e299
F src/tclsqlite.c 9efd29f79ded6a900aa3d142169c8bfe03b7affd
-F src/tokenize.c 5b066f314646d6c5396a253315e5e95d107e1800
+F src/tokenize.c 15c229fee77325334c6814652e429b0930eba6c1
F src/update.c 9194f548dafc9884d79489874e22974ed67cd6a2
F src/util.c 6b4327d7fbf684f8635155d4acb847ae991b3ebc
-F src/vdbe.c 11d8e4f6e25044ceace5e7a5c160b74b0537492c
-F src/vdbe.h 02b470d344caed04451c896be7a775068dbdf076
+F src/vdbe.c a60a9316f2ffbc020d5a9a73535b84e7f513e45d
+F src/vdbe.h ab574c91c6328c5795f68b84074fbcf860eae70e
F src/where.c bed9a8360cbfbf712bdc397c8e22216a5e5f9800
F test/all.test 66a8a5b8291a472157944edcdce51a320ebd1f35
F test/copy.test 641bd3cfaab61c4ee32889587e21e4c70788a97a
@@ -43,9 +43,9 @@ F tool/opcodeDoc.awk b3a2a3d5d3075b8bd90b7afe24283efdd586659c
F tool/renumberOps.awk 6d067177ad5f8d711b79577b462da9b3634bd0a9
F www/c_interface.tcl f875864edf7974157d1c257ca08de854660882a5
F www/changes.tcl 995d64c96978a996f0e9e46f2ce896355a7c87a7
-F www/index.tcl 600e85c207929bedb9c6fd221aa7875fd8f43edf
+F www/index.tcl a94e31dc690f07b0dfdb82c5ab6315e4840a336d
F www/sqlite.tcl 7deb564df188ad4523adecfe2365de6d09f6dfd9
-P 35a8f523e8389a1a6e41f6561500644b165d556e
-R 7e04f378295246aef19c10676ba30bf8
+P 0b7d9eb8ad771917c53587ea4d674f7e8d76121f
+R 73a74ce4d5a10f11090cf328a18ab074
U drh
-Z 23a52b4171f3850f1bc24517d67a8aa5
+Z ef837be79fdfd30ace7832037467a189
diff --git a/manifest.uuid b/manifest.uuid
index dcd9fbc3e8..30c7fcabd8 100644
--- a/manifest.uuid
+++ b/manifest.uuid
@@ -1 +1 @@
-0b7d9eb8ad771917c53587ea4d674f7e8d76121f
\ No newline at end of file
+1f0c4ffd98591e506201b6b0e6e60b9216ceb596
\ No newline at end of file
diff --git a/src/dbbe.c b/src/dbbe.c
index 0e96bf85e6..edbdccf2f2 100644
--- a/src/dbbe.c
+++ b/src/dbbe.c
@@ -30,7 +30,7 @@
** relatively simple to convert to a different database such
** as NDBM, SDBM, or BerkeleyDB.
**
-** $Id: dbbe.c,v 1.3 2000/05/31 02:27:49 drh Exp $
+** $Id: dbbe.c,v 1.4 2000/05/31 20:00:52 drh Exp $
*/
#include "sqliteInt.h"
#include
@@ -47,9 +47,20 @@ struct BeFile {
char *zName; /* Name of the file */
GDBM_FILE dbf; /* The file itself */
int nRef; /* Number of references */
+ int delOnClose; /* Delete when closing */
BeFile *pNext, *pPrev; /* Next and previous on list of open files */
};
+/*
+** The following are state variables for the RC4 algorithm. We
+** use RC4 as a random number generator. Each call to RC4 gives
+** a random 8-bit number.
+*/
+struct rc4 {
+ int i, j;
+ int s[256];
+};
+
/*
** The complete database is an instance of the following structure.
*/
@@ -59,6 +70,7 @@ struct Dbbe {
BeFile *pOpen; /* List of open files */
int nTemp; /* Number of temporary files created */
FILE **apTemp; /* Space to hold temporary file pointers */
+ struct rc4 rc4; /* The random number generator */
};
/*
@@ -74,6 +86,41 @@ struct DbbeTable {
int readPending; /* The fetch hasn't actually been done yet */
};
+/*
+** Initialize the RC4 algorithm.
+*/
+static void rc4init(struct rc4 *p, char *key, int keylen){
+ int i;
+ char k[256];
+ p->j = 0;
+ p->i = 0;
+ for(i=0; i<256; i++){
+ p->s[i] = i;
+ k[i] = key[i%keylen];
+ }
+ for(i=0; i<256; i++){
+ int t;
+ p->j = (p->j + p->s[i] + k[i]) & 0xff;
+ t = p->s[p->j];
+ p->s[p->j] = p->s[i];
+ p->s[i] = t;
+ }
+}
+
+/*
+** Get a single 8-bit random value from the RC4 algorithm.
+*/
+static int rc4byte(struct rc4 *p){
+ int t;
+ p->i = (p->i + 1) & 0xff;
+ p->j = (p->j + p->s[p->i]) & 0xff;
+ t = p->s[p->i];
+ 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 current driver scheme,
** the database name is the name of the directory
@@ -105,6 +152,8 @@ Dbbe *sqliteDbbeOpen(
strcpy(pNew->zDir, zName);
pNew->write = write;
pNew->pOpen = 0;
+ time(&statbuf.st_ctime);
+ rc4init(&pNew->rc4, (char*)&statbuf, sizeof(statbuf));
return pNew;
}
@@ -144,6 +193,22 @@ static char *sqliteFileOfTable(Dbbe *pBe, const char *zTable){
return zFile;
}
+/*
+** Generate a random filename with the given prefix.
+*/
+static void randomName(struct rc4 *pRc4, char *zBuf, char *zPrefix){
+ int i, j;
+ static const char zRandomChars[] = "abcdefghijklmnopqrstuvwxyz0123456789";
+ strcpy(zBuf, zPrefix);
+ j = strlen(zBuf);
+ for(i=0; i<15; i++){
+ int c = (rc4byte(pRc4) & 0x7f) % (sizeof(zRandomChars) - 1);
+ zBuf[j++] = zRandomChars[c];
+ }
+ zBuf[j] = 0;
+}
+
+
/*
** Open a new table cursor
*/
@@ -158,9 +223,14 @@ DbbeTable *sqliteDbbeOpenTable(
pTable = sqliteMalloc( sizeof(*pTable) );
if( pTable==0 ) return 0;
- zFile = sqliteFileOfTable(pBe, zTable);
- for(pFile=pBe->pOpen; pFile; pFile=pFile->pNext){
- if( strcmp(pFile->zName,zFile)==0 ) break;
+ if( zTable ){
+ zFile = sqliteFileOfTable(pBe, zTable);
+ for(pFile=pBe->pOpen; pFile; pFile=pFile->pNext){
+ if( strcmp(pFile->zName,zFile)==0 ) break;
+ }
+ }else{
+ pFile = 0;
+ zFile = 0;
}
if( pFile==0 ){
pFile = sqliteMalloc( sizeof(*pFile) );
@@ -176,7 +246,24 @@ DbbeTable *sqliteDbbeOpenTable(
}
pFile->pNext = pBe->pOpen;
pBe->pOpen = pFile;
- pFile->dbf = gdbm_open(pFile->zName, 0, GDBM_WRCREAT|GDBM_FAST, 0640, 0);
+ if( pFile->zName ){
+ pFile->dbf = gdbm_open(pFile->zName, 0, GDBM_WRCREAT|GDBM_FAST, 0640, 0);
+ }else{
+ int i, j, limit;
+ struct rc4 *pRc4;
+ char zRandom[50];
+ pRc4 = &pBe->rc4;
+ zFile = 0;
+ limit = 5;
+ do {
+ randomName(&pBe->rc4, zRandom, "_temp_table_");
+ sqliteFree(zFile);
+ zFile = sqliteFileOfTable(pBe, zRandom);
+ pFile->dbf = gdbm_open(zFile, 0, GDBM_WRCREAT|GDBM_FAST, 0640, 0);
+ }while( pFile->dbf==0 && limit-- >= 0);
+ pFile->zName = zFile;
+ pFile->delOnClose = 1;
+ }
}else{
sqliteFree(zFile);
pFile->nRef++;
@@ -240,6 +327,9 @@ void sqliteDbbeCloseTable(DbbeTable *pTable){
if( pFile->pNext ){
pFile->pNext->pPrev = pFile->pPrev;
}
+ if( pFile->delOnClose ){
+ unlink(pFile->zName);
+ }
sqliteFree(pFile->zName);
memset(pFile, 0, sizeof(*pFile));
sqliteFree(pFile);
@@ -275,6 +365,21 @@ int sqliteDbbeFetch(DbbeTable *pTable, int nKey, char *pKey){
return pTable->data.dptr!=0;
}
+/*
+** Return 1 if the given key is already in the table. Return 0
+** if it is not.
+*/
+int sqliteDbbeTest(DbbeTable *pTable, int nKey, char *pKey){
+ datum key;
+ int result = 0;
+ key.dsize = nKey;
+ key.dptr = pKey;
+ if( pTable->pFile && pTable->pFile->dbf ){
+ result = gdbm_exists(pTable->pFile->dbf, key);
+ }
+ return result;
+}
+
/*
** Copy bytes from the current key or data into a buffer supplied by
** the calling function. Return the number of bytes copied.
@@ -377,73 +482,22 @@ int sqliteDbbeNextKey(DbbeTable *pTable){
return rc;
}
-/*
-** The following are state variables for the RC4 algorithm. We
-** use RC4 as a random number generator. Each call to RC4 gives
-** a random 8-bit number.
-*/
-static struct {
- int i, j;
- int s[256];
-} rc4;
-
-/*
-** Initialize the RC4 algorithm.
-*/
-static void rc4init(char *key, int keylen){
- int i;
- char k[256];
- rc4.j = 0;
- rc4.i = 0;
- for(i=0; i<256; i++){
- rc4.s[i] = i;
- k[i] = key[i%keylen];
- }
- for(i=0; i<256; i++){
- int t;
- rc4.j = (rc4.j + rc4.s[i] + k[i]) & 0xff;
- t = rc4.s[rc4.j];
- rc4.s[rc4.j] = rc4.s[i];
- rc4.s[i] = t;
- }
-}
-
-/*
-** Get a single 8-bit random value from the RC4 algorithm.
-*/
-static int rc4byte(void){
- int t;
- rc4.i = (rc4.i + 1) & 0xff;
- rc4.j = (rc4.j + rc4.s[rc4.i]) & 0xff;
- t = rc4.s[rc4.i];
- rc4.s[rc4.i] = rc4.s[rc4.j];
- rc4.s[rc4.j] = t;
- t = rc4.s[rc4.i] + rc4.s[rc4.j];
- return t & 0xff;
-}
-
/*
** Get a new integer key.
*/
int sqliteDbbeNew(DbbeTable *pTable){
- static int isInit = 0;
int iKey;
datum key;
int go = 1;
int i;
+ struct rc4 *pRc4;
- if( !isInit ){
- struct stat statbuf;
- stat(pTable->pFile->zName, &statbuf);
- time(&statbuf.st_ctime);
- rc4init((char*)&statbuf, sizeof(statbuf));
- isInit = 1;
- }
if( pTable->pFile==0 || pTable->pFile->dbf==0 ) return 1;
+ pRc4 = &pTable->pBe->rc4;
while( go ){
iKey = 0;
for(i=0; i<4; i++){
- iKey = (iKey<<8) + rc4byte();
+ iKey = (iKey<<8) + rc4byte(pRc4);
}
key.dptr = (char*)&iKey;
key.dsize = 4;
@@ -488,8 +542,9 @@ int sqliteDbbeDelete(DbbeTable *pTable, int nKey, char *pKey){
*/
FILE *sqliteDbbeOpenTempFile(Dbbe *pBe){
char *zFile;
- char zBuf[30];
- int i;
+ char zBuf[50];
+ int i, j;
+ int limit;
for(i=0; inTemp; i++){
if( pBe->apTemp[i]==0 ) break;
@@ -499,9 +554,13 @@ FILE *sqliteDbbeOpenTempFile(Dbbe *pBe){
pBe->apTemp = sqliteRealloc(pBe->apTemp, pBe->nTemp*sizeof(FILE*) );
}
if( pBe->apTemp==0 ) return 0;
- sprintf(zBuf, "/_temp_%d~", i);
+ limit = 4;
zFile = 0;
- sqliteSetString(&zFile, pBe->zDir, zBuf, 0);
+ do{
+ randomName(&pBe->rc4, zBuf, "/_temp_file_");
+ sqliteFree(zFile);
+ sqliteSetString(&zFile, pBe->zDir, zBuf, 0);
+ }while( access(zFile,0) && limit-- >= 0 );
pBe->apTemp[i] = fopen(zFile, "w+");
sqliteFree(zFile);
return pBe->apTemp[i];
diff --git a/src/dbbe.h b/src/dbbe.h
index c9f424d498..b5e36011d6 100644
--- a/src/dbbe.h
+++ b/src/dbbe.h
@@ -28,7 +28,7 @@
** This library was originally designed to support the following
** backends: GDBM, NDBM, SDBM, Berkeley DB.
**
-** $Id: dbbe.h,v 1.2 2000/05/31 02:27:49 drh Exp $
+** $Id: dbbe.h,v 1.3 2000/05/31 20:00:52 drh Exp $
*/
#ifndef _SQLITE_DBBE_H_
#define _SQLITE_DBBE_H_
@@ -59,6 +59,9 @@ void sqliteDbbeClose(Dbbe*);
/* Open a particular table of a previously opened database.
** Create the table if it doesn't already exist and writeable!=0.
+**
+** If zTableName is 0 or "", then a temporary table is created that
+** will be deleted when closed.
*/
DbbeTable *sqliteDbbeOpenTable(Dbbe*, const char *zTableName, int writeable);
@@ -76,6 +79,11 @@ void sqliteDbbeCloseTable(DbbeTable*);
*/
int sqliteDbbeFetch(DbbeTable*, int nKey, char *pKey);
+/* Return 1 if the given key is already in the table. Return 0
+** if it is not.
+*/
+int sqliteDbbeTest(DbbeTable*, int nKey, char *pKey);
+
/* Retrieve the key or data used for the last fetch. Only size
** bytes are read beginning with the offset-th byte. The return
** value is the actual number of bytes read.
diff --git a/src/parse.y b/src/parse.y
index 556b92e05a..7ae7dbf7d2 100644
--- a/src/parse.y
+++ b/src/parse.y
@@ -26,7 +26,7 @@
** the parser. Lemon will also generate a header file containing
** numeric codes for all of the tokens.
**
-** @(#) $Id: parse.y,v 1.5 2000/05/31 18:20:14 drh Exp $
+** @(#) $Id: parse.y,v 1.6 2000/05/31 20:00:52 drh Exp $
*/
%token_prefix TK_
%token_type {Token}
@@ -125,10 +125,15 @@ cmd ::= DROP TABLE id(X). {sqliteDropTable(pParse,&X);}
// The select statement
//
cmd ::= select.
-select ::= SELECT selcollist(W) from(X) where_opt(Y) orderby_opt(Z).
- {sqliteSelect(pParse, W, X, Y, Z);}
-select ::= SELECT STAR from(X) where_opt(Y) orderby_opt(Z).
- {sqliteSelect(pParse, 0, X, Y, Z);}
+select ::= SELECT distinct(D) selcollist(W) from(X) where_opt(Y) orderby_opt(Z).
+ {sqliteSelect(pParse, W, X, Y, Z, D);}
+select ::= SELECT distinct(D) STAR from(X) where_opt(Y) orderby_opt(Z).
+ {sqliteSelect(pParse, 0, X, Y, Z, D);}
+
+%type distinct {int}
+
+distinct(A) ::= DISTINCT. {A = 1;}
+distinct(A) ::= . {A = 0;}
%type selcollist {ExprList*}
%destructor selcollist {sqliteExprListDelete($$);}
diff --git a/src/select.c b/src/select.c
index c0722f00e5..52e4d30d8d 100644
--- a/src/select.c
+++ b/src/select.c
@@ -24,7 +24,7 @@
** This file contains C code routines that are called by the parser
** to handle SELECT statements.
**
-** $Id: select.c,v 1.2 2000/05/31 18:20:14 drh Exp $
+** $Id: select.c,v 1.3 2000/05/31 20:00:52 drh Exp $
*/
#include "sqliteInt.h"
@@ -37,7 +37,8 @@ void sqliteSelect(
ExprList *pEList, /* List of fields to extract. NULL means "*" */
IdList *pTabList, /* List of tables to select from */
Expr *pWhere, /* The WHERE clause. May be NULL */
- ExprList *pOrderBy /* The ORDER BY clause. May be NULL */
+ ExprList *pOrderBy, /* The ORDER BY clause. May be NULL */
+ int distinct /* If true, only output distinct results */
){
int i, j;
WhereInfo *pWInfo;
@@ -121,6 +122,12 @@ void sqliteSelect(
pOrderBy = 0;
}
+ /* Turn off distinct if this is an aggregate
+ */
+ if( isAgg ){
+ distinct = 0;
+ }
+
/* Begin generating code.
*/
v = pParse->pVdbe;
@@ -188,7 +195,11 @@ void sqliteSelect(
}
/* Begin the database scan
- */
+ */
+ if( distinct ){
+ distinct = pTabList->nId*2+1;
+ sqliteVdbeAddOp(v, OP_Open, distinct, 0, 0, 0);
+ }
pWInfo = sqliteWhereBegin(pParse, pTabList, pWhere, 0);
if( pWInfo==0 ) goto select_cleanup;
@@ -199,6 +210,19 @@ void sqliteSelect(
sqliteExprCode(pParse, pEList->a[i].pExpr);
}
}
+
+ /* If the current result is not distinct, script the remainder
+ ** of this processing.
+ */
+ if( distinct ){
+ int isDistinct = sqliteVdbeMakeLabel(v);
+ sqliteVdbeAddOp(v, OP_MakeKey, pEList->nExpr, 1, 0, 0);
+ sqliteVdbeAddOp(v, OP_Distinct, distinct, isDistinct, 0, 0);
+ sqliteVdbeAddOp(v, OP_Pop, pEList->nExpr+1, 0, 0, 0);
+ sqliteVdbeAddOp(v, OP_Goto, 0, pWInfo->iContinue, 0, 0);
+ sqliteVdbeAddOp(v, OP_String, 0, 0, "", isDistinct);
+ sqliteVdbeAddOp(v, OP_Put, distinct, 0, 0, 0);
+ }
/* If there is no ORDER BY clause, then we can invoke the callback
** right away. If there is an ORDER BY, then we need to put the
diff --git a/src/sqliteInt.h b/src/sqliteInt.h
index 2c1491d8d3..0dd8ef8251 100644
--- a/src/sqliteInt.h
+++ b/src/sqliteInt.h
@@ -23,7 +23,7 @@
*************************************************************************
** Internal interface definitions for SQLite.
**
-** @(#) $Id: sqliteInt.h,v 1.5 2000/05/31 15:34:53 drh Exp $
+** @(#) $Id: sqliteInt.h,v 1.6 2000/05/31 20:00:52 drh Exp $
*/
#include "sqlite.h"
#include "dbbe.h"
@@ -249,7 +249,7 @@ void sqliteIdListAddAlias(IdList*, Token*);
void sqliteIdListDelete(IdList*);
void sqliteCreateIndex(Parse*, Token*, Token*, IdList*, Token*, Token*);
void sqliteDropIndex(Parse*, Token*);
-void sqliteSelect(Parse*, ExprList*, IdList*, Expr*, ExprList*);
+void sqliteSelect(Parse*, ExprList*, IdList*, Expr*, ExprList*, int);
void sqliteDeleteFrom(Parse*, Token*, Expr*);
void sqliteUpdate(Parse*, Token*, ExprList*, Expr*);
WhereInfo *sqliteWhereBegin(Parse*, IdList*, Expr*, int);
diff --git a/src/tokenize.c b/src/tokenize.c
index d47476ff7b..76e0b7a17a 100644
--- a/src/tokenize.c
+++ b/src/tokenize.c
@@ -27,7 +27,7 @@
** individual tokens and sends those tokens one-by-one over to the
** parser for analysis.
**
-** $Id: tokenize.c,v 1.4 2000/05/31 02:27:49 drh Exp $
+** $Id: tokenize.c,v 1.5 2000/05/31 20:00:53 drh Exp $
*/
#include "sqliteInt.h"
#include
@@ -61,6 +61,7 @@ static Keyword aKeywordTable[] = {
{ "DELETE", 0, TK_DELETE, 0 },
{ "DELIMITERS", 0, TK_DELIMITERS, 0 },
{ "DESC", 0, TK_DESC, 0 },
+ { "DISTINCT", 0, TK_DISTINCT, 0 },
{ "DROP", 0, TK_DROP, 0 },
{ "EXPLAIN", 0, TK_EXPLAIN, 0 },
{ "FROM", 0, TK_FROM, 0 },
diff --git a/src/vdbe.c b/src/vdbe.c
index 195210a30d..a4883a8cb7 100644
--- a/src/vdbe.c
+++ b/src/vdbe.c
@@ -41,7 +41,7 @@
** But other routines are also provided to help in building up
** a program instruction by instruction.
**
-** $Id: vdbe.c,v 1.4 2000/05/31 15:34:53 drh Exp $
+** $Id: vdbe.c,v 1.5 2000/05/31 20:00:53 drh Exp $
*/
#include "sqliteInt.h"
@@ -392,23 +392,23 @@ void sqliteVdbeDelete(Vdbe *p){
*/
static char *zOpName[] = { 0,
"Open", "Close", "Fetch", "New",
- "Put", "Delete", "Field", "Key",
- "Rewind", "Next", "Destroy", "Reorganize",
- "ResetIdx", "NextIdx", "PutIdx", "DeleteIdx",
- "ListOpen", "ListWrite", "ListRewind", "ListRead",
- "ListClose", "SortOpen", "SortPut", "SortMakeRec",
- "SortMakeKey", "Sort", "SortNext", "SortKey",
- "SortCallback", "SortClose", "FileOpen", "FileRead",
- "FileField", "FileClose", "MakeRecord", "MakeKey",
- "Goto", "If", "Halt", "ColumnCount",
- "ColumnName", "Callback", "Integer", "String",
- "Pop", "Dup", "Pull", "Add",
- "AddImm", "Subtract", "Multiply", "Divide",
- "Min", "Max", "Like", "Glob",
- "Eq", "Ne", "Lt", "Le",
- "Gt", "Ge", "IsNull", "NotNull",
- "Negative", "And", "Or", "Not",
- "Concat", "Noop",
+ "Put", "Distinct", "Delete", "Field",
+ "Key", "Rewind", "Next", "Destroy",
+ "Reorganize", "ResetIdx", "NextIdx", "PutIdx",
+ "DeleteIdx", "ListOpen", "ListWrite", "ListRewind",
+ "ListRead", "ListClose", "SortOpen", "SortPut",
+ "SortMakeRec", "SortMakeKey", "Sort", "SortNext",
+ "SortKey", "SortCallback", "SortClose", "FileOpen",
+ "FileRead", "FileField", "FileClose", "MakeRecord",
+ "MakeKey", "Goto", "If", "Halt",
+ "ColumnCount", "ColumnName", "Callback", "Integer",
+ "String", "Pop", "Dup", "Pull",
+ "Add", "AddImm", "Subtract", "Multiply",
+ "Divide", "Min", "Max", "Like",
+ "Glob", "Eq", "Ne", "Lt",
+ "Le", "Gt", "Ge", "IsNull",
+ "NotNull", "Negative", "And", "Or",
+ "Not", "Concat", "Noop",
};
/*
@@ -1247,7 +1247,7 @@ int sqliteVdbeExec(
break;
}
- /* Opcode: MakeKey P1 * *
+ /* Opcode: MakeKey P1 P2 *
**
** Convert the top P1 entries of the stack into a single entry suitable
** for use as the key in an index or a sort. The top P1 records are
@@ -1256,6 +1256,11 @@ int sqliteVdbeExec(
** lowest entry in the stack is the first field and the top of the
** stack becomes the last.
**
+ ** If P2 is not zero, then the original entries remain on the stack
+ ** and the new key is pushed on top. If P2 is zero, the original
+ ** data is popped off the stack first then the new key is pushed
+ ** back in its place.
+ **
** See also the SortMakeKey opcode.
*/
case OP_MakeKey: {
@@ -1280,7 +1285,7 @@ int sqliteVdbeExec(
if( itos ) zNewKey[j++] = '\t';
}
zNewKey[j] = 0;
- PopStack(p, nField);
+ if( pOp->p2==0 ) PopStack(p, nField);
NeedStack(p, p->tos+1);
p->tos++;
p->iStack[p->tos] = nByte;
@@ -1353,6 +1358,38 @@ int sqliteVdbeExec(
break;
}
+ /* Opcode: Distinct P1 P2 *
+ **
+ ** Use the top of the stack as a key. If a record with that key
+ ** does not exist in table P1, then jump to P2. If the record
+ ** does already exist, then fall thru. The record is not retrieved.
+ ** The key is not popped from the stack.
+ */
+ case OP_Distinct: {
+ int i = pOp->p1;
+ int tos = p->tos;
+ int alreadyExists = 0;
+ if( tos<0 ) goto not_enough_stack;
+ if( i>=0 && inTable && p->aTab[i].pTable ){
+ if( p->zStack[tos]==0 ){
+ alreadyExists = sqliteDbbeTest(p->aTab[i].pTable, sizeof(int),
+ (char*)&p->iStack[tos]);
+ }else{
+ alreadyExists = sqliteDbbeTest(p->aTab[i].pTable, p->iStack[tos],
+ p->zStack[tos]);
+ }
+ }
+ if( !alreadyExists ){
+ pc = pOp->p2;
+ if( pc<0 || pc>p->nOp ){
+ sqliteSetString(pzErrMsg, "jump destination out of range", 0);
+ rc = 1;
+ }
+ pc--;
+ }
+ break;
+ }
+
/* Opcode: New P1 * *
**
** Get a new integer key not previous used by table P1 and
diff --git a/src/vdbe.h b/src/vdbe.h
index 3672cfee35..9a01412a10 100644
--- a/src/vdbe.h
+++ b/src/vdbe.h
@@ -27,7 +27,7 @@
** or VDBE. The VDBE implements an abstract machine that runs a
** simple program to access and modify the underlying database.
**
-** $Id: vdbe.h,v 1.3 2000/05/31 02:27:50 drh Exp $
+** $Id: vdbe.h,v 1.4 2000/05/31 20:00:53 drh Exp $
*/
#ifndef _SQLITE_VDBE_H_
#define _SQLITE_VDBE_H_
@@ -76,83 +76,84 @@ typedef struct VdbeOp VdbeOp;
#define OP_Fetch 3
#define OP_New 4
#define OP_Put 5
-#define OP_Delete 6
-#define OP_Field 7
-#define OP_Key 8
-#define OP_Rewind 9
-#define OP_Next 10
+#define OP_Distinct 6
+#define OP_Delete 7
+#define OP_Field 8
+#define OP_Key 9
+#define OP_Rewind 10
+#define OP_Next 11
-#define OP_Destroy 11
-#define OP_Reorganize 12
+#define OP_Destroy 12
+#define OP_Reorganize 13
-#define OP_ResetIdx 13
-#define OP_NextIdx 14
-#define OP_PutIdx 15
-#define OP_DeleteIdx 16
+#define OP_ResetIdx 14
+#define OP_NextIdx 15
+#define OP_PutIdx 16
+#define OP_DeleteIdx 17
-#define OP_ListOpen 17
-#define OP_ListWrite 18
-#define OP_ListRewind 19
-#define OP_ListRead 20
-#define OP_ListClose 21
+#define OP_ListOpen 18
+#define OP_ListWrite 19
+#define OP_ListRewind 20
+#define OP_ListRead 21
+#define OP_ListClose 22
-#define OP_SortOpen 22
-#define OP_SortPut 23
-#define OP_SortMakeRec 24
-#define OP_SortMakeKey 25
-#define OP_Sort 26
-#define OP_SortNext 27
-#define OP_SortKey 28
-#define OP_SortCallback 29
-#define OP_SortClose 30
+#define OP_SortOpen 23
+#define OP_SortPut 24
+#define OP_SortMakeRec 25
+#define OP_SortMakeKey 26
+#define OP_Sort 27
+#define OP_SortNext 28
+#define OP_SortKey 29
+#define OP_SortCallback 30
+#define OP_SortClose 31
-#define OP_FileOpen 31
-#define OP_FileRead 32
-#define OP_FileField 33
-#define OP_FileClose 34
+#define OP_FileOpen 32
+#define OP_FileRead 33
+#define OP_FileField 34
+#define OP_FileClose 35
-#define OP_MakeRecord 35
-#define OP_MakeKey 36
+#define OP_MakeRecord 36
+#define OP_MakeKey 37
-#define OP_Goto 37
-#define OP_If 38
-#define OP_Halt 39
+#define OP_Goto 38
+#define OP_If 39
+#define OP_Halt 40
-#define OP_ColumnCount 40
-#define OP_ColumnName 41
-#define OP_Callback 42
+#define OP_ColumnCount 41
+#define OP_ColumnName 42
+#define OP_Callback 43
-#define OP_Integer 43
-#define OP_String 44
-#define OP_Pop 45
-#define OP_Dup 46
-#define OP_Pull 47
+#define OP_Integer 44
+#define OP_String 45
+#define OP_Pop 46
+#define OP_Dup 47
+#define OP_Pull 48
-#define OP_Add 48
-#define OP_AddImm 49
-#define OP_Subtract 50
-#define OP_Multiply 51
-#define OP_Divide 52
-#define OP_Min 53
-#define OP_Max 54
-#define OP_Like 55
-#define OP_Glob 56
-#define OP_Eq 57
-#define OP_Ne 58
-#define OP_Lt 59
-#define OP_Le 60
-#define OP_Gt 61
-#define OP_Ge 62
-#define OP_IsNull 63
-#define OP_NotNull 64
-#define OP_Negative 65
-#define OP_And 66
-#define OP_Or 67
-#define OP_Not 68
-#define OP_Concat 69
-#define OP_Noop 70
+#define OP_Add 49
+#define OP_AddImm 50
+#define OP_Subtract 51
+#define OP_Multiply 52
+#define OP_Divide 53
+#define OP_Min 54
+#define OP_Max 55
+#define OP_Like 56
+#define OP_Glob 57
+#define OP_Eq 58
+#define OP_Ne 59
+#define OP_Lt 60
+#define OP_Le 61
+#define OP_Gt 62
+#define OP_Ge 63
+#define OP_IsNull 64
+#define OP_NotNull 65
+#define OP_Negative 66
+#define OP_And 67
+#define OP_Or 68
+#define OP_Not 69
+#define OP_Concat 70
+#define OP_Noop 71
-#define OP_MAX 70
+#define OP_MAX 71
/*
** Prototypes for the VDBE interface. See comments on the implementation
diff --git a/www/index.tcl b/www/index.tcl
index 93bf168cf8..15f31b8f55 100644
--- a/www/index.tcl
+++ b/www/index.tcl
@@ -1,7 +1,7 @@
#
# Run this TCL script to generate HTML for the index.html file.
#
-set rcsid {$Id: index.tcl,v 1.6 2000/05/31 15:34:54 drh Exp $}
+set rcsid {$Id: index.tcl,v 1.7 2000/05/31 20:00:53 drh Exp $}
puts {
SQLite: An SQL Frontend For GDBM
@@ -119,6 +119,9 @@ puts {Related Sites
about the Berkeley DB library, see
http://www.sleepycat.com
+
+Here is a
+ tutorial on SQL.
}
puts {