SQLite4
Check-in [76297939d3]
Not logged in

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

Overview
Comment:Merge compression-id branch with trunk.
Downloads: Tarball | ZIP archive | SQL archive
Timelines: family | ancestors | descendants | both | trunk
Files: files | file ages | folders
SHA1: 76297939d385b893e2de7542da28465328a52b58
User & Date: dan 2013-02-08 11:30:48
Context
2013-02-08
14:39
Changes to lsm_unix.c to build on android: (a) do not use fdatasync() on android and (b) account for the fact that usleep() returns void on android. check-in: 302b222395 user: dan tags: trunk
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
11:58
Fix bug to do with block redirection. check-in: 7cc153f523 user: dan tags: trunk
Changes
Hide Diffs Side-by-Side Diffs Ignore Whitespace Patch

Changes to src/lsm.h.

    20     20   extern "C" {
    21     21   #endif
    22     22   
    23     23   /*
    24     24   ** Opaque handle types.
    25     25   */
    26     26   typedef struct lsm_compress lsm_compress;   /* Compression library functions */
           27  +typedef struct lsm_compress_factory lsm_compress_factory;
    27     28   typedef struct lsm_cursor lsm_cursor;       /* Database cursor handle */
    28     29   typedef struct lsm_db lsm_db;               /* Database connection handle */
    29     30   typedef struct lsm_env lsm_env;             /* Runtime environment */
    30     31   typedef struct lsm_file lsm_file;           /* OS file handle */
    31     32   typedef struct lsm_mutex lsm_mutex;         /* Mutex handle */
    32     33   
    33     34   /* 64-bit integer type used for file offsets. */
................................................................................
   102    103   #define LSM_IOERR     10
   103    104   #define LSM_CORRUPT   11
   104    105   #define LSM_FULL      13
   105    106   #define LSM_CANTOPEN  14
   106    107   #define LSM_PROTOCOL  15
   107    108   #define LSM_MISUSE    21
   108    109   
          110  +#define LSM_MISMATCH  50
          111  +
   109    112   /* 
   110    113   ** CAPI: Creating and Destroying Database Connection Handles
   111    114   **
   112    115   ** Open and close a database connection handle.
   113    116   */
   114    117   int lsm_new(lsm_env*, lsm_db **ppDb);
   115    118   int lsm_close(lsm_db *pDb);
................................................................................
   116    119   
   117    120   /* 
   118    121   ** CAPI: Connecting to a Database
   119    122   */
   120    123   int lsm_open(lsm_db *pDb, const char *zFilename);
   121    124   
   122    125   /*
   123         -** CAPI: Obtaining pointers to databases environments
          126  +** CAPI: Obtaining pointers to database environments
   124    127   **
   125    128   ** Return a pointer to the environment used by the database connection 
   126    129   ** passed as the first argument. Assuming the argument is valid, this 
   127    130   ** function always returns a valid environment pointer - it cannot fail.
   128    131   */
   129    132   lsm_env *lsm_get_env(lsm_db *pDb);
   130    133   
................................................................................
   251    254   **
   252    255   **   This option may only be used before lsm_open() is called. Invoking it
   253    256   **   after lsm_open() has been called results in an LSM_MISUSE error.
   254    257   **
   255    258   ** LSM_CONFIG_GET_COMPRESSION:
   256    259   **   Query the compression methods used to compress and decompress database
   257    260   **   content.
          261  +**
          262  +** LSM_CONFIG_SET_COMPRESSION_FACTORY:
          263  +**   Configure a factory method to be invoked in case of an LSM_MISMATCH
          264  +**   error.
   258    265   */
   259         -#define LSM_CONFIG_AUTOFLUSH           1
   260         -#define LSM_CONFIG_PAGE_SIZE           2
   261         -#define LSM_CONFIG_SAFETY              3
   262         -#define LSM_CONFIG_BLOCK_SIZE          4
   263         -#define LSM_CONFIG_AUTOWORK            5
   264         -#define LSM_CONFIG_MMAP                7
   265         -#define LSM_CONFIG_USE_LOG             8
   266         -#define LSM_CONFIG_AUTOMERGE           9
   267         -#define LSM_CONFIG_MAX_FREELIST       10
   268         -#define LSM_CONFIG_MULTIPLE_PROCESSES 11
   269         -#define LSM_CONFIG_AUTOCHECKPOINT     12
   270         -#define LSM_CONFIG_SET_COMPRESSION    13
   271         -#define LSM_CONFIG_GET_COMPRESSION    14
          266  +#define LSM_CONFIG_AUTOFLUSH                1
          267  +#define LSM_CONFIG_PAGE_SIZE                2
          268  +#define LSM_CONFIG_SAFETY                   3
          269  +#define LSM_CONFIG_BLOCK_SIZE               4
          270  +#define LSM_CONFIG_AUTOWORK                 5
          271  +#define LSM_CONFIG_MMAP                     7
          272  +#define LSM_CONFIG_USE_LOG                  8
          273  +#define LSM_CONFIG_AUTOMERGE                9
          274  +#define LSM_CONFIG_MAX_FREELIST            10
          275  +#define LSM_CONFIG_MULTIPLE_PROCESSES      11
          276  +#define LSM_CONFIG_AUTOCHECKPOINT          12
          277  +#define LSM_CONFIG_SET_COMPRESSION         13
          278  +#define LSM_CONFIG_GET_COMPRESSION         14
          279  +#define LSM_CONFIG_SET_COMPRESSION_FACTORY 15
   272    280   
   273    281   #define LSM_SAFETY_OFF    0
   274    282   #define LSM_SAFETY_NORMAL 1
   275    283   #define LSM_SAFETY_FULL   2
   276    284   
   277    285   /*
   278    286   ** CAPI: Compression and/or Encryption Hooks
................................................................................
   279    287   */
   280    288   struct lsm_compress {
   281    289     void *pCtx;
   282    290     unsigned int iId;
   283    291     int (*xBound)(void *, int nSrc);
   284    292     int (*xCompress)(void *, char *, int *, const char *, int);
   285    293     int (*xUncompress)(void *, char *, int *, const char *, int);
          294  +  void (*xFree)(void *pCtx);
          295  +};
          296  +
          297  +struct lsm_compress_factory {
          298  +  void *pCtx;
          299  +  int (*xFactory)(void *, lsm_db *, unsigned int);
          300  +  void (*xFree)(void *pCtx);
   286    301   };
   287    302   
          303  +#define LSM_COMPRESSION_EMPTY 0
          304  +#define LSM_COMPRESSION_NONE  1
   288    305   
   289    306   /*
   290    307   ** CAPI: Allocating and Freeing Memory
   291    308   **
   292    309   ** Invoke the memory allocation functions that belong to environment
   293    310   ** pEnv. Or the system defaults if no memory allocation functions have 
   294    311   ** been registered.
................................................................................
   411    428   **   accumulate new data written to the database. The other tree structure -
   412    429   **   the old tree - is a read-only tree holding older data and may be flushed 
   413    430   **   to disk at any time.
   414    431   ** 
   415    432   **   Assuming no error occurs, the location pointed to by the first of the two
   416    433   **   (int *) arguments is set to the size of the old in-memory tree in KB.
   417    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.
   418    440   */
   419    441   #define LSM_INFO_NWRITE           1
   420    442   #define LSM_INFO_NREAD            2
   421    443   #define LSM_INFO_DB_STRUCTURE     3
   422    444   #define LSM_INFO_LOG_STRUCTURE    4
   423    445   #define LSM_INFO_ARRAY_STRUCTURE  5
   424    446   #define LSM_INFO_PAGE_ASCII_DUMP  6
   425    447   #define LSM_INFO_PAGE_HEX_DUMP    7
   426    448   #define LSM_INFO_FREELIST         8
   427    449   #define LSM_INFO_ARRAY_PAGES      9
   428    450   #define LSM_INFO_CHECKPOINT_SIZE 10
   429    451   #define LSM_INFO_TREE_SIZE       11
   430         -
   431    452   #define LSM_INFO_FREELIST_SIZE   12
          453  +#define LSM_INFO_COMPRESSION_ID  13
   432    454   
   433    455   
   434    456   /* 
   435    457   ** CAPI: Opening and Closing Write Transactions
   436    458   **
   437    459   ** These functions are used to open and close transactions and nested 
   438    460   ** sub-transactions.

Changes to src/lsmInt.h.

   312    312     int nDfltPgsz;                  /* Configured by LSM_CONFIG_PAGE_SIZE */
   313    313     int nDfltBlksz;                 /* Configured by LSM_CONFIG_BLOCK_SIZE */
   314    314     int nMaxFreelist;               /* Configured by LSM_CONFIG_MAX_FREELIST */
   315    315     int bMmap;                      /* Configured by LSM_CONFIG_MMAP */
   316    316     i64 nAutockpt;                  /* Configured by LSM_CONFIG_AUTOCHECKPOINT */
   317    317     int bMultiProc;                 /* Configured by L_C_MULTIPLE_PROCESSES */
   318    318     lsm_compress compress;          /* Compression callbacks */
          319  +  lsm_compress_factory factory;   /* Compression callback factory */
   319    320   
   320    321     /* Sub-system handles */
   321    322     FileSystem *pFS;                /* On-disk portion of database */
   322    323     Database *pDatabase;            /* Database shared data */
   323    324   
   324    325     /* Client transaction context */
   325    326     Snapshot *pClient;              /* Client snapshot */
................................................................................
   332    333     IntArray rollback;              /* List of tree-nodes to roll back */
   333    334   
   334    335     /* Worker context */
   335    336     Snapshot *pWorker;              /* Worker snapshot (or NULL) */
   336    337     Freelist *pFreelist;            /* See sortedNewToplevel() */
   337    338     int bUseFreelist;               /* True to use pFreelist */
   338    339     int bIncrMerge;                 /* True if currently doing a merge */
          340  +
          341  +  int bInFactory;                 /* True if within factory.xFactory() */
   339    342   
   340    343     /* Debugging message callback */
   341    344     void (*xLog)(void *, int, const char *);
   342    345     void *pLogCtx;
   343    346   
   344    347     /* Work done notification callback */
   345    348     void (*xWork)(lsm_db *, void *);
................................................................................
   522    525   /*
   523    526   ** A snapshot of a database. A snapshot contains all the information required
   524    527   ** to read or write a database file on disk. See the description of struct
   525    528   ** Database below for futher details.
   526    529   */
   527    530   struct Snapshot {
   528    531     Database *pDatabase;            /* Database this snapshot belongs to */
          532  +  u32 iCmpId;                     /* Id of compression scheme */
   529    533     Level *pLevel;                  /* Pointer to level 0 of snapshot (or NULL) */
   530    534     i64 iId;                        /* Snapshot id */
   531    535     i64 iLogOff;                    /* Log file offset */
   532    536     Redirect redirect;              /* Block redirection array */
   533    537   
   534    538     /* Used by worker snapshots only */
   535    539     int nBlock;                     /* Number of blocks in database file */
................................................................................
   567    571   
   568    572   int lsmCheckpointSaveWorker(lsm_db *pDb, int);
   569    573   int lsmDatabaseFull(lsm_db *pDb);
   570    574   int lsmCheckpointSynced(lsm_db *pDb, i64 *piId, i64 *piLog, u32 *pnWrite);
   571    575   
   572    576   int lsmCheckpointSize(lsm_db *db, int *pnByte);
   573    577   
          578  +int lsmInfoCompressionId(lsm_db *db, u32 *piCmpId);
          579  +
   574    580   /* 
   575    581   ** Functions from file "lsm_tree.c".
   576    582   */
   577    583   int lsmTreeNew(lsm_env *, int (*)(void *, int, void *, int), Tree **ppTree);
   578    584   void lsmTreeRelease(lsm_env *, Tree *);
   579    585   int lsmTreeInit(lsm_db *);
   580    586   int lsmTreeRepair(lsm_db *);
................................................................................
   585    591   
   586    592   int lsmTreeSize(lsm_db *);
   587    593   int lsmTreeEndTransaction(lsm_db *pDb, int bCommit);
   588    594   int lsmTreeLoadHeader(lsm_db *pDb, int *);
   589    595   int lsmTreeLoadHeaderOk(lsm_db *, int);
   590    596   
   591    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);
   592    599   void lsmTreeRollback(lsm_db *pDb, TreeMark *pMark);
   593    600   void lsmTreeMark(lsm_db *pDb, TreeMark *pMark);
   594    601   
   595    602   int lsmTreeCursorNew(lsm_db *pDb, int, TreeCursor **);
   596    603   void lsmTreeCursorDestroy(TreeCursor *);
   597    604   
   598    605   int lsmTreeCursorSeek(TreeCursor *pCsr, void *pKey, int nKey, int *pRes);
................................................................................
   639    646   #endif
   640    647   
   641    648   /**************************************************************************
   642    649   ** Start of functions from "lsm_file.c".
   643    650   */
   644    651   int lsmFsOpen(lsm_db *, const char *);
   645    652   void lsmFsClose(FileSystem *);
          653  +
          654  +int lsmFsConfigure(lsm_db *db);
   646    655   
   647    656   int lsmFsBlockSize(FileSystem *);
   648    657   void lsmFsSetBlockSize(FileSystem *, int);
   649    658   
   650    659   int lsmFsPageSize(FileSystem *);
   651    660   void lsmFsSetPageSize(FileSystem *, int);
   652    661   
................................................................................
   888    897   int lsmFreelistAppend(lsm_env *pEnv, Freelist *p, int iBlk, i64 iId);
   889    898   
   890    899   int lsmDbMultiProc(lsm_db *);
   891    900   void lsmDbDeferredClose(lsm_db *, lsm_file *, LsmFile *);
   892    901   LsmFile *lsmDbRecycleFd(lsm_db *);
   893    902   
   894    903   int lsmWalkFreelist(lsm_db *, int, int (*)(void *, int, i64), void *);
          904  +
          905  +int lsmCheckCompressionId(lsm_db *, u32);
   895    906   
   896    907   
   897    908   /**************************************************************************
   898    909   ** functions in lsm_str.c
   899    910   */
   900    911   void lsmStringInit(LsmString*, lsm_env *pEnv);
   901    912   int lsmStringExtend(LsmString*, int);

Changes to src/lsm_ckpt.c.

    28     28   **
    29     29   **   Checkpoint header (see the CKPT_HDR_XXX #defines):
    30     30   **
    31     31   **     1. The checkpoint id MSW.
    32     32   **     2. The checkpoint id LSW.
    33     33   **     3. The number of integer values in the entire checkpoint, including 
    34     34   **        the two checksum values.
    35         -**     4. The total number of blocks in the database.
    36         -**     5. The block size.
    37         -**     6. The number of levels.
    38         -**     7. The nominal database page size.
    39         -**     8. The number of pages (in total) written to the database file.
           35  +**     4. The compression scheme id.
           36  +**     5. The total number of blocks in the database.
           37  +**     6. The block size.
           38  +**     7. The number of levels.
           39  +**     8. The nominal database page size.
           40  +**     9. The number of pages (in total) written to the database file.
    40     41   **
    41     42   **   Log pointer:
    42     43   **
    43     44   **     1. The log offset MSW.
    44     45   **     2. The log offset LSW.
    45     46   **     3. Log checksum 0.
    46     47   **     4. Log checksum 1.
................................................................................
   170    171    + (((x)&0x00FF0000)>>8)  + (((x)&0xFF000000)>>24) \
   171    172   )
   172    173   
   173    174   static const int one = 1;
   174    175   #define LSM_LITTLE_ENDIAN (*(u8 *)(&one))
   175    176   
   176    177   /* Sizes, in integers, of various parts of the checkpoint. */
   177         -#define CKPT_HDR_SIZE         8
          178  +#define CKPT_HDR_SIZE         9
   178    179   #define CKPT_LOGPTR_SIZE      4
   179    180   #define CKPT_APPENDLIST_SIZE  (LSM_APPLIST_SZ * 2)
   180    181   
   181    182   /* A #define to describe each integer in the checkpoint header. */
   182    183   #define CKPT_HDR_ID_MSW   0
   183    184   #define CKPT_HDR_ID_LSW   1
   184    185   #define CKPT_HDR_NCKPT    2
   185         -#define CKPT_HDR_NBLOCK   3
   186         -#define CKPT_HDR_BLKSZ    4
   187         -#define CKPT_HDR_NLEVEL   5
   188         -#define CKPT_HDR_PGSZ     6
   189         -#define CKPT_HDR_NWRITE   7
          186  +#define CKPT_HDR_CMPID    3
          187  +#define CKPT_HDR_NBLOCK   4
          188  +#define CKPT_HDR_BLKSZ    5
          189  +#define CKPT_HDR_NLEVEL   6
          190  +#define CKPT_HDR_PGSZ     7
          191  +#define CKPT_HDR_NWRITE   8
   190    192   
   191         -#define CKPT_HDR_LO_MSW     8
   192         -#define CKPT_HDR_LO_LSW     9
   193         -#define CKPT_HDR_LO_CKSUM1 10
   194         -#define CKPT_HDR_LO_CKSUM2 11
          193  +#define CKPT_HDR_LO_MSW     9
          194  +#define CKPT_HDR_LO_LSW    10
          195  +#define CKPT_HDR_LO_CKSUM1 11
          196  +#define CKPT_HDR_LO_CKSUM2 12
   195    197   
   196    198   typedef struct CkptBuffer CkptBuffer;
   197    199   
   198    200   /*
   199    201   ** Dynamic buffer used to accumulate data for a checkpoint.
   200    202   */
   201    203   struct CkptBuffer {
................................................................................
   445    447         ckptSetValue(&ckpt, iOut++, (p->iId >> 32) & 0xFFFFFFFF, &rc);
   446    448         ckptSetValue(&ckpt, iOut++, p->iId & 0xFFFFFFFF, &rc);
   447    449       }
   448    450     }
   449    451   
   450    452     /* Write the checkpoint header */
   451    453     assert( iId>=0 );
          454  +  assert( pSnap->iCmpId==pDb->compress.iId
          455  +       || pSnap->iCmpId==LSM_COMPRESSION_EMPTY 
          456  +  );
   452    457     ckptSetValue(&ckpt, CKPT_HDR_ID_MSW, (u32)(iId>>32), &rc);
   453    458     ckptSetValue(&ckpt, CKPT_HDR_ID_LSW, (u32)(iId&0xFFFFFFFF), &rc);
   454    459     ckptSetValue(&ckpt, CKPT_HDR_NCKPT, iOut+2, &rc);
          460  +  ckptSetValue(&ckpt, CKPT_HDR_CMPID, pDb->compress.iId, &rc);
   455    461     ckptSetValue(&ckpt, CKPT_HDR_NBLOCK, pSnap->nBlock, &rc);
   456    462     ckptSetValue(&ckpt, CKPT_HDR_BLKSZ, lsmFsBlockSize(pFS), &rc);
   457    463     ckptSetValue(&ckpt, CKPT_HDR_NLEVEL, nLevel, &rc);
   458    464     ckptSetValue(&ckpt, CKPT_HDR_PGSZ, lsmFsPageSize(pFS), &rc);
   459    465     ckptSetValue(&ckpt, CKPT_HDR_NWRITE, pSnap->nWrite, &rc);
   460    466   
   461    467     if( bCksum ){
................................................................................
   757    763   
   758    764   /*
   759    765   ** Initialize the shared-memory header with an empty snapshot. This function
   760    766   ** is called when no valid snapshot can be found in the database header.
   761    767   */
   762    768   static void ckptLoadEmpty(lsm_db *pDb){
   763    769     u32 aCkpt[] = {
   764         -    0,                  /* CKPT_HDR_ID_MSW */
   765         -    10,                 /* CKPT_HDR_ID_LSW */
   766         -    0,                  /* CKPT_HDR_NCKPT */
   767         -    0,                  /* CKPT_HDR_NBLOCK */
   768         -    0,                  /* CKPT_HDR_BLKSZ */
   769         -    0,                  /* CKPT_HDR_NLEVEL */
   770         -    0,                  /* CKPT_HDR_PGSZ */
   771         -    0,                  /* CKPT_HDR_OVFL */
   772         -    0,                  /* CKPT_HDR_NWRITE */
   773         -    0, 0, 1234, 5678,   /* The log pointer and initial checksum */
   774         -    0,0,0,0, 0,0,0,0,   /* The append list */
   775         -    0,                  /* The free block list */
   776         -    0, 0                /* Space for checksum values */
          770  +    0,                       /* CKPT_HDR_ID_MSW */
          771  +    10,                      /* CKPT_HDR_ID_LSW */
          772  +    0,                       /* CKPT_HDR_NCKPT */
          773  +    LSM_COMPRESSION_EMPTY,   /* CKPT_HDR_CMPID */
          774  +    0,                       /* CKPT_HDR_NBLOCK */
          775  +    0,                       /* CKPT_HDR_BLKSZ */
          776  +    0,                       /* CKPT_HDR_NLEVEL */
          777  +    0,                       /* CKPT_HDR_PGSZ */
          778  +    0,                       /* CKPT_HDR_OVFL */
          779  +    0,                       /* CKPT_HDR_NWRITE */
          780  +    0, 0, 1234, 5678,        /* The log pointer and initial checksum */
          781  +    0,0,0,0, 0,0,0,0,        /* The append list */
          782  +    0,                       /* The free block list */
          783  +    0, 0                     /* Space for checksum values */
   777    784     };
   778    785     u32 nCkpt = array_size(aCkpt);
   779    786     ShmHeader *pShm = pDb->pShmhdr;
   780    787   
   781    788     aCkpt[CKPT_HDR_NCKPT] = nCkpt;
   782    789     aCkpt[CKPT_HDR_BLKSZ] = pDb->nDfltBlksz;
   783    790     aCkpt[CKPT_HDR_PGSZ] = pDb->nDfltPgsz;
................................................................................
   874    881         }
   875    882       }
   876    883   
   877    884       lsmShmBarrier(pDb);
   878    885     }
   879    886     return LSM_PROTOCOL;
   880    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  +}
   881    900   
   882    901   int lsmCheckpointLoadOk(lsm_db *pDb, int iSnap){
   883    902     u32 *aShm;
   884    903     assert( iSnap==1 || iSnap==2 );
   885    904     aShm = (iSnap==1) ? pDb->pShmhdr->aSnap1 : pDb->pShmhdr->aSnap2;
   886    905     return (lsmCheckpointId(pDb->aSnapshot, 0)==lsmCheckpointId(aShm, 0) );
   887    906   }
