SQLite4
Check-in [a02dacd5bd]
Not logged in

Many hyperlinks are disabled.
Use anonymous login to enable hyperlinks.

Overview
Comment:Minor lsm optimizations.
Downloads: Tarball | ZIP archive | SQL archive
Timelines: family | ancestors | descendants | both | trunk
Files: files | file ages | folders
SHA1: a02dacd5bd54e993114953435c4c59fdf4218e4a
User & Date: dan 2013-02-28 15:56:47
Context
2013-02-28
19:09
Reuse existing lsm_cursor objects instead of always allocating new ones. check-in: 64895935bc user: dan tags: trunk
15:56
Minor lsm optimizations. check-in: a02dacd5bd user: dan tags: trunk
2013-02-26
17:35
Remove a stray 'breakpoint' from lsm3.test. check-in: 4ebadf909b user: dan tags: trunk
Changes
Hide Diffs Side-by-Side Diffs Ignore Whitespace Patch

Changes to lsm-test/lsmtest_main.c.

   563    563   "Options are:\n"
   564    564   "  -repeat  $repeat                 (default value 10)\n"
   565    565   "  -write   $write                  (default value 10000)\n"
   566    566   "  -pause   $pause                  (default value 0)\n"
   567    567   "  -fetch   $fetch                  (default value 0)\n"
   568    568   "  -keysize $keysize                (default value 12)\n"
   569    569   "  -valsize $valsize                (default value 100)\n"
   570         -"  -system  $system                 (default value \"lsm\"\n"
          570  +"  -system  $system                 (default value \"lsm\")\n"
   571    571   "\n"
   572    572   );
   573    573   }
   574    574   
   575    575   int do_speed_test2(int nArg, char **azArg){
   576    576     struct Option {
   577    577       const char *zOpt;
................................................................................
   642    642     }
   643    643     
   644    644     printf("#");
   645    645     for(i=0; i<ArraySize(aOpt); i++){
   646    646       if( aOpt[i].zOpt ){
   647    647         if( aOpt[i].eVal>=0 ){
   648    648           printf(" %s=%d", &aOpt[i].zOpt[1], aParam[aOpt[i].eVal]);
   649         -      }else{
          649  +      }else if( aOpt[i].eVal==-1 ){
   650    650           printf(" %s=\"%s\"", &aOpt[i].zOpt[1], zSystem);
   651    651         }
   652    652       }
   653    653     }
   654    654     printf("\n");
   655    655   
   656    656     defn.nMinKey = defn.nMaxKey = aParam[ST_KEYSIZE];
................................................................................
  1426   1426   #ifdef __linux__
  1427   1427   #include <sys/time.h>
  1428   1428   #include <sys/resource.h>
  1429   1429   
  1430   1430   static void lsmtest_rusage_report(void){
  1431   1431     int res;
  1432   1432     struct rusage r;
  1433         -  memset(&r, sizeof(r), 0);
         1433  +  memset(&r, 0, sizeof(r));
  1434   1434   
  1435   1435     res = getrusage(RUSAGE_SELF, &r);
  1436   1436     assert( res==0 );
  1437   1437   
  1438   1438     printf("# getrusage: { ru_maxrss %d ru_oublock %d ru_inblock %d }\n", 
  1439   1439         (int)r.ru_maxrss, (int)r.ru_oublock, (int)r.ru_inblock
  1440   1440     );
................................................................................
  1454   1454       {"writespeed",  do_writer_test},
  1455   1455       {"io",          st_do_io},
  1456   1456   
  1457   1457       {"insert",      do_insert},
  1458   1458       {"replay",      do_replay},
  1459   1459   
  1460   1460       {"speed",       do_speed_tests},
  1461         -    {"speed2",       do_speed_test2},
         1461  +    {"speed2",      do_speed_test2},
  1462   1462       {"show",        st_do_show},
  1463   1463       {"work",        st_do_work},
  1464   1464       {"test",        do_test},
  1465   1465       {0, 0}
  1466   1466     };
  1467   1467     int rc;                         /* Return Code */
  1468   1468     int iFunc;                      /* Index into aTest[] */

Changes to lsm-test/lsmtest_tdb3.c.

   812    812         if( rc!=0 ) return rc;
   813    813         eParam = aParam[i].eParam;
   814    814   
   815    815         z++;
   816    816         zStart = z;
   817    817         while( *z>='0' && *z<='9' ) z++;
   818    818         if( *z=='k' || *z=='K' ){
   819         -        iMul = 1024;
          819  +        iMul = 1;
   820    820           z++;
   821    821         }else if( *z=='M' || *z=='M' ){
   822         -        iMul = 1024 * 1024;
          822  +        iMul = 1024;
   823    823           z++;
   824    824         }
   825    825         nParam = z-zStart;
   826    826         if( nParam==0 || nParam>sizeof(zParam)-1 ) goto syntax_error;
   827    827         memcpy(zParam, zStart, nParam);
   828    828         zParam[nParam] = '\0';
   829    829         iVal = atoi(zParam) * iMul;
................................................................................
   837    837             case TEST_NO_RECOVERY:
   838    838               if( pLsm ) pLsm->bNoRecovery = iVal;
   839    839               break;
   840    840             case TEST_MT_MODE:
   841    841               if( pLsm ) nThread = iVal;
   842    842               break;
   843    843             case TEST_MT_MIN_CKPT:
   844         -            if( pLsm && iVal>0 ) pLsm->nMtMinCkpt = iVal;
          844  +            if( pLsm && iVal>0 ) pLsm->nMtMinCkpt = iVal*1024;
   845    845               break;
   846    846             case TEST_MT_MAX_CKPT:
   847         -            if( pLsm && iVal>0 ) pLsm->nMtMaxCkpt = iVal;
          847  +            if( pLsm && iVal>0 ) pLsm->nMtMaxCkpt = iVal*1024;
   848    848               break;
   849    849   #ifdef HAVE_ZLIB
   850    850             case TEST_COMPRESSION:
   851    851               testConfigureCompression(db);
   852    852               break;
   853    853   #endif
   854    854           }

