]> git.ipfire.org Git - thirdparty/sqlite.git/commitdiff
Optimize simple min() and max() queries. (CVS 382)
authordrh <drh@noemail.net>
Tue, 19 Feb 2002 15:00:07 +0000 (15:00 +0000)
committerdrh <drh@noemail.net>
Tue, 19 Feb 2002 15:00:07 +0000 (15:00 +0000)
FossilOrigin-Name: cc5abfe392bdb8c3ed00e0610bc2b41851bfc9d7

manifest
manifest.uuid
src/btree.c
src/btree.h
src/select.c
src/vdbe.c
src/vdbe.h

index 2d40caee726034023232a5dee62c3e6e6d2d9e0a..f80e6ba02c800b9118c56932768fdea72d727c87 100644 (file)
--- a/manifest
+++ b/manifest
@@ -1,5 +1,5 @@
-C Change\sthe\sname\sof\sthe\ssanity_check\sPRAGMA\sto\s"integrity_check"\sand\smake\nit\savailable\son\sall\scompiles.\s(CVS\s381)
-D 2002-02-19T13:39:21
+C Optimize\ssimple\smin()\sand\smax()\squeries.\s(CVS\s382)
+D 2002-02-19T15:00:07
 F Makefile.in 9fa4277413bf1d9cf91365f07d4108d7d87ed2af
 F Makefile.template 3372d45f8853afdb70bd30cc6fb50a3cd9069834
 F README a4c0ba11354ef6ba0776b400d057c59da47a4cc0
@@ -19,8 +19,8 @@ F ltmain.sh e9ed72eb1d690f447c13945eaf69e28af531eda1
 F publish.sh 5b59f4aff037aafa0e4a3b6fa599495dbd73f360
 F sqlite.1 2e2bb0529ef468ade9e4322bd609d0695fb9ded9
 F src/TODO af7f3cab0228e34149cf98e073aa83d45878e7e6
-F src/btree.c 715209d8fff117d2e3024134c5b626f440fb0395
-F src/btree.h 9fd16f33b9f2efe733fecff7ca000d2950a74d9f
+F src/btree.c 8256ea416850fbd2af0ae50779b1a4b604de13c2
+F src/btree.h 8abeabfe6e0b1a990b64fa457592a6482f6674f3
 F src/build.c 088acf87a92b00edda1206ccafac3518660b1b3b
 F src/delete.c f8ad71be53cf18656b6573de65395852fe817f0c
 F src/expr.c 7aff65ea0732b07d36925087ad611019103ad69a
@@ -36,7 +36,7 @@ F src/pager.h b28f004e2f5541dc60cc32db01bf80cf4d056283
 F src/parse.y b82278917959eefd05bd08c90e07a4fa5917ea51
 F src/printf.c 300a90554345751f26e1fc0c0333b90a66110a1d
 F src/random.c 19e8e00fe0df32a742f115773f57651be327cabe
-F src/select.c d2bbaf4cba97b4c40503d0dc47e8b729e7088e0b
+F src/select.c 6dadbd3ba1e86334c9af321b48f6e3df1992f602
 F src/shell.c c102dfe388c7618a668c944ff157c49cb48f28e3
 F src/shell.tcl 27ecbd63dd88396ad16d81ab44f73e6c0ea9d20e
 F src/sqlite.h.in f57074c84a2c112a5093ba7a9d9636aa9cacc87c
@@ -50,8 +50,8 @@ F src/threadtest.c 81f0598e0f031c1bd506af337fdc1b7e8dff263f
 F src/tokenize.c 9e98f94469694a763992860596137e78dbae0cc0
 F src/update.c 95459f94a061860bf8e5716b3426a5ba85c79103
 F src/util.c f31f3d6198a0d1296a16f5a6ceec423a932cbbf6
-F src/vdbe.c 3c788341b581c9381ab2ecd4868041ed57ff27ad
-F src/vdbe.h 20a662572485329cb0a6b648bf7c8cb6baec401d
+F src/vdbe.c 2b03a7d39f05321e5570373cb2f3734c3cf86b70
+F src/vdbe.h b4d35e159fbb80a74728b4a96e5b789fffce6f57
 F src/where.c f79bc3179379b46b131a67ab10713779368dceee
 F test/all.test 7a8a8a7a579ed2bb4d8976d55402f21eacd58049
 F test/bigrow.test 8ab252dba108f12ad64e337b0f2ff31a807ac578