................................................................................
   917    936       }else{
   918    937         return LSM_PROTOCOL;
   919    938       }
   920    939     }
   921    940   
   922    941     rc = lsmCheckpointDeserialize(pDb, 1, pShm->aSnap1, &pDb->pWorker);
   923    942     if( pDb->pWorker ) pDb->pWorker->pDatabase = pDb->pDatabase;
          943  +
          944  +  if( rc==LSM_OK ){
          945  +    rc = lsmCheckCompressionId(pDb, pDb->pWorker->iCmpId);
          946  +  }
   924    947   
   925    948   #if 0
   926    949     assert( rc!=LSM_OK || lsmFsIntegrityCheck(pDb) );
   927    950   #endif
   928    951     return rc;
   929    952   }
   930    953   
................................................................................
   946    969       int iIn = CKPT_HDR_SIZE + CKPT_APPENDLIST_SIZE + CKPT_LOGPTR_SIZE;
   947    970   
   948    971       pNew->iId = lsmCheckpointId(aCkpt, 0);
   949    972       pNew->nBlock = aCkpt[CKPT_HDR_NBLOCK];
   950    973       pNew->nWrite = aCkpt[CKPT_HDR_NWRITE];
   951    974       rc = ckptLoadLevels(pDb, aCkpt, &iIn, nLevel, &pNew->pLevel);
   952    975       pNew->iLogOff = lsmCheckpointLogOffset(aCkpt);
          976  +    pNew->iCmpId = aCkpt[CKPT_HDR_CMPID];
   953    977   
   954    978       /* Make a copy of the append-list */
   955    979       for(i=0; i<LSM_APPLIST_SZ; i++){
   956    980         u32 *a = &aCkpt[CKPT_HDR_SIZE + CKPT_LOGPTR_SIZE + i*2];
   957    981         pNew->aiAppend[i] = ckptRead64(a);
   958    982       }
   959    983   