Changes to main.mk.

    40     40   # Once the macros above are defined, the rest of this make script will
    41     41   # build the SQLite library and testing tools.
    42     42   ################################################################################
    43     43   
    44     44   # FIXME:  Required options for now.
    45     45   #
    46     46   OPTS += -DLSM_MUTEX_NONE
    47         -OPTS += -DSQLITE4_DEBUG=1 -DLSM_DEBUG=1
           47  +#OPTS += -DSQLITE4_DEBUG=1 -DLSM_DEBUG=1
    48     48   OPTS += -DHAVE_GMTIME_R
    49     49   OPTS += -DHAVE_LOCALTIME_R
    50     50   OPTS += -DHAVE_MALLOC_USABLE_SIZE
    51     51   OPTS += -DHAVE_USLEEP
    52         -OPTS += -DSQLITE4_MEMDEBUG=1
           52  +#OPTS += -DSQLITE4_MEMDEBUG=1
    53     53   #OPTS += -DSQLITE4_NO_SYNC=1 -DLSM_NO_SYNC=1
    54     54   OPTS += -DSQLITE4_OMIT_ANALYZE
    55     55   OPTS += -DSQLITE4_OMIT_AUTOMATIC_INDEX
    56     56   OPTS += -DSQLITE4_OMIT_BTREECOUNT
    57     57   OPTS += -DSQLITE4_OMIT_VIRTUALTABLE=1
    58     58   OPTS += -DSQLITE4_OMIT_XFER_OPT
    59     59   OPTS += -DSQLITE4_THREADSAFE=0