@@ -124,7 +124,7 @@ F www/speed.tcl 83457b2bf6bb430900bd48ca3dd98264d9a916a5
 F www/sqlite.tcl 8b5884354cb615049aed83039f8dfe1552a44279
 F www/tclsqlite.tcl 829b393d1ab187fd7a5e978631b3429318885c49
 F www/vdbe.tcl 2013852c27a02a091d39a766bc87cff329f21218
-P 72c5a92aa6e3ae712af152cff8d1dc5b997b538e
-R 50515068ff6057399f03cf57fa91cbb8
+P c6e9048e66c8d8e2d5f6c62aa724eef3e9d9f572
+R 41f437c3e65a91e5a2a52b6609362036
 U drh
-Z 763c5eb79aff590e79bd0703f9429a9c
+Z ee034e966e6d17b9655294654ff0765f
index f1345d0d14c9bd5103ceac7aef6293a92fe73b95..a91c486c4e6db1e1916c0c0b489c33c9a655d9a3 100644 (file)
@@ -1 +1 @@
-c6e9048e66c8d8e2d5f6c62aa724eef3e9d9f572
\ No newline at end of file
+cc5abfe392bdb8c3ed00e0610bc2b41851bfc9d7
\ No newline at end of file
index 8831ad478906f379ae14481febdec4e6f62210e6..b47d838305431fdfddfccff4088b8eba504b44e6 100644 (file)
@@ -9,7 +9,7 @@
 **    May you share freely, never taking more than you give.
 **
 *************************************************************************
-** $Id: btree.c,v 1.53 2002/02/19 13:39:22 drh Exp $
+** $Id: btree.c,v 1.54 2002/02/19 15:00:07 drh Exp $
 **
 ** This file implements a external (disk-based) database using BTrees.
 ** For a detailed discussion of BTrees, refer to
@@ -1325,6 +1325,30 @@ int sqliteBtreeFirst(BtCursor *pCur, int *pRes){
   return rc;
 }
 
+/* Move the cursor to the last entry in the table.  Return SQLITE_OK
+** on success.  Set *pRes to 0 if the cursor actually points to something
+** or set *pRes to 1 if the table is empty and there is no first element.
+*/
+int sqliteBtreeLast(BtCursor *pCur, int *pRes){
+  int rc;
+  Pgno pgno;
+  if( pCur->pPage==0 ) return SQLITE_ABORT;
+  rc = moveToRoot(pCur);
+  if( rc ) return rc;
+  if( pCur->pPage->nCell==0 ){
+    *pRes = 1;
+    return SQLITE_OK;
+  }
+  *pRes = 0;
+  while( (pgno = pCur->pPage->u.hdr.rightChild)!=0 ){
+    rc = moveToChild(pCur, pgno);
+    if( rc ) return rc;
+  }
+  pCur->idx = pCur->pPage->nCell-1;
+  pCur->bSkipNext = 0;
+  return rc;
+}
+
 /* Move the cursor so that it points to an entry near pKey.
 ** Return a success code.
 **
index 2c63f5d54478294ef4cae7feb2f27d7a9e6b8a7b..351aa28ecbcbaecee2b1651778411eeedc5685a9 100644 (file)
@@ -13,7 +13,7 @@
 ** subsystem.  See comments in the source code for a detailed description
 ** of what each interface routine does.
 **
-** @(#) $Id: btree.h,v 1.23 2002/02/19 13:39:22 drh Exp $
+** @(#) $Id: btree.h,v 1.24 2002/02/19 15:00:08 drh Exp $
 */
 #ifndef _BTREE_H_
 #define _BTREE_H_
@@ -43,6 +43,7 @@ int sqliteBtreeDelete(BtCursor*);
 int sqliteBtreeInsert(BtCursor*, const void *pKey, int nKey,
                                  const void *pData, int nData);
 int sqliteBtreeFirst(BtCursor*, int *pRes);
+int sqliteBtreeLast(BtCursor*, int *pRes);
 int sqliteBtreeNext(BtCursor*, int *pRes);
 int sqliteBtreeKeySize(BtCursor*, int *pSize);
 int sqliteBtreeKey(BtCursor*, int offset, int amt, char *zBuf);
index 866c63f9b576bb93eac3a4dd53f9101edd62a446..036c35ec5482ff960b5aa8ade3b5546f8442a219 100644 (file)
@@ -12,7 +12,7 @@
 ** This file contains C code routines that are called by the parser
 ** to handle SELECT statements in SQLite.
 **
