SQLite4
Check-in [bb85de9cd3]
Not logged in

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

Overview
Comment:Add the INFO_COMPRESSION_ID request. And the factory method for providing compression/encryption functions.
Downloads: Tarball | ZIP archive | SQL archive
Timelines: family | ancestors | descendants | both | compression-id
Files: files | file ages | folders
SHA1: bb85de9cd3a59efdfdc92ae6b87dc73b81ca63e3
User & Date: dan 2013-02-07 19:50:51
Context
2013-02-08
11:30
Merge compression-id branch with trunk. check-in: 76297939d3 user: dan tags: trunk
2013-02-07
19:50
Add the INFO_COMPRESSION_ID request. And the factory method for providing compression/encryption functions. Leaf check-in: bb85de9cd3 user: dan tags: compression-id
2013-02-06
19:43
Add API to register a compression-factory method with an lsm handle. check-in: 60908fd4d1 user: dan tags: compression-id
Changes
Hide Diffs Side-by-Side Diffs Ignore Whitespace Patch

Changes to src/lsm.h.

   299    299     int (*xFactory)(void *, lsm_db *, unsigned int);
   300    300     void (*xFree)(void *pCtx);
   301    301   };
   302    302   
   303    303   #define LSM_COMPRESSION_EMPTY 0
   304    304   #define LSM_COMPRESSION_NONE  1
   305    305   
   306         -
   307    306   /*
   308    307   ** CAPI: Allocating and Freeing Memory
   309    308   **
   310    309   ** Invoke the memory allocation functions that belong to environment
   311    310   ** pEnv. Or the system defaults if no memory allocation functions have 
   312    311   ** been registered.
   313    312   */
................................................................................
   429    428   **   accumulate new data written to the database. The other tree structure -
   430    429   **   the old tree - is a read-only tree holding older data and may be flushed 
   431    430   **   to disk at any time.
   432    431   ** 
   433    432   **   Assuming no error occurs, the location pointed to by the first of the two
   434    433   **   (int *) arguments is set to the size of the old in-memory tree in KB.
   435    434   **   The second is set to the size of the current, or live in-memory tree.
          435  +**
          436  +** LSM_INFO_COMPRESSION_ID:
          437  +**   This value should be followed by a single argument of type 
          438  +**   (unsigned int *). If successful, the location pointed to is populated 
          439  +**   with the database compression id before returning.
   436    440   */
   437    441   #define LSM_INFO_NWRITE           1
   438    442   #define LSM_INFO_NREAD            2
   439    443   #define LSM_INFO_DB_STRUCTURE     3
   440    444   #define LSM_INFO_LOG_STRUCTURE    4
   441    445   #define LSM_INFO_ARRAY_STRUCTURE  5
   442    446   #define LSM_INFO_PAGE_ASCII_DUMP  6
   443    447   #define LSM_INFO_PAGE_HEX_DUMP    7
   444    448   #define LSM_INFO_FREELIST         8
   445    449   #define LSM_INFO_ARRAY_PAGES      9
   446    450   #define LSM_INFO_CHECKPOINT_SIZE 10
   447    451   #define LSM_INFO_TREE_SIZE       11
   448         -
   449    452   #define LSM_INFO_FREELIST_SIZE   12
          453  +#define LSM_INFO_COMPRESSION_ID  13
   450    454   
   451    455   
   452    456   /* 
   453    457   ** CAPI: Opening and Closing Write Transactions
   454    458   **
   455    459   ** These functions are used to open and close transactions and nested 
   456    460   ** sub-transactions.

Changes to src/lsmInt.h.

   571    571   
   572    572   int lsmCheckpointSaveWorker(lsm_db *pDb, int);
   573    573   int lsmDatabaseFull(lsm_db *pDb);
   574    574   int lsmCheckpointSynced(lsm_db *pDb, i64 *piId, i64 *piLog, u32 *pnWrite);
   575    575   
   576    576   int lsmCheckpointSize(lsm_db *db, int *pnByte);
   577    577   
          578  +int lsmInfoCompressionId(lsm_db *db, u32 *piCmpId);
          579  +
   578    580   /* 
   579    581   ** Functions from file "lsm_tree.c".
   580    582   */
   581    583   int lsmTreeNew(lsm_env *, int (*)(void *, int, void *, int), Tree **ppTree);
   582    584   void lsmTreeRelease(lsm_env *, Tree *);
   583    585   int lsmTreeInit(lsm_db *);
   584    586   int lsmTreeRepair(lsm_db *);
