]> git.ipfire.org Git - thirdparty/sqlite.git/commitdiff
Incremental check-in of changes that will ultimately lead to a
authordrh <drh@noemail.net>
Fri, 5 Nov 2004 00:43:11 +0000 (00:43 +0000)
committerdrh <drh@noemail.net>
Fri, 5 Nov 2004 00:43:11 +0000 (00:43 +0000)
working autoincrement. (CVS 2056)

FossilOrigin-Name: 10c3d88305e404b9e4cc4eef0b8e5dc7864a5937

manifest
manifest.uuid
src/btree.c
src/build.c
src/delete.c
src/parse.y
src/sqliteInt.h
src/test3.c
src/vdbe.c

index c352386d74d12fec466e2007963ec46d9dce386c..d952a567396bb65d4a78f2c9ef8f1d67028cc7bd 100644 (file)
--- a/manifest
+++ b/manifest
@@ -1,5 +1,5 @@
-C All\stests\spass\swhen\sSQLITE_OMIT_INTEGRITY_CHECK\sis\sdefined.\s(CVS\s2055)
-D 2004-11-04T14:47:12
+C Incremental\scheck-in\sof\schanges\sthat\swill\sultimately\slead\sto\sa\nworking\sautoincrement.\s(CVS\s2056)
+D 2004-11-05T00:43:12
 F Makefile.in c4d2416860f472a1e3393714d0372074197565df
 F Makefile.linux-gcc a9e5a0d309fa7c38e7c14d3ecf7690879d3a5457
 F README a01693e454a00cc117967e3f9fdab2d4d52e9bc1
@@ -29,11 +29,11 @@ F sqlite3.def dbaeb20c153e1d366e8f421b55a573f5dfc00863
 F sqlite3.pc.in 985b9bf34192a549d7d370e0f0b6b34a4f61369a
 F src/attach.c e49d09dad9f5f9fb10b4b0c1be5a70ae4c45e689
 F src/auth.c 3b81f2a42f48a62c2c9c9b0eda31a157c681edea
-F src/btree.c a3e45d54eb1a81698f609693c22df382dfbf9151
+F src/btree.c 1b172b9d58608eeba1022e858fedce351c7090f5
 F src/btree.h 3166388fa58c5594d8064d38b43440d79da38fb6
-F src/build.c 1bf89a574108cbb03aed722f0ce97cf54469717d
+F src/build.c e72d6f76373998063cd85d592e7a402b8e24b721
 F src/date.c 34bdb0082db7ec2a83ef00063f7b44e61ee19dad
-F src/delete.c 52980e594e69e80374fb928fe611d5f75ca4e390
+F src/delete.c 832adc6fe1c07b7e28e1b4c1038d2b06f7397dd4
 F src/expr.c 3a43e508a3dc213703808bbcbb17633b88b57d17
 F src/func.c 600e506bccf7648df8ad03efb417560d0f7ad4c1
 F src/hash.c a97721a55440b7bea31ffe471bb2f6b4123cddd5
@@ -54,19 +54,19 @@ F src/os_win.c 9482dfc92f289b68205bb2c9315757c7e3946bfb
 F src/os_win.h 41a946bea10f61c158ce8645e7646b29d44f122b
 F src/pager.c a43e2a392be51966129e9afb18b81551c9f222b8
 F src/pager.h cbe4ba356d9dd3f30260f322b3dc77408164df14
-F src/parse.y 4a27450611ed2b8c359078e04daf93c50b1d22dd
+F src/parse.y e7f4e87a2ad14ccf5b8adcf1019fb8a355964579
 F src/pragma.c 44e192eb5928157bdb015926f858a7c6e3ef6c98
 F src/printf.c 7a92adc00b758cd5ce087dae80181a8bbdb70ed2
 F src/random.c eff68e3f257e05e81eae6c4d50a51eb88beb4ff3
 F src/select.c 156990c636102bb6b8de85e7ff3396a62568476b
 F src/shell.c 55adda3cf3c1cc2f6c1919aac17b2318f9c2a96f
 F src/sqlite.h.in 4f97b5907acfd2a5068cb0cec9d5178816734db7
-F src/sqliteInt.h 8b93c9d7b7343b9013ffb73cbd2cb6ea4f546c62
+F src/sqliteInt.h 126ec1947a91438a388112f3d282f625f7f73a0a
 F src/table.c 25b3ff2b39b7d87e8d4a5da0713d68dfc06cbee9
 F src/tclsqlite.c 0302e3f42f015d132d1291f3388c06e86c24a008
 F src/test1.c df1d1ca2c40cafefb9a29860f072c4d0fee1a7b5
 F src/test2.c b11fa244fff02190707dd0879987c37c75e61fc8
