Use a vdbe memory cell to allocate the space required for vdbe cursors. (CVS 4912)

FossilOrigin-Name: 047153648155654b0cd70b811935209d2e21776c
diff --git a/src/analyze.c b/src/analyze.c
index 4168fe6..aeb9972 100644
--- a/src/analyze.c
+++ b/src/analyze.c
@@ -11,7 +11,7 @@
 *************************************************************************
 ** This file contains code associated with the ANALYZE command.
 **
-** @(#) $Id: analyze.c,v 1.41 2008/01/25 15:04:49 drh Exp $
+** @(#) $Id: analyze.c,v 1.42 2008/03/25 09:47:35 danielk1977 Exp $
 */
 #ifndef SQLITE_OMIT_ANALYZE
 #include "sqliteInt.h"
@@ -74,9 +74,9 @@
   if( !createStat1 ){
     sqlite3TableLock(pParse, iDb, iRootPage, 1, "sqlite_stat1");
   }
+  sqlite3VdbeAddOp2(v, OP_SetNumColumns, 0, 3);
   sqlite3VdbeAddOp3(v, OP_OpenWrite, iStatCur, iRootPage, iDb);
   sqlite3VdbeChangeP5(v, createStat1);
-  sqlite3VdbeAddOp2(v, OP_SetNumColumns, iStatCur, 3);
 }
 
 /*
@@ -130,17 +130,17 @@
     /* Open a cursor to the index to be analyzed
     */
     assert( iDb==sqlite3SchemaToIndex(pParse->db, pIdx->pSchema) );
+    nCol = pIdx->nColumn;
+    sqlite3VdbeAddOp2(v, OP_SetNumColumns, 0, nCol+1);
     sqlite3VdbeAddOp4(v, OP_OpenRead, iIdxCur, pIdx->tnum, iDb,
         (char *)pKey, P4_KEYINFO_HANDOFF);
     VdbeComment((v, "%s", pIdx->zName));
-    nCol = pIdx->nColumn;
     regFields = iMem+nCol*2;
     regTemp = regRowid = regCol = regFields+3;
     regRec = regCol+1;
     if( regRec>pParse->nMem ){
       pParse->nMem = regRec;
     }
-    sqlite3VdbeAddOp2(v, OP_SetNumColumns, iIdxCur, nCol+1);
 
     /* Memory cells are used as follows:
     **
diff --git a/src/btree.c b/src/btree.c
index 38aa347..869e4e9 100644
--- a/src/btree.c
+++ b/src/btree.c
@@ -9,7 +9,7 @@
 **    May you share freely, never taking more than you give.
 **
 *************************************************************************
-** $Id: btree.c,v 1.443 2008/03/25 00:22:21 drh Exp $
+** $Id: btree.c,v 1.444 2008/03/25 09:47:35 danielk1977 Exp $
 **
 ** This file implements a external (disk-based) database using BTrees.
 ** See the header comment on "btreeInt.h" for additional information.
@@ -2677,18 +2677,16 @@
 ** will not work correctly.
 */
 static int btreeCursor(
-  Btree *p,                       /* The btree */
-  int iTable,                     /* Root page of table to open */
-  int wrFlag,                     /* 1 to write. 0 read-only */
-  struct KeyInfo *pKeyInfo,       /* First arg to comparison function */
-  BtCursor **ppCur                /* Write new cursor here */
+  Btree *p,                              /* The btree */
+  int iTable,                            /* Root page of table to open */
+  int wrFlag,                            /* 1 to write. 0 read-only */
+  struct KeyInfo *pKeyInfo,              /* First arg to comparison function */
+  BtCursor *pCur                         /* Space for new cursor */
 ){
   int rc;
-  BtCursor *pCur;
   BtShared *pBt = p->pBt;
 
   assert( sqlite3BtreeHoldsMutex(p) );
-  *ppCur = 0;
   if( wrFlag ){
     if( pBt->readOnly ){
       return SQLITE_READONLY;
@@ -2707,11 +2705,6 @@
       return SQLITE_READONLY;
     }
   }
-  pCur = sqlite3MallocZero( sizeof(*pCur) );
-  if( pCur==0 ){
-    rc = SQLITE_NOMEM;
-    goto create_cursor_exception;
-  }
   pCur->pgnoRoot = (Pgno)iTable;
   if( iTable==1 && sqlite3PagerPagecount(pBt->pPager)==0 ){
     rc = SQLITE_EMPTY;
@@ -2736,32 +2729,34 @@
   }
   pBt->pCursor = pCur;
   pCur->eState = CURSOR_INVALID;
-  *ppCur = pCur;
 
   return SQLITE_OK;
 
 create_cursor_exception:
   if( pCur ){
     releasePage(pCur->pPage);
-    sqlite3_free(pCur);
   }
   unlockBtreeIfUnused(pBt);
   return rc;
 }
 int sqlite3BtreeCursor(
-  Btree *p,                   /* The btree */
-  int iTable,                 /* Root page of table to open */
-  int wrFlag,                 /* 1 to write. 0 read-only */
-  struct KeyInfo *pKeyInfo,   /* First arg to xCompare() */
-  BtCursor **ppCur            /* Write new cursor here */
+  Btree *p,                                   /* The btree */
+  int iTable,                                 /* Root page of table to open */
+  int wrFlag,                                 /* 1 to write. 0 read-only */
+  struct KeyInfo *pKeyInfo,                   /* First arg to xCompare() */
+  BtCursor *pCur                              /* Write new cursor here */
 ){
   int rc;
   sqlite3BtreeEnter(p);
   p->pBt->db = p->db;
-  rc = btreeCursor(p, iTable, wrFlag, pKeyInfo, ppCur);
+  rc = btreeCursor(p, iTable, wrFlag, pKeyInfo, pCur);
   sqlite3BtreeLeave(p);
   return rc;
 }
+int sqlite3BtreeCursorSize(){
+  return sizeof(BtCursor);
+}
+
 
 
 /*
@@ -2769,25 +2764,26 @@
 ** when the last cursor is closed.
 */
 int sqlite3BtreeCloseCursor(BtCursor *pCur){
-  BtShared *pBt = pCur->pBt;
   Btree *pBtree = pCur->pBtree;
-
-  sqlite3BtreeEnter(pBtree);
-  pBt->db = pBtree->db;
-  clearCursorPosition(pCur);
-  if( pCur->pPrev ){
-    pCur->pPrev->pNext = pCur->pNext;
-  }else{
-    pBt->pCursor = pCur->pNext;
+  if( pBtree ){
+    BtShared *pBt = pCur->pBt;
+    sqlite3BtreeEnter(pBtree);
+    pBt->db = pBtree->db;
+    clearCursorPosition(pCur);
+    if( pCur->pPrev ){
+      pCur->pPrev->pNext = pCur->pNext;
+    }else{
+      pBt->pCursor = pCur->pNext;
+    }
+    if( pCur->pNext ){
+      pCur->pNext->pPrev = pCur->pPrev;
+    }
+    releasePage(pCur->pPage);
+    unlockBtreeIfUnused(pBt);
+    invalidateOverflowCache(pCur);
+    /* sqlite3_free(pCur); */
+    sqlite3BtreeLeave(pBtree);
   }
-  if( pCur->pNext ){
-    pCur->pNext->pPrev = pCur->pPrev;
-  }
-  releasePage(pCur->pPage);
-  unlockBtreeIfUnused(pBt);
-  invalidateOverflowCache(pCur);
-  sqlite3_free(pCur);
-  sqlite3BtreeLeave(pBtree);
   return SQLITE_OK;
 }
 
diff --git a/src/btree.h b/src/btree.h
index 51d0b77..97298da 100644
--- a/src/btree.h
+++ b/src/btree.h
@@ -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.95 2008/03/25 00:22:21 drh Exp $
+** @(#) $Id: btree.h,v 1.96 2008/03/25 09:47:35 danielk1977 Exp $
 */
 #ifndef _BTREE_H_
 #define _BTREE_H_
@@ -128,12 +128,13 @@
 void sqlite3BtreeTripAllCursors(Btree*, int);
 
 int sqlite3BtreeCursor(
-  Btree*,                  /* BTree containing table to open */
-  int iTable,              /* Index of root page */
-  int wrFlag,              /* 1 for writing.  0 for read-only */
-  struct KeyInfo*,         /* First argument to compare function */
-  BtCursor **ppCursor      /* Returned cursor */
+  Btree*,                              /* BTree containing table to open */
+  int iTable,                          /* Index of root page */
+  int wrFlag,                          /* 1 for writing.  0 for read-only */
+  struct KeyInfo*,                     /* First argument to compare function */
+  BtCursor *pCursor                    /* Space to write cursor structure */
 );
+int sqlite3BtreeCursorSize();
 
 int sqlite3BtreeCloseCursor(BtCursor*);
 int sqlite3BtreeMoveto(BtCursor*,const void *pKey,i64 nKey,int bias,int *pRes);
diff --git a/src/build.c b/src/build.c
index edf4337..6b24790 100644
--- a/src/build.c
+++ b/src/build.c
@@ -22,7 +22,7 @@
 **     COMMIT
 **     ROLLBACK
 **
-** $Id: build.c,v 1.475 2008/03/20 14:03:29 drh Exp $
+** $Id: build.c,v 1.476 2008/03/25 09:47:35 danielk1977 Exp $
 */
 #include "sqliteInt.h"
 #include <ctype.h>
@@ -608,8 +608,8 @@
 void sqlite3OpenMasterTable(Parse *p, int iDb){
   Vdbe *v = sqlite3GetVdbe(p);
   sqlite3TableLock(p, iDb, MASTER_ROOT, 1, SCHEMA_TABLE(iDb));
+  sqlite3VdbeAddOp2(v, OP_SetNumColumns, 0, 5);/* sqlite_master has 5 columns */
   sqlite3VdbeAddOp3(v, OP_OpenWrite, 0, MASTER_ROOT, iDb);
-  sqlite3VdbeAddOp2(v, OP_SetNumColumns, 0, 5); /* sqlite_master has 5 columns */
 }
 
 /*
diff --git a/src/delete.c b/src/delete.c
index dd97f18..6a07384 100644
--- a/src/delete.c
+++ b/src/delete.c
@@ -12,7 +12,7 @@
 ** This file contains C code routines that are called by the parser
 ** in order to generate code for DELETE FROM statements.
 **
-** $Id: delete.c,v 1.162 2008/03/19 20:42:14 drh Exp $
+** $Id: delete.c,v 1.163 2008/03/25 09:47:35 danielk1977 Exp $
 */
 #include "sqliteInt.h"
 
@@ -75,9 +75,9 @@
   v = sqlite3GetVdbe(p);
   assert( opcode==OP_OpenWrite || opcode==OP_OpenRead );
   sqlite3TableLock(p, iDb, pTab->tnum, (opcode==OP_OpenWrite), pTab->zName);
+  sqlite3VdbeAddOp2(v, OP_SetNumColumns, 0, pTab->nCol);
   sqlite3VdbeAddOp3(v, opcode, iCur, pTab->tnum, iDb);
   VdbeComment((v, "%s", pTab->zName));
-  sqlite3VdbeAddOp2(v, OP_SetNumColumns, iCur, pTab->nCol);
 }
 
 
@@ -322,8 +322,8 @@
     /* Open the pseudo-table used to store OLD if there are triggers.
     */
     if( triggers_exist ){
+      sqlite3VdbeAddOp2(v, OP_SetNumColumns, 0, pTab->nCol);
       sqlite3VdbeAddOp1(v, OP_OpenPseudo, oldIdx);
-      sqlite3VdbeAddOp2(v, OP_SetNumColumns, oldIdx, pTab->nCol);
     }
 
     /* Delete every item whose key was written to the list during the
diff --git a/src/expr.c b/src/expr.c
index 7ee47a4..d35b3cd 100644
--- a/src/expr.c
+++ b/src/expr.c
@@ -12,7 +12,7 @@
 ** This file contains routines used for analyzing expressions and
 ** for generating VDBE code that evaluates expressions in SQLite.
 **
-** $Id: expr.c,v 1.356 2008/03/20 16:30:18 drh Exp $
+** $Id: expr.c,v 1.357 2008/03/25 09:47:35 danielk1977 Exp $
 */
 #include "sqliteInt.h"
 #include <ctype.h>
@@ -1639,11 +1639,11 @@
           iAddr = sqlite3VdbeAddOp1(v, OP_If, iMem);
           sqlite3VdbeAddOp2(v, OP_Integer, 1, iMem);
   
+          sqlite3VdbeAddOp2(v, OP_SetNumColumns, 0, pIdx->nColumn);
           sqlite3VdbeAddOp4(v, OP_OpenRead, iTab, pIdx->tnum, iDb,
                                pKey,P4_KEYINFO_HANDOFF);
           VdbeComment((v, "%s", pIdx->zName));
           eType = IN_INDEX_INDEX;
-          sqlite3VdbeAddOp2(v, OP_SetNumColumns, iTab, pIdx->nColumn);
 
           sqlite3VdbeJumpHere(v, iAddr);
         }
@@ -1719,10 +1719,9 @@
       ** is used.
       */
       pExpr->iTable = pParse->nTab++;
-      addr = sqlite3VdbeAddOp1(v, OP_OpenEphemeral, pExpr->iTable);
+      addr = sqlite3VdbeAddOp2(v, OP_OpenEphemeral, pExpr->iTable, 1);
       memset(&keyInfo, 0, sizeof(keyInfo));
       keyInfo.nField = 1;
-      sqlite3VdbeAddOp2(v, OP_SetNumColumns, pExpr->iTable, 1);
 
       if( pExpr->pSelect ){
         /* Case 1:     expr IN (SELECT ...)
diff --git a/src/insert.c b/src/insert.c
index 9a2cb56..f7950d0 100644
--- a/src/insert.c
+++ b/src/insert.c
@@ -12,7 +12,7 @@
 ** This file contains C code routines that are called by the parser
 ** to handle INSERT statements in SQLite.
 **
-** $Id: insert.c,v 1.232 2008/03/19 20:42:14 drh Exp $
+** $Id: insert.c,v 1.233 2008/03/25 09:47:35 danielk1977 Exp $
 */
 #include "sqliteInt.h"
 
@@ -537,8 +537,7 @@
       ** back up and execute the SELECT code above.
       */
       sqlite3VdbeJumpHere(v, iInitCode);
-      sqlite3VdbeAddOp2(v, OP_OpenEphemeral, srcTab, 0);
-      sqlite3VdbeAddOp2(v, OP_SetNumColumns, srcTab, nColumn);
+      sqlite3VdbeAddOp2(v, OP_OpenEphemeral, srcTab, nColumn);
       sqlite3VdbeAddOp2(v, OP_Goto, 0, iSelectLoop);
       sqlite3VdbeResolveLabel(v, iCleanup);
     }else{
@@ -629,8 +628,8 @@
   /* Open the temp table for FOR EACH ROW triggers
   */
   if( triggers_exist ){
+    sqlite3VdbeAddOp2(v, OP_SetNumColumns, 0, pTab->nCol);
     sqlite3VdbeAddOp2(v, OP_OpenPseudo, newIdx, 0);
-    sqlite3VdbeAddOp2(v, OP_SetNumColumns, newIdx, pTab->nCol);
   }
     
   /* Initialize the count of rows to be inserted
diff --git a/src/prepare.c b/src/prepare.c
index 19eb4df..bdb3d37 100644
--- a/src/prepare.c
+++ b/src/prepare.c
@@ -13,7 +13,7 @@
 ** interface, and routines that contribute to loading the database schema
 ** from disk.
 **
-** $Id: prepare.c,v 1.81 2008/03/25 00:22:21 drh Exp $
+** $Id: prepare.c,v 1.82 2008/03/25 09:47:35 danielk1977 Exp $
 */
 #include "sqliteInt.h"
 #include <ctype.h>
@@ -207,12 +207,16 @@
     }
     return SQLITE_OK;
   }