-** $Id: select.c,v 1.61 2002/02/18 03:21:46 drh Exp $
+** $Id: select.c,v 1.62 2002/02/19 15:00:08 drh Exp $
 */
 #include "sqliteInt.h"
 
@@ -710,6 +710,102 @@ static int multiSelect(Parse *pParse, Select *p, int eDest, int iParm){
   return 0;
 }
 
+/*
+** Analyze the SELECT statement passed in as an argument to see if it
+** is a simple min() or max() query.  If it is and this query can be
+** satisfied using a single seek to the beginning or end of an index,
+** then generate the code for this SELECT return 1.  If this is not a 
+** simple min() or max() query, then return 0;
+**
+** A simply min() or max() query looks like this:
+**
+**    SELECT min(a) FROM table;
+**    SELECT max(a) FROM table;
+**
+** The query may have only a single table in its FROM argument.  There
+** can be no GROUP BY or HAVING or WHERE clauses.  The result set must
+** be the min() or max() of a single column of the table.  The column
+** in the min() or max() function must be indexed.
+**
+** The parameters to this routine are the same as for sqliteSelect().
+** See the header comment on that routine for additional information.
+*/
+static int simpleMinMaxQuery(Parse *pParse, Select *p, int eDest, int iParm){
+  Expr *pExpr;
+  int iCol;
+  Table *pTab;
+  Index *pIdx;
+  int base;
+  Vdbe *v;
+  int openOp;
+  int seekOp;
+  int cont;
+  ExprList eList;
+  struct ExprList_item eListItem;
+
+  /* Check to see if this query is a simple min() or max() query.  Return
+  ** zero if it is  not.
+  */
+  if( p->pGroupBy || p->pHaving || p->pWhere ) return 0;
+  if( p->pSrc->nId!=1 ) return 0;
+  if( p->pEList->nExpr!=1 ) return 0;
+  pExpr = p->pEList->a[0].pExpr;
+  if( pExpr->op!=TK_AGG_FUNCTION ) return 0;
+  if( pExpr->pList==0 || pExpr->pList->nExpr!=1 ) return 0;
+  if( pExpr->iColumn!=FN_Min && pExpr->iColumn!=FN_Max ) return 0;
+  seekOp = pExpr->iColumn==FN_Min ? OP_Rewind : OP_Last;
+  pExpr = pExpr->pList->a[0].pExpr;
+  if( pExpr->op!=TK_COLUMN ) return 0;
+  iCol = pExpr->iColumn;
+  pTab = p->pSrc->a[0].pTab;
+
+  /* If we get to here, it means the query is of the correct form.
+  ** Check to make sure we have an index.
+  */
+  if( iCol<0 ){
+    pIdx = 0;
+  }else{
+    for(pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext){
+      assert( pIdx->nColumn>=1 );
+      if( pIdx->aiColumn[0]==iCol ) break;
+    }
+    if( pIdx==0 ) return 0;
+  }
+
+  /* Identify column names if we will be using in the callback.  This
+  ** step is skipped if the output is going to a table or a memory cell.
+  */
+  v = sqliteGetVdbe(pParse);
+  if( v==0 ) return 0;
+  if( eDest==SRT_Callback ){
+    generateColumnNames(pParse, p->pSrc, p->pEList);
+  }
+
+  /* Begin generating code
+  */
+  base = pParse->nTab;
+  eList.nExpr = 1;
+  memset(&eListItem, 0, sizeof(eListItem));
+  eList.a = &eListItem;
+  eList.a[0].pExpr = pExpr;
+  openOp = pTab->isTemp ? OP_OpenAux : OP_Open;
+  sqliteVdbeAddOp(v, openOp, base, pTab->tnum);
+  if( pIdx==0 ){
+    sqliteVdbeAddOp(v, seekOp, base, 0);
+  }else{
+    sqliteVdbeAddOp(v, openOp, base+1, pIdx->tnum);
+    sqliteVdbeAddOp(v, seekOp, base+1, 0);
+    sqliteVdbeAddOp(v, OP_IdxRecno, base+1, 0);
+    sqliteVdbeAddOp(v, OP_Close, base+1, 0);
+    sqliteVdbeAddOp(v, OP_MoveTo, base, 0);
+  }
+  cont = sqliteVdbeMakeLabel(v);
+  selectInnerLoop(pParse, &eList, base, 1, 0, -1, eDest, iParm, cont, cont);
+  sqliteVdbeResolveLabel(v, cont);
+  sqliteVdbeAddOp(v, OP_Close, base, 0);
+  return 1;
+}
+
 /*
 ** Generate code for the given SELECT statement.
 **
@@ -911,6 +1007,13 @@ int sqliteSelect(
     }
   }
 
+  /* Check for the special case of a min() or max() function by itself
+  ** in the result set.
+  */
+  if( simpleMinMaxQuery(pParse, p, eDest, iParm) ){
+    goto select_end;
+  }
+
   /* Begin generating code.
   */
   v = sqliteGetVdbe(pParse);