-F src/test3.c fdae1ed48add4b5df60f59a7c22e9d0b34265b55
+F src/test3.c de9edf178c02707cd37fd80b54e4c2ea77251cc0
 F src/test4.c 7c6b9fc33dd1f3f93c7f1ee6e5e6d016afa6c1df
 F src/test5.c b001fa7f1b9e2dc5c2331de62fc641b5ab2bd7a1
 F src/tokenize.c bf9de9689b3bb813d65784bf54472804bf9595e6
@@ -75,7 +75,7 @@ F src/update.c 7b17b281d600bf3e220b3c5718e0883442dee722
 F src/utf.c f4f83acd73389090e32d6589d307fc55d794c7ed
 F src/util.c 005fdf2d008f3429d081766ad6098fdd86d8d8e6
 F src/vacuum.c ecb4a2c6f1ac5cc9b394dc64d3bb14ca650c4f60
-F src/vdbe.c cf7eb35b5a649c11345c5f85ad7b1511253431cc
+F src/vdbe.c b324acdaff3b1f21cd369bbb0df30e1c3fa828f4
 F src/vdbe.h 067ca8d6750ba4f69a50284765e5883dee860181
 F src/vdbeInt.h 6017100adff362b8dfa37a69e3f1431f084bfa5b
 F src/vdbeapi.c 3965bf4678ae32c05f73550c1b5be3268f9f3006
@@ -252,7 +252,7 @@ F www/tclsqlite.tcl 560ecd6a916b320e59f2917317398f3d59b7cc25
 F www/vdbe.tcl 59288db1ac5c0616296b26dce071c36cb611dfe9
 F www/version3.tcl 092a01f5ef430d2c4acc0ae558d74c4bb89638a0
 F www/whentouse.tcl fdacb0ba2d39831e8a6240d05a490026ad4c4e4c
-P 1da361fae82d420be63c53f8e3efaccac24f348a
-R 91d65eb4622d90500501298966d2317c
+P 158a2d16a8630e3b5892120f6ea68f2b0dc47eb3
+R 7e671e2bcee0e6583c9f1a1b858bd032
 U drh
-Z aa82f4ff31655559fe92ab11f9dd6086
+Z 257ce5d18cf67227ffb08447220e1214
index 74c7122a9d61f9784be272b72c67b90f6c552a33..7a52f9be8797bf4f2bb986ec900c7783ecf69e93 100644 (file)
@@ -1 +1 @@
-158a2d16a8630e3b5892120f6ea68f2b0dc47eb3
\ No newline at end of file
+10c3d88305e404b9e4cc4eef0b8e5dc7864a5937
\ No newline at end of file
index a91b234f8fa2705e235ccbb4b6ad6a00c5b99d49..3b53e7dbc89f5a8677ce5df6b60e66e8166713b3 100644 (file)
@@ -9,7 +9,7 @@
 **    May you share freely, never taking more than you give.
 **
 *************************************************************************
-** $Id: btree.c,v 1.204 2004/11/04 14:30:05 danielk1977 Exp $
+** $Id: btree.c,v 1.205 2004/11/05 00:43:12 drh Exp $
 **
 ** This file implements a external (disk-based) database using BTrees.
 ** For a detailed discussion of BTrees, refer to