Changes to src/lsm_file.c.

   506    506       pFS->zDb = (char *)&pFS[1];
   507    507       pFS->zLog = &pFS->zDb[nDb+1];
   508    508       pFS->nPagesize = LSM_DFLT_PAGE_SIZE;
   509    509       pFS->nBlocksize = LSM_DFLT_BLOCK_SIZE;
   510    510       pFS->nMetasize = 4 * 1024;
   511    511       pFS->pDb = pDb;
   512    512       pFS->pEnv = pDb->pEnv;
   513         -    if( pDb->compress.xCompress ){
   514         -      pFS->pCompress = &pDb->compress;
   515         -    }else{
   516         -      pFS->bUseMmap = pDb->bMmap;
   517         -    }
   518    513   
   519    514       /* Make a copy of the database and log file names. */
   520    515       memcpy(pFS->zDb, zDb, nDb+1);
   521    516       memcpy(pFS->zLog, zDb, nDb);
   522    517       memcpy(&pFS->zLog[nDb], "-log", 5);
   523    518   
   524    519       /* Allocate the hash-table here. At some point, it should be changed
................................................................................
   547    542         pFS->szSector = lsmEnvSectorSize(pFS->pEnv, pFS->fdDb);
   548    543       }
   549    544     }
   550    545   
   551    546     pDb->pFS = pFS;
   552    547     return rc;
   553    548   }
          549  +
          550  +/*
          551  +** Configure the file-system object according to the current values of
          552  +** the LSM_CONFIG_MMAP and LSM_CONFIG_SET_COMPRESSION options.
          553  +*/
          554  +int lsmFsConfigure(lsm_db *db){
          555  +  FileSystem *pFS = db->pFS;
          556  +  lsm_env *pEnv = pFS->pEnv;
          557  +  Page *pPg;
          558  +
          559  +  assert( pFS->nOut==0 );
          560  +  assert( pFS->pWaiting==0 );
          561  +
          562  +  /* Reset any compression/decompression buffers already allocated */
          563  +  lsmFree(pEnv, pFS->aIBuffer);
          564  +  lsmFree(pEnv, pFS->aOBuffer);
          565  +  pFS->nBuffer = 0;
          566  +
          567  +  /* Unmap the file, if it is currently mapped */
          568  +  if( pFS->pMap ){
          569  +    lsmEnvRemap(pEnv, pFS->fdDb, -1, &pFS->pMap, &pFS->nMap);
          570  +    pFS->bUseMmap = 0;
          571  +  }
          572  +
          573  +  /* Free all allocate page structures */
          574  +  pPg = pFS->pLruFirst;
          575  +  while( pPg ){
          576  +    Page *pNext = pPg->pLruNext;
          577  +    if( pPg->flags & PAGE_FREE ) lsmFree(pEnv, pPg->aData);
          578  +    lsmFree(pEnv, pPg);
          579  +    pPg = pNext;
          580  +  }
          581  +
          582  +  /* Zero pointers that point to deleted page objects */
          583  +  pFS->nCacheAlloc = 0;
          584  +  pFS->pLruFirst = 0;
          585  +  pFS->pLruLast = 0;
          586  +  pFS->pFree = 0;
          587  +
          588  +  /* Configure the FileSystem object */
          589  +  if( db->compress.xCompress ){
          590  +    pFS->pCompress = &db->compress;
          591  +    pFS->bUseMmap = 0;
          592  +  }else{
          593  +    pFS->pCompress = 0;
          594  +    pFS->bUseMmap = db->bMmap;
          595  +  }
          596  +
          597  +  return LSM_OK;
          598  +}
   554    599   
   555    600   /*
   556    601   ** Close and destroy a FileSystem object.
   557    602   */
   558    603   void lsmFsClose(FileSystem *pFS){
   559    604     if( pFS ){
   560    605       Page *pPg;

Changes to src/lsm_main.c.

    92     92     pDb->nMerge = LSM_DFLT_AUTOMERGE;
    93     93     pDb->nMaxFreelist = LSM_MAX_FREELIST_ENTRIES;
    94     94     pDb->bUseLog = LSM_DFLT_USE_LOG;
    95     95     pDb->iReader = -1;
    96     96     pDb->bMultiProc = LSM_DFLT_MULTIPLE_PROCESSES;
    97     97     pDb->bMmap = LSM_DFLT_MMAP;
    98     98     pDb->xLog = xLog;
           99  +  pDb->compress.iId = LSM_COMPRESSION_NONE;
    99    100     return LSM_OK;
   100    101   }
   101    102   
   102    103   lsm_env *lsm_get_env(lsm_db *pDb){
   103    104     assert( pDb->pEnv );
   104    105     return pDb->pEnv;
   105    106   }
................................................................................
   190    191         rc = LSM_MISUSE_BKPT;
   191    192       }else{
   192    193         lsmFreeSnapshot(pDb->pEnv, pDb->pClient);
   193    194         pDb->pClient = 0;
   194    195         lsmDbDatabaseRelease(pDb);
   195    196         lsmLogClose(pDb);
   196    197         lsmFsClose(pDb->pFS);
          198  +      
          199  +      /* Invoke any destructors registered for the compression or 
          200  +      ** compression factory callbacks.  */
          201  +      if( pDb->factory.xFree ) pDb->factory.xFree(pDb->factory.pCtx);
          202  +      if( pDb->compress.xFree ) pDb->compress.xFree(pDb->compress.pCtx);
          203  +
   197    204         lsmFree(pDb->pEnv, pDb->rollback.aArray);
   198    205         lsmFree(pDb->pEnv, pDb->aTrans);
   199    206         lsmFree(pDb->pEnv, pDb->apShm);
   200    207         lsmFree(pDb->pEnv, pDb);
   201    208       }
   202    209     }
   203    210     return rc;