................................................................................
   589    591   
   590    592   int lsmTreeSize(lsm_db *);
   591    593   int lsmTreeEndTransaction(lsm_db *pDb, int bCommit);
   592    594   int lsmTreeLoadHeader(lsm_db *pDb, int *);
   593    595   int lsmTreeLoadHeaderOk(lsm_db *, int);
   594    596   
   595    597   int lsmTreeInsert(lsm_db *pDb, void *pKey, int nKey, void *pVal, int nVal);
          598  +int lsmTreeDelete(lsm_db *db, void *pKey1, int nKey1, void *pKey2, int nKey2);
   596    599   void lsmTreeRollback(lsm_db *pDb, TreeMark *pMark);
   597    600   void lsmTreeMark(lsm_db *pDb, TreeMark *pMark);
   598    601   
   599    602   int lsmTreeCursorNew(lsm_db *pDb, int, TreeCursor **);
   600    603   void lsmTreeCursorDestroy(TreeCursor *);
   601    604   
   602    605   int lsmTreeCursorSeek(TreeCursor *pCsr, void *pKey, int nKey, int *pRes);

Changes to src/lsm_ckpt.c.

   447    447         ckptSetValue(&ckpt, iOut++, (p->iId >> 32) & 0xFFFFFFFF, &rc);
   448    448         ckptSetValue(&ckpt, iOut++, p->iId & 0xFFFFFFFF, &rc);
   449    449       }
   450    450     }
   451    451   
   452    452     /* Write the checkpoint header */
   453    453     assert( iId>=0 );
          454  +  assert( pSnap->iCmpId==pDb->compress.iId
          455  +       || pSnap->iCmpId==LSM_COMPRESSION_EMPTY 
          456  +  );
   454    457     ckptSetValue(&ckpt, CKPT_HDR_ID_MSW, (u32)(iId>>32), &rc);
   455    458     ckptSetValue(&ckpt, CKPT_HDR_ID_LSW, (u32)(iId&0xFFFFFFFF), &rc);
   456    459     ckptSetValue(&ckpt, CKPT_HDR_NCKPT, iOut+2, &rc);
   457         -  ckptSetValue(&ckpt, CKPT_HDR_CMPID, pSnap->iCmpId, &rc);
          460  +  ckptSetValue(&ckpt, CKPT_HDR_CMPID, pDb->compress.iId, &rc);
   458    461     ckptSetValue(&ckpt, CKPT_HDR_NBLOCK, pSnap->nBlock, &rc);
   459    462     ckptSetValue(&ckpt, CKPT_HDR_BLKSZ, lsmFsBlockSize(pFS), &rc);
   460    463     ckptSetValue(&ckpt, CKPT_HDR_NLEVEL, nLevel, &rc);
   461    464     ckptSetValue(&ckpt, CKPT_HDR_PGSZ, lsmFsPageSize(pFS), &rc);
   462    465     ckptSetValue(&ckpt, CKPT_HDR_NWRITE, pSnap->nWrite, &rc);
   463    466   
   464    467     if( bCksum ){
................................................................................
   878    881         }
   879    882       }
   880    883   
   881    884       lsmShmBarrier(pDb);
   882    885     }
   883    886     return LSM_PROTOCOL;
   884    887   }
          888  +
          889  +int lsmInfoCompressionId(lsm_db *db, u32 *piCmpId){
          890  +  int rc;
          891  +
          892  +  assert( db->pClient==0 && db->pWorker==0 );
          893  +  rc = lsmCheckpointLoad(db, 0);
          894  +  if( rc==LSM_OK ){
          895  +    *piCmpId = db->aSnapshot[CKPT_HDR_CMPID];
          896  +  }
          897  +
          898  +  return rc;
          899  +}
   885    900   
   886    901   int lsmCheckpointLoadOk(lsm_db *pDb, int iSnap){
   887    902     u32 *aShm;
   888    903     assert( iSnap==1 || iSnap==2 );
   889    904     aShm = (iSnap==1) ? pDb->pShmhdr->aSnap1 : pDb->pShmhdr->aSnap2;
   890    905     return (lsmCheckpointId(pDb->aSnapshot, 0)==lsmCheckpointId(aShm, 0) );
   891    906   }