@@ -4412,6 +4412,18 @@ int sqlite3BtreeClearTable(Btree *pBt, int iTable){
 **
 ** This routine will fail with SQLITE_LOCKED if there are any open
 ** cursors on the table.
+**
+** If AUTOVACUUM is enabled and the page at iTable is not the last
+** root page in the database file, then the last root page 
+** in the database file is moved into the slot formerly occupied by
+** iTable and that last slot formerly occupied by the last root page
+** is added to the freelist instead of iTable.  In this say, all
+** root pages are kept at the beginning of the database file, which
+** is necessary for AUTOVACUUM to work right.  *piMoved is set to the 
+** page number that used to be the last root page in the file before
+** the move.  If no page gets moved, *piMoved is set to 0.
+** The last root page is recorded in meta[3] and the value of
+** meta[3] is updated by this procedure.
 */
 int sqlite3BtreeDropTable(Btree *pBt, int iTable, int *piMoved){
   int rc;
@@ -4434,7 +4446,7 @@ int sqlite3BtreeDropTable(Btree *pBt, int iTable, int *piMoved){
   rc = sqlite3BtreeClearTable(pBt, iTable);
   if( rc ) return rc;
 
-  if( piMoved ) *piMoved = 0;
+  *piMoved = 0;
 
   if( iTable>1 ){
 #ifdef SQLITE_OMIT_AUTOVACUUM
@@ -4521,8 +4533,9 @@ int sqlite3BtreeGetMeta(Btree *pBt, int idx, u32 *pMeta){
   *pMeta = get4byte(&pP1[36 + idx*4]);
   sqlite3pager_unref(pP1);
 
-  /* The current implementation is unable to handle writes to an autovacuumed
-  ** database.  So make such a database readonly. */
+  /* If autovacuumed is disabled in the implementation but we are
+  ** trying to access an autovacuumed database, then make the
+  ** database readonly. */
 #ifdef SQLITE_OMIT_AUTOVACUUM
   if( idx==4 && *pMeta>0 ) pBt->readOnly = 1;
 #endif
index b88f7de8eedfc31c3292ec670500d93c4db60563..23c2fbeefd79e3b7ceeea8a0876dc62b5d4faf2f 100644 (file)
@@ -23,7 +23,7 @@
 **     ROLLBACK
 **     PRAGMA
 **
-** $Id: build.c,v 1.260 2004/11/04 14:47:12 drh Exp $
+** $Id: build.c,v 1.261 2004/11/05 00:43:12 drh Exp $
 */
 #include "sqliteInt.h"
 #include <ctype.h>
@@ -54,6 +54,7 @@ void sqlite3FinishCoding(Parse *pParse){
   Vdbe *v;
 
   if( sqlite3_malloc_failed ) return;
+  if( pParse->nested ) return;
 
   /* Begin by generating some termination code at the end of the
   ** vdbe program
@@ -112,6 +113,35 @@ void sqlite3FinishCoding(Parse *pParse){
   pParse->cookieGoto = 0;
 }
 
+/*
+** Run the parser and code generator recursively in order to generate
+** code for the SQL statement given onto the end of the pParse context
+** currently under construction.  When the parser is run recursively
+** this way, the final OP_Halt is not appended and other initialization
+** and finalization steps are omitted because those are handling by the
+** outermost parser.
+**
+** Not everything is nestable.  This facility is designed to permit
+** INSERT, UPDATE, and DELETE operations against SQLITE_MASTER.  Use
+** care if you decide to try to use this routine for some other purpose.
+*/
+void sqlite3NestedParse(Parse *pParse, const char *zFormat, ...){
+  va_list ap;
+  char *zSql;
+  int rc;
+  Parse savedState;
+  if( pParse->nErr ) return;
+  assert( pParse->nested<10 );  /* Nesting should only be of limited depth */
+  va_start(ap, zFormat);
+  zSql = sqlite3VMPrintf(zFormat, ap);
+  va_end(ap);
+  pParse->nested++;
+  savedState = *pParse;
+  rc = sqlite3RunParser(pParse, zSql, 0);
+  sqliteFree(zSql);
+  pParse->nested--;
+}
+
 /*
 ** Locate the in-memory structure that describes a particular database
 ** table given the name of that table and (optionally) the name of the
@@ -836,7 +866,12 @@ void sqlite3AddDefaultValue(Parse *pParse, Token *pVal, int minusFlag){
 ** If the key is not an INTEGER PRIMARY KEY, then create a unique
 ** index for the key.  No index is created for INTEGER PRIMARY KEYs.
 */
-void sqlite3AddPrimaryKey(Parse *pParse, ExprList *pList, int onError){
+void sqlite3AddPrimaryKey(
+  Parse *pParse,    /* Parsing context */
+  ExprList *pList,  /* List of field names to be indexed */
+  int onError,      /* What to do with a uniqueness conflict */
+  int autoInc       /* True if the AUTOINCREMENT keyword is present */
+){
   Table *pTab = pParse->pNewTable;
   char *zType = 0;
   int iCol = -1, i;
@@ -867,6 +902,10 @@ void sqlite3AddPrimaryKey(Parse *pParse, ExprList *pList, int onError){
   if( zType && sqlite3StrICmp(zType, "INTEGER")==0 ){
     pTab->iPKey = iCol;
     pTab->keyConf = onError;
+    pTab->autoInc = autoInc;
+  }else if( autoInc ){
+    sqlite3ErrorMsg(pParse, "AUTOINCREMENT is only allowed on an "
+       "INTEGER PRIMARY KEY");
   }else{
     sqlite3CreateIndex(pParse, 0, 0, 0, pList, onError, 0, 0);
     pList = 0;
index a476c4fdeda9e7190e19f3f42c3e9dcc52438923..17448ae211607f6f471bb243eb6451e28b561f55 100644 (file)
@@ -12,7 +12,7 @@
 ** This file contains C code routines that are called by the parser
 ** to handle DELETE FROM statements.
 **
-** $Id: delete.c,v 1.85 2004/11/04 04:42:28 drh Exp $
+** $Id: delete.c,v 1.86 2004/11/05 00:43:12 drh Exp $
 */
 #include "sqliteInt.h"
 
@@ -38,7 +38,8 @@ Table *sqlite3SrcListLookup(Parse *pParse, SrcList *pSrc){
 ** writable return 0;
 */
 int sqlite3IsReadOnly(Parse *pParse, Table *pTab, int viewOk){
-  if( pTab->readOnly && (pParse->db->flags & SQLITE_WriteSchema)==0 ){
+  if( pTab->readOnly && (pParse->db->flags & SQLITE_WriteSchema)==0
+        && pParse->nested==0 ){
     sqlite3ErrorMsg(pParse, "table %s may not be modified", pTab->zName);
     return 1;
   }
index bce3dbfc5a94a6be91924253247df41736de86d3..90e4b1394f0265997d08e5861e28b6f901ff6af4 100644 (file)
@@ -14,7 +14,7 @@
 ** the parser.  Lemon will also generate a header file containing
 ** numeric codes for all of the tokens.
 **
-** @(#) $Id: parse.y,v 1.147 2004/11/03 13:59:05 drh Exp $
+** @(#) $Id: parse.y,v 1.148 2004/11/05 00:43:12 drh Exp $
 */
 %token_prefix TK_
 %token_type {Token}
@@ -213,7 +213,8 @@ carg ::= DEFAULT NULL.
 //
 ccons ::= NULL onconf.
 ccons ::= NOT NULL onconf(R).               {sqlite3AddNotNull(pParse, R);}
-ccons ::= PRIMARY KEY sortorder onconf(R).  {sqlite3AddPrimaryKey(pParse,0,R);}
+ccons ::= PRIMARY KEY sortorder onconf(R) autoinc(I).
+                                     {sqlite3AddPrimaryKey(pParse,0,R,I);}
 ccons ::= UNIQUE onconf(R).          {sqlite3CreateIndex(pParse,0,0,0,0,R,0,0);}
 ccons ::= CHECK LP expr RP onconf.
 ccons ::= REFERENCES nm(T) idxlist_opt(TA) refargs(R).
@@ -221,6 +222,11 @@ ccons ::= REFERENCES nm(T) idxlist_opt(TA) refargs(R).
 ccons ::= defer_subclause(D).   {sqlite3DeferForeignKey(pParse,D);}
 ccons ::= COLLATE id(C).  {sqlite3AddCollateType(pParse, C.z, C.n);}
 
+// The optional AUTOINCREMENT keyword
+%type autoinc {int}
+autoinc(X) ::= .         {X = 0;}
+autoinc(X) ::= AUTOINC.  {X = 1;}
+
 // The next group of rules parses the arguments to a REFERENCES clause
 // that determine if the referential integrity checking is deferred or
 // or immediate and which determine what action to take if a ref-integ
@@ -256,8 +262,8 @@ conslist ::= conslist COMMA tcons.
 conslist ::= conslist tcons.
 conslist ::= tcons.
 tcons ::= CONSTRAINT nm.
-tcons ::= PRIMARY KEY LP idxlist(X) RP onconf(R).
-                                             {sqlite3AddPrimaryKey(pParse,X,R);}
+tcons ::= PRIMARY KEY LP idxlist(X) RP onconf(R) autoinc(I).
+                                         {sqlite3AddPrimaryKey(pParse,X,R,I);}
 tcons ::= UNIQUE LP idxlist(X) RP onconf(R).
                                        {sqlite3CreateIndex(pParse,0,0,0,X,R,0,0);}
 tcons ::= CHECK expr onconf.
index 9244476c82a1a724e82dcbac88b7d16293824f4c..b8b924cd87291db86a4f4f454df0f22a88e33653 100644 (file)
@@ -11,7 +11,7 @@
 *************************************************************************
 ** Internal interface definitions for SQLite.
 **
-** @(#) $Id: sqliteInt.h,v 1.330 2004/11/04 14:30:05 danielk1977 Exp $
+** @(#) $Id: sqliteInt.h,v 1.331 2004/11/05 00:43:12 drh Exp $
 */
 #ifndef _SQLITEINT_H_
 #define _SQLITEINT_H_
@@ -573,6 +573,7 @@ struct Table {
   u8 isTransient;  /* True if automatically deleted when VDBE finishes */
   u8 hasPrimKey;   /* True if there exists a primary key */
   u8 keyConf;      /* What to do in case of uniqueness conflict on iPKey */
+  u8 autoInc;      /* True if the integer primary key is autoincrement */
   Trigger *pTrigger; /* List of SQL triggers on this table */
   FKey *pFKey;       /* Linked list of all foreign keys in this table */
   char *zColAff;     /* String defining the affinity of each column */
@@ -1010,6 +1011,7 @@ struct Parse {
   u8 useAgg;           /* If true, extract field values from the aggregator
                        ** while generating expressions.  Normally false */
   u8 checkSchema;      /* Causes schema cookie check after an error */
+  u8 nested;           /* Number of nested calls to the parser/code generator */
   int nErr;            /* Number of errors seen */
   int nTab;            /* Number of previously allocated VDBE cursors */
   int nMem;            /* Number of memory cells used so far */
@@ -1254,7 +1256,7 @@ void sqlite3OpenMasterTable(Vdbe *v, int);
 void sqlite3StartTable(Parse*,Token*,Token*,Token*,int,int);
 void sqlite3AddColumn(Parse*,Token*);
 void sqlite3AddNotNull(Parse*, int);
-void sqlite3AddPrimaryKey(Parse*, ExprList*, int);
+void sqlite3AddPrimaryKey(Parse*, ExprList*, int, int);
 void sqlite3AddColumnType(Parse*,Token*,Token*);
 void sqlite3AddDefaultValue(Parse*,Token*,int);
 void sqlite3AddCollateType(Parse*, const char*, int);
@@ -1298,6 +1300,7 @@ void sqlite3ExprCode(Parse*, Expr*);
 int sqlite3ExprCodeExprList(Parse*, ExprList*);
 void sqlite3ExprIfTrue(Parse*, Expr*, int, int);
 void sqlite3ExprIfFalse(Parse*, Expr*, int, int);
+void sqlite3NextedParse(Parse*, const char*, ...);
 Table *sqlite3FindTable(sqlite3*,const char*, const char*);
 Table *sqlite3LocateTable(Parse*,const char*, const char*);
 Index *sqlite3FindIndex(sqlite3*,const char*, const char*);
index 9c8b566874f02888f6ed9a852e4231d929295884..da7ce0e7d3fefdb03b817374e55381083791059d 100644 (file)
@@ -13,7 +13,7 @@
 ** is not included in the SQLite library.  It is used for automated
 ** testing of the SQLite library.
 **
-** $Id: test3.c,v 1.55 2004/11/04 14:47:12 drh Exp $
+** $Id: test3.c,v 1.56 2004/11/05 00:43:12 drh Exp $
 */
 #include "sqliteInt.h"
 #include "pager.h"
@@ -315,6 +315,7 @@ static int btree_drop_table(
   Btree *pBt;
   int iTable;
   int rc;
+  int notUsed1;
   if( argc!=3 ){
     Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
        " ID TABLENUM\"", 0);
@@ -322,7 +323,7 @@ static int btree_drop_table(
   }
   pBt = sqlite3TextToPtr(argv[1]);
   if( Tcl_GetInt(interp, argv[2], &iTable) ) return TCL_ERROR;
-  rc = sqlite3BtreeDropTable(pBt, iTable, 0);
+  rc = sqlite3BtreeDropTable(pBt, iTable, &notUsed1);
   if( rc!=SQLITE_OK ){
     Tcl_AppendResult(interp, errorName(rc), 0);
     return TCL_ERROR;
index 9e4ef43b4e2650b2fdd1d9376d1fcc15d748629f..bb2106c2f1922a9d7ce65d34dc8c83dfab636af3 100644 (file)
@@ -43,7 +43,7 @@
 ** in this file for details.  If in doubt, do not deviate from existing
 ** commenting and indentation practices when changing or adding code.
 **
-** $Id: vdbe.c,v 1.423 2004/11/04 14:30:06 danielk1977 Exp $
+** $Id: vdbe.c,v 1.424 2004/11/05 00:43:12 drh Exp $
 */
 #include "sqliteInt.h"
 #include "os.h"
@@ -2813,12 +2813,19 @@ case OP_NotExists: {
   break;
 }
 
-/* Opcode: NewRecno P1 * *
+/* Opcode: NewRecno P1 P2 *
 **
 ** Get a new integer record number used as the key to a table.
 ** The record number is not previously used as a key in the database
 ** table that cursor P1 points to.  The new record number is pushed 
 ** onto the stack.
+**
+** If P2>0 then P2 is a memory cell that holds the largest previously
+** generated record number.  No new record numbers are allowed to be less
+** than this value.  When this value reaches 0x7fffffff, a SQLITE_FULL
+** error is generated.  The P2 memory cell is updated with the generated
+** record number.  This P2 mechanism is used to help implement the
+** AUTOINCREMENT feature.
 */
 case OP_NewRecno: {
   int i = pOp->p1;
@@ -2882,6 +2889,24 @@ case OP_NewRecno: {
           }
         }
       }
+
+#ifndef SQLITE_OMIT_AUTOINCREMENT
+      if( pOp->p2 ){
+        Mem *pMem;
+        assert( pOp->p2>0 && pOp->p2<p->nMem );  /* P2 is a valid memory cell */
+        pMem = &p->aMem[pOp->p2];
+        assert( (pMem->flags & MEM_Int)!=0 );  /* mem(P2) holds an integer */
+        if( pMem->i==0x7fffffffffffffff || pC->useRandomRowid ){
+          rc = SQLITE_FULL;
+          goto abort_due_to_error;
+        }
+        if( v<pMem->i+1 ){
+          v = pMem->i + 1;
+        }
+        pMem->i = v;
+      }
+#endif
+
       if( v<0x7fffffffffffffff ){
         pC->nextRowidValid = 1;
         pC->nextRowid = v+1;
@@ -2890,6 +2915,7 @@ case OP_NewRecno: {
       }
     }
     if( pC->useRandomRowid ){
+      assert( pOp->p2==0 );  /* SQLITE_FULL must have occurred prior to this */
       v = db->priorNewRowid;
       cnt = 0;
       do{
@@ -3607,15 +3633,24 @@ case OP_IdxIsNull: {
 ** P2==1 then the table to be clear is in the auxiliary database file
 ** that is used to store tables create using CREATE TEMPORARY TABLE.
 **
+** If AUTOVACUUM is enabled then it is possible that another root page
+** might be moved into the newly deleted root page in order to keep all
+** root pages contiguous at the beginning of the database.  The former
+** value of the root page that moved - its value before the move occurred -
+** is pushed onto the stack.  If no page movement was required (because
+** the table being dropped was already the last one in the database) then
+** a zero is pushed onto the stack.  If AUTOVACUUM is disabled at 
+** then a zero is pushed onto the stack.
+**
 ** See also: Clear
 */
 case OP_Destroy: {
   int iMoved;
   rc = sqlite3BtreeDropTable(db->aDb[pOp->p2].pBt, pOp->p1, &iMoved);
-#ifndef SQLITE_OMIT_AUTOVACUUM
   pTos++;
   pTos->flags = MEM_Int;
   pTos->i = iMoved;
+#ifndef SQLITE_OMIT_AUTOVACUUM
   if( iMoved!=0 ){
     sqlite3RootPageMoved(&db->aDb[pOp->p2], iMoved, pOp->p1);
   }
@@ -4071,6 +4106,30 @@ case OP_MemLoad: {
   break;
 }
 
+#ifndef SQLITE_OMIT_AUTOINCREMENT
+/* Opcode: MemMax P1 * *
+**
+** Set the value of memory cell P1 to the maximum of its current value
+** and the value on the top of the stack.  The stack is unchanged.
+**
+** This instruction throws an error if the memory cell is not initially
+** an integer.
+*/
+case OP_MemMax: {
+  int i = pOp->p1;
+  Mem *pMem;
+  assert( pTos>=p->aStack );
+  assert( i>=0 && i<p->nMem );
+  pMem = &p->aMem[i];
+  assert( pMem->flags==MEM_Int );
+  Integerify(pTos);
+  if( pMem->i<pTos->i){
+    pMem->i = pTos->i;
+  }
+  break;
+}
+#endif /* SQLITE_OMIT_AUTOINCREMENT */
+
 /* Opcode: MemIncr P1 P2 *
 **
 ** Increment the integer valued memory cell P1 by 1.  If P2 is not zero