index a356a15180a5a1aed7cb6125e4fc82069dfeab8a..788c5a9d22c4d862ef9a068364b4ec200cf6f974 100644 (file)
@@ -30,7 +30,7 @@
 ** But other routines are also provided to help in building up
 ** a program instruction by instruction.
 **
-** $Id: vdbe.c,v 1.118 2002/02/19 13:39:23 drh Exp $
+** $Id: vdbe.c,v 1.119 2002/02/19 15:00:08 drh Exp $
 */
 #include "sqliteInt.h"
 #include <ctype.h>
@@ -866,29 +866,30 @@ static char *zOpName[] = { 0,
   "Close",             "MoveTo",            "NewRecno",          "PutIntKey",
   "PutStrKey",         "Distinct",          "Found",             "NotFound",
   "IsUnique",          "NotExists",         "Delete",            "Column",
-  "KeyAsData",         "Recno",             "FullKey",           "Rewind",
-  "Next",              "Destroy",           "Clear",             "CreateIndex",
-  "CreateTable",       "IntegrityCk",       "IdxPut",            "IdxDelete",
-  "IdxRecno",          "IdxGT",             "IdxGE",             "MemLoad",
-  "MemStore",          "ListWrite",         "ListRewind",        "ListRead",
-  "ListReset",         "SortPut",           "SortMakeRec",       "SortMakeKey",
-  "Sort",              "SortNext",          "SortCallback",      "SortReset",
-  "FileOpen",          "FileRead",          "FileColumn",        "AggReset",
-  "AggFocus",          "AggIncr",           "AggNext",           "AggSet",
-  "AggGet",            "SetInsert",         "SetFound",          "SetNotFound",
-  "MakeRecord",        "MakeKey",           "MakeIdxKey",        "IncrKey",
-  "Goto",              "If",                "Halt",              "ColumnCount",
-  "ColumnName",        "Callback",          "NullCallback",      "Integer",
-  "String",            "Pop",               "Dup",               "Pull",
-  "Push",              "MustBeInt",         "Add",               "AddImm",
-  "Subtract",          "Multiply",          "Divide",            "Remainder",
-  "BitAnd",            "BitOr",             "BitNot",            "ShiftLeft",
-  "ShiftRight",        "AbsValue",          "Precision",         "Min",
-  "Max",               "Like",              "Glob",              "Eq",
-  "Ne",                "Lt",                "Le",                "Gt",
-  "Ge",                "IsNull",            "NotNull",           "Negative",
-  "And",               "Or",                "Not",               "Concat",
-  "Noop",              "Strlen",            "Substr",            "Limit",
+  "KeyAsData",         "Recno",             "FullKey",           "Last",
+  "Rewind",            "Next",              "Destroy",           "Clear",
+  "CreateIndex",       "CreateTable",       "IntegrityCk",       "IdxPut",
+  "IdxDelete",         "IdxRecno",          "IdxGT",             "IdxGE",
+  "MemLoad",           "MemStore",          "ListWrite",         "ListRewind",
+  "ListRead",          "ListReset",         "SortPut",           "SortMakeRec",
+  "SortMakeKey",       "Sort",              "SortNext",          "SortCallback",
+  "SortReset",         "FileOpen",          "FileRead",          "FileColumn",
+  "AggReset",          "AggFocus",          "AggIncr",           "AggNext",
+  "AggSet",            "AggGet",            "SetInsert",         "SetFound",
+  "SetNotFound",       "MakeRecord",        "MakeKey",           "MakeIdxKey",
+  "IncrKey",           "Goto",              "If",                "Halt",
+  "ColumnCount",       "ColumnName",        "Callback",          "NullCallback",
+  "Integer",           "String",            "Pop",               "Dup",
+  "Pull",              "Push",              "MustBeInt",         "Add",
+  "AddImm",            "Subtract",          "Multiply",          "Divide",
+  "Remainder",         "BitAnd",            "BitOr",             "BitNot",
+  "ShiftLeft",         "ShiftRight",        "AbsValue",          "Precision",
+  "Min",               "Max",               "Like",              "Glob",
+  "Eq",                "Ne",                "Lt",                "Le",
+  "Gt",                "Ge",                "IsNull",            "NotNull",
+  "Negative",          "And",               "Or",                "Not",
+  "Concat",            "Noop",              "Strlen",            "Substr",
+  "Limit",           
 };
 
 /*
@@ -3096,7 +3097,11 @@ case OP_Column: {
     /* Figure out how many bytes in the column data and where the column
     ** data begins.
     */