................................................................................
   954    969       int iIn = CKPT_HDR_SIZE + CKPT_APPENDLIST_SIZE + CKPT_LOGPTR_SIZE;
   955    970   
   956    971       pNew->iId = lsmCheckpointId(aCkpt, 0);
   957    972       pNew->nBlock = aCkpt[CKPT_HDR_NBLOCK];
   958    973       pNew->nWrite = aCkpt[CKPT_HDR_NWRITE];
   959    974       rc = ckptLoadLevels(pDb, aCkpt, &iIn, nLevel, &pNew->pLevel);
   960    975       pNew->iLogOff = lsmCheckpointLogOffset(aCkpt);
   961         -
   962    976       pNew->iCmpId = aCkpt[CKPT_HDR_CMPID];
   963         -    if( pNew->iCmpId==LSM_COMPRESSION_EMPTY ){
   964         -      pNew->iCmpId = pDb->compress.iId;
   965         -    }
   966    977   
   967    978       /* Make a copy of the append-list */
   968    979       for(i=0; i<LSM_APPLIST_SZ; i++){
   969    980         u32 *a = &aCkpt[CKPT_HDR_SIZE + CKPT_LOGPTR_SIZE + i*2];
   970    981         pNew->aiAppend[i] = ckptRead64(a);
   971    982       }
   972    983   