................................................................................
   333    340           pDb->bMultiProc = *piVal = (*piVal!=0);
   334    341         }
   335    342         break;
   336    343       }
   337    344   
   338    345       case LSM_CONFIG_SET_COMPRESSION: {
   339    346         lsm_compress *p = va_arg(ap, lsm_compress *);
   340         -      if( pDb->pDatabase ){
   341         -        /* If lsm_open() has been called, this call is against the rules. */
          347  +      if( pDb->iReader>=0 && pDb->bInFactory==0 ){
          348  +        /* May not change compression schemes with an open transaction */
   342    349           rc = LSM_MISUSE_BKPT;
   343    350         }else{
   344         -        memcpy(&pDb->compress, p, sizeof(lsm_compress));
          351  +        if( pDb->compress.xFree ){
          352  +          /* Invoke any destructor belonging to the current compression. */
          353  +          pDb->compress.xFree(pDb->compress.pCtx);
          354  +        }
          355  +        if( p->xBound==0 ){
          356  +          memset(&pDb->compress, 0, sizeof(lsm_compress));
          357  +          pDb->compress.iId = LSM_COMPRESSION_NONE;
          358  +        }else{
          359  +          memcpy(&pDb->compress, p, sizeof(lsm_compress));
          360  +        }
          361  +        rc = lsmFsConfigure(pDb);
          362  +      }
          363  +      break;
          364  +    }
          365  +
          366  +    case LSM_CONFIG_SET_COMPRESSION_FACTORY: {
          367  +      lsm_compress_factory *p = va_arg(ap, lsm_compress_factory *);
          368  +      if( pDb->factory.xFree ){
          369  +        /* Invoke any destructor belonging to the current factory. */
          370  +        pDb->factory.xFree(pDb->factory.pCtx);
   345    371         }
          372  +      memcpy(&pDb->factory, p, sizeof(lsm_compress_factory));
   346    373         break;
   347    374       }
   348    375   
   349    376       case LSM_CONFIG_GET_COMPRESSION: {
   350    377         lsm_compress *p = va_arg(ap, lsm_compress *);
   351    378         memcpy(p, &pDb->compress, sizeof(lsm_compress));
   352    379         break;
................................................................................
   428    455     return 0;
   429    456   }
   430    457   
   431    458   int lsmInfoFreelist(lsm_db *pDb, char **pzOut){
   432    459     Snapshot *pWorker;              /* Worker snapshot */
   433    460     int bUnlock = 0;
   434    461     LsmString s;
   435         -  int i;
   436    462     int rc;
   437    463   
   438    464     /* Obtain the worker snapshot */
   439    465     rc = infoGetWorker(pDb, &pWorker, &bUnlock);
   440    466     if( rc!=LSM_OK ) return rc;
   441    467   
   442    468     lsmStringInit(&s, pDb->pEnv);
................................................................................
   448    474     }
   449    475   
   450    476     /* Release the snapshot and return */
   451    477     infoFreeWorker(pDb, bUnlock);
   452    478     return rc;
   453    479   }
   454    480   
   455         -static int infoFreelistSize(lsm_db *pDb, int *pnFree, int *pnWaiting){
   456         -}
   457         -
   458    481   static int infoTreeSize(lsm_db *db, int *pnOldKB, int *pnNewKB){
   459    482     ShmHeader *pShm = db->pShmhdr;
   460    483     TreeHeader *p = &pShm->hdr1;
   461    484   
   462    485     /* The following code suffers from two race conditions, as it accesses and
   463    486     ** trusts the contents of shared memory without verifying checksums:
   464    487     **
................................................................................
   564    587   
   565    588       case LSM_INFO_TREE_SIZE: {
   566    589         int *pnOld = va_arg(ap, int *);
   567    590         int *pnNew = va_arg(ap, int *);
   568    591         rc = infoTreeSize(pDb, pnOld, pnNew);
   569    592         break;
   570    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  +    }
   571    604   
   572    605       default:
   573    606         rc = LSM_MISUSE;
   574    607         break;
   575    608     }
   576    609   
   577    610     va_end(ap);
................................................................................
   835    868     assert_db_state( pDb );
   836    869   
   837    870     /* A value less than zero means close the innermost nested transaction. */
   838    871     if( iLevel<0 ) iLevel = LSM_MAX(0, pDb->nTransOpen - 1);
   839    872   
   840    873     if( iLevel<pDb->nTransOpen ){
   841    874       if( iLevel==0 ){
   842         -      int bAutowork = 0;
   843         -
   844    875         /* Commit the transaction to disk. */
   845    876         if( rc==LSM_OK ) rc = lsmLogCommit(pDb);
   846    877         if( rc==LSM_OK && pDb->eSafety==LSM_SAFETY_FULL ){
   847    878           rc = lsmFsSyncLog(pDb->pFS);
   848    879         }
   849    880         lsmFinishWriteTrans(pDb, (rc==LSM_OK));
   850    881       }

Changes to src/lsm_shared.c.

   430    430     if( rc==LSM_OK ){
   431    431       assert( p );
   432    432       rc = lsmFsOpen(pDb, zName);
   433    433     }
   434    434     if( rc==LSM_OK ){
   435    435       rc = doDbConnect(pDb);
   436    436     }
          437  +  if( rc==LSM_OK ){
          438  +    rc = lsmFsConfigure(pDb);
          439  +  }
   437    440   
   438    441     return rc;
   439    442   }
   440    443   
   441    444   static void dbDeferClose(lsm_db *pDb){
   442    445     if( pDb->pFS ){
   443    446       LsmFile *pLsmFile = 0;
................................................................................
   909    912       lsmFreeSnapshot(pDb->pEnv, pDb->pWorker);
   910    913       pDb->pWorker = 0;
   911    914     }
   912    915   
   913    916     lsmShmLock(pDb, LSM_LOCK_WORKER, LSM_LOCK_UNLOCK, 0);
   914    917   }
   915    918   
   916         -
   917    919   /*
   918    920   ** Called when recovery is finished.
   919    921   */
   920    922   int lsmFinishRecovery(lsm_db *pDb){
   921    923     lsmTreeEndTransaction(pDb, 1);
   922    924     return LSM_OK;
   923    925   }
          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  +*/
          941  +int lsmCheckCompressionId(lsm_db *pDb, u32 iReq){
          942  +  if( iReq!=LSM_COMPRESSION_EMPTY && pDb->compress.iId!=iReq ){
          943  +    if( pDb->factory.xFactory ){
          944  +      pDb->bInFactory = 1;
          945  +      pDb->factory.xFactory(pDb->factory.pCtx, pDb, iReq);
          946  +      pDb->bInFactory = 0;
          947  +    }
          948  +    if( pDb->compress.iId!=iReq ){
          949  +      /* Incompatible */
          950  +      return LSM_MISMATCH;
          951  +    }
          952  +  }
          953  +  /* Compatible */
          954  +  return LSM_OK;
          955  +}
   924    956   
   925    957   /*
   926    958   ** Begin a read transaction. This function is a no-op if the connection
   927    959   ** passed as the only argument already has an open read transaction.
   928    960   */
   929    961   int lsmBeginReadTrans(lsm_db *pDb){
   930    962     const int MAX_READLOCK_ATTEMPTS = 10;
................................................................................
   971   1003             ** lsm_sorted.c is changed to work directly from the serialized
   972   1004             ** version of the snapshot.  */
   973   1005             if( pDb->pClient==0 ){
   974   1006               rc = lsmCheckpointDeserialize(pDb, 0, pDb->aSnapshot,&pDb->pClient);
   975   1007             }
   976   1008             assert( (rc==LSM_OK)==(pDb->pClient!=0) );
   977   1009             assert( pDb->iReader>=0 );
         1010  +
         1011  +          /* Check that the client has the right compression hooks loaded.
         1012  +          ** If not, set rc to LSM_MISMATCH.  */
         1013  +          if( rc==LSM_OK ){
         1014  +            rc = lsmCheckCompressionId(pDb, pDb->pClient->iCmpId);
         1015  +          }
   978   1016           }else{
   979   1017             rc = lsmReleaseReadlock(pDb);
   980   1018           }
   981   1019         }
         1020  +
   982   1021         if( rc==LSM_BUSY ){
   983   1022           rc = LSM_OK;
   984   1023         }
   985   1024       }
   986   1025   #if 0
   987   1026   if( rc==LSM_OK && pDb->pClient ){
   988   1027     fprintf(stderr, 

Changes to src/lsm_unix.c.

   190    190   
   191    191     if( p->pMap ){
   192    192       munmap(p->pMap, p->nMap);
   193    193       *ppOut = p->pMap = 0;
   194    194       *pnOut = p->nMap = 0;
   195    195     }
   196    196   
   197         -  memset(&buf, 0, sizeof(buf));
   198         -  prc = fstat(p->fd, &buf);
   199         -  if( prc!=0 ) return LSM_IOERR_BKPT;
   200         -  iSz = buf.st_size;
   201         -  if( iSz<iMin ){
   202         -    iSz = ((iMin + (2<<20) - 1) / (2<<20)) * (2<<20);
   203         -    prc = ftruncate(p->fd, iSz);
          197  +  if( iMin>=0 ){
          198  +    memset(&buf, 0, sizeof(buf));
          199  +    prc = fstat(p->fd, &buf);
   204    200       if( prc!=0 ) return LSM_IOERR_BKPT;
          201  +    iSz = buf.st_size;
          202  +    if( iSz<iMin ){
          203  +      iSz = ((iMin + (2<<20) - 1) / (2<<20)) * (2<<20);
          204  +      prc = ftruncate(p->fd, iSz);
          205  +      if( prc!=0 ) return LSM_IOERR_BKPT;
          206  +    }
          207  +
          208  +    p->pMap = mmap(0, iSz, PROT_READ|PROT_WRITE, MAP_SHARED, p->fd, 0);
          209  +    p->nMap = iSz;
   205    210     }
   206    211   
   207         -  p->pMap = mmap(0, iSz, PROT_READ|PROT_WRITE, MAP_SHARED, p->fd, 0);
   208         -  p->nMap = iSz;
   209         -
   210    212     *ppOut = p->pMap;
   211    213     *pnOut = p->nMap;
   212    214     return LSM_OK;
   213    215   }
   214    216   
   215    217   static int lsmPosixOsFullpath(
   216    218     lsm_env *pEnv,

Added test/lsm4.test.

            1  +# 2013 February 06
            2  +#
            3  +# The author disclaims copyright to this source code.  In place of
            4  +# a legal notice, here is a blessing:
            5  +#
            6  +#    May you do good and not evil.
            7  +#    May you find forgiveness for yourself and forgive others.
            8  +#    May you share freely, never taking more than you give.
            9  +#
           10  +#***********************************************************************
           11  +#
           12  +
           13  +set testdir [file dirname $argv0]
           14  +source $testdir/tester.tcl
           15  +set testprefix lsm4
           16  +db close
           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  +
           24  +proc db_fetch {db key} {
           25  +  db csr_open csr
           26  +  csr seek $key eq
           27  +  set ret [csr value]
           28  +  csr close
           29  +  set ret
           30  +}
           31  +
           32  +do_test 1.1 {
           33  +  lsm_open db test.db 
           34  +  db config {set_compression noop}
           35  +  db write 1 abc
           36  +  db write 2 def
           37  +  db close
           38  +} {}
           39  +
           40  +do_test 1.2 {
           41  +  lsm_open db test.db 
           42  +  db config {set_compression noop}
           43  +  list [db_fetch db 1] [db_fetch db 2]
           44  +} {abc def}
           45  +
           46  +do_test 1.3 {
           47  +  db close
           48  +  lsm_open db test.db 
           49  +  db config {set_compression rle}
           50  +  list [catch {db_fetch db 1} msg] $msg
           51  +} {1 {error in lsm_csr_open() - 50}}
           52  +
           53  +do_test 1.4 {
           54  +  db close
           55  +  lsm_open db test.db 
           56  +  list [catch {db_fetch db 1} msg] $msg
           57  +} {1 {error in lsm_csr_open() - 50}}
           58  +
           59  +do_test 1.5 {
           60  +  db config {set_compression_factory true}
           61  +  list [db_fetch db 1] [db_fetch db 2]
           62  +} {abc def}
           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)
          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}
          116  +
          117  +finish_test
          118  +

Changes to test/test_lsm.c.

     5      5   ** a legal notice, here is a blessing:
     6      6   **
     7      7   **    May you do good and not evil.
     8      8   **    May you find forgiveness for yourself and forgive others.
     9      9   **    May you share freely, never taking more than you give.
    10     10   **
    11     11   *************************************************************************
    12         -**
    13     12   */
    14     13   
    15     14   #include <tcl.h>
    16     15   #include "lsm.h"
    17     16   #include "sqlite4.h"
    18     17   #include <assert.h>
    19     18   #include <string.h>
    20     19   
    21     20   extern int getDbPointer(Tcl_Interp *interp, const char *zA, sqlite4 **ppDb);
    22     21   extern const char *sqlite4TestErrorName(int);
           22  +
           23  +/*************************************************************************
           24  +*/
           25  +#define ENCRYPTION_XOR_MASK 0x23b2bbb6
           26  +static int testCompressEncBound(void *pCtx, int nSrc){
           27  +  return nSrc;
           28  +}
           29  +static int testCompressEncCompress(
           30  +  void *pCtx, 
           31  +  char *pOut, int *pnOut, 
           32  +  const char *pIn, int nIn
           33  +){
           34  +  int i;
           35  +  unsigned int *aIn = (unsigned int *)pOut;
           36  +  unsigned int *aOut = (unsigned int *)pIn;
           37  +
           38  +  assert( (nIn%4)==0 );
           39  +  for(i=0; i<(nIn/4); i++){
           40  +    aOut[i] = (aIn[i] ^ ENCRYPTION_XOR_MASK);
           41  +  }
           42  +  *pnOut = nIn;
           43  +
           44  +  return LSM_OK;
           45  +}
           46  +static int testCompressEncUncompress(
           47  +  void *pCtx, 
           48  +  char *pOut, int *pnOut, 
           49  +  const char *pIn, int nIn
           50  +){
           51  +  return testCompressEncCompress(pCtx, pOut, pnOut, pIn, nIn);
           52  +}
           53  +static void testCompressEncFree(void *pCtx){
           54  +  /* no-op */
           55  +}
           56  +/* 
           57  +** End of compression routines "encrypt".
           58  +*************************************************************************/
           59  +
           60  +/*************************************************************************
           61  +*/
           62  +static int testCompressRleBound(void *pCtx, int nSrc){
           63  +  return nSrc*2;
           64  +}
           65  +static int testCompressRleCompress(
           66  +  void *pCtx, 
           67  +  char *pOut, int *pnOut, 
           68  +  const char *pIn, int nIn
           69  +){
           70  +  int iOut = 0;
           71  +  int i;
           72  +  char c;
           73  +  int n;
           74  +
           75  +  c = pIn[0];
           76  +  n = 1;
           77  +  for(i=1; i<nIn; i++){
           78  +    if( pIn[i]==c && n<127 ){
           79  +      n++;
           80  +    }else{
           81  +      pOut[iOut++] = c;
           82  +      pOut[iOut++] = (char)n;
           83  +      c = pIn[i];
           84  +      n = 1;
           85  +    }
           86  +  }
           87  +
           88  +  pOut[iOut++] = c;
           89  +  pOut[iOut++] = (char)n;
           90  +  *pnOut = iOut;
           91  +
           92  +  return LSM_OK;
           93  +}
           94  +static int testCompressRleUncompress(
           95  +  void *pCtx, 
           96  +  char *pOut, int *pnOut, 
           97  +  const char *pIn, int nIn
           98  +){
           99  +  int i;
          100  +  int iOut = 0;
          101  +
          102  +  for(i=0; i<nIn; i+=2){
          103  +    int iRep;
          104  +    char c = pIn[i];
          105  +    int n = (int)(pIn[i+1]);
          106  +
          107  +    for(iRep=0; iRep<n; iRep++){
          108  +      pOut[iOut++] = c;
          109  +    }
          110  +  }
          111  +
          112  +  *pnOut = iOut;
          113  +  return LSM_OK;
          114  +}
          115  +static void testCompressRleFree(void *pCtx){
          116  +}
          117  +/* 
          118  +** End of compression routines "rle".
          119  +*************************************************************************/
          120  +
          121  +/*************************************************************************
          122  +*/
          123  +static int testCompressNoopBound(void *pCtx, int nSrc){
          124  +  return nSrc;
          125  +}
          126  +static int testCompressNoopCompress(
          127  +  void *pCtx, 
          128  +  char *pOut, int *pnOut, 
          129  +  const char *pIn, int nIn
          130  +){
          131  +  *pnOut = nIn;
          132  +  memcpy(pOut, pIn, nIn);
          133  +  return LSM_OK;
          134  +}
          135  +static int testCompressNoopUncompress(
          136  +  void *pCtx, 
          137  +  char *pOut, int *pnOut, 
          138  +  const char *pIn, int nIn
          139  +){
          140  +  *pnOut = nIn;
          141  +  memcpy(pOut, pIn, nIn);
          142  +  return LSM_OK;
          143  +}
          144  +static void testCompressNoopFree(void *pCtx){
          145  +}
          146  +/* 
          147  +** End of compression routines "noop".
          148  +*************************************************************************/
    23    149   
    24    150   /*
    25    151   ** TCLCMD:    sqlite4_lsm_config DB DBNAME PARAM ...
    26    152   */
    27    153   static int test_sqlite4_lsm_config(
    28    154     void * clientData,
    29    155     Tcl_Interp *interp,
................................................................................
    30    156     int objc,
    31    157     Tcl_Obj *CONST objv[]
    32    158   ){
    33    159     struct Switch {
    34    160       const char *zSwitch;
    35    161       int iVal;
    36    162     } aParam[] = {
    37         -    { "log-size",       LSM_CONFIG_LOG_SIZE }, 
    38    163       { "safety",         LSM_CONFIG_SAFETY }, 
    39    164       { "autoflush",      LSM_CONFIG_AUTOFLUSH }, 
    40    165       { "mmap",           LSM_CONFIG_MMAP }, 
    41    166       { "page-size",      LSM_CONFIG_PAGE_SIZE }, 
    42    167       { "autowork",       LSM_CONFIG_AUTOWORK }, 
    43    168       { 0, 0 }
    44    169     };
................................................................................
   282    407       Tcl_SetResult(interp, (char *)sqlite4TestErrorName(rc), TCL_STATIC);
   283    408       return TCL_ERROR;
   284    409     }
   285    410   
   286    411     Tcl_ResetResult(interp);
   287    412     return TCL_OK;
   288    413   }
          414  +
          415  +static int testConfigureSetCompression(
          416  +  Tcl_Interp *interp, 
          417  +  lsm_db *db, 
          418  +  Tcl_Obj *pCmp,
          419  +  unsigned int iId
          420  +){
          421  +  struct CompressionScheme {
          422  +    const char *zName;
          423  +    lsm_compress cmp;
          424  +  } aCmp[] = {
          425  +    { "encrypt", { 0, 43, 
          426  +        testCompressEncBound, testCompressEncCompress,
          427  +        testCompressEncUncompress, testCompressEncFree
          428  +    } },
          429  +    { "rle", { 0, 44, 
          430  +        testCompressRleBound, testCompressRleCompress,
          431  +        testCompressRleUncompress, testCompressRleFree
          432  +    } },
          433  +    { "noop", { 0, 45, 
          434  +        testCompressNoopBound, testCompressNoopCompress,
          435  +        testCompressNoopUncompress, testCompressNoopFree
          436  +    } },
          437  +    { 0, {0, 0, 0, 0, 0, 0} }
          438  +  };
          439  +  int iOpt;
          440  +  int rc;
          441  +
          442  +  if( interp ){
          443  +    rc = Tcl_GetIndexFromObjStruct(
          444  +        interp, pCmp, aCmp, sizeof(aCmp[0]), "scheme", 0, &iOpt
          445  +        );
          446  +    if( rc!=TCL_OK ) return rc;
          447  +  }else{
          448  +    int nOpt = sizeof(aCmp)/sizeof(aCmp[0]);
          449  +    for(iOpt=0; iOpt<nOpt; iOpt++){
          450  +      if( iId==aCmp[iOpt].cmp.iId ) break;
          451  +    }
          452  +    if( iOpt==nOpt ) return 0;
          453  +  }
          454  +
          455  +  rc = lsm_config(db, LSM_CONFIG_SET_COMPRESSION, &aCmp[iOpt].cmp);
          456  +  return rc;
          457  +}
          458  +
          459  +static int testCompressFactory(void *pCtx, lsm_db *db, unsigned int iId){
          460  +  return testConfigureSetCompression(0, db, 0, iId);
          461  +}
          462  +
          463  +static int testConfigureSetFactory(
          464  +  Tcl_Interp *interp, 
          465  +  lsm_db *db, 
          466  +  Tcl_Obj *pArg
          467  +){
          468  +  lsm_compress_factory aFactory[2] = {
          469  +    { 0, 0, 0 },
          470  +    { 0, testCompressFactory, 0 },
          471  +  };
          472  +  int bArg = 0;
          473  +  int rc;
          474  +
          475  +  rc = Tcl_GetBooleanFromObj(interp, pArg, &bArg);
          476  +  if( rc!=TCL_OK ) return rc;
          477  +  assert( bArg==1 || bArg==0 );
          478  +
          479  +  rc = lsm_config(db, LSM_CONFIG_SET_COMPRESSION_FACTORY, &aFactory[bArg]);
          480  +  return rc;
          481  +}
   289    482   
   290    483   static int testConfigureLsm(Tcl_Interp *interp, lsm_db *db, Tcl_Obj *pObj){
   291    484     struct Lsmconfig {
   292    485       const char *zOpt;
   293    486       int eOpt;
   294    487     } aConfig[] = {
   295         -    { "autoflush",        LSM_CONFIG_AUTOFLUSH },
   296         -    { "page_size",        LSM_CONFIG_PAGE_SIZE },
   297         -    { "block_size",       LSM_CONFIG_BLOCK_SIZE },
   298         -    { "safety",           LSM_CONFIG_SAFETY },
   299         -    { "autowork",         LSM_CONFIG_AUTOWORK },
   300         -    { "autocheckpoint",   LSM_CONFIG_AUTOCHECKPOINT },
   301         -    { "log_size",         LSM_CONFIG_LOG_SIZE },
   302         -    { "mmap",             LSM_CONFIG_MMAP },
   303         -    { "use_log",          LSM_CONFIG_USE_LOG },
   304         -    { "automerge",        LSM_CONFIG_AUTOMERGE },
   305         -    { "max_freelist",     LSM_CONFIG_MAX_FREELIST },
   306         -    { "multi_proc",       LSM_CONFIG_MULTIPLE_PROCESSES },
          488  +    { "autoflush",               LSM_CONFIG_AUTOFLUSH },
          489  +    { "page_size",               LSM_CONFIG_PAGE_SIZE },
          490  +    { "block_size",              LSM_CONFIG_BLOCK_SIZE },
          491  +    { "safety",                  LSM_CONFIG_SAFETY },
          492  +    { "autowork",                LSM_CONFIG_AUTOWORK },
          493  +    { "autocheckpoint",          LSM_CONFIG_AUTOCHECKPOINT },
          494  +    { "mmap",                    LSM_CONFIG_MMAP },
          495  +    { "use_log",                 LSM_CONFIG_USE_LOG },
          496  +    { "automerge",               LSM_CONFIG_AUTOMERGE },
          497  +    { "max_freelist",            LSM_CONFIG_MAX_FREELIST },
          498  +    { "multi_proc",              LSM_CONFIG_MULTIPLE_PROCESSES },
          499  +    { "set_compression",         LSM_CONFIG_SET_COMPRESSION },
          500  +    { "set_compression_factory", LSM_CONFIG_SET_COMPRESSION_FACTORY },
   307    501       { 0, 0 }
   308    502     };
   309    503     int nElem;
   310    504     int i;
   311    505     Tcl_Obj **apElem;
   312    506     int rc;
   313    507   
................................................................................
   321    515         if( i==(nElem-1) ){
   322    516           Tcl_ResetResult(interp);
   323    517           Tcl_AppendResult(interp, "option \"", Tcl_GetString(apElem[i]), 
   324    518               "\" requires an argument", 0
   325    519               );
   326    520           rc = TCL_ERROR;
   327    521         }else{
   328         -        int iVal;
   329         -        rc = Tcl_GetIntFromObj(interp, apElem[i+1], &iVal);
   330         -        if( rc==TCL_OK ){
   331         -          lsm_config(db, aConfig[iOpt].eOpt, &iVal);
          522  +        if( aConfig[iOpt].eOpt==LSM_CONFIG_SET_COMPRESSION ){
          523  +          rc = testConfigureSetCompression(interp, db, apElem[i+1], 0);
          524  +        }
          525  +        else if( aConfig[iOpt].eOpt==LSM_CONFIG_SET_COMPRESSION_FACTORY ){
          526  +          rc = testConfigureSetFactory(interp, db, apElem[i+1]);
          527  +        }
          528  +        else {
          529  +          int iVal;
          530  +          rc = Tcl_GetIntFromObj(interp, apElem[i+1], &iVal);
          531  +          if( rc==TCL_OK ){
          532  +            lsm_config(db, aConfig[iOpt].eOpt, &iVal);
          533  +          }
          534  +          Tcl_SetObjResult(interp, Tcl_NewIntObj(iVal));
   332    535           }
   333    536         }
   334    537       }
   335    538     }
   336    539   
   337    540     return rc;
   338    541   }
          542  +
   339    543   
   340    544   typedef struct TclLsmCursor TclLsmCursor;
   341    545   typedef struct TclLsm TclLsm;
   342    546   
   343    547   struct TclLsm {
   344    548     lsm_db *db;
   345    549   };