+  curMain = sqlite3MallocZero(sqlite3BtreeCursorSize());
+  if( !curMain ){
+    rc = SQLITE_NOMEM;
+    goto error_out;
+  }
   sqlite3BtreeEnter(pDb->pBt);
-  rc = sqlite3BtreeCursor(pDb->pBt, MASTER_ROOT, 0, 0, &curMain);
+  rc = sqlite3BtreeCursor(pDb->pBt, MASTER_ROOT, 0, 0, curMain);
   if( rc!=SQLITE_OK && rc!=SQLITE_EMPTY ){
     sqlite3SetString(pzErrMsg, sqlite3ErrStr(rc), (char*)0);
-    sqlite3BtreeLeave(pDb->pBt);
-    goto error_out;
+    goto leave_error_out;
   }
 
   /* Get the database meta information.
@@ -239,9 +243,7 @@
     }
     if( rc ){
       sqlite3SetString(pzErrMsg, sqlite3ErrStr(rc), (char*)0);
-      sqlite3BtreeCloseCursor(curMain);
-      sqlite3BtreeLeave(pDb->pBt);
-      goto error_out;
+      goto leave_error_out;
     }
   }else{
     memset(meta, 0, sizeof(meta));
@@ -261,11 +263,10 @@
     }else{
       /* If opening an attached database, the encoding much match ENC(db) */
       if( meta[4]!=ENC(db) ){
-        sqlite3BtreeCloseCursor(curMain);
         sqlite3SetString(pzErrMsg, "attached databases must use the same"
             " text encoding as main database", (char*)0);
-        sqlite3BtreeLeave(pDb->pBt);
-        return SQLITE_ERROR;
+        rc = SQLITE_ERROR;
+        goto leave_error_out;
       }
     }
   }else{
@@ -290,10 +291,9 @@
     pDb->pSchema->file_format = 1;
   }
   if( pDb->pSchema->file_format>SQLITE_MAX_FILE_FORMAT ){
-    sqlite3BtreeCloseCursor(curMain);
     sqlite3SetString(pzErrMsg, "unsupported file format", (char*)0);
-    sqlite3BtreeLeave(pDb->pBt);
-    return SQLITE_ERROR;
+    rc = SQLITE_ERROR;
+    goto leave_error_out;
   }
 
   /* Ticket #2804:  When we open a database in the newer file format,
@@ -336,7 +336,6 @@
       sqlite3AnalysisLoad(db, iDb);
     }
 #endif
-    sqlite3BtreeCloseCursor(curMain);
   }
   if( db->mallocFailed ){
     /* sqlite3SetString(pzErrMsg, "out of memory", (char*)0); */