Changes to src/lsm_main.c.

   455    455     return 0;
   456    456   }
   457    457   
   458    458   int lsmInfoFreelist(lsm_db *pDb, char **pzOut){
   459    459     Snapshot *pWorker;              /* Worker snapshot */
   460    460     int bUnlock = 0;
   461    461     LsmString s;
   462         -  int i;
   463    462     int rc;
   464    463   
   465    464     /* Obtain the worker snapshot */
   466    465     rc = infoGetWorker(pDb, &pWorker, &bUnlock);
   467    466     if( rc!=LSM_OK ) return rc;
   468    467   
   469    468     lsmStringInit(&s, pDb->pEnv);
................................................................................
   475    474     }
   476    475   
   477    476     /* Release the snapshot and return */
   478    477     infoFreeWorker(pDb, bUnlock);
   479    478     return rc;
   480    479   }
   481    480   
   482         -static int infoFreelistSize(lsm_db *pDb, int *pnFree, int *pnWaiting){
   483         -}
   484         -
   485    481   static int infoTreeSize(lsm_db *db, int *pnOldKB, int *pnNewKB){
   486    482     ShmHeader *pShm = db->pShmhdr;
   487    483     TreeHeader *p = &pShm->hdr1;
   488    484   
   489    485     /* The following code suffers from two race conditions, as it accesses and
   490    486     ** trusts the contents of shared memory without verifying checksums:
   491    487     **
................................................................................
   591    587   
   592    588       case LSM_INFO_TREE_SIZE: {
   593    589         int *pnOld = va_arg(ap, int *);
   594    590         int *pnNew = va_arg(ap, int *);
   595    591         rc = infoTreeSize(pDb, pnOld, pnNew);
   596    592         break;
   597    593       }
          594  +
          595  +    case LSM_INFO_COMPRESSION_ID: {
          596  +      unsigned int *piOut = va_arg(ap, unsigned int *);
          597  +      if( pDb->pClient ){
          598  +        *piOut = pDb->pClient->iCmpId;
          599  +      }else{
          600  +        rc = lsmInfoCompressionId(pDb, piOut);
          601  +      }
          602  +      break;
          603  +    }
   598    604   
   599    605       default:
   600    606         rc = LSM_MISUSE;
   601    607         break;
   602    608     }
   603    609   
   604    610     va_end(ap);
................................................................................
   862    868     assert_db_state( pDb );
   863    869   
   864    870     /* A value less than zero means close the innermost nested transaction. */
   865    871     if( iLevel<0 ) iLevel = LSM_MAX(0, pDb->nTransOpen - 1);
   866    872   
   867    873     if( iLevel<pDb->nTransOpen ){
   868    874       if( iLevel==0 ){
   869         -      int bAutowork = 0;
   870         -
   871    875         /* Commit the transaction to disk. */
   872    876         if( rc==LSM_OK ) rc = lsmLogCommit(pDb);
   873    877         if( rc==LSM_OK && pDb->eSafety==LSM_SAFETY_FULL ){
   874    878           rc = lsmFsSyncLog(pDb->pFS);
   875    879         }
   876    880         lsmFinishWriteTrans(pDb, (rc==LSM_OK));
   877    881       }

Changes to src/lsm_shared.c.

   912    912       lsmFreeSnapshot(pDb->pEnv, pDb->pWorker);
   913    913       pDb->pWorker = 0;
   914    914     }
   915    915   
   916    916     lsmShmLock(pDb, LSM_LOCK_WORKER, LSM_LOCK_UNLOCK, 0);
   917    917   }
   918    918   
   919         -
   920    919   /*
   921    920   ** Called when recovery is finished.
   922    921   */
   923    922   int lsmFinishRecovery(lsm_db *pDb){
   924    923     lsmTreeEndTransaction(pDb, 1);
   925    924     return LSM_OK;
   926    925   }
   927    926   
          927  +/*
          928  +** Check if the currently configured compression functions
          929  +** (LSM_CONFIG_SET_COMPRESSION) are compatible with a database that has its
          930  +** compression id set to iReq. Compression routines are compatible if iReq
          931  +** is zero (indicating the database is empty), or if it is equal to the 
          932  +** compression id of the configured compression routines.
          933  +**
          934  +** If the check shows that the current compression are incompatible and there
          935  +** is a compression factory registered, give it a chance to install new
          936  +** compression routines.
          937  +**
          938  +** If, after any registered factory is invoked, the compression functions
          939  +** are still incompatible, return LSM_MISMATCH. Otherwise, LSM_OK.
          940  +*/
   928    941   int lsmCheckCompressionId(lsm_db *pDb, u32 iReq){
   929         -  if( pDb->compress.iId!=iReq ){
          942  +  if( iReq!=LSM_COMPRESSION_EMPTY && pDb->compress.iId!=iReq ){
   930    943       if( pDb->factory.xFactory ){
   931    944         pDb->bInFactory = 1;
   932    945         pDb->factory.xFactory(pDb->factory.pCtx, pDb, iReq);
   933    946         pDb->bInFactory = 0;
   934    947       }
   935    948       if( pDb->compress.iId!=iReq ){
          949  +      /* Incompatible */
   936    950         return LSM_MISMATCH;
   937    951       }
   938    952     }
          953  +  /* Compatible */
   939    954     return LSM_OK;
   940    955   }
   941    956   
   942    957   /*
   943    958   ** Begin a read transaction. This function is a no-op if the connection
   944    959   ** passed as the only argument already has an open read transaction.
   945    960   */
................................................................................
   991   1006               rc = lsmCheckpointDeserialize(pDb, 0, pDb->aSnapshot,&pDb->pClient);
   992   1007             }
   993   1008             assert( (rc==LSM_OK)==(pDb->pClient!=0) );
   994   1009             assert( pDb->iReader>=0 );
   995   1010   
   996   1011             /* Check that the client has the right compression hooks loaded.
   997   1012             ** If not, set rc to LSM_MISMATCH.  */
   998         -          assert( rc!=LSM_OK || pDb->pClient->iCmpId!=LSM_COMPRESSION_EMPTY );
   999         -          if( rc==LSM_OK && pDb->pClient->iCmpId!=pDb->compress.iId ){
         1013  +          if( rc==LSM_OK ){
  1000   1014               rc = lsmCheckCompressionId(pDb, pDb->pClient->iCmpId);
  1001   1015             }
  1002   1016           }else{
  1003   1017             rc = lsmReleaseReadlock(pDb);
  1004   1018           }
  1005   1019         }
  1006   1020   