-    if( payloadSize<256 ){
+    if( payloadSize==0 ){
+      aStack[tos].flags = STK_Null;
+      p->tos = tos;
+      break;
+    }else if( payloadSize<256 ){
       idxWidth = 1;
     }else if( payloadSize<65536 ){
       idxWidth = 2;
@@ -3216,6 +3221,29 @@ case OP_FullKey: {
   break;
 }
 
+/* Opcode: Last P1 P2 *
+**
+** The next use of the Recno or Column or Next instruction for P1 
+** will refer to the last entry in the database table or index.
+** If the table or index is empty and P2>0, then jump immediately to P2.
+** If P2 is 0 or if the table or index is not empty, fall through
+** to the following instruction.
+*/
+case OP_Last: {
+  int i = pOp->p1;
+  BtCursor *pCrsr;
+
+  if( VERIFY( i>=0 && i<p->nCursor && ) (pCrsr = p->aCsr[i].pCursor)!=0 ){
+    int res;
+    sqliteBtreeLast(pCrsr, &res);
+    p->aCsr[i].atFirst = res==0;
+    if( res && pOp->p2>0 ){
+      pc = pOp->p2 - 1;
+    }
+  }
+  break;
+}
+
 /* Opcode: Rewind P1 P2 *
 **
 ** The next use of the Recno or Column or Next instruction for P1 
index 7936b13e406505554359a03b310393220c0307b4..2f10d591f8660f871469c608fd1df0835ddacbcf 100644 (file)
@@ -15,7 +15,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.42 2002/02/19 13:39:23 drh Exp $
+** $Id: vdbe.h,v 1.43 2002/02/19 15:00:08 drh Exp $
 */
 #ifndef _SQLITE_VDBE_H_
 #define _SQLITE_VDBE_H_
@@ -97,112 +97,113 @@ typedef struct VdbeOp VdbeOp;
 #define OP_KeyAsData          25
 #define OP_Recno              26
 #define OP_FullKey            27
-#define OP_Rewind             28
-#define OP_Next               29
-
-#define OP_Destroy            30
-#define OP_Clear              31
-#define OP_CreateIndex        32
-#define OP_CreateTable        33
-#define OP_IntegrityCk        34
-
-#define OP_IdxPut             35
-#define OP_IdxDelete          36
-#define OP_IdxRecno           37
-#define OP_IdxGT              38
-#define OP_IdxGE              39
-
-#define OP_MemLoad            40
-#define OP_MemStore           41
-
-#define OP_ListWrite          42
-#define OP_ListRewind         43
-#define OP_ListRead           44
-#define OP_ListReset          45
-
-#define OP_SortPut            46
-#define OP_SortMakeRec        47
-#define OP_SortMakeKey        48
-#define OP_Sort               49
-#define OP_SortNext           50
-#define OP_SortCallback       51
-#define OP_SortReset          52
-
-#define OP_FileOpen           53
-#define OP_FileRead           54
-#define OP_FileColumn         55
-
-#define OP_AggReset           56
-#define OP_AggFocus           57
-#define OP_AggIncr            58
-#define OP_AggNext            59
-#define OP_AggSet             60
-#define OP_AggGet             61
-
-#define OP_SetInsert          62
-#define OP_SetFound           63
-#define OP_SetNotFound        64
-
-#define OP_MakeRecord         65
-#define OP_MakeKey            66
-#define OP_MakeIdxKey         67
-#define OP_IncrKey            68
-
-#define OP_Goto               69
-#define OP_If                 70
-#define OP_Halt               71
-
-#define OP_ColumnCount        72
-#define OP_ColumnName         73
-#define OP_Callback           74
-#define OP_NullCallback       75
-
-#define OP_Integer            76
-#define OP_String             77
-#define OP_Pop                78
-#define OP_Dup                79
-#define OP_Pull               80
-#define OP_Push               81
-#define OP_MustBeInt          82
-
-#define OP_Add                83
-#define OP_AddImm             84
-#define OP_Subtract           85
-#define OP_Multiply           86
-#define OP_Divide             87
-#define OP_Remainder          88
-#define OP_BitAnd             89
-#define OP_BitOr              90
-#define OP_BitNot             91
-#define OP_ShiftLeft          92
-#define OP_ShiftRight         93
-#define OP_AbsValue           94
-#define OP_Precision          95
-#define OP_Min                96
-#define OP_Max                97
-#define OP_Like               98
-#define OP_Glob               99
-#define OP_Eq                100
-#define OP_Ne                101
-#define OP_Lt                102
-#define OP_Le                103
-#define OP_Gt                104
-#define OP_Ge                105
-#define OP_IsNull            106
-#define OP_NotNull           107
-#define OP_Negative          108
-#define OP_And               109
-#define OP_Or                110
-#define OP_Not               111
-#define OP_Concat            112
-#define OP_Noop              113
-
-#define OP_Strlen            114
-#define OP_Substr            115
-
-#define OP_Limit             116
-
-#define OP_MAX               116
+#define OP_Last               28
+#define OP_Rewind             29
+#define OP_Next               30
+
+#define OP_Destroy            31
+#define OP_Clear              32
+#define OP_CreateIndex        33
+#define OP_CreateTable        34
+#define OP_IntegrityCk        35
+
+#define OP_IdxPut             36
+#define OP_IdxDelete          37
+#define OP_IdxRecno           38
+#define OP_IdxGT              39
+#define OP_IdxGE              40
+
+#define OP_MemLoad            41
+#define OP_MemStore           42
+
+#define OP_ListWrite          43
+#define OP_ListRewind         44
+#define OP_ListRead           45
+#define OP_ListReset          46
+
+#define OP_SortPut            47
+#define OP_SortMakeRec        48
+#define OP_SortMakeKey        49
+#define OP_Sort               50
+#define OP_SortNext           51
+#define OP_SortCallback       52
+#define OP_SortReset          53
+
+#define OP_FileOpen           54
+#define OP_FileRead           55
+#define OP_FileColumn         56
+
+#define OP_AggReset           57
+#define OP_AggFocus           58
+#define OP_AggIncr            59
+#define OP_AggNext            60
+#define OP_AggSet             61
+#define OP_AggGet             62
+
+#define OP_SetInsert          63
+#define OP_SetFound           64
+#define OP_SetNotFound        65
+
+#define OP_MakeRecord         66
+#define OP_MakeKey            67
+#define OP_MakeIdxKey         68
+#define OP_IncrKey            69
+
+#define OP_Goto               70
+#define OP_If                 71
+#define OP_Halt               72
+
+#define OP_ColumnCount        73
+#define OP_ColumnName         74
+#define OP_Callback           75
+#define OP_NullCallback       76
+
+#define OP_Integer            77
+#define OP_String             78
+#define OP_Pop                79
+#define OP_Dup                80
+#define OP_Pull               81
+#define OP_Push               82
+#define OP_MustBeInt          83
+
+#define OP_Add                84
+#define OP_AddImm             85
+#define OP_Subtract           86
+#define OP_Multiply           87
+#define OP_Divide             88
+#define OP_Remainder          89
+#define OP_BitAnd             90
+#define OP_BitOr              91
+#define OP_BitNot             92
+#define OP_ShiftLeft          93
+#define OP_ShiftRight         94
+#define OP_AbsValue           95
+#define OP_Precision          96
+#define OP_Min                97
+#define OP_Max                98
+#define OP_Like               99
+#define OP_Glob              100
+#define OP_Eq                101
+#define OP_Ne                102
+#define OP_Lt                103
+#define OP_Le                104
+#define OP_Gt                105
+#define OP_Ge                106
+#define OP_IsNull            107
+#define OP_NotNull           108
+#define OP_Negative          109
+#define OP_And               110
+#define OP_Or                111
+#define OP_Not               112
+#define OP_Concat            113
+#define OP_Noop              114
+
+#define OP_Strlen            115
+#define OP_Substr            116
+
+#define OP_Limit             117
+
+#define OP_MAX               117
 
 /*
 ** Prototypes for the VDBE interface.  See comments on the implementation