@@ -355,6 +354,14 @@
     DbSetProperty(db, iDb, DB_SchemaLoaded);
     rc = SQLITE_OK;
   }
+
+  /* Jump here for an error that occurs after successfully allocating
+  ** curMain and calling sqlite3BtreeEnter(). For an error that occurs
+  ** before that point, jump to error_out.
+  */
+leave_error_out:
+  sqlite3BtreeCloseCursor(curMain);
+  sqlite3_free(curMain);
   sqlite3BtreeLeave(pDb->pBt);
 
 error_out:
@@ -441,23 +448,32 @@
   int cookie;
   int allOk = 1;
 
-  assert( sqlite3_mutex_held(db->mutex) );
-  for(iDb=0; allOk && iDb<db->nDb; iDb++){
-    Btree *pBt;
-    pBt = db->aDb[iDb].pBt;
-    if( pBt==0 ) continue;
-    rc = sqlite3BtreeCursor(pBt, MASTER_ROOT, 0, 0, &curTemp);
-    if( rc==SQLITE_OK ){
-      rc = sqlite3BtreeGetMeta(pBt, 1, (u32 *)&cookie);
-      if( rc==SQLITE_OK && cookie!=db->aDb[iDb].pSchema->schema_cookie ){
-        allOk = 0;
+  curTemp = (BtCursor *)sqlite3_malloc(sqlite3BtreeCursorSize());
+  if( curTemp ){
+    assert( sqlite3_mutex_held(db->mutex) );
+    for(iDb=0; allOk && iDb<db->nDb; iDb++){
+      Btree *pBt;
+      pBt = db->aDb[iDb].pBt;
+      if( pBt==0 ) continue;
+      memset(curTemp, 0, sqlite3BtreeCursorSize());
+      rc = sqlite3BtreeCursor(pBt, MASTER_ROOT, 0, 0, curTemp);
+      if( rc==SQLITE_OK ){
+        rc = sqlite3BtreeGetMeta(pBt, 1, (u32 *)&cookie);
+        if( rc==SQLITE_OK && cookie!=db->aDb[iDb].pSchema->schema_cookie ){
+          allOk = 0;
+        }
+        sqlite3BtreeCloseCursor(curTemp);
       }
-      sqlite3BtreeCloseCursor(curTemp);
+      if( rc==SQLITE_NOMEM || rc==SQLITE_IOERR_NOMEM ){
+        db->mallocFailed = 1;
+      }
     }
-    if( rc==SQLITE_NOMEM || rc==SQLITE_IOERR_NOMEM ){
-      db->mallocFailed = 1;
-    }
+    sqlite3_free(curTemp);
+  }else{
+    allOk = 0;
+    db->mallocFailed = 1;
   }
+
   return allOk;
 }
 
diff --git a/src/select.c b/src/select.c
index 18532b5..3df5f40 100644
--- a/src/select.c
+++ b/src/select.c
@@ -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.417 2008/03/22 01:07:18 drh Exp $
+** $Id: select.c,v 1.418 2008/03/25 09:47:35 danielk1977 Exp $
 */
 #include "sqliteInt.h"
 
@@ -789,8 +789,8 @@
   iTab = pOrderBy->iECursor;
   if( eDest==SRT_Callback || eDest==SRT_Subroutine ){
     pseudoTab = pParse->nTab++;
+    sqlite3VdbeAddOp2(v, OP_SetNumColumns, 0, nColumn);
     sqlite3VdbeAddOp2(v, OP_OpenPseudo, pseudoTab, 0);
-    sqlite3VdbeAddOp2(v, OP_SetNumColumns, pseudoTab, nColumn);
   }
   addr = 1 + sqlite3VdbeAddOp2(v, OP_Sort, iTab, brk);
   codeOffset(v, p, cont);
@@ -3316,10 +3316,9 @@
       */
       sAggInfo.sortingIdx = pParse->nTab++;
       pKeyInfo = keyInfoFromExprList(pParse, pGroupBy);
-      addrSortingIdx =
-          sqlite3VdbeAddOp4(v, OP_OpenEphemeral, sAggInfo.sortingIdx,
-                         sAggInfo.nSortingColumn, 0,
-                         (char*)pKeyInfo, P4_KEYINFO_HANDOFF);
+      addrSortingIdx = sqlite3VdbeAddOp4(v, OP_OpenEphemeral, 
+          sAggInfo.sortingIdx, sAggInfo.nSortingColumn, 
+          0, (char*)pKeyInfo, P4_KEYINFO_HANDOFF);
 
       /* Initialize memory locations used by GROUP BY aggregate processing
       */
diff --git a/src/test3.c b/src/test3.c
index 8a90707..e3922f6 100644
--- a/src/test3.c
+++ b/src/test3.c
@@ -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.92 2008/03/25 00:22:21 drh Exp $
+** $Id: test3.c,v 1.93 2008/03/25 09:47:35 danielk1977 Exp $
 */
 #include "sqliteInt.h"
 #include "btreeInt.h"
@@ -711,10 +711,13 @@
   pBt = sqlite3TextToPtr(argv[1]);
   if( Tcl_GetInt(interp, argv[2], &iTable) ) return TCL_ERROR;
   if( Tcl_GetBoolean(interp, argv[3], &wrFlag) ) return TCL_ERROR;
+  pCur = (BtCursor *)ckalloc(sqlite3BtreeCursorSize());
+  memset(pCur, 0, sqlite3BtreeCursorSize());
   sqlite3BtreeEnter(pBt);
-  rc = sqlite3BtreeCursor(pBt, iTable, wrFlag, 0, &pCur);
+  rc = sqlite3BtreeCursor(pBt, iTable, wrFlag, 0, pCur);
   sqlite3BtreeLeave(pBt);
   if( rc ){
+    ckfree((char *)pCur);
     Tcl_AppendResult(interp, errorName(rc), 0);
     return TCL_ERROR;
   }
@@ -748,6 +751,7 @@
   sqlite3BtreeEnter(pBt);
   rc = sqlite3BtreeCloseCursor(pCur);
   sqlite3BtreeLeave(pBt);
+  ckfree((char *)pCur);
   if( rc ){
     Tcl_AppendResult(interp, errorName(rc), 0);
     return TCL_ERROR;
diff --git a/src/test_malloc.c b/src/test_malloc.c
index ce2f718..3595a06 100644
--- a/src/test_malloc.c
+++ b/src/test_malloc.c
@@ -13,7 +13,7 @@
 ** This file contains code used to implement test interfaces to the
 ** memory allocation subsystem.
 **
-** $Id: test_malloc.c,v 1.18 2008/03/21 14:22:44 danielk1977 Exp $
+** $Id: test_malloc.c,v 1.19 2008/03/25 09:47:35 danielk1977 Exp $
 */
 #include "sqliteInt.h"
 #include "tcl.h"
@@ -507,7 +507,7 @@
   return TCL_OK;
 }
 
-#define MALLOC_LOG_FRAMES 5
+#define MALLOC_LOG_FRAMES 10 
 static Tcl_HashTable aMallocLog;
 static int mallocLogEnabled = 0;
 
diff --git a/src/update.c b/src/update.c
index f6faca2..6f479a5 100644
--- a/src/update.c
+++ b/src/update.c
@@ -12,7 +12,7 @@
 ** This file contains C code routines that are called by the parser
 ** to handle UPDATE statements.
 **
-** $Id: update.c,v 1.172 2008/03/19 20:42:14 drh Exp $
+** $Id: update.c,v 1.173 2008/03/25 09:47:35 danielk1977 Exp $
 */
 #include "sqliteInt.h"
 
@@ -302,10 +302,10 @@
 
     /* Create pseudo-tables for NEW and OLD
     */
+    sqlite3VdbeAddOp2(v, OP_SetNumColumns, 0, pTab->nCol);
     sqlite3VdbeAddOp2(v, OP_OpenPseudo, oldIdx, 0);
-    sqlite3VdbeAddOp2(v, OP_SetNumColumns, oldIdx, pTab->nCol);
+    sqlite3VdbeAddOp2(v, OP_SetNumColumns, 0, pTab->nCol);
     sqlite3VdbeAddOp2(v, OP_OpenPseudo, newIdx, 0);
-    sqlite3VdbeAddOp2(v, OP_SetNumColumns, newIdx, pTab->nCol);
 
     iGoto = sqlite3VdbeAddOp2(v, OP_Goto, 0, 0);
     addr = sqlite3VdbeMakeLabel(v);
diff --git a/src/vdbe.c b/src/vdbe.c
index 5256b65..e491340 100644
--- a/src/vdbe.c
+++ b/src/vdbe.c
@@ -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.716 2008/03/25 00:22:21 drh Exp $
+** $Id: vdbe.c,v 1.717 2008/03/25 09:47:35 danielk1977 Exp $
 */
 #include "sqliteInt.h"
 #include <ctype.h>
@@ -208,15 +208,66 @@
 ** Allocate cursor number iCur.  Return a pointer to it.  Return NULL
 ** if we run out of memory.
 */
-static Cursor *allocateCursor(Vdbe *p, int iCur, int iDb){
-  Cursor *pCx;
+static Cursor *allocateCursor(
+  Vdbe *p, 
+  int iCur, 
+  Op *pOp,
+  int iDb, 
+  int isBtreeCursor
+){
+  /* Find the memory cell that will be used to store the blob of memory
+  ** required for this Cursor structure. It is convenient to use a 
+  ** vdbe memory cell to manage the memory allocation required for a
+  ** Cursor structure for the following reasons:
+  **
+  **   * Sometimes cursor numbers are used for a couple of different
+  **     purposes in a vdbe program. The different uses might require
+  **     different sized allocations. Memory cells provide growable
+  **     allocations.
+  **
+  **   * When using ENABLE_MEMORY_MANAGEMENT, memory cell buffers can
+  **     be freed lazily via the sqlite3_release_memory() API. This
+  **     minimizes the number of malloc calls made by the system.
+  **
+  ** Memory cells for cursors are allocated at the top of the address
+  ** space. Memory cell (p->nMem) corresponds to cursor 0. Space for
+  ** cursor 1 is managed by memory cell (p->nMem-1), etc.
+  */
+  Mem *pMem = &p->aMem[p->nMem-iCur];
+
+  Cursor *pCx = 0;
+  /* If the opcode of pOp is OP_SetNumColumns, then pOp->p2 contains
+  ** the number of fields in the records contained in the table or
+  ** index being opened. Use this to reserve space for the 
+  ** Cursor.aType[] array.
+  */
+  int nField = 0;
+  if( pOp->opcode==OP_SetNumColumns || pOp->opcode==OP_OpenEphemeral ){
+    nField = pOp->p2;
+  }
+
+
+  int nByte = 
+      sizeof(Cursor) + 
+      (isBtreeCursor?sqlite3BtreeCursorSize():0) + 
+      2*nField*sizeof(u32);
+
   assert( iCur<p->nCursor );
   if( p->apCsr[iCur] ){
     sqlite3VdbeFreeCursor(p, p->apCsr[iCur]);
+    p->apCsr[iCur] = 0;
   }
-  p->apCsr[iCur] = pCx = sqlite3MallocZero( sizeof(Cursor) );
-  if( pCx ){
+  if( SQLITE_OK==sqlite3VdbeMemGrow(pMem, nByte, 0) ){
+    p->apCsr[iCur] = pCx = (Cursor *)pMem->z;
+    memset(pMem->z, 0, nByte);
     pCx->iDb = iDb;
+    pCx->nField = nField;
+    if( nField ){
+      pCx->aType = (u32 *)&pMem->z[sizeof(Cursor)];
+    }
+    if( isBtreeCursor ){
+      pCx->pCursor = (BtCursor *)&pMem->z[sizeof(Cursor)+2*nField*sizeof(u32)];
+    }
   }
   return pCx;
 }
@@ -1773,22 +1824,23 @@
   break;
 }
 
-/* Opcode: SetNumColumns P1 P2 * * *
+/* Opcode: SetNumColumns * P2 * * *
 **
-** Before the OP_Column opcode can be executed on a cursor, this
-** opcode must be called to set the number of fields in the table.
+** This opcode sets the number of columns for the cursor opened by the
+** following instruction to P2.
 **
-** This opcode sets the number of columns for cursor P1 to P2.
+** An OP_SetNumColumns is only useful if it occurs immediately before 
+** one of the following opcodes:
 **
-** If OP_KeyAsData is to be applied to cursor P1, it must be executed
-** before this op-code.
+**     OpenRead
+**     OpenWrite
+**     OpenPseudo
+**
+** If the OP_Column opcode is to be executed on a cursor, then
+** this opcode must be present immediately before the opcode that
+** opens the cursor.
 */
 case OP_SetNumColumns: {
-  Cursor *pC;
-  assert( (pOp->p1)<p->nCursor );
-  assert( p->apCsr[pOp->p1]!=0 );
-  pC = p->apCsr[pOp->p1];
-  pC->nField = pOp->p2;
   break;
 }
 
@@ -1893,8 +1945,8 @@
   /* Read and parse the table header.  Store the results of the parse
   ** into the record header cache fields of the cursor.
   */
+  aType = pC->aType;
   if( pC->cacheStatus==p->cacheCtr ){
-    aType = pC->aType;
     aOffset = pC->aOffset;
   }else{
     u8 *zIdx;        /* Index into header */
@@ -1903,13 +1955,7 @@
     int szHdrSz;     /* Size of the header size field at start of record */
     int avail;       /* Number of bytes of available data */
 
-    aType = pC->aType;
-    if( aType==0 ){
-      pC->aType = aType = sqlite3DbMallocRaw(db, 2*nField*sizeof(aType) );
-    }
-    if( aType==0 ){
-      goto no_mem;
-    }
+    assert(aType);
     pC->aOffset = aOffset = &aType[nField];
     pC->payloadSize = payloadSize;
     pC->cacheStatus = p->cacheCtr;
@@ -2515,10 +2561,10 @@
     assert( p2>=2 );
   }
   assert( i>=0 );
-  pCur = allocateCursor(p, i, iDb);
+  pCur = allocateCursor(p, i, &pOp[-1], iDb, 1);
   if( pCur==0 ) goto no_mem;
   pCur->nullRow = 1;
-  rc = sqlite3BtreeCursor(pX, p2, wrFlag, pOp->p4.p, &pCur->pCursor);
+  rc = sqlite3BtreeCursor(pX, p2, wrFlag, pOp->p4.p, pCur->pCursor);
   if( pOp->p4type==P4_KEYINFO ){
     pCur->pKeyInfo = pOp->p4.pKeyInfo;
     pCur->pIncrKey = &pCur->pKeyInfo->incrKey;
@@ -2561,6 +2607,7 @@
     case SQLITE_EMPTY: {
       pCur->isTable = pOp->p4type!=P4_KEYINFO;
       pCur->isIndex = !pCur->isTable;
+      pCur->pCursor = 0;
       rc = SQLITE_OK;
       break;
     }
@@ -2600,7 +2647,7 @@
       SQLITE_OPEN_TRANSIENT_DB;
 
   assert( i>=0 );
-  pCx = allocateCursor(p, i, -1);
+  pCx = allocateCursor(p, i, pOp, -1, 1);
   if( pCx==0 ) goto no_mem;
   pCx->nullRow = 1;
   rc = sqlite3BtreeFactory(db, 0, 1, SQLITE_DEFAULT_TEMP_CACHE_SIZE, openFlags,
@@ -2621,19 +2668,18 @@
       if( rc==SQLITE_OK ){
         assert( pgno==MASTER_ROOT+1 );
         rc = sqlite3BtreeCursor(pCx->pBt, pgno, 1, 
-                                (KeyInfo*)pOp->p4.z, &pCx->pCursor);
+                                (KeyInfo*)pOp->p4.z, pCx->pCursor);
         pCx->pKeyInfo = pOp->p4.pKeyInfo;
         pCx->pKeyInfo->enc = ENC(p->db);
         pCx->pIncrKey = &pCx->pKeyInfo->incrKey;
       }
       pCx->isTable = 0;
     }else{
-      rc = sqlite3BtreeCursor(pCx->pBt, MASTER_ROOT, 1, 0, &pCx->pCursor);
+      rc = sqlite3BtreeCursor(pCx->pBt, MASTER_ROOT, 1, 0, pCx->pCursor);
       pCx->isTable = 1;
       pCx->pIncrKey = &pCx->bogusIncrKey;
     }
   }
-  pCx->nField = pOp->p2;
   pCx->isIndex = !pCx->isTable;
   break;
 }
@@ -2654,7 +2700,7 @@
   int i = pOp->p1;
   Cursor *pCx;
   assert( i>=0 );
-  pCx = allocateCursor(p, i, -1);
+  pCx = allocateCursor(p, i, &pOp[-1], -1, 0);
   if( pCx==0 ) goto no_mem;
   pCx->nullRow = 1;
   pCx->pseudoTable = 1;
@@ -4390,7 +4436,7 @@
     pVtabCursor->pVtab = pVtab;
 
     /* Initialise vdbe cursor object */
-    pCur = allocateCursor(p, pOp->p1, -1);
+    pCur = allocateCursor(p, pOp->p1, &pOp[-1], -1, 0);
     if( pCur ){
       pCur->pVtabCursor = pVtabCursor;
       pCur->pModule = pVtabCursor->pVtab->pModule;
diff --git a/src/vdbeaux.c b/src/vdbeaux.c
index be7c276..7e7824a 100644
--- a/src/vdbeaux.c
+++ b/src/vdbeaux.c
@@ -958,6 +958,17 @@
    */
   p->magic = VDBE_MAGIC_RUN;
 
+  /* For each cursor required, also allocate a memory cell. Memory
+  ** cells (nMem+1-nCursor)..nMem, inclusive, will never be used by
+  ** the vdbe program. Instead they are used to allocate space for
+  ** Cursor/BtCursor structures. The blob of memory associated with 
+  ** cursor 0 is stored in memory cell nMem. Memory cell (nMem-1)
+  ** stores the blob of memory associated with cursor 1, etc.
+  **
+  ** See also: allocateCursor().
+  */
+  nMem += nCursor;
+
   /*
   ** Allocation space for registers.
   */
@@ -1026,8 +1037,8 @@
 }
 
 /*
-** Close a VDBE cursor and release all the resources that cursor happens
-** to hold.
+** Close a VDBE cursor and release all the resources that cursor 
+** happens to hold.
 */
 void sqlite3VdbeFreeCursor(Vdbe *p, Cursor *pCx){
   if( pCx==0 ){
@@ -1051,8 +1062,9 @@
   }
 #endif
   sqlite3_free(pCx->pData);
-  sqlite3_free(pCx->aType);
-  sqlite3_free(pCx);
+  memset(pCx, 0, sizeof(Cursor));
+  /* sqlite3_free(pCx->aType); */
+  /* sqlite3_free(pCx); */
 }
 
 /*
@@ -1084,6 +1096,7 @@
   for(i=1; i<=p->nMem; i++){
     MemSetTypeFlag(&p->aMem[i], MEM_Null);
   }
+
   releaseMemArray(&p->aMem[1], p->nMem);
   sqlite3VdbeFifoClear(&p->sFifo);
   if( p->contextStack ){
diff --git a/src/vdbeblob.c b/src/vdbeblob.c
index 70d5ed8..1d456ad 100644
--- a/src/vdbeblob.c
+++ b/src/vdbeblob.c
@@ -12,7 +12,7 @@
 **
 ** This file contains code used to implement incremental BLOB I/O.
 **
-** $Id: vdbeblob.c,v 1.20 2008/01/25 15:04:50 drh Exp $
+** $Id: vdbeblob.c,v 1.21 2008/03/25 09:47:35 danielk1977 Exp $
 */
 
 #include "sqliteInt.h"
@@ -70,16 +70,17 @@
     /* One of the following two instructions is replaced by an
     ** OP_Noop before exection.
     */
-    {OP_OpenRead, 0, 0, 0},        /* 2: Open cursor 0 for reading */
-    {OP_OpenWrite, 0, 0, 0},       /* 3: Open cursor 0 for read/write */
+    {OP_SetNumColumns, 0, 0, 0},   /* 2: Num cols for cursor */
+    {OP_OpenRead, 0, 0, 0},        /* 3: Open cursor 0 for reading */
     {OP_SetNumColumns, 0, 0, 0},   /* 4: Num cols for cursor */
+    {OP_OpenWrite, 0, 0, 0},       /* 5: Open cursor 0 for read/write */
 
-    {OP_Variable, 1, 1, 0},        /* 5: Push the rowid to the stack */
-    {OP_NotExists, 0, 10, 1},      /* 6: Seek the cursor */
-    {OP_Column, 0, 0, 1},          /* 7  */
-    {OP_ResultRow, 1, 0, 0},       /* 8  */
-    {OP_Close, 0, 0, 0},           /* 9  */
-    {OP_Halt, 0, 0, 0},            /* 10 */
+    {OP_Variable, 1, 1, 0},        /* 6: Push the rowid to the stack */
+    {OP_NotExists, 0, 10, 1},      /* 7: Seek the cursor */
+    {OP_Column, 0, 0, 1},          /* 8  */
+    {OP_ResultRow, 1, 0, 0},       /* 9  */
+    {OP_Close, 0, 0, 0},           /* 10  */
+    {OP_Halt, 0, 0, 0},            /* 11 */
   };
 
   Vdbe *v = 0;