................................................................................
   371    575   static void test_lsm_del(void *ctx){
   372    576     TclLsm *p = (TclLsm *)ctx;
   373    577     if( p ){
   374    578       lsm_close(p->db);
   375    579       ckfree((char *)p);
   376    580     }
   377    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  +}
   378    614   
   379    615   /*
   380    616   ** Usage: CSR sub-command ...
   381    617   */
   382    618   static int test_lsm_cursor_cmd(
   383    619     void * clientData,
   384    620     Tcl_Interp *interp,
................................................................................
   517    753       /*  5 */ {"commit",       1, "LEVEL"},
   518    754       /*  6 */ {"rollback",     1, "LEVEL"},
   519    755       /*  7 */ {"csr_open",     1, "CSR"},
   520    756       /*  8 */ {"work",        -1, "?NMERGE? NPAGE"},
   521    757       /*  9 */ {"flush",        0, ""},
   522    758       /* 10 */ {"config",       1, "LIST"},
   523    759       /* 11 */ {"checkpoint",   0, ""},
          760  +    /* 12 */ {"info",         1, "OPTION"},
   524    761       {0, 0, 0}
   525    762     };
   526    763     int iCmd;
   527    764     int rc;
   528    765     TclLsm *p = (TclLsm *)clientData;
   529    766   
   530    767     if( objc<2 ){
................................................................................
   619    856         return TCL_OK;
   620    857       }
   621    858   
   622    859       case 8: assert( 0==strcmp(aCmd[8].zCmd, "work") ); {
   623    860         int nWork = 0;
   624    861         int nMerge = 1;
   625    862         int nWrite = 0;
   626         -      int i;
   627    863   
   628    864         if( objc==3 ){
   629    865           rc = Tcl_GetIntFromObj(interp, objv[2], &nWork);
   630    866         }else if( objc==4 ){
   631    867           rc = Tcl_GetIntFromObj(interp, objv[2], &nMerge);
   632    868           if( rc!=TCL_OK ) return rc;
   633    869           rc = Tcl_GetIntFromObj(interp, objv[3], &nWork);
................................................................................
   652    888         return testConfigureLsm(interp, p->db, objv[2]);
   653    889       }
   654    890   
   655    891       case 11: assert( 0==strcmp(aCmd[11].zCmd, "checkpoint") ); {
   656    892         rc = lsm_checkpoint(p->db, 0);
   657    893         return test_lsm_error(interp, "lsm_checkpoint", rc);
   658    894       }
          895  +
          896  +    case 12: assert( 0==strcmp(aCmd[12].zCmd, "info") ); {
          897  +      return testInfoLsm(interp, p->db, objv[2]);
          898  +    }
   659    899   
   660    900       default:
   661    901         assert( 0 );
   662    902     }
   663    903   
   664    904     Tcl_AppendResult(interp, "internal error", 0);
   665    905     return TCL_ERROR;