Changes to test/lsm4.test.

    11     11   #
    12     12   
    13     13   set testdir [file dirname $argv0]
    14     14   source $testdir/tester.tcl
    15     15   set testprefix lsm4
    16     16   db close
    17     17   
           18  +# Compression scheme ids (defined in test_lsm.c):
           19  +#
           20  +set compression_id(encrypt) 43
           21  +set compression_id(rle)     44
           22  +set compression_id(noop)    45
           23  +
    18     24   proc db_fetch {db key} {
    19     25     db csr_open csr
    20     26     csr seek $key eq
    21     27     set ret [csr value]
    22     28     csr close
    23     29     set ret
    24     30   }
................................................................................
    51     57   } {1 {error in lsm_csr_open() - 50}}
    52     58   
    53     59   do_test 1.5 {
    54     60     db config {set_compression_factory true}
    55     61     list [db_fetch db 1] [db_fetch db 2]
    56     62   } {abc def}
    57     63   
           64  +do_test 1.6 { db info compression_id } $compression_id(noop)
           65  +db close
           66  +
           67  +#-------------------------------------------------------------------------
           68  +#
           69  +forcedelete test.db
           70  +
           71  +do_test 2.1 {
           72  +  lsm_open db test.db
           73  +  db info compression_id
           74  +} {0}
           75  +
           76  +do_test 2.2 {
           77  +  db write 1 abc
           78  +  db write 2 abc
           79  +  db info compression_id
           80  +} {0}
           81  +
           82  +do_test 2.3 {
           83  +  lsm_open db2 test.db
           84  +  db2 info compression_id
           85  +} {0}
           86  +
           87  +do_test 2.4 {
           88  +  db close
           89  +  db2 info compression_id
           90  +} {0}
           91  +
           92  +do_test 2.5 {
           93  +  db2 close
           94  +  lsm_open db test.db
           95  +  db info compression_id
           96  +} {1}
           97  +
           98  +db close
           99  +forcedelete test.db
          100  +
          101  +do_test 2.6 {
          102  +  lsm_open db test.db
          103  +  db config {set_compression rle}
          104  +  db write 3 three
          105  +  db write 4 four
          106  +  db close
          107  +
          108  +  lsm_open db test.db
          109  +  db info compression_id
          110  +} $compression_id(rle)
    58    111   
          112  +do_test 2.7 {
          113  +  db config {set_compression rle}
          114  +  list [db_fetch db 3] [db_fetch db 4]
          115  +} {three four}
    59    116   
    60    117   finish_test
    61    118   

Changes to test/test_lsm.c.

    18     18   #include <string.h>
    19     19   
    20     20   extern int getDbPointer(Tcl_Interp *interp, const char *zA, sqlite4 **ppDb);
    21     21   extern const char *sqlite4TestErrorName(int);
    22     22   
    23     23   /*************************************************************************
    24     24   */
    25         -#define ENCRYPTION_XOR_MASK 0xa3b2bbb6
           25  +#define ENCRYPTION_XOR_MASK 0x23b2bbb6
    26     26   static int testCompressEncBound(void *pCtx, int nSrc){
    27     27     return nSrc;
    28     28   }
    29     29   static int testCompressEncCompress(
    30     30     void *pCtx, 
    31     31     char *pOut, int *pnOut, 
    32     32     const char *pIn, int nIn
................................................................................
    44     44     return LSM_OK;
    45     45   }
    46     46   static int testCompressEncUncompress(
    47     47     void *pCtx, 
    48     48     char *pOut, int *pnOut, 
    49     49     const char *pIn, int nIn
    50     50   ){
    51         -  return testCompressEncUncompress(pCtx, pOut, pnOut, pIn, nIn);
           51  +  return testCompressEncCompress(pCtx, pOut, pnOut, pIn, nIn);
    52     52   }
    53     53   static void testCompressEncFree(void *pCtx){
    54     54     /* no-op */
    55     55   }
    56     56   /* 
    57     57   ** End of compression routines "encrypt".
    58     58   *************************************************************************/