Changes to src/lsmInt.h.

   166    166   */
   167    167   #define LSM_START_DELETE 0x01     /* Start of open-ended delete range */
   168    168   #define LSM_END_DELETE   0x02     /* End of open-ended delete range */
   169    169   #define LSM_POINT_DELETE 0x04     /* Delete this key */
   170    170   #define LSM_INSERT       0x08     /* Insert this key and value */
   171    171   #define LSM_SEPARATOR    0x10     /* True if entry is separator key only */
   172    172   #define LSM_SYSTEMKEY    0x20     /* True if entry is a system key (FREELIST) */
          173  +
          174  +#define LSM_CONTIGUOUS   0x40     /* Used in lsm_tree.c */
   173    175   
   174    176   /*
   175    177   ** A string that can grow by appending.
   176    178   */
   177    179   struct LsmString {
   178    180     lsm_env *pEnv;              /* Run-time environment */
   179    181     int n;                      /* Size of string.  -1 indicates error */

Changes to src/lsm_file.c.

  1328   1328         p = lsmMallocZeroRc(pFS->pEnv, sizeof(Page), &rc);
  1329   1329         if( rc ) return rc;
  1330   1330         fsPageAddToLru(pFS, p);
  1331   1331         p->pFS = pFS;
  1332   1332       }
  1333   1333       p->aData = &((u8 *)pFS->pMap)[pFS->nPagesize * (iReal-1)];
  1334   1334       p->iPg = iReal;
         1335  +    assert( (p->flags & PAGE_FREE)==0 );
  1335   1336     }else{
  1336   1337   
  1337   1338       /* Search the hash-table for the page */
  1338   1339       iHash = fsHashKey(pFS->nHash, iReal);
  1339   1340       for(p=pFS->apHash[iHash]; p; p=p->pHashNext){
  1340   1341         if( p->iPg==iReal) break;
  1341   1342       }

Changes to src/lsm_main.c.

   751    751   /*
   752    752   ** Open a new cursor handle. 
   753    753   **
   754    754   ** If there are currently no other open cursor handles, and no open write
   755    755   ** transaction, open a read transaction here.
   756    756   */
   757    757   int lsm_csr_open(lsm_db *pDb, lsm_cursor **ppCsr){
   758         -  int rc;                         /* Return code */
          758  +  int rc = LSM_OK;                /* Return code */
   759    759     MultiCursor *pCsr = 0;          /* New cursor object */
   760    760   
   761    761     /* Open a read transaction if one is not already open. */
   762    762     assert_db_state(pDb);
   763    763   
   764    764     if( pDb->pShmhdr==0 ){
   765    765       assert( pDb->bReadonly );
   766    766       rc = lsmBeginRoTrans(pDb);
   767         -  }else{
          767  +  }else if( pDb->iReader<0 ){
   768    768       rc = lsmBeginReadTrans(pDb);
   769    769     }
   770    770   
   771    771     /* Allocate the multi-cursor. */
   772    772     if( rc==LSM_OK ) rc = lsmMCursorNew(pDb, &pCsr);
   773    773   
   774    774     /* If an error has occured, set the output to NULL and delete any partially

Changes to src/lsm_shared.c.

  1333   1333     dbReleaseReadlock(pDb);
  1334   1334   }
  1335   1335   
  1336   1336   /*
  1337   1337   ** Open a write transaction.
  1338   1338   */
  1339   1339   int lsmBeginWriteTrans(lsm_db *pDb){
  1340         -  int rc;                         /* Return code */
         1340  +  int rc = LSM_OK;                /* Return code */
  1341   1341     ShmHeader *pShm = pDb->pShmhdr; /* Shared memory header */
  1342   1342   
  1343   1343     assert( pDb->nTransOpen==0 );
  1344   1344     assert( pDb->bDiscardOld==0 );
  1345   1345     assert( pDb->bReadonly==0 );
  1346   1346   
  1347   1347     /* If there is no read-transaction open, open one now. */
  1348         -  rc = lsmBeginReadTrans(pDb);
         1348  +  if( pDb->iReader<0 ){
         1349  +    rc = lsmBeginReadTrans(pDb);
         1350  +  }
  1349   1351   
  1350   1352     /* Attempt to take the WRITER lock */
  1351   1353     if( rc==LSM_OK ){
  1352   1354       rc = lsmShmLock(pDb, LSM_LOCK_WRITER, LSM_LOCK_EXCL, 0);
  1353   1355     }
  1354   1356   
  1355   1357     /* If the previous writer failed mid-transaction, run emergency rollback. */

Changes to src/lsm_tree.c.

   263    263   ** discarded.
   264    264   */
   265    265   static void intArrayTruncate(IntArray *p, int nVal){
   266    266     p->nArray = nVal;
   267    267   }
   268    268   /* End of IntArray methods.
   269    269   ***********************************************************************/
          270  +
          271  +static int treeKeycmp(void *p1, int n1, void *p2, int n2){
          272  +  int res;
          273  +  res = memcmp(p1, p2, LSM_MIN(n1, n2));
          274  +  if( res==0 ) res = (n1-n2);
          275  +  return res;
          276  +}
   270    277   
   271    278   /*
   272    279   ** The pointer passed as the first argument points to an interior node,
   273    280   ** not a leaf. This function returns the offset of the iCell'th child
   274    281   ** sub-tree of the node.
   275    282   */
   276    283   static u32 getChildPtr(TreeNode *p, int iVersion, int iCell){
................................................................................
   283    290   ** Given an offset within the *-shm file, return the associated chunk number.
   284    291   */
   285    292   static int treeOffsetToChunk(u32 iOff){
   286    293     assert( LSM_SHM_CHUNK_SIZE==(1<<15) );
   287    294     return (int)(iOff>>15);
   288    295   }
   289    296   
          297  +#define treeShmptrUnsafe(pDb, iPtr) \
          298  +(&((u8*)((pDb)->apShm[(iPtr)>>15]))[(iPtr) & (LSM_SHM_CHUNK_SIZE-1)])
          299  +
   290    300   /*
   291    301   ** Return a pointer to the mapped memory location associated with *-shm 
   292    302   ** file offset iPtr.
   293    303   */
   294    304   static void *treeShmptr(lsm_db *pDb, u32 iPtr){
   295    305   
   296    306     assert( (iPtr>>15)<pDb->nShm );
   297    307     assert( pDb->apShm[iPtr>>15] );
   298    308   
   299         -  return iPtr?(&((u8*)(pDb->apShm[iPtr>>15]))[iPtr & (LSM_SHM_CHUNK_SIZE-1)]):0;
          309  +  return iPtr ? treeShmptrUnsafe(pDb, iPtr) : 0;
   300    310   }
   301    311   
   302    312   static ShmChunk * treeShmChunk(lsm_db *pDb, int iChunk){
   303    313     return (ShmChunk *)(pDb->apShm[iChunk]);
   304    314   }
   305    315   
   306    316   static ShmChunk * treeShmChunkRc(lsm_db *pDb, int iChunk, int *pRc){
................................................................................
   561    571   }
   562    572   
   563    573   /*
   564    574   ** Return a pointer to the mapping of the TreeKey object that the cursor
   565    575   ** is pointing to. 
   566    576   */
   567    577   static TreeKey *csrGetKey(TreeCursor *pCsr, TreeBlob *pBlob, int *pRc){
   568         -  TreeKey *pRet = (TreeKey *)treeShmkey(pCsr->pDb,
   569         -      pCsr->apTreeNode[pCsr->iNode]->aiKeyPtr[pCsr->aiCell[pCsr->iNode]], 
   570         -      TKV_LOADVAL, pBlob, pRc
   571         -  );
   572         -  assert( pRet==0 || assertFlagsOk(pRet->flags) );
          578  +  TreeKey *pRet;
          579  +  lsm_db *pDb = pCsr->pDb;
          580  +  u32 iPtr = pCsr->apTreeNode[pCsr->iNode]->aiKeyPtr[pCsr->aiCell[pCsr->iNode]];
          581  +
          582  +  assert( iPtr );
          583  +  pRet = treeShmptrUnsafe(pDb, iPtr);
          584  +  if( !(pRet->flags & LSM_CONTIGUOUS) ){
          585  +    pRet = treeShmkey(pDb, iPtr, TKV_LOADVAL, pBlob, pRc);
          586  +  }
          587  +
   573    588     return pRet;
   574    589   }
   575    590   
   576    591   /*
   577    592   ** Save the current position of tree cursor pCsr.
   578    593   */
   579    594   int lsmTreeCursorSave(TreeCursor *pCsr){
................................................................................
   717    732     u32 *piPtr, 
   718    733     void *pKey, int nKey,           /* Key data */
   719    734     void *pVal, int nVal,           /* Value data (or nVal<0 for delete) */
   720    735     int *pRc
   721    736   ){
   722    737     TreeKey *p;
   723    738     u32 iPtr;
          739  +  u32 iEnd;
   724    740     int nRem;
   725    741     u8 *a;
   726    742     int n;
   727    743   
   728    744     /* Allocate space for the TreeKey structure itself */
   729    745     *piPtr = iPtr = treeShmalloc(pDb, 1, sizeof(TreeKey), pRc);
   730    746     p = treeShmptr(pDb, iPtr);
................................................................................
   750    766         memcpy(aAlloc, &a[n-nRem], nAlloc);
   751    767         nRem -= nAlloc;
   752    768       }
   753    769       a = pVal;
   754    770       n = nRem = nVal;
   755    771       pVal = 0;
   756    772     }
          773  +
          774  +  iEnd = iPtr + sizeof(TreeKey) + nKey + LSM_MAX(0, nVal);
          775  +  if( (iPtr & ~(LSM_SHM_CHUNK_SIZE-1))!=(iEnd & ~(LSM_SHM_CHUNK_SIZE-1)) ){
          776  +    p->flags = 0;
          777  +  }else{
          778  +    p->flags = LSM_CONTIGUOUS;
          779  +  }
   757    780   
   758    781     if( *pRc ) return 0;
   759    782   #if 0
   760    783     printf("store: %d %s\n", (int)iPtr, (char *)pKey);
   761    784   #endif
   762    785     return p;
   763    786   }
................................................................................
  1436   1459     int res;                        /* Result of seek operation on csr */
  1437   1460   
  1438   1461     assert( nVal>=0 || pVal==0 );
  1439   1462     assert_tree_looks_ok(LSM_OK, pTree);
  1440   1463     assert( flags==LSM_INSERT       || flags==LSM_POINT_DELETE 
  1441   1464          || flags==LSM_START_DELETE || flags==LSM_END_DELETE 
  1442   1465     );
         1466  +  assert( (flags & LSM_CONTIGUOUS)==0 );
  1443   1467   #if 0
  1444   1468     dump_tree_contents(pDb, "before");
  1445   1469   #endif
  1446   1470   
  1447   1471     if( p->iRoot ){
  1448   1472       TreeKey *pRes;                /* Key at end of seek operation */
  1449   1473       treeCursorInit(pDb, 0, &csr);
................................................................................
  1493   1517     }else{
  1494   1518       memset(&csr, 0, sizeof(TreeCursor));
  1495   1519     }
  1496   1520   
  1497   1521     /* Allocate and populate a new key-value pair structure */
  1498   1522     pTreeKey = newTreeKey(pDb, &iTreeKey, pKey, nKey, pVal, nVal, &rc);
  1499   1523     if( rc!=LSM_OK ) return rc;
  1500         -  pTreeKey->flags = flags;
         1524  +  assert( pTreeKey->flags==0 || pTreeKey->flags==LSM_CONTIGUOUS );
         1525  +  pTreeKey->flags |= flags;
  1501   1526   
  1502   1527     if( p->iRoot==0 ){
  1503   1528       /* The tree is completely empty. Add a new root node and install
  1504   1529       ** (pKey/nKey) as the middle entry. Even though it is a leaf at the
  1505   1530       ** moment, use newTreeNode() to allocate the node (i.e. allocate enough
  1506   1531       ** space for the fields used by interior nodes). This is because the
  1507   1532       ** treeInsert() routine may convert this node to an interior node. */
................................................................................
  1779   1804   ){
  1780   1805     int rc = LSM_OK;
  1781   1806     int bDone = 0;
  1782   1807     TreeRoot *p = &db->treehdr.root;
  1783   1808     TreeBlob blob = {0, 0};
  1784   1809   
  1785   1810     /* The range must be sensible - that (key1 < key2). */
  1786         -  assert( db->xCmp(pKey1, nKey1, pKey2, nKey2)<0 );
         1811  +  assert( treeKeycmp(pKey1, nKey1, pKey2, nKey2)<0 );
  1787   1812     assert( assert_delete_ranges_match(db) );
  1788   1813   
  1789   1814   #if 0
  1790   1815     static int nCall = 0;
  1791   1816     printf("\n");
  1792   1817     nCall++;
  1793   1818     printf("%d delete %s .. %s\n", nCall, (char *)pKey1, (char *)pKey2);
................................................................................
  1811   1836   
  1812   1837       /* If there is no such entry, or if it is greater than pKey2, then the
  1813   1838       ** tree now contains no keys in the range being deleted. In this case
  1814   1839       ** break out of the loop.  */
  1815   1840       bDone = 1;
  1816   1841       if( lsmTreeCursorValid(&csr) ){
  1817   1842         lsmTreeCursorKey(&csr, 0, &pDel, &nDel);
  1818         -      if( db->xCmp(pDel, nDel, pKey2, nKey2)<0 ) bDone = 0;
         1843  +      if( treeKeycmp(pDel, nDel, pKey2, nKey2)<0 ) bDone = 0;
  1819   1844       }
  1820   1845   
  1821   1846       if( bDone==0 ){
  1822   1847         if( csr.iNode==(p->nHeight-1) ){
  1823   1848           /* The element to delete already lies on a leaf node */
  1824   1849           rc = treeDeleteEntry(db, &csr, 0);
  1825   1850         }else{
................................................................................
  1928   1953   static int treeCsrCompare(TreeCursor *pCsr, void *pKey, int nKey){
  1929   1954     TreeKey *p;
  1930   1955     int cmp = 0;
  1931   1956     int rc = LSM_OK;
  1932   1957     assert( pCsr->iNode>=0 );
  1933   1958     p = csrGetKey(pCsr, &pCsr->blob, &rc);
  1934   1959     if( p ){
  1935         -    cmp = pCsr->pDb->xCmp(TKV_KEY(p), p->nKey, pKey, nKey);
         1960  +    cmp = treeKeycmp(TKV_KEY(p), p->nKey, pKey, nKey);
  1936   1961     }
  1937   1962     return cmp;
  1938   1963   }
  1939   1964   #endif
  1940   1965   
  1941   1966   
  1942   1967   /*
................................................................................
  1953   1978   **
  1954   1979   **   * If the tree is empty, leave the cursor at EOF and set *pRes to -1.
  1955   1980   */
  1956   1981   int lsmTreeCursorSeek(TreeCursor *pCsr, void *pKey, int nKey, int *pRes){
  1957   1982     int rc = LSM_OK;                /* Return code */
  1958   1983     lsm_db *pDb = pCsr->pDb;
  1959   1984     TreeRoot *pRoot = pCsr->pRoot;
  1960         -  int (*xCmp)(void *, int, void *, int) = pDb->xCmp;
  1961         -
  1962   1985     u32 iNodePtr;                   /* Location of current node in search */
  1963   1986   
  1964   1987     /* Discard any saved position data */
  1965   1988     treeCursorRestore(pCsr, 0);
  1966   1989   
  1967   1990     iNodePtr = pRoot->iRoot;
  1968   1991     if( iNodePtr==0 ){
................................................................................
  1973   1996     }else{
  1974   1997       TreeBlob b = {0, 0};
  1975   1998       int res = 0;                  /* Result of comparison function */
  1976   1999       int iNode = -1;
  1977   2000       while( iNodePtr ){
  1978   2001         TreeNode *pNode;            /* Node at location iNodePtr */
  1979   2002         int iTest;                  /* Index of second key to test (0 or 2) */
         2003  +      u32 iTreeKey;
  1980   2004         TreeKey *pTreeKey;          /* Key to compare against */
  1981   2005   
  1982         -      pNode = (TreeNode *)treeShmptr(pDb, iNodePtr);
         2006  +      pNode = (TreeNode *)treeShmptrUnsafe(pDb, iNodePtr);
  1983   2007         iNode++;
  1984   2008         pCsr->apTreeNode[iNode] = pNode;
  1985   2009   
  1986   2010         /* Compare (pKey/nKey) with the key in the middle slot of B-tree node
  1987   2011         ** pNode. The middle slot is never empty. If the comparison is a match,
  1988   2012         ** then the search is finished. Break out of the loop. */
  1989         -      pTreeKey = treeShmkey(pDb, pNode->aiKeyPtr[1], TKV_LOADKEY, &b, &rc);
  1990         -      if( rc!=LSM_OK ) break;
  1991         -      res = xCmp((void *)&pTreeKey[1], pTreeKey->nKey, pKey, nKey);
         2013  +      pTreeKey = treeShmptrUnsafe(pDb, pNode->aiKeyPtr[1]);
         2014  +      if( !(pTreeKey->flags & LSM_CONTIGUOUS) ){
         2015  +        pTreeKey = treeShmkey(pDb, pNode->aiKeyPtr[1], TKV_LOADKEY, &b, &rc);
         2016  +        if( rc!=LSM_OK ) break;
         2017  +      }
         2018  +      res = treeKeycmp((void *)&pTreeKey[1], pTreeKey->nKey, pKey, nKey);
  1992   2019         if( res==0 ){
  1993   2020           pCsr->aiCell[iNode] = 1;
  1994   2021           break;
  1995   2022         }
  1996   2023   
  1997   2024         /* Based on the results of the previous comparison, compare (pKey/nKey)
  1998   2025         ** to either the left or right key of the B-tree node, if such a key
  1999   2026         ** exists. */
  2000   2027         iTest = (res>0 ? 0 : 2);
  2001         -      pTreeKey = treeShmkey(pDb, pNode->aiKeyPtr[iTest], TKV_LOADKEY, &b, &rc);
  2002         -      if( rc ) break;
  2003         -      if( pTreeKey==0 ){
  2004         -        iTest = 1;
  2005         -      }else{
  2006         -        res = xCmp((void *)&pTreeKey[1], pTreeKey->nKey, pKey, nKey);
         2028  +      iTreeKey = pNode->aiKeyPtr[iTest];
         2029  +      if( iTreeKey ){
         2030  +        pTreeKey = treeShmptrUnsafe(pDb, iTreeKey);
         2031  +        if( !(pTreeKey->flags & LSM_CONTIGUOUS) ){
         2032  +          pTreeKey = treeShmkey(pDb, iTreeKey, TKV_LOADKEY, &b, &rc);
         2033  +          if( rc ) break;
         2034  +        }
         2035  +        res = treeKeycmp((void *)&pTreeKey[1], pTreeKey->nKey, pKey, nKey);
  2007   2036           if( res==0 ){
  2008   2037             pCsr->aiCell[iNode] = iTest;
  2009   2038             break;
  2010   2039           }
         2040  +      }else{
         2041  +        iTest = 1;
  2011   2042         }
  2012   2043   
  2013   2044         if( iNode<(pRoot->nHeight-1) ){
  2014   2045           iNodePtr = getChildPtr(pNode, pRoot->iTransId, iTest + (res<0));
  2015   2046         }else{
  2016   2047           iNodePtr = 0;
  2017   2048         }
................................................................................
  2088   2119         if( iCell<3 && pCsr->apTreeNode[pCsr->iNode]->aiKeyPtr[iCell] ) break;
  2089   2120       }
  2090   2121     }
  2091   2122   
  2092   2123   #ifndef NDEBUG
  2093   2124     if( pCsr->iNode>=0 ){
  2094   2125       TreeKey *pK2 = csrGetKey(pCsr, &pCsr->blob, &rc);
  2095         -    assert( rc || pDb->xCmp(TKV_KEY(pK2),pK2->nKey,TKV_KEY(pK1),pK1->nKey)>=0 );
         2126  +    assert( rc||treeKeycmp(TKV_KEY(pK2),pK2->nKey,TKV_KEY(pK1),pK1->nKey)>=0 );
  2096   2127     }
  2097   2128     tblobFree(pDb, &key1);
  2098   2129   #endif
  2099   2130   
  2100   2131     return rc;
  2101   2132   }
  2102   2133   
................................................................................
  2156   2187       }while( (--pCsr->iNode)>=0 );
  2157   2188       pCsr->aiCell[pCsr->iNode] = iCell;
  2158   2189     }
  2159   2190   
  2160   2191   #ifndef NDEBUG
  2161   2192     if( pCsr->iNode>=0 ){
  2162   2193       TreeKey *pK2 = csrGetKey(pCsr, &pCsr->blob, &rc);
  2163         -    assert( rc || pDb->xCmp(TKV_KEY(pK2), pK2->nKey, TKV_KEY(pK1), pK1->nKey)<0 );
         2194  +    assert( rc || treeKeycmp(TKV_KEY(pK2),pK2->nKey,TKV_KEY(pK1),pK1->nKey)<0 );
  2164   2195     }
  2165   2196     tblobFree(pDb, &key1);
  2166   2197   #endif
  2167   2198   
  2168   2199     return rc;
  2169   2200   }
  2170   2201   
................................................................................
  2210   2241     return rc;
  2211   2242   }
  2212   2243   
  2213   2244   int lsmTreeCursorFlags(TreeCursor *pCsr){
  2214   2245     int flags = 0;
  2215   2246     if( pCsr && pCsr->iNode>=0 ){
  2216   2247       int rc = LSM_OK;
  2217         -    TreeKey *pKey = (TreeKey *)treeShmptr(pCsr->pDb,
         2248  +    TreeKey *pKey = (TreeKey *)treeShmptrUnsafe(pCsr->pDb,
  2218   2249           pCsr->apTreeNode[pCsr->iNode]->aiKeyPtr[pCsr->aiCell[pCsr->iNode]]
  2219   2250       );
  2220   2251       assert( rc==LSM_OK );
  2221         -    flags = pKey->flags;
         2252  +    flags = (pKey->flags & ~LSM_CONTIGUOUS);
  2222   2253     }
  2223   2254     return flags;
  2224   2255   }
  2225   2256   
  2226   2257   int lsmTreeCursorKey(TreeCursor *pCsr, int *pFlags, void **ppKey, int *pnKey){
  2227   2258     TreeKey *pTreeKey;
  2228   2259     int rc = LSM_OK;

Changes to src/vdbe.c.

  2569   2569       p->expired = 0;
  2570   2570     }
  2571   2571     break;
  2572   2572   }
  2573   2573   
  2574   2574   /* Opcode: VerifyCookie P1 P2 P3 * *
  2575   2575   **
  2576         -** cHECK THe value of global database parameter number 0 (the
         2576  +** Check the value of global database parameter number 0 (the
  2577   2577   ** schema version) and make sure it is equal to P2 and that the
  2578   2578   ** generation counter on the local schema parse equals P3.
  2579   2579   **
  2580   2580   ** P1 is the database number which is 0 for the main database file
  2581   2581   ** and 1 for the file holding temporary tables and some higher number
  2582   2582   ** for auxiliary databases.
  2583   2583   **