Changes to www/lsmapi.wiki.

    17     17   
    18     18   <h1>LSM API Topics</h1>
    19     19   <ol>
    20     20   <li><a href="#database" style=text-decoration:none>Database Runtime Environment</a>
    21     21   <li><a href="#lsm" style=text-decoration:none>LSM Error Codes</a>
    22     22   <li><a href="#creating" style=text-decoration:none>Creating and Destroying Database Connection Handles</a>
    23     23   <li><a href="#connecting" style=text-decoration:none>Connecting to a Database</a>
    24         -<li><a href="#obtaining" style=text-decoration:none>Obtaining pointers to databases environments</a>
           24  +<li><a href="#obtaining" style=text-decoration:none>Obtaining pointers to database environments</a>
    25     25   <li><a href="#configuring" style=text-decoration:none>Configuring a database connection.</a>
    26     26   <li><a href="#compression" style=text-decoration:none>Compression and/or Encryption Hooks</a>
    27     27   <li><a href="#allocating" style=text-decoration:none>Allocating and Freeing Memory</a>
    28     28   <li><a href="#querying" style=text-decoration:none>Querying a Connection For Operational Data</a>
    29     29   <li><a href="#opening" style=text-decoration:none>Opening and Closing Write Transactions</a>
    30     30   <li><a href="#writing" style=text-decoration:none>Writing to a Database</a>
    31     31   <li><a href="#explicit" style=text-decoration:none>Explicit Database Work and Checkpointing</a>
................................................................................
    63     63   <span style=display:block;float:left;width:35ex><a href=#lsm_new>lsm_new</a></span>
    64     64   <span style=display:block;float:left;width:35ex><a href=#lsm_open>lsm_open</a></span>
    65     65   <span style=display:block;float:left;width:35ex><a href=#lsm_rollback>lsm_rollback</a></span>
    66     66   <span style=display:block;float:left;width:35ex><a href=#lsm_work>lsm_work</a></span>
    67     67   <br style=clear:both>
    68     68   <h1 style=clear:both>All LSM API Types</h1>
    69     69   <span style=display:block;float:left;width:35ex><a href=#lsm_compress>lsm_compress</a></span>
           70  +<span style=display:block;float:left;width:35ex><a href=#lsm_compress>lsm_compress</a></span>
    70     71   <span style=display:block;float:left;width:35ex><a href=#lsm_env>lsm_env</a></span>
    71     72   <br style=clear:both>
    72     73   <h1>All LSM API Constants</h1>
    73     74   <span style=display:block;float:left;width:35ex><a href=#LSM_BUSY>LSM_BUSY</a></span>
    74     75   <span style=display:block;float:left;width:35ex><a href=#LSM_CANTOPEN>LSM_CANTOPEN</a></span>
    75     76   <span style=display:block;float:left;width:35ex><a href=#LSM_CONFIG_AUTOCHECKPOINT>LSM_CONFIG_AUTOCHECKPOINT</a></span>
    76     77   <span style=display:block;float:left;width:35ex><a href=#LSM_CONFIG_AUTOFLUSH>LSM_CONFIG_AUTOFLUSH</a></span>
    77     78   <span style=display:block;float:left;width:35ex><a href=#LSM_CONFIG_AUTOMERGE>LSM_CONFIG_AUTOMERGE</a></span>
    78     79   <span style=display:block;float:left;width:35ex><a href=#LSM_CONFIG_AUTOWORK>LSM_CONFIG_AUTOWORK</a></span>
    79     80   <span style=display:block;float:left;width:35ex><a href=#LSM_CONFIG_BLOCK_SIZE>LSM_CONFIG_BLOCK_SIZE</a></span>
    80     81   <span style=display:block;float:left;width:35ex><a href=#LSM_CONFIG_GET_COMPRESSION>LSM_CONFIG_GET_COMPRESSION</a></span>
    81         -<span style=display:block;float:left;width:35ex><a href=#LSM_CONFIG_LOG_SIZE>LSM_CONFIG_LOG_SIZE</a></span>
    82     82   <span style=display:block;float:left;width:35ex><a href=#LSM_CONFIG_MAX_FREELIST>LSM_CONFIG_MAX_FREELIST</a></span>
    83     83   <span style=display:block;float:left;width:35ex><a href=#LSM_CONFIG_MMAP>LSM_CONFIG_MMAP</a></span>
    84     84   <span style=display:block;float:left;width:35ex><a href=#LSM_CONFIG_MULTIPLE_PROCESSES>LSM_CONFIG_MULTIPLE_PROCESSES</a></span>
    85     85   <span style=display:block;float:left;width:35ex><a href=#LSM_CONFIG_PAGE_SIZE>LSM_CONFIG_PAGE_SIZE</a></span>
    86     86   <span style=display:block;float:left;width:35ex><a href=#LSM_CONFIG_SAFETY>LSM_CONFIG_SAFETY</a></span>
    87     87   <span style=display:block;float:left;width:35ex><a href=#LSM_CONFIG_SET_COMPRESSION>LSM_CONFIG_SET_COMPRESSION</a></span>
           88  +<span style=display:block;float:left;width:35ex><a href=#LSM_CONFIG_SET_COMPRESSION_FACTORY>LSM_CONFIG_SET_COMPRESSION_FACTORY</a></span>
    88     89   <span style=display:block;float:left;width:35ex><a href=#LSM_CONFIG_USE_LOG>LSM_CONFIG_USE_LOG</a></span>
    89     90   <span style=display:block;float:left;width:35ex><a href=#LSM_CORRUPT>LSM_CORRUPT</a></span>
    90     91   <span style=display:block;float:left;width:35ex><a href=#LSM_ERROR>LSM_ERROR</a></span>
    91     92   <span style=display:block;float:left;width:35ex><a href=#LSM_FULL>LSM_FULL</a></span>
    92     93   <span style=display:block;float:left;width:35ex><a href=#LSM_INFO_ARRAY_PAGES>LSM_INFO_ARRAY_PAGES</a></span>
    93     94   <span style=display:block;float:left;width:35ex><a href=#LSM_INFO_ARRAY_STRUCTURE>LSM_INFO_ARRAY_STRUCTURE</a></span>
    94     95   <span style=display:block;float:left;width:35ex><a href=#LSM_INFO_CHECKPOINT_SIZE>LSM_INFO_CHECKPOINT_SIZE</a></span>
    95     96   <span style=display:block;float:left;width:35ex><a href=#LSM_INFO_DB_STRUCTURE>LSM_INFO_DB_STRUCTURE</a></span>
    96     97   <span style=display:block;float:left;width:35ex><a href=#LSM_INFO_FREELIST>LSM_INFO_FREELIST</a></span>
           98  +<span style=display:block;float:left;width:35ex><a href=#LSM_INFO_FREELIST_SIZE>LSM_INFO_FREELIST_SIZE</a></span>
    97     99   <span style=display:block;float:left;width:35ex><a href=#LSM_INFO_LOG_STRUCTURE>LSM_INFO_LOG_STRUCTURE</a></span>
    98    100   <span style=display:block;float:left;width:35ex><a href=#LSM_INFO_NREAD>LSM_INFO_NREAD</a></span>
    99    101   <span style=display:block;float:left;width:35ex><a href=#LSM_INFO_NWRITE>LSM_INFO_NWRITE</a></span>
   100    102   <span style=display:block;float:left;width:35ex><a href=#LSM_INFO_PAGE_ASCII_DUMP>LSM_INFO_PAGE_ASCII_DUMP</a></span>
   101    103   <span style=display:block;float:left;width:35ex><a href=#LSM_INFO_PAGE_HEX_DUMP>LSM_INFO_PAGE_HEX_DUMP</a></span>
   102    104   <span style=display:block;float:left;width:35ex><a href=#LSM_INFO_TREE_SIZE>LSM_INFO_TREE_SIZE</a></span>
   103    105   <span style=display:block;float:left;width:35ex><a href=#LSM_IOERR>LSM_IOERR</a></span>