................................................................................
   535    535           }
   536    536         }
   537    537       }
   538    538     }
   539    539   
   540    540     return rc;
   541    541   }
          542  +
   542    543   
   543    544   typedef struct TclLsmCursor TclLsmCursor;
   544    545   typedef struct TclLsm TclLsm;
   545    546   
   546    547   struct TclLsm {
   547    548     lsm_db *db;
   548    549   };
................................................................................
   574    575   static void test_lsm_del(void *ctx){
   575    576     TclLsm *p = (TclLsm *)ctx;
   576    577     if( p ){
   577    578       lsm_close(p->db);
   578    579       ckfree((char *)p);
   579    580     }
   580    581   }
          582  +
          583  +static int testInfoLsm(Tcl_Interp *interp, lsm_db *db, Tcl_Obj *pObj){
          584  +  struct Lsminfo {
          585  +    const char *zOpt;
          586  +    int eOpt;
          587  +  } aInfo[] = {
          588  +    { "compression_id",          LSM_INFO_COMPRESSION_ID },
          589  +    { 0, 0 }
          590  +  };
          591  +  int rc;
          592  +  int iOpt;
          593  +
          594  +  rc = Tcl_GetIndexFromObjStruct(
          595  +      interp, pObj, aInfo, sizeof(aInfo[0]), "option", 0, &iOpt
          596  +  );
          597  +  if( rc==LSM_OK ){
          598  +    switch( aInfo[iOpt].eOpt ){
          599  +      case LSM_INFO_COMPRESSION_ID: {
          600  +        unsigned int iCmpId = 0;
          601  +        rc = lsm_info(db, LSM_INFO_COMPRESSION_ID, &iCmpId);
          602  +        if( rc==LSM_OK ){
          603  +          Tcl_SetObjResult(interp, Tcl_NewWideIntObj((Tcl_WideInt)iCmpId));
          604  +        }else{
          605  +          test_lsm_error(interp, "lsm_info", rc);
          606  +        }
          607  +        break;
          608  +      }
          609  +    }
          610  +  }
          611  +
          612  +  return rc;
          613  +}
   581    614   
   582    615   /*
   583    616   ** Usage: CSR sub-command ...
   584    617   */
   585    618   static int test_lsm_cursor_cmd(
   586    619     void * clientData,
   587    620     Tcl_Interp *interp,
................................................................................
   720    753       /*  5 */ {"commit",       1, "LEVEL"},
   721    754       /*  6 */ {"rollback",     1, "LEVEL"},
   722    755       /*  7 */ {"csr_open",     1, "CSR"},
   723    756       /*  8 */ {"work",        -1, "?NMERGE? NPAGE"},
   724    757       /*  9 */ {"flush",        0, ""},
   725    758       /* 10 */ {"config",       1, "LIST"},
   726    759       /* 11 */ {"checkpoint",   0, ""},
          760  +    /* 12 */ {"info",         1, "OPTION"},
   727    761       {0, 0, 0}
   728    762     };
   729    763     int iCmd;
   730    764     int rc;
   731    765     TclLsm *p = (TclLsm *)clientData;
   732    766   
   733    767     if( objc<2 ){
................................................................................
   854    888         return testConfigureLsm(interp, p->db, objv[2]);
   855    889       }
   856    890   
   857    891       case 11: assert( 0==strcmp(aCmd[11].zCmd, "checkpoint") ); {
   858    892         rc = lsm_checkpoint(p->db, 0);
   859    893         return test_lsm_error(interp, "lsm_checkpoint", rc);
   860    894       }
          895  +
          896  +    case 12: assert( 0==strcmp(aCmd[12].zCmd, "info") ); {
          897  +      return testInfoLsm(interp, p->db, objv[2]);
          898  +    }
   861    899   
   862    900       default:
   863    901         assert( 0 );
   864    902     }
   865    903   
   866    904     Tcl_AppendResult(interp, "internal error", 0);
   867    905     return TCL_ERROR;

