:-) (CVS 20)

FossilOrigin-Name: 01d85b35e9c4ca5619ad21a4232a8f8bf9ec3538
diff --git a/src/build.c b/src/build.c
index 4b29cc1..fd0aaee 100644
--- a/src/build.c
+++ b/src/build.c
@@ -24,7 +24,7 @@
 ** This file contains C code routines that are called by the parser
 ** when syntax rules are reduced.
 **
-** $Id: build.c,v 1.9 2000/05/30 19:22:26 drh Exp $
+** $Id: build.c,v 1.10 2000/05/31 02:27:49 drh Exp $
 */
 #include "sqliteInt.h"
 
@@ -1528,3 +1528,46 @@
 copy_cleanup:
   return;
 }
+
+/*
+** The non-standard VACUUM command is used to clean up the database,
+** collapse free space, etc.  It is modelled after the VACUUM command
+** in PostgreSQL.
+*/
+void sqliteVacuum(Parse *pParse, Token *pTableName){
+  char *zName;
+  Vdbe *v;
+
+  if( pTableName ){
+    zName = sqliteTableNameFromToken(pTableName);
+  }else{
+    zName = 0;
+  }
+  if( zName && sqliteFindIndex(pParse->db, zName)==0
+    && sqliteFindTable(pParse->db, zName)==0 ){
+    sqliteSetString(&pParse->zErrMsg, "no such table or index: ", zName, 0);
+    pParse->nErr++;
+    goto vacuum_cleanup;
+  }
+  v = pParse->pVdbe = sqliteVdbeCreate(pParse->db->pBe);
+  if( v==0 ) goto vacuum_cleanup;
+  if( zName ){
+    sqliteVdbeAddOp(v, OP_Reorganize, 0, 0, zName, 0);
+  }else{
+    int h;
+    Table *pTab;
+    Index *pIdx;
+    for(h=0; h<N_HASH; h++){
+      for(pTab=pParse->db->apTblHash[h]; pTab; pTab=pTab->pHash){
+        sqliteVdbeAddOp(v, OP_Reorganize, 0, 0, pTab->zName, 0);
+        for(pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext){
+          sqliteVdbeAddOp(v, OP_Reorganize, 0, 0, pIdx->zName, 0);
+        }
+      }
+    }
+  }
+
+vacuum_cleanup:
+  sqliteFree(zName);
+  return;
+}
diff --git a/src/dbbe.c b/src/dbbe.c
index 51cf86b..0e96bf8 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.2 2000/05/30 18:45:24 drh Exp $
+** $Id: dbbe.c,v 1.3 2000/05/31 02:27:49 drh Exp $
 */
 #include "sqliteInt.h"
 #include <gdbm.h>
@@ -200,6 +200,22 @@
 }
 
 /*
+** Reorganize a table to reduce search times and disk usage.
+*/
+void sqliteDbbeReorganizeTable(Dbbe *pBe, const char *zTable){
+  char *zFile;            /* Name of the table file */
+  DbbeTable *pTab;
+
+  pTab = sqliteDbbeOpenTable(pBe, zTable, 1);
+  if( pTab && pTab->pFile && pTab->pFile->dbf ){
+    gdbm_reorganize(pTab->pFile->dbf);
+  }
+  if( pTab ){
+    sqliteDbbeCloseTable(pTab);
+  }
+}
+
+/*
 ** Close a table previously opened by sqliteDbbeOpenTable().
 */
 void sqliteDbbeCloseTable(DbbeTable *pTable){
diff --git a/src/dbbe.h b/src/dbbe.h
index 26e83a6..c9f424d 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.1 2000/05/29 14:26:01 drh Exp $
+** $Id: dbbe.h,v 1.2 2000/05/31 02:27:49 drh Exp $
 */
 #ifndef _SQLITE_DBBE_H_
 #define _SQLITE_DBBE_H_
@@ -65,6 +65,9 @@
 /* Delete a table from the database */
 void sqliteDbbeDropTable(Dbbe*, const char *zTableName);
 
+/* Reorganize a table to speed access or reduce its disk usage */
+void sqliteDbbeReorganizeTable(Dbbe*, const char *zTableName);
+
 /* Close a table */
 void sqliteDbbeCloseTable(DbbeTable*);
 
diff --git a/src/parse.y b/src/parse.y
index 7b58846..89d0035 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.2 2000/05/30 16:27:04 drh Exp $
+** @(#) $Id: parse.y,v 1.3 2000/05/31 02:27:49 drh Exp $
 */
 %token_prefix TK_
 %token_type {Token}
@@ -236,7 +236,7 @@
 
 %left OR.
 %left AND.
-%left EQ NE ISNULL NOTNULL IS.
+%left EQ NE ISNULL NOTNULL IS LIKE GLOB.
 %left GT GE LT LE.
 %left PLUS MINUS.
 %left STAR SLASH PERCENT.
@@ -264,7 +264,9 @@
 expr(A) ::= expr(X) GE expr(Y).    {A = sqliteExpr(TK_GE, X, Y, 0);}
 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) IS 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) 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);}
