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 {