@@ -168,9 +169,9 @@
       /* Remove either the OP_OpenWrite or OpenRead. Set the P2 
       ** parameter of the other to pTab->tnum. 
       */
-      sqlite3VdbeChangeToNoop(v, (flags ? 2 : 3), 1);
-      sqlite3VdbeChangeP2(v, (flags ? 3 : 2), pTab->tnum);
-      sqlite3VdbeChangeP3(v, (flags ? 3 : 2), iDb);
+      sqlite3VdbeChangeToNoop(v, (flags ? 3 : 5), 1);
+      sqlite3VdbeChangeP2(v, (flags ? 5 : 3), pTab->tnum);
+      sqlite3VdbeChangeP3(v, (flags ? 5 : 3), iDb);
 
       /* Configure the OP_SetNumColumns. Configure the cursor to
       ** think that the table has one more column than it really
@@ -179,7 +180,7 @@
       ** we can invoke OP_Column to fill in the vdbe cursors type 
       ** and offset cache without causing any IO.
       */
-      sqlite3VdbeChangeP2(v, 4, pTab->nCol+1);
+      sqlite3VdbeChangeP2(v, flags ? 4 : 2, pTab->nCol+1);
       if( !db->mallocFailed ){
         sqlite3VdbeMakeReady(v, 1, 1, 1, 0);
       }