@@ -309,3 +311,6 @@
     {sqliteCopy(pParse,&X,&Y,&Z);}
 cmd ::= COPY id(X) FROM id(Y).
     {sqliteCopy(pParse,&X,&Y,0);}
+
+cmd ::= VACUUM.                {sqliteVacuum(pParse,0);}
+cmd ::= VACUUM id(X).          {sqliteVacuum(pParse,&X);}
diff --git a/src/shell.c b/src/shell.c
index b044ad4..4cc8a30 100644
--- a/src/shell.c
+++ b/src/shell.c
@@ -24,7 +24,7 @@
 ** This file contains code to implement the "sqlite" command line
 ** utility for accessing SQLite databases.
 **
-** $Id: shell.c,v 1.3 2000/05/30 18:45:24 drh Exp $
+** $Id: shell.c,v 1.4 2000/05/31 02:27:49 drh Exp $
 */
 #include <stdlib.h>
 #include <string.h>
@@ -300,7 +300,7 @@
     data.showHeader = 0;
     data.mode = MODE_List;
     sprintf(zSql, "SELECT name FROM sqlite_master "
-                  "WHERE type='index' AND tbl_name='%.00s' "
+                  "WHERE type='index' AND tbl_name LIKE '%.00s' "
                   "ORDER BY name", azArg[1]);
     sqlite_exec(db, zSql, callback, &data, &zErrMsg);
     if( zErrMsg ){
@@ -343,7 +343,7 @@
     data.showHeader = 0;
     data.mode = MODE_List;
     if( nArg>1 ){
-      sprintf(zSql, "SELECT sql FROM sqlite_master WHERE name='%.900s'",
+      sprintf(zSql, "SELECT sql FROM sqlite_master WHERE name LIKE '%.900s'",
          azArg[1]);
     }else{
       sprintf(zSql, "SELECT sql FROM sqlite_master "
diff --git a/src/sqliteInt.h b/src/sqliteInt.h
index 7bbabff..c64a147 100644
--- a/src/sqliteInt.h
+++ b/src/sqliteInt.h
@@ -23,7 +23,7 @@
 *************************************************************************
 ** Internal interface definitions for SQLite.
 **
-** @(#) $Id: sqliteInt.h,v 1.3 2000/05/30 16:27:04 drh Exp $
+** @(#) $Id: sqliteInt.h,v 1.4 2000/05/31 02:27:49 drh Exp $
 */
 #include "sqlite.h"
 #include "dbbe.h"
@@ -248,3 +248,6 @@
 void sqliteExprIfFalse(Parse*, Expr*, int);
 Table *sqliteFindTable(sqlite*,char*);
 void sqliteCopy(Parse*, Token*, Token*, Token*);
+void sqliteVacuum(Parse*, Token*);
+int sqliteGlobCompare(const char*,const char*);
+int sqliteLikeCompare(const unsigned char*,const unsigned char*);
diff --git a/src/tokenize.c b/src/tokenize.c
index ac062d0..d47476f 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.3 2000/05/30 16:27:04 drh Exp $
+** $Id: tokenize.c,v 1.4 2000/05/31 02:27:49 drh Exp $
 */
 #include "sqliteInt.h"
 #include <ctype.h>
@@ -64,12 +64,14 @@
   { "DROP",              0, TK_DROP,             0 },
   { "EXPLAIN",           0, TK_EXPLAIN,          0 },
   { "FROM",              0, TK_FROM,             0 },
+  { "GLOB",              0, TK_GLOB,             0 },
   { "INDEX",             0, TK_INDEX,            0 },
   { "INSERT",            0, TK_INSERT,           0 },
   { "INTO",              0, TK_INTO,             0 },
   { "IS",                0, TK_IS,               0 },
   { "ISNULL",            0, TK_ISNULL,           0 },
   { "KEY",               0, TK_KEY,              0 },
+  { "LIKE",              0, TK_LIKE,             0 },
   { "NOT",               0, TK_NOT,              0 },
   { "NOTNULL",           0, TK_NOTNULL,          0 },
   { "NULL",              0, TK_NULL,             0 },
@@ -83,6 +85,7 @@
   { "UNIQUE",            0, TK_UNIQUE,           0 },
   { "UPDATE",            0, TK_UPDATE,           0 },
   { "USING",             0, TK_USING,            0 },
+  { "VACUUM",            0, TK_VACUUM,           0 },
   { "VALUES",            0, TK_VALUES,           0 },
   { "WHERE",             0, TK_WHERE,            0 },
 };
diff --git a/src/util.c b/src/util.c
index 1c59ad7..b5bf19c 100644
--- a/src/util.c
+++ b/src/util.c
@@ -26,7 +26,7 @@
 ** This file contains functions for allocating memory, comparing
 ** strings, and stuff like that.
 **
-** $Id: util.c,v 1.6 2000/05/30 16:27:04 drh Exp $
+** $Id: util.c,v 1.7 2000/05/31 02:27:49 drh Exp $
 */
 #include "sqliteInt.h"
 #include <stdarg.h>
@@ -563,3 +563,138 @@
   if( *a=='-' ) res = -res;
   return res;
 }
+
+/*
+** Compare two strings for equality where the first string can
+** potentially be a "glob" expression.  Return true (1) if they
+** are the same and false (0) if they are different.
+**
+** Globbing rules:
+**
+**      '*'       Matches any sequence of zero or more characters.
+**
+**      '?'       Matches exactly one character.
+**
+**     [...]      Matches one character from the enclosed list of
+**                characters.
+**
+**     [^...]     Matches one character not in the enclosed list.
+**
+** With the [...] and [^...] matching, a ']' character can be included
+** in the list by making it the first character after '[' or '^'.  A
+** range of characters can be specified using '-'.  Example:
+** "[a-z]" matches any single lower-case letter.  To match a '-', make
+** it the last character in the list.
+**
+** This routine is usually quick, but can be N**2 in the worst case.
+**
+** Hints: to match '*' or '?', put them in "[]".  Like this:
+**
+**         abc[*]xyz        Matches "abc*xyz" only
+*/
+int sqliteGlobCompare(const char *zPattern, const char *zString){
+  register char c;
+  int invert;
+  int seen;
+  char c2;
+
+  while( (c = *zPattern)!=0 ){
+    switch( c ){
+      case '*':
+        while( zPattern[1]=='*' ) zPattern++;
+        if( zPattern[1]==0 ) return 1;
+        c = zPattern[1];
+        if( c=='[' || c=='?' ){
+          while( *zString && sqliteGlobCompare(&zPattern[1],zString)==0 ){
+            zString++;
+          }
+          return *zString!=0;
+        }else{
+          while( (c2 = *zString)!=0 ){
+            while( c2 != 0 && c2 != c ){ c2 = *++zString; }
+            if( sqliteGlobCompare(&zPattern[1],zString) ) return 1;
+            zString++;
+          }
+          return 0;
+        }
+      case '?':
+        if( *zString==0 ) return 0;
+        break;
+      case '[':
+        seen = 0;
+        invert = 0;
+        c = *zString;
+        if( c==0 ) return 0;
+        c2 = *++zPattern;
+        if( c2=='^' ){ invert = 1; c2 = *++zPattern; }
+        if( c2==']' ){
+          if( c==']' ) seen = 1;
+          c2 = *++zPattern;
+        }
+        while( (c2 = *zPattern)!=0 && c2!=']' ){
+          if( c2=='-' && zPattern[1]!=']' && zPattern[1]!=0 ){
+            if( c>zPattern[-1] && c<zPattern[1] ) seen = 1;
+          }else if( c==c2 ){
+            seen = 1;
+          }
+          zPattern++;
+        }
+        if( c2==0 || (seen ^ invert)==0 ) return 0;
+        break;
+      default:
+        if( c != *zString ) return 0;
+        break;
+    }
+    zPattern++;
+    zString++;
+  }
+  return *zString==0;
+}
+
+/*
+** Compare two strings for equality using the "LIKE" operator of
+** SQL.  The '%' character matches any sequence of 0 or more
+** characters and '_' matches any single character.  Case is
+** not significant.
+**
+** This routine is just an adaptation of the sqliteGlobCompare()
+** routine above.
+*/
+int 
+sqliteLikeCompare(const unsigned char *zPattern, const unsigned char *zString){
+  register char c;
+  int invert;
+  int seen;
+  char c2;
+
+  while( (c = UpperToLower[*zPattern])!=0 ){
+    switch( c ){
+      case '%':
+        while( zPattern[1]=='%' ) zPattern++;
+        if( zPattern[1]==0 ) return 1;
+        c = UpperToLower[0xff & zPattern[1]];
+        if( c=='_' ){
+          while( *zString && sqliteLikeCompare(&zPattern[1],zString)==0 ){
+            zString++;
+          }
+          return *zString!=0;
+        }else{
+          while( (c2 = UpperToLower[*zString])!=0 ){
+            while( c2 != 0 && c2 != c ){ c2 = UpperToLower[*++zString]; }
+            if( sqliteLikeCompare(&zPattern[1],zString) ) return 1;
+            zString++;
+          }
+          return 0;
+        }
+      case '_':
+        if( *zString==0 ) return 0;
+        break;
+      default:
+        if( c != UpperToLower[*zString] ) return 0;
+        break;
+    }
+    zPattern++;
+    zString++;
+  }
+  return *zString==0;
+}
diff --git a/src/vdbe.c b/src/vdbe.c
index de87f96..62ed2e9 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.2 2000/05/30 16:27:04 drh Exp $
+** $Id: vdbe.c,v 1.3 2000/05/31 02:27:50 drh Exp $
 */
 #include "sqliteInt.h"
 
@@ -391,23 +391,24 @@
 ** this array, then copy and paste it into this file, if you want.
 */
 static char *zOpName[] = { 0,
-  "Open",           "Close",          "Destroy",        "Fetch",
-  "New",            "Put",            "Delete",         "Field",
-  "Key",            "Rewind",         "Next",           "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",            "Eq",             "Ne",             "Lt",
-  "Le",             "Gt",             "Ge",             "IsNull",
-  "NotNull",        "Negative",       "And",            "Or",
-  "Not",            "Concat",         "Noop",         
+  "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",         
 };
 
 /*
@@ -997,6 +998,67 @@
         break;
       }
 
+      /* Opcode: Like P1 P2 *
+      **
+      ** Pop the top two elements from the stack.  The top-most is a
+      ** "like" pattern -- the right operand of the SQL "LIKE" operator.
+      ** The lower element is the string to compare against the like
+      ** pattern.  Jump to P2 if the two compare, and fall through without
+      ** jumping if they do not.  The '%' in the top-most element matches
+      ** any sequence of zero or more characters in the lower element.  The
+      ** '_' character in the topmost matches any single character of the
+      ** lower element.  Case is ignored for this comparison.
+      **
+      ** If P1 is not zero, the sense of the test is inverted and we
+      ** have a "NOT LIKE" operator.  The jump is made if the two values
+      ** are different.
+      */
+      case OP_Like: {
+        int tos = p->tos;
+        int nos = tos - 1;
+        int c;
+        if( nos<0 ) goto not_enough_stack;
+        Stringify(p, tos);
+        Stringify(p, nos);
+        c = sqliteLikeCompare(p->zStack[tos], p->zStack[nos]);
+        PopStack(p, 2);
+        if( pOp->p1 ) c = !c;
+        if( c ) pc = pOp->p2-1;
+        break;
+      }
+
+      /* Opcode: Glob P1 P2 *
+      **
+      ** Pop the top two elements from the stack.  The top-most is a
+      ** "glob" pattern.  The lower element is the string to compare 
+      ** against the glob pattern.
+      **
+      ** Jump to P2 if the two compare, and fall through without
+      ** jumping if they do not.  The '*' in the top-most element matches
+      ** any sequence of zero or more characters in the lower element.  The
+      ** '?' character in the topmost matches any single character of the
+      ** lower element.  [...] matches a range of characters.  [^...]
+      ** matches any character not in the range.  Case is significant
+      ** for globs.
+      **
+      ** If P1 is not zero, the sense of the test is inverted and we
+      ** have a "NOT GLOB" operator.  The jump is made if the two values
+      ** are different.
+      */
+      case OP_Glob: {
+        int tos = p->tos;
+        int nos = tos - 1;
+        int c;
+        if( nos<0 ) goto not_enough_stack;
+        Stringify(p, tos);
+        Stringify(p, nos);
+        c = sqliteGlobCompare(p->zStack[tos], p->zStack[nos]);
+        PopStack(p, 2);
+        if( pOp->p1 ) c = !c;
+        if( c ) pc = pOp->p2-1;
+        break;
+      }
+
       /* Opcode: And * * *
       **
       ** Pop two values off the stack.  Take the logical AND of the
@@ -1581,6 +1643,15 @@
         break;
       }
 
+      /* Opcode: Reorganize * * P3
+      **
+      ** Compress, optimize, and tidy up the GDBM file named by P3.
+      */
+      case OP_Reorganize: {
+        sqliteDbbeReorganizeTable(p->pBe, pOp->p3);
+        break;
+      }
+
       /* Opcode: ListOpen P1 * *
       **
       ** Open a file used for temporary storage of index numbers.  P1
diff --git a/src/vdbe.h b/src/vdbe.h
index d5dca04..3672cfe 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.2 2000/05/30 16:27:05 drh Exp $
+** $Id: vdbe.h,v 1.3 2000/05/31 02:27:50 drh Exp $
 */
 #ifndef _SQLITE_VDBE_H_
 #define _SQLITE_VDBE_H_
@@ -73,81 +73,86 @@
 */
 #define OP_Open                1
 #define OP_Close               2
-#define OP_Destroy             3
-#define OP_Fetch               4
-#define OP_New                 5
-#define OP_Put                 6
-#define OP_Delete              7
-#define OP_Field               8
-#define OP_Key                 9
-#define OP_Rewind             10
-#define OP_Next               11
-#define OP_ResetIdx           12
-#define OP_NextIdx            13
-#define OP_PutIdx             14
-#define OP_DeleteIdx          15
+#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_ListOpen           16
-#define OP_ListWrite          17
-#define OP_ListRewind         18
-#define OP_ListRead           19
-#define OP_ListClose          20
+#define OP_Destroy            11
+#define OP_Reorganize         12
 
-#define OP_SortOpen           21
-#define OP_SortPut            22
-#define OP_SortMakeRec        23
-#define OP_SortMakeKey        24
-#define OP_Sort               25
-#define OP_SortNext           26
-#define OP_SortKey            27
-#define OP_SortCallback       28
-#define OP_SortClose          29
+#define OP_ResetIdx           13
+#define OP_NextIdx            14
+#define OP_PutIdx             15
+#define OP_DeleteIdx          16
 
-#define OP_FileOpen           30
-#define OP_FileRead           31
-#define OP_FileField          32
-#define OP_FileClose          33
+#define OP_ListOpen           17
+#define OP_ListWrite          18
+#define OP_ListRewind         19
+#define OP_ListRead           20
+#define OP_ListClose          21
 
-#define OP_MakeRecord         34
-#define OP_MakeKey            35
+#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_Goto               36
-#define OP_If                 37
-#define OP_Halt               38
+#define OP_FileOpen           31
+#define OP_FileRead           32
+#define OP_FileField          33
+#define OP_FileClose          34
 
-#define OP_ColumnCount        39
-#define OP_ColumnName         40
-#define OP_Callback           41
+#define OP_MakeRecord         35
+#define OP_MakeKey            36
 
-#define OP_Integer            42
-#define OP_String             43
-#define OP_Pop                44
-#define OP_Dup                45
-#define OP_Pull               46
+#define OP_Goto               37
+#define OP_If                 38
+#define OP_Halt               39
 
-#define OP_Add                47
-#define OP_AddImm             48
-#define OP_Subtract           49
-#define OP_Multiply           50
-#define OP_Divide             51
-#define OP_Min                52
-#define OP_Max                53
-#define OP_Eq                 54
-#define OP_Ne                 55
-#define OP_Lt                 56
-#define OP_Le                 57
-#define OP_Gt                 58
-#define OP_Ge                 59
-#define OP_IsNull             60
-#define OP_NotNull            61
-#define OP_Negative           62
-#define OP_And                63
-#define OP_Or                 64
-#define OP_Not                65
-#define OP_Concat             66
-#define OP_Noop               67
+#define OP_ColumnCount        40
+#define OP_ColumnName         41
+#define OP_Callback           42
 
-#define OP_MAX                67
+#define OP_Integer            43
+#define OP_String             44
+#define OP_Pop                45
+#define OP_Dup                46
+#define OP_Pull               47
+
+#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_MAX                70
 
 /*
 ** Prototypes for the VDBE interface.  See comments on the implementation
diff --git a/src/where.c b/src/where.c
index 4efabff..217bfd3 100644
--- a/src/where.c
+++ b/src/where.c
@@ -25,7 +25,7 @@
 ** the WHERE clause of SQL statements.  Also found here are subroutines
 ** to generate VDBE code to evaluate expressions.
 **
-** $Id: where.c,v 1.3 2000/05/30 20:17:49 drh Exp $
+** $Id: where.c,v 1.4 2000/05/31 02:27:50 drh Exp $
 */
 #include "sqliteInt.h"
 
@@ -368,12 +368,14 @@
     case TK_SLASH:    op = OP_Divide;   break;
     case TK_AND:      op = OP_And;      break;
     case TK_OR:       op = OP_Or;       break;
-    case TK_LT:       op = OP_Ge;       break;
-    case TK_LE:       op = OP_Gt;       break;
-    case TK_GT:       op = OP_Le;       break;
-    case TK_GE:       op = OP_Lt;       break;
-    case TK_NE:       op = OP_Eq;       break;
-    case TK_EQ:       op = OP_Ne;       break;
+    case TK_LT:       op = OP_Lt;       break;
+    case TK_LE:       op = OP_Le;       break;
+    case TK_GT:       op = OP_Gt;       break;
+    case TK_GE:       op = OP_Ge;       break;
+    case TK_NE:       op = OP_Ne;       break;
+    case TK_EQ:       op = OP_Eq;       break;
+    case TK_LIKE:     op = OP_Like;     break;
+    case TK_GLOB:     op = OP_Glob;     break;
     case TK_ISNULL:   op = OP_IsNull;   break;
     case TK_NOTNULL:  op = OP_NotNull;  break;
     case TK_NOT:      op = OP_Not;      break;
@@ -421,14 +423,16 @@
     case TK_GT:
     case TK_GE:
     case TK_NE:
-    case TK_EQ: {
+    case TK_EQ: 
+    case TK_LIKE: 
+    case TK_GLOB: {
       int dest;
-      sqliteVdbeAddOp(v, OP_Integer, 0, 0, 0, 0);
+      sqliteVdbeAddOp(v, OP_Integer, 1, 0, 0, 0);
       sqliteExprCode(pParse, pExpr->pLeft);
       sqliteExprCode(pParse, pExpr->pRight);
       dest = sqliteVdbeCurrentAddr(v) + 2;
       sqliteVdbeAddOp(v, op, 0, dest, 0, 0);
-      sqliteVdbeAddOp(v, OP_AddImm, 1, 0, 0, 0);
+      sqliteVdbeAddOp(v, OP_AddImm, -1, 0, 0, 0);
       break;
     }
     case TK_NOT:
@@ -466,6 +470,8 @@
     case TK_GE:       op = OP_Ge;       break;
     case TK_NE:       op = OP_Ne;       break;
     case TK_EQ:       op = OP_Eq;       break;
+    case TK_LIKE:     op = OP_Like;     break;
+    case TK_GLOB:     op = OP_Glob;     break;
     case TK_ISNULL:   op = OP_IsNull;   break;
     case TK_NOTNULL:  op = OP_NotNull;  break;
     default:  break;
@@ -483,12 +489,18 @@
       sqliteExprIfTrue(pParse, pExpr->pRight, dest);
       break;
     }
+    case TK_NOT: {
+      sqliteExprIfFalse(pParse, pExpr->pLeft, dest);
+      break;
+    }
     case TK_LT:
     case TK_LE:
     case TK_GT:
     case TK_GE:
     case TK_NE:
-    case TK_EQ: {
+    case TK_EQ:
+    case TK_LIKE:
+    case TK_GLOB: {
       sqliteExprCode(pParse, pExpr->pLeft);
       sqliteExprCode(pParse, pExpr->pRight);
       sqliteVdbeAddOp(v, op, 0, dest, 0, 0);
@@ -523,6 +535,8 @@
     case TK_GE:       op = OP_Lt;       break;
     case TK_NE:       op = OP_Eq;       break;
     case TK_EQ:       op = OP_Ne;       break;
+    case TK_LIKE:     op = OP_Like;     break;
+    case TK_GLOB:     op = OP_Glob;     break;
     case TK_ISNULL:   op = OP_NotNull;  break;
     case TK_NOTNULL:  op = OP_IsNull;   break;
     default:  break;
@@ -540,6 +554,10 @@
       sqliteVdbeResolveLabel(v, d2);
       break;
     }
+    case TK_NOT: {
+      sqliteExprIfTrue(pParse, pExpr->pLeft, dest);
+      break;
+    }
     case TK_LT:
     case TK_LE:
     case TK_GT:
@@ -551,6 +569,13 @@
       sqliteVdbeAddOp(v, op, 0, dest, 0, 0);
       break;
     }
+    case TK_LIKE:
+    case TK_GLOB: {
+      sqliteExprCode(pParse, pExpr->pLeft);
+      sqliteExprCode(pParse, pExpr->pRight);
+      sqliteVdbeAddOp(v, op, 1, dest, 0, 0);
+      break;
+    }
     case TK_ISNULL:
     case TK_NOTNULL: {
       sqliteExprCode(pParse, pExpr->pLeft);