Changes to www/lsmusr.wiki.

   370    370   the next entry. After lsm_csr_next() is called to advance past the final
   371    371   entry in the database, the cursor is left pointing to no entry at all,
   372    372   lsm_csr_valid() returns 0, and the loop is finished. API function 
   373    373   <a href=lsmapi.wiki#lsm_csr_key>lsm_csr_key()</a> is used to retrieve the
   374    374   key associated with each database entry visited.
   375    375   
   376    376   <verbatim>
   377         -  for(rc = lsm_csr_first(csr); lsm_csr_valid(csr); rc = lsm_csr_next(csr)){
          377  +  for(rc=lsm_csr_first(csr); rc==LSM_OK && lsm_csr_valid(csr); rc=lsm_csr_next(csr)){
   378    378       const void *pKey; int nKey;
   379    379       const void *pVal; int nVal;
   380    380   
   381    381       rc = lsm_csr_key(csr, &pKey, &nKey);
   382    382       if( rc==LSM_OK ) rc = lsm_csr_value(csr, &pVal, &nVal);
   383         -    if( rc!=LSM_OK ) break;
          383  +    if( rc==LSM_OK ) break;
   384    384   
   385    385       /* At this point pKey points to the current key (size nKey bytes) and
   386    386       ** pVal points to the corresponding value (size nVal bytes).  */
   387    387     }
   388    388   </verbatim>
   389    389   
   390    390   <p> The example code above could be modified to iterate backwards through
................................................................................
   723    723       int (*xBound)(void *pCtx, int nIn);
   724    724       int (*xCompress)(void *pCtx, void *pOut, int *pnOut, const void *pIn, int nIn);
   725    725       int (*xUncompress)(void *pCtx, void *pOut, int *pnOut, const void *pIn, int nIn);
   726    726       void (*xFree)(void *pCtx);
   727    727     };
   728    728   </verbatim>
   729    729   
   730         -<p><i> Explain how the hooks work here (same as zipvfs) </i>
          730  +<p><span style=color:red> Explain how the hooks work here (same as zipvfs)
          731  +</span>
   731    732   
   732         -<p><i> Example code? Using zlib? Or something simple like an RLE
   733         -implementation?</i>
          733  +<p><span style=color:red> Example code? Using zlib? Or something simple like an
          734  +RLE implementation?</span>
   734    735   
   735    736   <p>The database file header of any LSM database contains a 32-bit unsigned
   736    737   "compression id" field. If the database is not a compressed database, this
   737    738   field is set to 1. Otherwise, it is set to an application supplied value
   738    739   identifying the compression and/or encryption scheme in use. Application
   739    740   compression scheme ids must be greater than or equal to 10000. Values smaller
   740    741   than 10000 are reserved for internal use.
   741    742   
   742         -<p>The lsm_compression_id() API may be used to read the compression id from
   743         -a database connection. Because the compression id is stored in the database
          743  +<p>The lsm_info() API may be used to read the compression id from a database 
          744  +connection as follows: 
          745  +
          746  +<verbatim>
          747  +  unsigned int iCompressionId;
          748  +  rc = lsm_info(db, LSM_INFO_COMPRESSION_ID, &iCompressionId);
          749  +  if( rc==LSM_OK ){
          750  +    /* Variable iCompressionId now contains the db compression id */
          751  +  }
          752  +</verbatim>
          753  +
          754  +Because the compression id is stored in the database
   744    755   header, it may be read before any required compression or encryption hooks
   745    756   are configured.
   746    757   
   747    758   <verbatim>
   748    759     #define LSM_COMPRESSION_EMPTY    0
   749    760     #define LSM_COMPRESSION_NONE     1
   750         -  int lsm_compression_id(lsm_db *db, u32 *piId);
   751    761   </verbatim>
   752    762   
   753    763   <p>When a database is opened for the first time, before it is first written,
   754         -the compression id field is set to LSM_COMPRESSION_EMPTY (0). The first time
   755         -a transaction is committed, the database compression id is set to a copy of 
   756         -the lsm_compress.iId field of the compression hooks for the database handle
   757         -committing the transaction, or to LSM_COMPRESSION_NONE (1) if no compression
   758         -hooks are configured.
          764  +the compression id field is set to LSM_COMPRESSION_EMPTY (0). After data is
          765  +written into the database file, the database compression id is set to a copy 
          766  +of the lsm_compress.iId field of the compression hooks for the database handle
          767  +doing the writing, or to LSM_COMPRESSION_NONE (1) if no compression hooks 
          768  +are configured.
   759    769   
   760    770   <p>Once the compression id is set to something other than 
   761         -LSM_COMPRESSION_EMPTY, when a database handle opens a read or write 
   762         -transaction on the database, the compression id is compared against the 
   763         -lsm_compress.iId field of the configured compression hooks, or against LSM_COMPRESSION_NONE if no compression hooks are configured. If the compression id
   764         -does not match, then an LSM_MISMATCH error is returned and the operation 
   765         -fails (no transaction or database cursor is opened).
          771  +LSM_COMPRESSION_EMPTY, when a database handle attempts to read or write the
          772  +database file, the compression id is compared against the lsm_compress.iId 
          773  +field of the configured compression hooks, or against LSM_COMPRESSION_NONE if
          774  +no compression hooks are configured. If the compression id does not match, then
          775  +an LSM_MISMATCH error is returned and the operation fails (no transaction or
          776  +database cursor is opened).
   766    777   
   767         -<p><i>Maybe there should be a way to register a mismatch-handler callback.
   768         -Otherwise, applications have to handle LSM_MISMATCH everywhere...
   769         -</i>
          778  +<p>It is also possible to register a compression factory callback with a 
          779  +database handle. If one is registered, the compression factory callback is
          780  +invoked instead of returning LSM_MISMATCH if the configured compression hooks
          781  +do not match the compression id of a database. If the callback registers
          782  +compatible compression hooks with the database handle (using the normal
          783  +lsm_config() interface), then the database read or write operation resumes
          784  +after it returns. Otherwise, if the compression factory callback does not
          785  +register new, compatible, compression hooks with the database handle,
          786  +LSM_MISMATCH is returned to the user.
   770    787   
          788  +<p>A compression factory callback is registered with a database handle
          789  +by calling lsm_config() with the second argument set to
          790  +LSM_CONFIG_SET_COMPRESSION_FACTORY, and the third argument set to point to
          791  +an instance of structure lsm_compress_factory. The lsm_config() copies the
          792  +contents of the structure - it does not retain a pointer to it.
          793  +
          794  +<verbatim>
          795  +  typedef struct lsm_compress_factory lsm_compress_factory;
          796  +  struct lsm_compress_factory {
          797  +    void *pCtx;
          798  +    int (*xFactory)(void *pCtx, lsm_db *db, unsigned int iCompressionId);
          799  +    void (*xFree)(void *pCtx);
          800  +  };
          801  +</verbatim>
          802  +
          803  +<p><span style=color:red> Explain how the xFactory hook works here. </span>
   771    804   
   772    805   <h1 id=performance_tuning>6. Performance Tuning</h1>
   773    806   
   774    807   <p> This section describes the various measures that can be taken in order to
   775    808   fine-tune LSM in order to improve performance in specific circumstances.
   776    809   Sub-section 6.1 contains a high-level overview of the 
   777    810   <a href=#overview_of_lsm_architecture>system architecture</a>