................................................................................
   178    180   <verbatim>int lsm_new(lsm_env*, lsm_db **ppDb);
   179    181   int lsm_close(lsm_db *pDb);
   180    182   </verbatim>
   181    183   <p>Open and close a database connection handle.
   182    184   <h2 id=connecting>Connecting to a Database<a id=lsm_open></a></h2>
   183    185   <verbatim>int lsm_open(lsm_db *pDb, const char *zFilename);
   184    186   </verbatim>
   185         -<h2 id=obtaining>Obtaining pointers to databases environments</h2>
          187  +<h2 id=obtaining>Obtaining pointers to database environments</h2>
   186    188   <verbatim>lsm_env *lsm_get_env(lsm_db *pDb);
   187    189   lsm_env *lsm_default_env(void);
   188    190   </verbatim>
   189    191   <p>Return a pointer to the environment used by the database connection 
   190    192   passed as the first argument. Assuming the argument is valid, this 
   191    193   function always returns a valid environment pointer - it cannot fail.
   192    194   The lsm_default_env() function returns a pointer to the default LSM
   193    195   environment for the current platform.
   194         -<h2 id=configuring>Configuring a database connection.<a id=lsm_config></a><a id=LSM_CONFIG_AUTOFLUSH></a><a id=LSM_CONFIG_PAGE_SIZE></a><a id=LSM_CONFIG_SAFETY></a><a id=LSM_CONFIG_BLOCK_SIZE></a><a id=LSM_CONFIG_AUTOWORK></a><a id=LSM_CONFIG_LOG_SIZE></a><a id=LSM_CONFIG_MMAP></a><a id=LSM_CONFIG_USE_LOG></a><a id=LSM_CONFIG_AUTOMERGE></a><a id=LSM_CONFIG_MAX_FREELIST></a><a id=LSM_CONFIG_MULTIPLE_PROCESSES></a><a id=LSM_CONFIG_AUTOCHECKPOINT></a><a id=LSM_CONFIG_SET_COMPRESSION></a><a id=LSM_CONFIG_GET_COMPRESSION></a><a id=LSM_SAFETY_OFF></a><a id=LSM_SAFETY_NORMAL></a><a id=LSM_SAFETY_FULL></a></h2>
          196  +<h2 id=configuring>Configuring a database connection.<a id=lsm_config></a><a id=LSM_CONFIG_AUTOFLUSH></a><a id=LSM_CONFIG_PAGE_SIZE></a><a id=LSM_CONFIG_SAFETY></a><a id=LSM_CONFIG_BLOCK_SIZE></a><a id=LSM_CONFIG_AUTOWORK></a><a id=LSM_CONFIG_MMAP></a><a id=LSM_CONFIG_USE_LOG></a><a id=LSM_CONFIG_AUTOMERGE></a><a id=LSM_CONFIG_MAX_FREELIST></a><a id=LSM_CONFIG_MULTIPLE_PROCESSES></a><a id=LSM_CONFIG_AUTOCHECKPOINT></a><a id=LSM_CONFIG_SET_COMPRESSION></a><a id=LSM_CONFIG_GET_COMPRESSION></a><a id=LSM_CONFIG_SET_COMPRESSION_FACTORY></a><a id=LSM_SAFETY_OFF></a><a id=LSM_SAFETY_NORMAL></a><a id=LSM_SAFETY_FULL></a></h2>
   195    197   <verbatim>int lsm_config(lsm_db *, int, ...);
   196         -#define LSM_CONFIG_AUTOFLUSH           1
   197         -#define LSM_CONFIG_PAGE_SIZE           2
   198         -#define LSM_CONFIG_SAFETY              3
   199         -#define LSM_CONFIG_BLOCK_SIZE          4
   200         -#define LSM_CONFIG_AUTOWORK            5
   201         -#define LSM_CONFIG_LOG_SIZE            6
   202         -#define LSM_CONFIG_MMAP                7
   203         -#define LSM_CONFIG_USE_LOG             8
   204         -#define LSM_CONFIG_AUTOMERGE           9
   205         -#define LSM_CONFIG_MAX_FREELIST       10
   206         -#define LSM_CONFIG_MULTIPLE_PROCESSES 11
   207         -#define LSM_CONFIG_AUTOCHECKPOINT     12
   208         -#define LSM_CONFIG_SET_COMPRESSION    13
   209         -#define LSM_CONFIG_GET_COMPRESSION    14
          198  +#define LSM_CONFIG_AUTOFLUSH                1
          199  +#define LSM_CONFIG_PAGE_SIZE                2
          200  +#define LSM_CONFIG_SAFETY                   3
          201  +#define LSM_CONFIG_BLOCK_SIZE               4
          202  +#define LSM_CONFIG_AUTOWORK                 5
          203  +#define LSM_CONFIG_MMAP                     7
          204  +#define LSM_CONFIG_USE_LOG                  8
          205  +#define LSM_CONFIG_AUTOMERGE                9
          206  +#define LSM_CONFIG_MAX_FREELIST            10
          207  +#define LSM_CONFIG_MULTIPLE_PROCESSES      11
          208  +#define LSM_CONFIG_AUTOCHECKPOINT          12
          209  +#define LSM_CONFIG_SET_COMPRESSION         13
          210  +#define LSM_CONFIG_GET_COMPRESSION         14
          211  +#define LSM_CONFIG_SET_COMPRESSION_FACTORY 15
   210    212   #define LSM_SAFETY_OFF    0
   211    213   #define LSM_SAFETY_NORMAL 1
   212    214   #define LSM_SAFETY_FULL   2
   213    215   </verbatim>
   214    216   <p>The lsm_config() function is used to configure a database connection.
   215    217   The following values may be passed as the second argument to lsm_config().
   216         -<p><dl><dt>LSM_CONFIG_AUTOFLUSH<dd>A read/write integer parameter. This value determines the maximum amount
   217         -of space (in bytes) used to accumulate writes in main-memory before 
   218         -they are flushed to a level 0 segment.
          218  +<p><dl><dt>LSM_CONFIG_AUTOFLUSH<dd>A read/write integer parameter. 
          219  +<p>This value determines the amount of data allowed to accumulate in a
          220  +live in-memory tree before it is marked as old. After committing a
          221  +transaction, a connection checks if the size of the live in-memory tree,
          222  +including data structure overhead, is greater than the value of this
          223  +option in KB. If it is, and there is not already an old in-memory tree,
          224  +the live in-memory tree is marked as old.
          225  +<p>The maximum allowable value is 1048576 (1GB). There is no minimum 
          226  +value. If this parameter is set to zero, then an attempt is made to
          227  +mark the live in-memory tree as old after each transaction is committed.
          228  +<p>The default value is 1024 (1MB).
   219    229   <p><dt>LSM_CONFIG_PAGE_SIZE<dd>A read/write integer parameter. This parameter may only be set before
   220    230   lsm_open() has been called.
   221         -<p><dt>LSM_CONFIG_BLOCK_SIZE<dd>A read/write integer parameter. This parameter may only be set before
   222         -lsm_open() has been called.
   223         -<p><dt>LSM_CONFIG_LOG_SIZE<dd>A read/write integer parameter.
          231  +<p><dt>LSM_CONFIG_BLOCK_SIZE<dd>A read/write integer parameter. 
          232  +<p>This parameter may only be set before lsm_open() has been called. It
          233  +must be set to a power of two between 64 and 65536, inclusive (block 
          234  +sizes between 64KB and 64MB).
          235  +<p>If the connection creates a new database, the block size of the new
          236  +database is set to the value of this option in KB. After lsm_open()
          237  +has been called, querying this parameter returns the actual block
          238  +size of the opened database.
          239  +<p>The default value is 1024 (1MB blocks).
   224    240   <p><dt>LSM_CONFIG_SAFETY<dd>A read/write integer parameter. Valid values are 0, 1 (the default) 
   225    241   and 2. This parameter determines how robust the database is in the
   226    242   face of a system crash (e.g. a power failure or operating system 
   227    243   crash). As follows:
   228    244   <p>0 (off):    No robustness. A system crash may corrupt the database.
   229    245   <p>1 (normal): Some robustness. A system crash may not corrupt the
   230    246   database file, but recently committed transactions may
   231    247   be lost following recovery.
   232    248   <p>2 (full):   Full robustness. A system crash may not corrupt the
   233    249   database file. Following recovery the database file
   234    250   contains all successfully committed transactions.
   235    251   <p><dt>LSM_CONFIG_AUTOWORK<dd>A read/write integer parameter.
   236    252   <p><dt>LSM_CONFIG_AUTOCHECKPOINT<dd>A read/write integer parameter.
          253  +<p>If this option is set to non-zero value N, then a checkpoint is
          254  +automatically attempted after each N KB of data have been written to 
          255  +the database file.
          256  +<p>The amount of uncheckpointed data already written to the database file
          257  +is a global parameter. After performing database work (writing to the
          258  +database file), the process checks if the total amount of uncheckpointed 
          259  +data exceeds the value of this paramter. If so, a checkpoint is performed.
          260  +This means that this option may cause the connection to perform a 
          261  +checkpoint even if the current connection has itself written very little
          262  +data into the database file.
          263  +<p>The default value is 2048 (checkpoint every 2MB).
   237    264   <p><dt>LSM_CONFIG_MMAP<dd>A read/write integer parameter. True to use mmap() to access the 
   238    265   database file. False otherwise.
   239    266   <p><dt>LSM_CONFIG_USE_LOG<dd>A read/write boolean parameter. True (the default) to use the log
   240    267   file normally. False otherwise.
   241    268   <p><dt>LSM_CONFIG_AUTOMERGE<dd>A read/write integer parameter. The minimum number of segments to
   242    269   merge together at a time. Default value 4.
   243    270   <p><dt>LSM_CONFIG_MAX_FREELIST<dd>A read/write integer parameter. The maximum number of free-list 