diff --git a/src/where.c b/src/where.c
index bcb7785..71bc51b 100644
--- a/src/where.c
+++ b/src/where.c
@@ -16,7 +16,7 @@
 ** so is applicable.  Because this module is responsible for selecting
 ** indices, you might also think of this module as the "query optimizer".
 **
-** $Id: where.c,v 1.290 2008/03/17 17:08:33 drh Exp $
+** $Id: where.c,v 1.291 2008/03/25 09:47:35 danielk1977 Exp $
 */
 #include "sqliteInt.h"
 
@@ -2223,7 +2223,7 @@
         Bitmask b = pTabItem->colUsed;
         int n = 0;
         for(; b; b=b>>1, n++){}
-        sqlite3VdbeChangeP2(v, sqlite3VdbeCurrentAddr(v)-1, n);
+        sqlite3VdbeChangeP2(v, sqlite3VdbeCurrentAddr(v)-2, n);
         assert( n<=pTab->nCol );
       }
     }else{
@@ -2233,10 +2233,10 @@
     if( (pIx = pLevel->pIdx)!=0 ){
       KeyInfo *pKey = sqlite3IndexKeyinfo(pParse, pIx);
       assert( pIx->pSchema==pTab->pSchema );
+      sqlite3VdbeAddOp2(v, OP_SetNumColumns, 0, pIx->nColumn+1);
       sqlite3VdbeAddOp4(v, OP_OpenRead, iIdxCur, pIx->tnum, iDb,
                         (char*)pKey, P4_KEYINFO_HANDOFF);
       VdbeComment((v, "%s", pIx->zName));
-      sqlite3VdbeAddOp2(v, OP_SetNumColumns, iIdxCur, pIx->nColumn+1);
     }
     sqlite3CodeVerifySchema(pParse, iDb);
   }