-C added\sAgg\sopcodes\sto\sthe\svdbe\s(CVS\s54)
-D 2000-06-05T21:39:49
+C :-)\s(CVS\s55)
+D 2000-06-06T01:50:43
F COPYRIGHT 74a8a6531a42e124df07ab5599aad63870fa0bd4
F Makefile.in 17ba1ccf8d2d40c627796bba8f72952365d6d644
F README 51f6a4e7408b34afa5bc1c0485f61b6a4efb6958
F src/dbbe.c ae8b5d2cdb4fa7dd11313059984be9457fa77f63
F src/dbbe.h a8a46f71238e0f09f3ec08fd9d1c8c7f4cdc49bf
F src/delete.c e11433c14ed5cc8553cba14296b3baa3c23054bc
-F src/expr.c 7e87558f88e7a52a902d78446843bda25015531e
+F src/expr.c b309517f59017f3a7c0db76a54bc3bc00e8fea38
F src/insert.c 5e69dd70c3f91cf5ec5090f39fd6cd8e135af9bf
F src/main.c 93a7ad14bb5a82ad13ad59da23ef674a94b0c3d6
-F src/parse.y 79b403240985c71d3f80abe3943e57482287428f
-F src/select.c ab379f969283819ac81b72d0bf7c7aa3fc9389db
+F src/parse.y 9c459212441d4786bb011945d6a587568368e202
+F src/select.c d90d577aa7687c860f2ce22dacabdbecb600f609
F src/shell.c 5fa24c0bb678782ffe9070128e3e160674f297eb
F src/sqlite.h 58da0a8590133777b741f9836beaef3d58f40268
-F src/sqliteInt.h c01eef2760cebee09c8e1710faf2ebd6fc8a16b6
+F src/sqliteInt.h 6f31db9b08bc7aec193c84f6f08b0f6c7ce9f270
F src/tclsqlite.c 9f358618ae803bedf4fb96da5154fd45023bc1f7
-F src/tokenize.c 15c229fee77325334c6814652e429b0930eba6c1
+F src/tokenize.c f190e16ebb82dd60497796022f1e56e2e0527975
F src/update.c 3f05d5082fd2c34f15d1e4a4db17355ad8807a78
F src/util.c 33f9baa01e45394ef0cf85361a0e872987884315
-F src/vdbe.c e14cf771e4f487feb433fc6c0c6bd9536167a953
-F src/vdbe.h 7b3cd6b122b72ce8d2f5ab40069748d32f1d9df0
+F src/vdbe.c 03177425b174d865f2c37b188a9df8a37f4eaff2
+F src/vdbe.h 49e0f9c6742860e224cd81d1278059f5d029dfb6
F src/where.c 6b840a726b06b5122f112e3bc3c142a230af6251
F test/all.test 0950c135cab7e60c07bd745ccfad1476211e5bd7
F test/copy.test 73c3783535db538c8ebd8fffb931376864fc3226
F www/changes.tcl 567cc6066d87460bdedff8e5bbc20f41ddaadf77
F www/index.tcl f8189a7898f6d06307c34047b9d7e00860026e44
F www/sqlite.tcl 2f933ce18cffd34a0a020a82435ab937137970fd
-P 3dbfa558ef01ac9249a3f40da49bc71cc7b040bc
-R 2c42d01480de541004757564bdb21603
+P e9ed5d2a3639161fb58856275ba9495f21d366bf
+R ad06a10e10e9d51600332d684a457b6e
U drh
-Z 3c51c450bc15ba9b865ce0cc0b812208
+Z b21141bb852afc7c732c3c3467253d37
-e9ed5d2a3639161fb58856275ba9495f21d366bf
\ No newline at end of file
+bd8b2645cc16759571c8c622dce4752bd35c04cf
\ No newline at end of file
*************************************************************************
** This file contains C code routines used for processing expressions
**
-** $Id: expr.c,v 1.6 2000/06/05 18:54:46 drh Exp $
+** $Id: expr.c,v 1.7 2000/06/06 01:50:43 drh Exp $
*/
#include "sqliteInt.h"
+/*
+** Walk an expression tree. Return 1 if the expression is constant
+** and 0 if it involves variables.
+*/
+static int isConstant(Expr *p){
+ switch( p->op ){
+ case TK_ID:
+ case TK_FIELD:
+ case TK_DOT:
+ return 0;
+ default: {
+ if( p->pLeft && !isConstant(p->pLeft) ) return 0;
+ if( p->pRight && !isConstant(p->pRight) ) return 0;
+ if( p->pList ){
+ int i;
+ for(i=0; i<p->pList->nExpr; i++){
+ if( !isConstant(p->pList->a[i].pExpr) ) return 0;
+ }
+ }
+ break;
+ }
+ }
+ return 1;
+}
+
/*
** This routine walks an expression tree and resolves references to
** table fields. Nodes of the form ID.ID or ID resolve into an
** value. The iField value is changed to the index of the field of the
** referenced table.
**
-** This routine also looks for SELECTs that are part of an expression.
+** We also check for instances of the IN operator. IN comes in two
+** forms:
+**
+** expr IN (exprlist)
+** and
+** expr IN (SELECT ...)
+**
+** The first form is handled by creating a set holding the list
+** of allowed values. The second form causes the SELECT to generate
+** a temporary table.
+**
+** This routine also looks for scalar SELECTs that are part of an expression.
** If it finds any, it generates code to write the value of that select
** into a memory cell.
**
break;
}
+ case TK_IN: {
+ Vdbe *v = pParse->pVdbe;
+ if( v==0 ){
+ v = pParse->pVdbe = sqliteVdbeCreate(pParse->db->pBe);
+ }
+ if( v==0 ) return 1;
+ if( pExpr->pSelect ){
+ /* Case 1: expr IN (SELECT ...)
+ **
+ ** Generate code to write the results of the select into a temporary
+ ** table. The cursor number of the temporary table is stored in
+ ** iTable.
+ */
+ pExpr->iTable = pParse->nTab++;
+ sqliteVdbeAddOp(v, OP_Open, pExpr->iTable, 0, 0, 0);
+ if( sqliteSelect(pParse, pExpr->pSelect, SRT_Set, pExpr->iTable) );
+ }else if( pExpr->pList ){
+ /* Case 2: expr IN (exprlist)
+ **
+ ** Create a set to put the exprlist values in. The Set id is stored
+ ** in iTable.
+ */
+ int i, iSet;
+ for(i=0; i<pExpr->pList->nExpr; i++){
+ Expr *pE2 = pExpr->pList->a[i].pExpr;
+ if( sqliteExprCheck(pParse, pE2, 0, 0) ){
+ return 1;
+ }
+ if( !isConstant(pE2) ){
+ sqliteSetString(&pParse->zErrMsg,
+ "right-hand side of IN operator must be constant", 0);
+ pParse->nErr++;
+ return 1;
+ }
+ }
+ iSet = pExpr->iTable = pParse->nSet++;
+ for(i=0; i<pExpr->pList->nExpr; i++){
+ Expr *pE2 = pExpr->pList->a[i].pExpr;
+ switch( pE2->op ){
+ case TK_FLOAT:
+ case TK_INTEGER:
+ case TK_STRING: {
+ int addr = sqliteVdbeAddOp(v, OP_SetInsert, iSet, 0, 0, 0);
+ sqliteVdbeChangeP3(v, addr, pE2->token.z, pE2->token.n);
+ sqliteVdbeDequoteP3(v, addr);
+ break;
+ }
+ default: {
+ sqliteExprCode(pParse, pE2);
+ sqliteVdbeAddOp(v, OP_SetInsert, iSet, 0, 0, 0);
+ break;
+ }
+ }
+ }
+ }
+ }
+
case TK_SELECT: {
+ /* This has to be a scalar SELECT. Generate code to put the
+ ** value of this select in a memory cell and record the number
+ ** of the memory cell in iField.
+ */
pExpr->iField = pParse->nMem++;
- if( sqliteSelect(pParse, pExpr->pSelect, -1, pExpr->iField) ){
+ if( sqliteSelect(pParse, pExpr->pSelect, SRT_Mem, pExpr->iField) ){
return 1;
}
break;
if( nErr==0 && pExpr->pRight ){
nErr = sqliteExprCheck(pParse, pExpr->pRight, 0, 0);
}
+ if( nErr==0 && pExpr->pList ){
+ int n = pExpr->pList->nExpr;
+ int i;
+ for(i=0; nErr==0 && i<n; i++){
+ nErr = sqliteExprCheck(pParse, pExpr->pList->a[i].pExpr, 0, 0);
+ }
+ }
break;
}
}
sqliteVdbeAddOp(v, OP_MemLoad, pExpr->iField, 0, 0, 0);
break;
}
+ case TK_IN: {
+ int addr;
+ sqliteVdbeAddOp(v, OP_Integer, 0, 0, 0, 0);
+ sqliteExprCode(pParse, pExpr->pLeft);
+ addr = sqliteVdbeCurrentAddr(v);
+ if( pExpr->pSelect ){
+ sqliteVdbeAddOp(v, OP_Found, pExpr->iTable, addr+2, 0, 0);
+ }else{
+ sqliteVdbeAddOp(v, OP_SetFound, pExpr->iTable, addr+2, 0, 0);
+ }
+ sqliteVdbeAddOp(v, OP_AddImm, 1, 0, 0, 0);
+ break;
+ }
+ case TK_BETWEEN: {
+ int lbl = sqliteVdbeMakeLabel(v);
+ sqliteVdbeAddOp(v, OP_Integer, 0, 0, 0, 0);
+ sqliteExprIfFalse(pParse, pExpr, lbl);
+ sqliteVdbeAddOp(v, OP_AddImm, 1, 0, 0, 0);
+ sqliteVdbeResolveLabel(v, lbl);
+ break;
+ }
}
return;
}
sqliteVdbeAddOp(v, op, 0, dest, 0, 0);
break;
}
+ case TK_IN: {
+ if( pExpr->pSelect ){
+ sqliteVdbeAddOp(v, OP_Found, pExpr->iTable, dest, 0, 0);
+ }else{
+ sqliteVdbeAddOp(v, OP_SetFound, pExpr->iTable, dest, 0, 0);
+ }
+ break;
+ }
+ case TK_BETWEEN: {
+ int lbl = sqliteVdbeMakeLabel(v);
+ sqliteExprCode(pParse, pExpr->pLeft);
+ sqliteVdbeAddOp(v, OP_Dup, 0, 0, 0, 0);
+ sqliteExprCode(pParse, pExpr->pList->a[0].pExpr);
+ sqliteVdbeAddOp(v, OP_Lt, 0, lbl, 0, 0);
+ sqliteExprCode(pParse, pExpr->pList->a[1].pExpr);
+ sqliteVdbeAddOp(v, OP_Le, 0, dest, 0, 0);
+ sqliteVdbeAddOp(v, OP_Integer, 0, 0, 0, 0);
+ sqliteVdbeAddOp(v, OP_Pop, 1, 0, 0, lbl);
+ break;
+ }
default: {
sqliteExprCode(pParse, pExpr);
sqliteVdbeAddOp(v, OP_If, 0, dest, 0, 0);
sqliteVdbeAddOp(v, op, 0, dest, 0, 0);
break;
}
+ case TK_IN: {
+ if( pExpr->pSelect ){
+ sqliteVdbeAddOp(v, OP_NotFound, pExpr->iTable, dest, 0, 0);
+ }else{
+ sqliteVdbeAddOp(v, OP_SetNotFound, pExpr->iTable, dest, 0, 0);
+ }
+ break;
+ }
+ case TK_BETWEEN: {
+ int addr;
+ sqliteExprCode(pParse, pExpr->pLeft);
+ sqliteVdbeAddOp(v, OP_Dup, 0, 0, 0, 0);
+ sqliteExprCode(pParse, pExpr->pList->a[0].pExpr);
+ addr = sqliteVdbeCurrentAddr(v);
+ sqliteVdbeAddOp(v, OP_Ge, 0, addr+3, 0, 0);
+ sqliteVdbeAddOp(v, OP_Pop, 1, 0, 0, 0);
+ sqliteVdbeAddOp(v, OP_Goto, 0, dest, 0, 0);
+ sqliteExprCode(pParse, pExpr->pList->a[1].pExpr);
+ sqliteVdbeAddOp(v, OP_Gt, 0, dest, 0, 0);
+ break;
+ }
default: {
sqliteExprCode(pParse, pExpr);
sqliteVdbeAddOp(v, OP_Not, 0, 0, 0, 0);
** the parser. Lemon will also generate a header file containing
** numeric codes for all of the tokens.
**
-** @(#) $Id: parse.y,v 1.10 2000/06/05 18:54:46 drh Exp $
+** @(#) $Id: parse.y,v 1.11 2000/06/06 01:50:43 drh Exp $
*/
%token_prefix TK_
%token_type {Token}
// The select statement
//
cmd ::= select(X). {
- sqliteSelect(pParse, X, -1, -1);
+ sqliteSelect(pParse, X, SRT_Callback, 0);
sqliteSelectDelete(X);
}
//
%type distinct {int}
distinct(A) ::= DISTINCT. {A = 1;}
+distinct(A) ::= ALL. {A = 0;}
distinct(A) ::= . {A = 0;}
// selcollist is a list of expressions that are to become the return
%left OR.
%left AND.
%right NOT.
-%left EQ NE ISNULL NOTNULL IS LIKE GLOB.
+%left EQ NE ISNULL NOTNULL IS LIKE GLOB BETWEEN IN.
%left GT GE LT LE.
%left PLUS MINUS.
%left STAR SLASH PERCENT.
expr(A) ::= expr(X) NE expr(Y). {A = sqliteExpr(TK_NE, X, Y, 0);}
expr(A) ::= expr(X) EQ expr(Y). {A = sqliteExpr(TK_EQ, X, Y, 0);}
expr(A) ::= expr(X) LIKE expr(Y). {A = sqliteExpr(TK_LIKE, X, Y, 0);}
-expr(A) ::= expr(X) GLOB expr(Y). {A = sqliteExpr(TK_GLOB,X,Y,0);}
-// expr(A) ::= expr(X) IS expr(Y). {A = sqliteExpr(TK_EQ, X, Y, 0);}
+expr(A) ::= expr(X) GLOB expr(Y). {A = sqliteExpr(TK_GLOB,X,Y,0);}
expr(A) ::= expr(X) PLUS expr(Y). {A = sqliteExpr(TK_PLUS, X, Y, 0);}
expr(A) ::= expr(X) MINUS expr(Y). {A = sqliteExpr(TK_MINUS, X, Y, 0);}
expr(A) ::= expr(X) STAR expr(Y). {A = sqliteExpr(TK_STAR, X, Y, 0);}
A = sqliteExpr(TK_SELECT, 0, 0, 0);
A->pSelect = X;
}
+expr(A) ::= expr(W) BETWEEN expr(X) AND expr(Y). {
+ ExprList *pList = sqliteExprListAppend(0, X, 0);
+ pList = sqliteExprListAppend(pList, Y, 0);
+ A = sqliteExpr(TK_BETWEEN, W, 0, 0);
+ A->pList = pList;
+}
+expr(A) ::= expr(X) IN LP exprlist(Y) RP. {
+ A = sqliteExpr(TK_IN, X, 0, 0);
+ A->pList = Y;
+}
+expr(A) ::= expr(X) IN LP select(Y) RP. {
+ A = sqliteExpr(TK_IN, X, 0, 0);
+ A->pSelect = Y;
+}
+
+
%type exprlist {ExprList*}
%destructor exprlist {sqliteExprListDelete($$);}
** This file contains C code routines that are called by the parser
** to handle SELECT statements.
**
-** $Id: select.c,v 1.8 2000/06/05 18:54:46 drh Exp $
+** $Id: select.c,v 1.9 2000/06/06 01:50:43 drh Exp $
*/
#include "sqliteInt.h"
/*
** Generate code for the given SELECT statement.
**
-** If iDest<0 and iMem<0, then the results of the query are sent to
-** the callback function. If iDest>=0 then the results are written to
-** an open cursor with the index iDest. The calling function is
-** responsible for having that cursor open. If iDest<0 and iMem>=0
-** then the result should be a single value which is then stored in
-** memory location iMem of the virtual machine.
+** The results are distributed in various ways depending on the
+** value of eDest and iParm.
+**
+** eDest Value Result
+** ------------ -------------------------------------------
+** SRT_Callback Invoke the callback for each row of the result.
+**
+** SRT_Mem Store first result in memory cell iParm
+**
+** SRT_Set Store results as keys of a table with cursor iParm
+**
+** SRT_Table Store results in a regular table with cursor iParm
**
** This routine returns the number of errors. If any errors are
** encountered, then an appropriate error message is left in
int sqliteSelect(
Parse *pParse, /* The parser context */
Select *p, /* The SELECT statement being coded. */
- int iDest, /* Write results to this cursor */
- int iMem /* Save result in this memory location, if >=0 */
+ int eDest, /* One of SRT_Callback, SRT_Mem, SRT_Set, SRT_Table */
+ int iParm /* Save result in this memory location, if >=0 */
){
int i, j;
WhereInfo *pWInfo;
/* If writing to memory, only a single column may be output.
*/
- if( iMem>=0 && pEList->nExpr>1 ){
+ if( (eDest==SRT_Mem || eDest==SRT_Set) && pEList->nExpr>1 ){
sqliteSetString(&pParse->zErrMsg, "only a single result allowed for "
"a SELECT that is part of an expression", 0);
pParse->nErr++;
}
}
- /* ORDER BY is ignored if
- **
- ** (1) this is an aggregate query like count(*)
- ** since only one row will be returned.
- **
- ** (2) We are writing the result to another table, since the
- ** order will get scrambled again after inserting.
- **
- ** (3) We are writing to a memory cell, since there is only
- ** one result.
+ /* ORDER BY is ignored if we are not invoking callbacks.
*/
- if( isAgg || iDest>=0 || iMem>=0 ){
+ if( isAgg || eDest!=SRT_Callback ){
pOrderBy = 0;
}
/* Turn off distinct if this is an aggregate or writing to memory.
*/
- if( isAgg || iMem>=0 ){
+ if( isAgg || eDest==SRT_Mem ){
isDistinct = 0;
}
/* Identify column names if we will be using a callback. This
** step is skipped if the output is going to a table or a memory cell.
*/
- if( iDest<0 && iMem<0 ){
+ if( eDest==SRT_Callback ){
sqliteVdbeAddOp(v, OP_ColumnCount, pEList->nExpr, 0, 0, 0);
for(i=0; i<pEList->nExpr; i++){
Expr *p;
/* Initialize the memory cell to NULL
*/
- if( iMem>=0 ){
+ if( eDest==SRT_Mem ){
sqliteVdbeAddOp(v, OP_Null, 0, 0, 0, 0);
- sqliteVdbeAddOp(v, OP_MemStore, iMem, 0, 0, 0);
+ sqliteVdbeAddOp(v, OP_MemStore, iParm, 0, 0, 0);
}
/* Begin the database scan
}
sqliteVdbeAddOp(v, op, p1, 0, 0, 0);
}
- }else if( iDest>=0 ){
+ }else if( eDest==SRT_Table ){
sqliteVdbeAddOp(v, OP_MakeRecord, pEList->nExpr, 0, 0, 0);
- sqliteVdbeAddOp(v, OP_New, iDest, 0, 0, 0);
+ sqliteVdbeAddOp(v, OP_New, iParm, 0, 0, 0);
sqliteVdbeAddOp(v, OP_Pull, 1, 0, 0, 0);
- sqliteVdbeAddOp(v, OP_Put, iDest, 0, 0, 0);
- }else if( iMem>=0 ){
- sqliteVdbeAddOp(v, OP_MemStore, iMem, 0, 0, 0);
+ sqliteVdbeAddOp(v, OP_Put, iParm, 0, 0, 0);
+ }else if( eDest==SRT_Set ){
+ sqliteVdbeAddOp(v, OP_String, 0, 0, "", 0);
+ sqliteVdbeAddOp(v, OP_Put, iParm, 0, 0, 0);
+ }else if( eDest==SRT_Mem ){
+ sqliteVdbeAddOp(v, OP_MemStore, iParm, 0, 0, 0);
sqliteVdbeAddOp(v, OP_Goto, 0, pWInfo->iBreak, 0, 0);
}else{
sqliteVdbeAddOp(v, OP_Callback, pEList->nExpr, 0, 0, 0);
** exactly once.
*/
if( isAgg ){
- if( iDest>=0 ){
+ if( eDest==SRT_Table ){
sqliteVdbeAddOp(v, OP_MakeRecord, pEList->nExpr, 0, 0, 0);
- sqliteVdbeAddOp(v, OP_New, iDest, 0, 0, 0);
+ sqliteVdbeAddOp(v, OP_New, iParm, 0, 0, 0);
sqliteVdbeAddOp(v, OP_Pull, 1, 0, 0, 0);
- sqliteVdbeAddOp(v, OP_Put, iDest, 0, 0, 0);
- }else if( iMem>=0 ){
- sqliteVdbeAddOp(v, OP_MemStore, iMem, 0, 0, 0);
+ sqliteVdbeAddOp(v, OP_Put, iParm, 0, 0, 0);
+ }else if( eDest==SRT_Set ){
+ sqliteVdbeAddOp(v, OP_String, 0, 0, "", 0);
+ sqliteVdbeAddOp(v, OP_Put, iParm, 0, 0, 0);
+ }else if( eDest==SRT_Mem ){
+ sqliteVdbeAddOp(v, OP_MemStore, iParm, 0, 0, 0);
}else{
sqliteVdbeAddOp(v, OP_Callback, pEList->nExpr, 0, 0, 0);
}
*************************************************************************
** Internal interface definitions for SQLite.
**
-** @(#) $Id: sqliteInt.h,v 1.15 2000/06/05 18:54:46 drh Exp $
+** @(#) $Id: sqliteInt.h,v 1.16 2000/06/06 01:50:43 drh Exp $
*/
#include "sqlite.h"
#include "dbbe.h"
ExprList *pOrderBy; /* The ORDER BY clause */
};
+/*
+** The results of a select can be distributed in several ways.
+*/
+#define SRT_Callback 1 /* Invoke a callback with each row of result */
+#define SRT_Mem 2 /* Store result in a memory cell */
+#define SRT_Set 3 /* Store result in a table for use with "IN" */
+#define SRT_Table 4 /* Store result in a regular table */
+
/*
** An SQL parser context
*/
int nErr; /* Number of errors seen */
int nTab; /* Number of previously allocated cursors */
int nMem; /* Number of memory cells used so far */
+ int nSet; /* Number of sets used so far */
};
/*
** individual tokens and sends those tokens one-by-one over to the
** parser for analysis.
**
-** $Id: tokenize.c,v 1.5 2000/05/31 20:00:53 drh Exp $
+** $Id: tokenize.c,v 1.6 2000/06/06 01:50:43 drh Exp $
*/
#include "sqliteInt.h"
#include <ctype.h>
** These are the keywords
*/
static Keyword aKeywordTable[] = {
+ { "ALL", 0, TK_ALL, 0 },
{ "AND", 0, TK_AND, 0 },
{ "AS", 0, TK_AS, 0 },
{ "ASC", 0, TK_ASC, 0 },
+ { "BETWEEN", 0, TK_BETWEEN, 0 },
{ "BY", 0, TK_BY, 0 },
{ "CHECK", 0, TK_CHECK, 0 },
{ "CONSTRAINT", 0, TK_CONSTRAINT, 0 },
{ "EXPLAIN", 0, TK_EXPLAIN, 0 },
{ "FROM", 0, TK_FROM, 0 },
{ "GLOB", 0, TK_GLOB, 0 },
+ { "IN", 0, TK_IN, 0 },
{ "INDEX", 0, TK_INDEX, 0 },
{ "INSERT", 0, TK_INSERT, 0 },
{ "INTO", 0, TK_INTO, 0 },
** But other routines are also provided to help in building up
** a program instruction by instruction.
**
-** $Id: vdbe.c,v 1.17 2000/06/05 21:39:49 drh Exp $
+** $Id: vdbe.c,v 1.18 2000/06/06 01:50:43 drh Exp $
*/
#include "sqliteInt.h"
#include <unistd.h>
};
typedef struct Mem Mem;
+/*
+** Allowed values for Stack.flags
+*/
+#define STK_Null 0x0001 /* Value is NULL */
+#define STK_Str 0x0002 /* Value is a string */
+#define STK_Int 0x0004 /* Value is an integer */
+#define STK_Real 0x0008 /* Value is a real number */
+#define STK_Dyn 0x0010 /* Need to call sqliteFree() on zStack[*] */
+
/*
** An Agg structure describes and Aggregator. Each Agg consists of
** zero or more Aggregator elements (AggElem). Each AggElem contains
};
/*
-** Allowed values for Stack.flags
+** A Set structure is used for quick testing to see if a value
+** is part of a small set. Sets are used to implement code like
+** this:
+** x.y IN ('hi','hoo','hum')
*/
-#define STK_Null 0x0001 /* Value is NULL */
-#define STK_Str 0x0002 /* Value is a string */
-#define STK_Int 0x0004 /* Value is an integer */
-#define STK_Real 0x0008 /* Value is a real number */
-#define STK_Dyn 0x0010 /* Need to call sqliteFree() on zStack[*] */
+typedef struct Set Set;
+typedef struct SetElem SetElem;
+struct Set {
+ SetElem *pAll; /* All elements of this set */
+ SetElem *apHash[41]; /* A hash table for all elements in this set */
+};
+struct SetElem {
+ SetElem *pHash; /* Next element with the same hash on zKey */
+ SetElem *pNext; /* Next element in a list of them all */
+ char zKey[1]; /* Value of this key */
+};
/*
** An instance of the virtual machine
int nMem; /* Number of memory locations currently allocated */
Mem *aMem; /* The memory locations */
Agg agg; /* Aggregate information */
+ int nSet; /* Number of sets allocated */
+ Set *aSet; /* An array of sets */
};
/*
return pFocus;
}
+/*
+** Erase all information from a Set
+*/
+static void SetClear(Set *p){
+ SetElem *pElem, *pNext;
+ for(pElem=p->pAll; pElem; pElem=pNext){
+ pNext = pElem->pNext;
+ sqliteFree(pElem);
+ }
+ memset(p, 0, sizeof(*p));
+}
+
+/*
+** Insert a new element into the set
+*/
+static void SetInsert(Set *p, char *zKey){
+ SetElem *pElem;
+ int h = sqliteHashNoCase(zKey, 0) % ArraySize(p->apHash);
+ for(pElem=p->apHash[h]; pElem; pElem=pElem->pHash){
+ if( strcmp(pElem->zKey, zKey)==0 ) return;
+ }
+ pElem = sqliteMalloc( sizeof(pElem) + strlen(zKey) );
+ if( pElem==0 ) return;
+ strcpy(pElem->zKey, zKey);
+ pElem->pNext = p->pAll;
+ p->pAll = pElem;
+ pElem->pHash = p->apHash[h];
+ p->apHash[h] = pElem;
+}
+
+/*
+** Return TRUE if an element is in the set. Return FALSE if not.
+*/
+static int SetTest(Set *p, char *zKey){
+ SetElem *pElem;
+ int h = sqliteHashNoCase(zKey, 0) % ArraySize(p->apHash);
+ for(pElem=p->apHash[h]; pElem; pElem=pElem->pHash){
+ if( strcmp(pElem->zKey, zKey)==0 ) return 1;
+ }
+ return 0;
+}
+
/*
** Convert the given stack entity into a string if it isn't one
** already. Return non-zero if we run out of memory.
}
p->nLineAlloc = 0;
AggReset(&p->agg);
+ for(i=0; i<p->nSet; i++){
+ SetClear(&p->aSet[i]);
+ }
+ sqliteFree(p->aSet);
+ p->aSet = 0;
+ p->nSet = 0;
}
/*
*/
static char *zOpName[] = { 0,
"Open", "Close", "Fetch", "New",
- "Put", "Distinct", "Delete", "Field",
- "Key", "Rewind", "Next", "Destroy",
- "Reorganize", "ResetIdx", "NextIdx", "PutIdx",
- "DeleteIdx", "MemLoad", "MemStore", "ListOpen",
- "ListWrite", "ListRewind", "ListRead", "ListClose",
- "SortOpen", "SortPut", "SortMakeRec", "SortMakeKey",
- "Sort", "SortNext", "SortKey", "SortCallback",
- "SortClose", "FileOpen", "FileRead", "FileField",
- "FileClose", "AggReset", "AggFocus", "AggIncr",
- "AggNext", "AggSet", "AggGet", "MakeRecord",
- "MakeKey", "Goto", "If", "Halt",
- "ColumnCount", "ColumnName", "Callback", "Integer",
- "String", "Null", "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", "Found", "NotFound",
+ "Delete", "Field", "Key", "Rewind",
+ "Next", "Destroy", "Reorganize", "ResetIdx",
+ "NextIdx", "PutIdx", "DeleteIdx", "MemLoad",
+ "MemStore", "ListOpen", "ListWrite", "ListRewind",
+ "ListRead", "ListClose", "SortOpen", "SortPut",
+ "SortMakeRec", "SortMakeKey", "Sort", "SortNext",
+ "SortKey", "SortCallback", "SortClose", "FileOpen",
+ "FileRead", "FileField", "FileClose", "AggReset",
+ "AggFocus", "AggIncr", "AggNext", "AggSet",
+ "AggGet", "SetInsert", "SetFound", "SetNotFound",
+ "SetClear", "MakeRecord", "MakeKey", "Goto",
+ "If", "Halt", "ColumnCount", "ColumnName",
+ "Callback", "Integer", "String", "Null",
+ "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",
};
/*
** does already exist, then fall thru. The record is not retrieved.
** The key is not popped from the stack.
*/
- case OP_Distinct: {
+ /* Opcode: Found P1 P2 *
+ **
+ ** Use the top of the stack as a key. If a record with that key
+ ** does exist in table P1, then jump to P2. If the record
+ ** does not exist, then fall thru. The record is not retrieved.
+ ** The key is popped from the stack.
+ */
+ /* Opcode: NotFound P1 P2 *
+ **
+ ** Use the top of the stack as a key. If a record with that key
+ ** does exist in table P1, then jump to P2. If the record
+ ** does not exist, then fall thru. The record is not retrieved.
+ ** The key is popped from the stack.
+ */
+ case OP_Distinct:
+ case OP_NotFound:
+ case OP_Found: {
int i = pOp->p1;
int tos = p->tos;
int alreadyExists = 0;
p->zStack[tos]);
}
}
- if( !alreadyExists ){
- pc = pOp->p2 - 1;
+ if( pOp->opcode==OP_Found ){
+ if( alreadyExists ) pc = pOp->p2 - 1;
+ }else{
+ if( !alreadyExists ) pc = pOp->p2 - 1;
+ }
+ if( pOp->opcode!=OP_Distinct ){
+ PopStack(p, 1);
}
break;
}
break;
}
+ /* Opcode: SetClear P1 * *
+ **
+ ** Remove all elements from the given Set.
+ */
+ case OP_SetClear: {
+ int i = pOp->p1;
+ if( i>=0 && i<p->nSet ){
+ SetClear(&p->aSet[i]);
+ }
+ break;
+ }
+
+ /* Opcode: SetInsert P1 * P3
+ **
+ ** If Set p1 does not exist then create it. Then insert value
+ ** P3 into that set. If P3 is NULL, then insert the top of the
+ ** stack into the set.
+ */
+ case OP_SetInsert: {
+ int i = pOp->p1;
+ if( p->nSet<=i ){
+ p->aSet = sqliteRealloc(p->aSet, (i+1)*sizeof(p->aSet[0]) );
+ if( p->aSet==0 ) goto no_mem;
+ memset(&p->aSet[p->nSet], 0, sizeof(p->aSet[0])*(i+1 - p->nSet));
+ p->nSet = i+1;
+ }
+ if( pOp->p3 ){
+ SetInsert(&p->aSet[i], pOp->p3);
+ }else{
+ int tos = p->tos;
+ if( tos<0 ) goto not_enough_stack;
+ Stringify(p, tos);
+ SetInsert(&p->aSet[i], p->zStack[tos]);
+ PopStack(p, 1);
+ }
+ break;
+ }
+
+ /* Opcode: SetFound P1 P2 *
+ **
+ ** Pop the stack once and compare the value popped off with the
+ ** contents of set P1. If the element popped exists in set P1,
+ ** then jump to P2. Otherwise fall through.
+ */
+ case OP_SetFound: {
+ int i = pOp->p1;
+ int tos = p->tos;
+ if( tos<0 ) goto not_enough_stack;
+ Stringify(p, tos);
+ if( i>=0 && i<p->nSet && SetTest(&p->aSet[i], p->zStack[tos]) ){
+ pc = pOp->p2 - 1;
+ }
+ PopStack(p, 1);
+ }
+
+ /* Opcode: SetNotFound P1 P2 *
+ **
+ ** Pop the stack once and compare the value popped off with the
+ ** contents of set P1. If the element popped does not exists in
+ ** set P1, then jump to P2. Otherwise fall through.
+ */
+ case OP_SetNotFound: {
+ int i = pOp->p1;
+ int tos = p->tos;
+ if( tos<0 ) goto not_enough_stack;
+ Stringify(p, tos);
+ if( i>=0 && i<p->nSet && !SetTest(&p->aSet[i], p->zStack[tos]) ){
+ pc = pOp->p2 - 1;
+ }
+ PopStack(p, 1);
+ }
+
/* An other opcode is illegal...
*/
default: {
** 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.7 2000/06/05 21:39:49 drh Exp $
+** $Id: vdbe.h,v 1.8 2000/06/06 01:50:44 drh Exp $
*/
#ifndef _SQLITE_VDBE_H_
#define _SQLITE_VDBE_H_
#define OP_New 4
#define OP_Put 5
#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 12
-#define OP_Reorganize 13
-
-#define OP_ResetIdx 14
-#define OP_NextIdx 15
-#define OP_PutIdx 16
-#define OP_DeleteIdx 17
-
-#define OP_MemLoad 18
-#define OP_MemStore 19
-
-#define OP_ListOpen 20
-#define OP_ListWrite 21
-#define OP_ListRewind 22
-#define OP_ListRead 23
-#define OP_ListClose 24
-
-#define OP_SortOpen 25
-#define OP_SortPut 26
-#define OP_SortMakeRec 27
-#define OP_SortMakeKey 28
-#define OP_Sort 29
-#define OP_SortNext 30
-#define OP_SortKey 31
-#define OP_SortCallback 32
-#define OP_SortClose 33
-
-#define OP_FileOpen 34
-#define OP_FileRead 35
-#define OP_FileField 36
-#define OP_FileClose 37
-
-#define OP_AggReset 38
-#define OP_AggFocus 39
-#define OP_AggIncr 40
-#define OP_AggNext 41
-#define OP_AggSet 42
-#define OP_AggGet 43
-
-#define OP_MakeRecord 44
-#define OP_MakeKey 45
-
-#define OP_Goto 46
-#define OP_If 47
-#define OP_Halt 48
-
-#define OP_ColumnCount 49
-#define OP_ColumnName 50
-#define OP_Callback 51
-
-#define OP_Integer 52
-#define OP_String 53
-#define OP_Null 54
-#define OP_Pop 55
-#define OP_Dup 56
-#define OP_Pull 57
-
-#define OP_Add 58
-#define OP_AddImm 59
-#define OP_Subtract 60
-#define OP_Multiply 61
-#define OP_Divide 62
-#define OP_Min 63
-#define OP_Max 64
-#define OP_Like 65
-#define OP_Glob 66
-#define OP_Eq 67
-#define OP_Ne 68
-#define OP_Lt 69
-#define OP_Le 70
-#define OP_Gt 71
-#define OP_Ge 72
-#define OP_IsNull 73
-#define OP_NotNull 74
-#define OP_Negative 75
-#define OP_And 76
-#define OP_Or 77
-#define OP_Not 78
-#define OP_Concat 79
-#define OP_Noop 80
-
-#define OP_MAX 80
+#define OP_Found 7
+#define OP_NotFound 8
+#define OP_Delete 9
+#define OP_Field 10
+#define OP_Key 11
+#define OP_Rewind 12
+#define OP_Next 13
+
+#define OP_Destroy 14
+#define OP_Reorganize 15
+
+#define OP_ResetIdx 16
+#define OP_NextIdx 17
+#define OP_PutIdx 18
+#define OP_DeleteIdx 19
+
+#define OP_MemLoad 20
+#define OP_MemStore 21
+
+#define OP_ListOpen 22
+#define OP_ListWrite 23
+#define OP_ListRewind 24
+#define OP_ListRead 25
+#define OP_ListClose 26
+
+#define OP_SortOpen 27
+#define OP_SortPut 28
+#define OP_SortMakeRec 29
+#define OP_SortMakeKey 30
+#define OP_Sort 31
+#define OP_SortNext 32
+#define OP_SortKey 33
+#define OP_SortCallback 34
+#define OP_SortClose 35
+
+#define OP_FileOpen 36
+#define OP_FileRead 37
+#define OP_FileField 38
+#define OP_FileClose 39
+
+#define OP_AggReset 40
+#define OP_AggFocus 41
+#define OP_AggIncr 42
+#define OP_AggNext 43
+#define OP_AggSet 44
+#define OP_AggGet 45
+
+#define OP_SetInsert 46
+#define OP_SetFound 47
+#define OP_SetNotFound 48
+#define OP_SetClear 49
+
+#define OP_MakeRecord 50
+#define OP_MakeKey 51
+
+#define OP_Goto 52
+#define OP_If 53
+#define OP_Halt 54
+
+#define OP_ColumnCount 55
+#define OP_ColumnName 56
+#define OP_Callback 57
+
+#define OP_Integer 58
+#define OP_String 59
+#define OP_Null 60
+#define OP_Pop 61
+#define OP_Dup 62
+#define OP_Pull 63
+
+#define OP_Add 64
+#define OP_AddImm 65
+#define OP_Subtract 66
+#define OP_Multiply 67
+#define OP_Divide 68
+#define OP_Min 69
+#define OP_Max 70
+#define OP_Like 71
+#define OP_Glob 72
+#define OP_Eq 73
+#define OP_Ne 74
+#define OP_Lt 75
+#define OP_Le 76
+#define OP_Gt 77
+#define OP_Ge 78
+#define OP_IsNull 79
+#define OP_NotNull 80
+#define OP_Negative 81
+#define OP_And 82
+#define OP_Or 83
+#define OP_Not 84
+#define OP_Concat 85
+#define OP_Noop 86
+
+#define OP_MAX 86
/*
** Prototypes for the VDBE interface. See comments on the implementation