................................................................................
   255    282   content. The argument to this option should be a pointer to a structure
   256    283   of type lsm_compress. The lsm_config() method takes a copy of the 
   257    284   structures contents.
   258    285   <p>This option may only be used before lsm_open() is called. Invoking it
   259    286   after lsm_open() has been called results in an LSM_MISUSE error.
   260    287   <p><dt>LSM_CONFIG_GET_COMPRESSION<dd>Query the compression methods used to compress and decompress database
   261    288   content.
   262         -</dl><h2 id=compression>Compression and/or Encryption Hooks<a id=lsm_compress></a></h2>
          289  +<p><dt>LSM_CONFIG_SET_COMPRESSION_FACTORY<dd>Configure a factory method to be invoked in case of an LSM_MISMATCH
          290  +error.
          291  +</dl><h2 id=compression>Compression and/or Encryption Hooks<a id=lsm_compress></a><a id=lsm_compress></a></h2>
   263    292   <verbatim>struct lsm_compress {
   264    293     void *pCtx;
   265    294     unsigned int iId;
   266    295     int (*xBound)(void *, int nSrc);
   267    296     int (*xCompress)(void *, char *, int *, const char *, int);
   268    297     int (*xUncompress)(void *, char *, int *, const char *, int);
          298  +  void (*xFree)(void *pCtx);
          299  +};
          300  +struct lsm_compress_factory {
          301  +  void *pCtx;
          302  +  int (*xFactory)(void *, lsm_db *, u32);
          303  +  void (*xFree)(void *pCtx);
   269    304   };
   270    305   </verbatim>
   271    306   <h2 id=allocating>Allocating and Freeing Memory<a id=lsm_free></a></h2>
   272    307   <verbatim>void *lsm_malloc(lsm_env*, size_t);
   273    308   void *lsm_realloc(lsm_env*, void *, size_t);
   274    309   void lsm_free(lsm_env*, void *);
   275    310   </verbatim>
   276    311   <p>Invoke the memory allocation functions that belong to environment
   277    312   pEnv. Or the system defaults if no memory allocation functions have 
   278    313   been registered.
   279         -<h2 id=querying>Querying a Connection For Operational Data<a id=lsm_info></a><a id=LSM_INFO_NWRITE></a><a id=LSM_INFO_NREAD></a><a id=LSM_INFO_DB_STRUCTURE></a><a id=LSM_INFO_LOG_STRUCTURE></a><a id=LSM_INFO_ARRAY_STRUCTURE></a><a id=LSM_INFO_PAGE_ASCII_DUMP></a><a id=LSM_INFO_PAGE_HEX_DUMP></a><a id=LSM_INFO_FREELIST></a><a id=LSM_INFO_ARRAY_PAGES></a><a id=LSM_INFO_CHECKPOINT_SIZE></a><a id=LSM_INFO_TREE_SIZE></a></h2>
          314  +<h2 id=querying>Querying a Connection For Operational Data<a id=lsm_info></a><a id=LSM_INFO_NWRITE></a><a id=LSM_INFO_NREAD></a><a id=LSM_INFO_DB_STRUCTURE></a><a id=LSM_INFO_LOG_STRUCTURE></a><a id=LSM_INFO_ARRAY_STRUCTURE></a><a id=LSM_INFO_PAGE_ASCII_DUMP></a><a id=LSM_INFO_PAGE_HEX_DUMP></a><a id=LSM_INFO_FREELIST></a><a id=LSM_INFO_ARRAY_PAGES></a><a id=LSM_INFO_CHECKPOINT_SIZE></a><a id=LSM_INFO_TREE_SIZE></a><a id=LSM_INFO_FREELIST_SIZE></a></h2>
   280    315   <verbatim>int lsm_info(lsm_db *, int, ...);
   281    316   #define LSM_INFO_NWRITE           1
   282    317   #define LSM_INFO_NREAD            2
   283    318   #define LSM_INFO_DB_STRUCTURE     3
   284    319   #define LSM_INFO_LOG_STRUCTURE    4
   285    320   #define LSM_INFO_ARRAY_STRUCTURE  5
   286    321   #define LSM_INFO_PAGE_ASCII_DUMP  6
   287    322   #define LSM_INFO_PAGE_HEX_DUMP    7
   288    323   #define LSM_INFO_FREELIST         8
   289    324   #define LSM_INFO_ARRAY_PAGES      9
   290    325   #define LSM_INFO_CHECKPOINT_SIZE 10
   291    326   #define LSM_INFO_TREE_SIZE       11
          327  +#define LSM_INFO_FREELIST_SIZE   12
   292    328   </verbatim>
   293    329   <p>Query a database connection for operational statistics or data.
   294    330   The following values may be passed as the second argument to lsm_info().
   295    331   <p><dl><dt>LSM_INFO_NWRITE<dd>The third parameter should be of type (int *). The location pointed
   296    332   to by the third parameter is set to the number of 4KB pages written to
   297    333   the database file during the lifetime of this connection. 
   298    334   <p><dt>LSM_INFO_NREAD<dd>The third parameter should be of type (int *). The location pointed
................................................................................
   350    386   to is populated with a pointer to a nul-terminated string containing
   351    387   the string representation of a Tcl data-structure. The returned 
   352    388   string should be eventually freed by the caller using lsm_free().
   353    389   <p>The Tcl structure returned is a list containing one element for each
   354    390   free block in the database. The element itself consists of two 
   355    391   integers - the block number and the id of the snapshot that freed it.
   356    392   <p><dt>LSM_INFO_CHECKPOINT_SIZE<dd>The third argument should be of type (int *). The location pointed to
   357         -by this argument is populated with the number of bytes written to the
          393  +by this argument is populated with the number of KB written to the
   358    394   database file since the most recent checkpoint.
   359    395   <p><dt>LSM_INFO_TREE_SIZE<dd>If this value is passed as the second argument to an lsm_info() call, it
   360    396   should be followed by two arguments of type (int *) (for a total of four
   361    397   arguments).
   362    398   <p>At any time, there are either one or two tree structures held in shared
   363    399   memory that new database clients will access (there may also be additional
   364    400   tree structures being used by older clients - this API does not provide
   365    401   information on them). One tree structure - the current tree - is used to
   366    402   accumulate new data written to the database. The other tree structure -
   367    403   the old tree - is a read-only tree holding older data and may be flushed 
   368    404   to disk at any time.
   369    405   <p>Assuming no error occurs, the location pointed to by the first of the two
   370         -(int *) arguments is set to the size of the old in-memory tree in bytes.
          406  +(int *) arguments is set to the size of the old in-memory tree in KB.
   371    407   The second is set to the size of the current, or live in-memory tree.
   372    408   </dl><h2 id=opening>Opening and Closing Write Transactions<a id=lsm_begin></a><a id=lsm_commit></a><a id=lsm_rollback></a></h2>
   373    409   <verbatim>int lsm_begin(lsm_db *pDb, int iLevel);
   374    410   int lsm_commit(lsm_db *pDb, int iLevel);
   375    411   int lsm_rollback(lsm_db *pDb, int iLevel);
   376    412   </verbatim>
   377    413   <p>These functions are used to open and close transactions and nested 
................................................................................
   405    441   Delete a value from the database. No error is returned if the specified
   406    442   key value does not exist in the database.
   407    443   Delete all database entries with keys that are greater than (pKey1/nKey1) 
   408    444   and smaller than (pKey2/nKey2). Note that keys (pKey1/nKey1) and
   409    445   (pKey2/nKey2) themselves, if they exist in the database, are not deleted.
   410    446   <p>Return LSM_OK if successful, or an LSM error code otherwise.
   411    447   <h2 id=explicit>Explicit Database Work and Checkpointing<a id=lsm_work></a><a id=lsm_flush></a><a id=lsm_checkpoint></a></h2>
   412         -<verbatim>int lsm_work(lsm_db *pDb, int nMerge, int nPage, int *pnWrite);
          448  +<verbatim>int lsm_work(lsm_db *pDb, int nMerge, int nKB, int *pnWrite);
   413    449   int lsm_flush(lsm_db *pDb);
   414         -int lsm_checkpoint(lsm_db *pDb, int *pnByte);
          450  +int lsm_checkpoint(lsm_db *pDb, int *pnKB);
   415    451   </verbatim>
   416    452   <p>This function is called by a thread to work on the database structure.
   417    453   Attempt to checkpoint the current database snapshot. Return an LSM
   418    454   error code if an error occurs or LSM_OK otherwise.
   419    455   <p>If the current snapshot has already been checkpointed, calling this 
   420         -function is a no-op. In this case if pnByte is not NULL, *pnByte is
          456  +function is a no-op. In this case if pnKB is not NULL, *pnKB is
   421    457   set to 0. Or, if the current snapshot is successfully checkpointed
   422         -by this function and pbCkpt is not NULL, *pnByte is set to the number
          458  +by this function and pbKB is not NULL, *pnKB is set to the number
   423    459   of bytes written to the database file since the previous checkpoint
   424    460   (the same measure as returned by the LSM_INFO_CHECKPOINT_SIZE query).
   425    461   <h2 id=opening>Opening and Closing Database Cursors<a id=lsm_csr_open></a><a id=lsm_csr_close></a></h2>
   426    462   <verbatim>int lsm_csr_open(lsm_db *pDb, lsm_cursor **ppCsr);
   427    463   int lsm_csr_close(lsm_cursor *pCsr);
   428    464   </verbatim>
   429    465   <p>Open and close a database cursor.

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>
................................................................................
  1146   1179   <p>The example code below might be executed in a background thread or process
  1147   1180   in order to perform database work and checkpointing. In this case all other
  1148   1181   clients should set the LSM_CONFIG_AUTOWORK parameter to zero.
  1149   1182   
  1150   1183   <verbatim>
  1151   1184     int rc;
  1152   1185     lsm_db *db;
  1153         -  int nCkpt = 4*1024*1024;
         1186  +  int nCkpt = 4*1024;             /* 4096KB == 4MB */
  1154   1187   
  1155   1188     /* Open a database connection to database "test.db". 
  1156   1189     **
  1157   1190     ** Configure the connection to automatically checkpoint the database after
  1158   1191     ** writing each 4MB of data to it (instead of the default 2MB). As well
  1159   1192     ** as to auto-work, the LSM_CONFIG_AUTOCHECKPOINT parameter applies to data
  1160   1193     ** written by explicit calls to lsm_work().
................................................................................
  1164   1197     lsm_open(db, "test.db");
  1165   1198   
  1166   1199     while( 1 ){
  1167   1200       int nWrite;
  1168   1201   
  1169   1202       /* Attempt up to 512KB of work. Set nWrite to the number of bytes
  1170   1203       ** actually written to disk.  */
  1171         -    rc = lsm_work(db, 2, 512*1024, &nWrite);
         1204  +    rc = lsm_work(db, 2, 512, &nWrite);
  1172   1205       if( rc!=LSM_OK && rc!=LSM_BUSY ){
  1173   1206         /* Anything other than LSM_OK or LSM_BUSY is a problem. LSM_BUSY
  1174   1207         ** indicates that some other client has taken the WORKER lock. Any
  1175   1208         ** other error indicates something has gone quite wrong.  */
  1176   1209         lsm_close(db);
  1177   1210         return rc;
  1178   1211       }