SQLite4
Check-in [7268cf7535]
Not logged in

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

Overview
Comment:Fix lsmview.tcl so that it can view databases compressed with zlib.
Downloads: Tarball | ZIP archive | SQL archive
Timelines: family | ancestors | descendants | both | trunk
Files: files | file ages | folders
SHA1: 7268cf7535c8e1cbc984bc6a6e22efaffe656288
User & Date: dan 2012-11-06 19:14:08
Context
2012-11-07
20:08
Remove the LSM_WORK_OPTIMIZE flag. Add free-list management related tests and fixes. check-in: 91912a39ca user: dan tags: trunk
2012-11-06
19:14
Fix lsmview.tcl so that it can view databases compressed with zlib. check-in: 7268cf7535 user: dan tags: trunk
11:49
Fix block pointer related bug introduced by the previous commit. check-in: 579ee866b7 user: dan tags: trunk
Changes
Hide Diffs Unified Diffs Ignore Whitespace Patch

Changes to lsm-test/lsmtest.h.

78
79
80
81
82
83
84



85
86
87
88
89
90
91
*/
int test_lsm_open(const char *zFilename, int bClear, TestDb **ppDb);
int test_lsm_lomem_open(const char *zFilename, int bClear, TestDb **ppDb);
int test_lsm_zip_open(const char *zFilename, int bClear, TestDb **ppDb);
int test_lsm_small_open(const char *zFilename, int bClear, TestDb **ppDb);
int test_lsm_mt2(const char *zFilename, int bClear, TestDb **ppDb);
int test_lsm_mt3(const char *zFilename, int bClear, TestDb **ppDb);




/* Functions in testutil.c. */
int  testPrngInit(void);
u32  testPrngValue(u32 iVal);
void testPrngArray(u32 iVal, u32 *aOut, int nOut);
void testPrngString(u32 iVal, char *aOut, int nOut);








>
>
>







78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
*/
int test_lsm_open(const char *zFilename, int bClear, TestDb **ppDb);
int test_lsm_lomem_open(const char *zFilename, int bClear, TestDb **ppDb);
int test_lsm_zip_open(const char *zFilename, int bClear, TestDb **ppDb);
int test_lsm_small_open(const char *zFilename, int bClear, TestDb **ppDb);
int test_lsm_mt2(const char *zFilename, int bClear, TestDb **ppDb);
int test_lsm_mt3(const char *zFilename, int bClear, TestDb **ppDb);

int tdb_lsm_configure(lsm_db *, const char *);


/* Functions in testutil.c. */
int  testPrngInit(void);
u32  testPrngValue(u32 iVal);
void testPrngArray(u32 iVal, u32 *aOut, int nOut);
void testPrngString(u32 iVal, char *aOut, int nOut);

Changes to lsm-test/lsmtest_func.c.

60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75


76
77
78

79
80
81




82
83
84
85
86
87
88

89
90






91


92
93
94

95
96
97




98
99

100
101
102
103
104
105

106
107
108
109
110
111
112
113
114
115

116
117
118
119
120
121

122
123
124
125
126
127
128
129
130
131
132
133
134






135
136
137
138
139
140
141
142
143
 usage:
  testPrintUsage("?-optimize? ?-n N? DATABASE");
  return -1;
}


/*
**   lsmtest show DATABASE ?array|page-ascii|page-hex PGNO?
*/
int do_show(int nArg, char **azArg){
  lsm_db *pDb;
  int rc;
  const char *zDb;

  int eOpt = LSM_INFO_DB_STRUCTURE;
  unsigned int iPg = 0;



  struct Option {
    const char *zName;

    int eOpt;
  } aOpt [] = { 
    { "array",      LSM_INFO_ARRAY_STRUCTURE },




    { "page-ascii", LSM_INFO_PAGE_ASCII_DUMP },
    { "page-hex",   LSM_INFO_PAGE_HEX_DUMP },
    { "freelist",   LSM_INFO_FREELIST },
    { 0, 0 } 
  };

  char *z = 0;


  if( nArg<1 || nArg>3 ) goto usage;









  if( nArg>1 ){
    rc = testArgSelect(aOpt, "option", azArg[1], &eOpt);
    if( rc!=0 ) return rc;

    eOpt = aOpt[eOpt].eOpt;
    if( eOpt==LSM_INFO_FREELIST ){
      if( nArg!=2 ) goto usage;




    }else{
      if( nArg!=3 ) goto usage;

      iPg = atoi(azArg[2]);
    }
  }
  zDb = azArg[0];

  rc = lsm_new(0, &pDb);

  if( rc!=LSM_OK ){
    testPrintError("lsm_new(): rc=%d\n", rc);
  }else{
    rc = lsm_open(pDb, zDb);
    if( rc!=LSM_OK ){
      testPrintError("lsm_open(): rc=%d\n", rc);
    }
  }

  if( rc==LSM_OK ){

    switch( eOpt ){
      case LSM_INFO_DB_STRUCTURE:
      case LSM_INFO_FREELIST:
        rc = lsm_info(pDb, eOpt, &z);
        break;
      case LSM_INFO_ARRAY_STRUCTURE:

      case LSM_INFO_PAGE_ASCII_DUMP:
      case LSM_INFO_PAGE_HEX_DUMP:
        rc = lsm_info(pDb, eOpt, iPg, &z);
        break;
      default:
        assert( !"no chance" );
    }

    if( rc==LSM_OK ){
      printf("%s\n", z ? z : "");
      fflush(stdout);
    }
    lsm_free(lsm_get_env(pDb), z);






  }

  lsm_close(pDb);
  return rc;

 usage:
  testPrintUsage("DATABASE ?array|page-ascii|page-hex PGNO?");
  return -1;
}







|








>
>



>


|
>
>
>
>
|
|
<



|
>

<
>
>
>
>
>
>
|
>
>
|
|

>

|
<
>
>
>
>

<
>
|


|


>










>
|
|
|
|
|
|
>
|
|
|
|
|
|
|

|
|
|
|
|
>
>
>
>
>
>









60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90

91
92
93
94
95
96

97
98
99
100
101
102
103
104
105
106
107
108
109
110
111

112
113
114
115
116

117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
 usage:
  testPrintUsage("?-optimize? ?-n N? DATABASE");
  return -1;
}


/*
**   lsmtest show ?-config LSM-CONFIG? DATABASE ?COMMAND ?PGNO??
*/
int do_show(int nArg, char **azArg){
  lsm_db *pDb;
  int rc;
  const char *zDb;

  int eOpt = LSM_INFO_DB_STRUCTURE;
  unsigned int iPg = 0;
  int bConfig = 0;
  const char *zConfig = "";

  struct Option {
    const char *zName;
    int bConfig;
    int eOpt;
  } aOpt [] = { 
    { "array",       0, LSM_INFO_ARRAY_STRUCTURE },
    { "array-pages", 0, LSM_INFO_ARRAY_PAGES },
    { "blocksize",   1, LSM_CONFIG_BLOCK_SIZE },
    { "pagesize",    1, LSM_CONFIG_PAGE_SIZE },
    { "freelist",    0, LSM_INFO_FREELIST },
    { "page-ascii",  0, LSM_INFO_PAGE_ASCII_DUMP },
    { "page-hex",    0, LSM_INFO_PAGE_HEX_DUMP },

    { 0, 0 } 
  };

  char *z = 0; 
  int iDb = 0;                    /* Index of DATABASE in azArg[] */


  /* Check if there is a "-config" option: */
  if( nArg>2 && strlen(azArg[0])>1 
   && memcmp(azArg[0], "-config", strlen(azArg[0]))==0
  ){
    zConfig = azArg[1];
    iDb = 2;
  }
  if( nArg<(iDb+1) ) goto usage;

  if( nArg>(iDb+1) ){
    rc = testArgSelect(aOpt, "option", azArg[iDb+1], &eOpt);
    if( rc!=0 ) return rc;
    bConfig = aOpt[eOpt].bConfig;
    eOpt = aOpt[eOpt].eOpt;
    if( (bConfig==0 && eOpt==LSM_INFO_FREELIST)

     || (bConfig==1 && eOpt==LSM_CONFIG_BLOCK_SIZE)
     || (bConfig==1 && eOpt==LSM_CONFIG_PAGE_SIZE)
    ){
      if( nArg!=(iDb+2) ) goto usage;
    }else{

      if( nArg!=(iDb+3) ) goto usage;
      iPg = atoi(azArg[iDb+2]);
    }
  }
  zDb = azArg[iDb];

  rc = lsm_new(0, &pDb);
  tdb_lsm_configure(pDb, zConfig);
  if( rc!=LSM_OK ){
    testPrintError("lsm_new(): rc=%d\n", rc);
  }else{
    rc = lsm_open(pDb, zDb);
    if( rc!=LSM_OK ){
      testPrintError("lsm_open(): rc=%d\n", rc);
    }
  }

  if( rc==LSM_OK ){
    if( bConfig==0 ){
      switch( eOpt ){
        case LSM_INFO_DB_STRUCTURE:
        case LSM_INFO_FREELIST:
          rc = lsm_info(pDb, eOpt, &z);
          break;
        case LSM_INFO_ARRAY_STRUCTURE:
        case LSM_INFO_ARRAY_PAGES:
        case LSM_INFO_PAGE_ASCII_DUMP:
        case LSM_INFO_PAGE_HEX_DUMP:
          rc = lsm_info(pDb, eOpt, iPg, &z);
          break;
        default:
          assert( !"no chance" );
      }

      if( rc==LSM_OK ){
        printf("%s\n", z ? z : "");
        fflush(stdout);
      }
      lsm_free(lsm_get_env(pDb), z);
    }else{
      int iRes = -1;
      lsm_config(pDb, eOpt, &iRes);
      printf("%d\n", iRes);
      fflush(stdout);
    }
  }

  lsm_close(pDb);
  return rc;

 usage:
  testPrintUsage("DATABASE ?array|page-ascii|page-hex PGNO?");
  return -1;
}

Changes to lsm-test/lsmtest_tdb3.c.

671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
...
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
...
795
796
797
798
799
800
801




802
803
804
805
806
807
808
  if( p->xWork ) p->xWork(db, p->pWorkCtx);
}

#define TEST_NO_RECOVERY -1
#define TEST_THREADS     -2
#define TEST_COMPRESSION -3

static int test_lsm_config_str(
  LsmDb *pLsm,
  lsm_db *db, 
  int bWorker,
  const char *zStr,
  int *pnThread
){
  struct CfgParam {
................................................................................
      iVal = atoi(zParam) * iMul;

      if( eParam>0 ){
        if( bWorker || aParam[i].bWorker==0 ){
          lsm_config(db, eParam, &iVal);
        }
      }else{
        if( pLsm ){
          switch( eParam ){
            case TEST_NO_RECOVERY:
              pLsm->bNoRecovery = iVal;
              break;
            case TEST_THREADS:
              nThread = iVal;
              break;
#ifdef HAVE_ZLIB
            case TEST_COMPRESSION:
              testConfigureCompression(db);
              break;
#endif
          }
        }
      }
    }else if( z!=zStart ){
      goto syntax_error;
    }
  }

................................................................................
    for(i=0; rc==0 && i<pLsm->nWorker; i++){
      rc = test_lsm_config_str(0, pLsm->aWorker[i].pWorker, 1, zStr, 0);
    }
#endif
  }
  return rc;
}





static int testLsmStartWorkers(LsmDb *, int, const char *, const char *);

static int testLsmOpen(
  const char *zCfg,
  const char *zFilename, 
  int bClear, 







|







 







<
|
|
|
|
|
|
|

|
|
|

<







 







>
>
>
>







671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
...
752
753
754
755
756
757
758

759
760
761
762
763
764
765
766
767
768
769
770

771
772
773
774
775
776
777
...
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
  if( p->xWork ) p->xWork(db, p->pWorkCtx);
}

#define TEST_NO_RECOVERY -1
#define TEST_THREADS     -2
#define TEST_COMPRESSION -3

int test_lsm_config_str(
  LsmDb *pLsm,
  lsm_db *db, 
  int bWorker,
  const char *zStr,
  int *pnThread
){
  struct CfgParam {
................................................................................
      iVal = atoi(zParam) * iMul;

      if( eParam>0 ){
        if( bWorker || aParam[i].bWorker==0 ){
          lsm_config(db, eParam, &iVal);
        }
      }else{

        switch( eParam ){
          case TEST_NO_RECOVERY:
            if( pLsm ) pLsm->bNoRecovery = iVal;
            break;
          case TEST_THREADS:
            if( pLsm ) nThread = iVal;
            break;
#ifdef HAVE_ZLIB
          case TEST_COMPRESSION:
            testConfigureCompression(db);
            break;
#endif

        }
      }
    }else if( z!=zStart ){
      goto syntax_error;
    }
  }

................................................................................
    for(i=0; rc==0 && i<pLsm->nWorker; i++){
      rc = test_lsm_config_str(0, pLsm->aWorker[i].pWorker, 1, zStr, 0);
    }
#endif
  }
  return rc;
}

int tdb_lsm_configure(lsm_db *db, const char *zConfig){
  return test_lsm_config_str(0, db, 0, zConfig, 0);
}

static int testLsmStartWorkers(LsmDb *, int, const char *, const char *);

static int testLsmOpen(
  const char *zCfg,
  const char *zFilename, 
  int bClear, 

Changes to src/lsm.h.

324
325
326
327
328
329
330













331
332
333
334
335
336
337
...
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
...
367
368
369
370
371
372
373

374
375
376
377
378
379
380
**     the identified array. For example, if the array occupies database
**     pages 993 to 1024, then pages 2048 to 2777, then the returned string
**     will be "993 1024 2048 2777".
**
**     If the specified integer argument does not correspond to the first
**     page of any database array, LSM_ERROR is returned and the output
**     pointer is set to a NULL value.













**
**   LSM_INFO_PAGE_ASCII_DUMP
**     As with LSM_INFO_ARRAY_STRUCTURE, there should be two arguments passed
**     with calls that specify this option - an integer page number and a
**     (char **) used to return a nul-terminated string that must be later
**     freed using lsm_free(). In this case the output string is populated
**     with a human-readable description of the page content.
................................................................................
**     human-readable output message will report the systems failure to 
**     interpret the page data.
**
**   LSM_INFO_PAGE_HEX_DUMP
**     This argument is similar to PAGE_ASCII_DUMP, except that keys and
**     values are represented using hexadecimal notation instead of ascii.
**
**   LSM_INFO_LOG_STRUCTURE
**     The third argument should be of type (char **). The location pointed
**     to is populated with a pointer to a nul-terminated string containing
**     the string representation of a Tcl data-structure. The returned 
**     string should be eventually freed by the caller using lsm_free().
**
**     The Tcl structure returned is a list of six integers that describe
**     the current structure of the log file.
**
**   LSM_INFO_FREELIST
**     The third argument should be of type (char **). The location pointed
**     to is populated with a pointer to a nul-terminated string containing
**     the string representation of a Tcl data-structure. The returned 
**     string should be eventually freed by the caller using lsm_free().
**
**     The Tcl structure returned is a list containing one element for each
................................................................................
#define LSM_INFO_NREAD            2
#define LSM_INFO_DB_STRUCTURE     3
#define LSM_INFO_LOG_STRUCTURE    4
#define LSM_INFO_ARRAY_STRUCTURE  5
#define LSM_INFO_PAGE_ASCII_DUMP  6
#define LSM_INFO_PAGE_HEX_DUMP    7
#define LSM_INFO_FREELIST         8



/* 
** Open and close transactions and nested transactions.
**
** lsm_begin():
**   Used to open transactions and sub-transactions. A successful call to 







>
>
>
>
>
>
>
>
>
>
>
>
>







 







<
<
<
<
<
<
<
<
<







 







>







324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
...
353
354
355
356
357
358
359









360
361
362
363
364
365
366
...
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
**     the identified array. For example, if the array occupies database
**     pages 993 to 1024, then pages 2048 to 2777, then the returned string
**     will be "993 1024 2048 2777".
**
**     If the specified integer argument does not correspond to the first
**     page of any database array, LSM_ERROR is returned and the output
**     pointer is set to a NULL value.
**
**   LSM_INFO_LOG_STRUCTURE
**     The third argument should be of type (char **). The location pointed
**     to is populated with a pointer to a nul-terminated string containing
**     the string representation of a Tcl data-structure. The returned 
**     string should be eventually freed by the caller using lsm_free().
**
**     The Tcl structure returned is a list of six integers that describe
**     the current structure of the log file.
**
**   LSM_INFO_ARRAY_PAGES
**
**     
**
**   LSM_INFO_PAGE_ASCII_DUMP
**     As with LSM_INFO_ARRAY_STRUCTURE, there should be two arguments passed
**     with calls that specify this option - an integer page number and a
**     (char **) used to return a nul-terminated string that must be later
**     freed using lsm_free(). In this case the output string is populated
**     with a human-readable description of the page content.
................................................................................
**     human-readable output message will report the systems failure to 
**     interpret the page data.
**
**   LSM_INFO_PAGE_HEX_DUMP
**     This argument is similar to PAGE_ASCII_DUMP, except that keys and
**     values are represented using hexadecimal notation instead of ascii.
**









**   LSM_INFO_FREELIST
**     The third argument should be of type (char **). The location pointed
**     to is populated with a pointer to a nul-terminated string containing
**     the string representation of a Tcl data-structure. The returned 
**     string should be eventually freed by the caller using lsm_free().
**
**     The Tcl structure returned is a list containing one element for each
................................................................................
#define LSM_INFO_NREAD            2
#define LSM_INFO_DB_STRUCTURE     3
#define LSM_INFO_LOG_STRUCTURE    4
#define LSM_INFO_ARRAY_STRUCTURE  5
#define LSM_INFO_PAGE_ASCII_DUMP  6
#define LSM_INFO_PAGE_HEX_DUMP    7
#define LSM_INFO_FREELIST         8
#define LSM_INFO_ARRAY_PAGES      9


/* 
** Open and close transactions and nested transactions.
**
** lsm_begin():
**   Used to open transactions and sub-transactions. A successful call to 

Changes to src/lsmInt.h.

668
669
670
671
672
673
674

675
676
677
678
679
680
681
void lsmFsDeferClose(FileSystem *pFS, LsmFile **pp);

/* And to sync the db file */
int lsmFsSyncDb(FileSystem *);

/* Used by lsm_info(ARRAY_STRUCTURE) and lsm_config(MMAP) */
int lsmInfoArrayStructure(lsm_db *pDb, Pgno iFirst, char **pzOut);

int lsmConfigMmap(lsm_db *pDb, int *piParam);

int lsmEnvOpen(lsm_env *, const char *, lsm_file **);
int lsmEnvClose(lsm_env *pEnv, lsm_file *pFile);
int lsmEnvLock(lsm_env *pEnv, lsm_file *pFile, int iLock, int eLock);

int lsmEnvShmMap(lsm_env *, lsm_file *, int, int, void **); 







>







668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
void lsmFsDeferClose(FileSystem *pFS, LsmFile **pp);

/* And to sync the db file */
int lsmFsSyncDb(FileSystem *);

/* Used by lsm_info(ARRAY_STRUCTURE) and lsm_config(MMAP) */
int lsmInfoArrayStructure(lsm_db *pDb, Pgno iFirst, char **pzOut);
int lsmInfoArrayPages(lsm_db *pDb, Pgno iFirst, char **pzOut);
int lsmConfigMmap(lsm_db *pDb, int *piParam);

int lsmEnvOpen(lsm_env *, const char *, lsm_file **);
int lsmEnvClose(lsm_env *pEnv, lsm_file *pFile);
int lsmEnvLock(lsm_env *pEnv, lsm_file *pFile, int iLock, int eLock);

int lsmEnvShmMap(lsm_env *, lsm_file *, int, int, void **); 

Changes to src/lsm_file.c.

705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
....
1081
1082
1083
1084
1085
1086
1087
1088
1089
1090
1091
1092
1093
1094
1095
....
2001
2002
2003
2004
2005
2006
2007

2008
2009
2010
2011
2012
2013
2014
2015
2016
....
2072
2073
2074
2075
2076
2077
2078
















2079
2080
2081
2082
2083
2084
2085
2086
2087
2088
2089
2090
2091
2092
2093
2094
2095
2096
2097
2098
2099
....
2101
2102
2103
2104
2105
2106
2107
2108
2109
2110
2111
2112
2113
2114
2115
2116
2117
2118
2119
2120
2121
2122
2123
2124
2125
2126
....
2134
2135
2136
2137
2138
2139
2140





























































2141
2142
2143
2144
2145
2146
2147
** Return true if page iPg is the first page on its block.
**
** This function is only called in non-compressed database mode.
*/
static int fsIsFirst(FileSystem *pFS, Pgno iPg){
  const int nPagePerBlock = (pFS->nBlocksize / pFS->nPagesize);
  assert( !pFS->pCompress );

  return ( (iPg % nPagePerBlock)==1
        || (iPg<nPagePerBlock && iPg==fsFirstPageOnBlock(pFS, 1))
  );
}

/*
** Given a page reference, return a pointer to the in-memory buffer of the
................................................................................
        }
        if( rc==LSM_OK ){
          int n = pFS->nPagesize;
          rc = p->xUncompress(p->pCtx, 
              (char *)pPg->aData, &n, 
              (const char *)pFS->aIBuffer, pPg->nCompress
          );
          if( rc==LSM_OK && n!=pPg->nData ){
            rc = LSM_CORRUPT_BKPT;
          }
        }
      }
    }
  }
  return rc;
................................................................................
    assert( pPg->nRef>0 );
    pPg->nRef--;
    if( pPg->nRef==0 && pPg->iPg!=0 ){
      FileSystem *pFS = pPg->pFS;
      rc = lsmFsPagePersist(pPg);
      pFS->nOut--;


      assert( fsIsFirst(pPg->pFS, pPg->iPg)==0 
           || pPg->pFS->pCompress 
           || (pPg->flags & PAGE_HASPREV)
      );
      pPg->aData -= (pPg->flags & PAGE_HASPREV);
      pPg->flags &= ~PAGE_HASPREV;

      if( pFS->bUseMmap ){
        pPg->pHashNext = pFS->pFree;
................................................................................

/*
** Helper function for lsmInfoArrayStructure().
*/
static Segment *startsWith(Segment *pRun, Pgno iFirst){
  return (iFirst==pRun->iFirst) ? pRun : 0;
}

















/*
** This function implements the lsm_info(LSM_INFO_ARRAY_STRUCTURE) request.
** If successful, *pzOut is set to point to a nul-terminated string 
** containing the array structure and LSM_OK is returned. The caller should
** eventually free the string using lsmFree().
**
** If an error occurs, *pzOut is set to NULL and an LSM error code returned.
*/
int lsmInfoArrayStructure(lsm_db *pDb, Pgno iFirst, char **pzOut){
  int rc = LSM_OK;
  Snapshot *pWorker;              /* Worker snapshot */
  Segment *pArray = 0;            /* Array to report on */
  Level *pLvl;                    /* Used to iterate through db levels */
  int bUnlock = 0;

  *pzOut = 0;
  if( iFirst==0 ) return LSM_ERROR;

  /* Obtain the worker snapshot */
  pWorker = pDb->pWorker;
................................................................................
    rc = lsmBeginWork(pDb);
    if( rc!=LSM_OK ) return rc;
    pWorker = pDb->pWorker;
    bUnlock = 1;
  }

  /* Search for the array that starts on page iFirst */
  for(pLvl=lsmDbSnapshotLevel(pWorker); pLvl && pArray==0; pLvl=pLvl->pNext){
    if( 0==(pArray = startsWith(&pLvl->lhs, iFirst)) ){
      int i;
      for(i=0; i<pLvl->nRight; i++){
        if( (pArray = startsWith(&pLvl->aRhs[i], iFirst)) ) break;
      }
    }
  }

  if( pArray==0 ){
    /* Could not find the requested array. This is an error. */
    *pzOut = 0;
    rc = LSM_ERROR;
  }else{
    FileSystem *pFS = pDb->pFS;
    LsmString str;
    int iBlk;
    int iLastBlk;
   
................................................................................
      fsBlockNext(pFS, iBlk, &iBlk);
      lsmStringAppendf(&str, " %d", fsFirstPageOnBlock(pFS, iBlk));
    }
    lsmStringAppendf(&str, " %d", pArray->iLastPg);

    *pzOut = str.z;
  }






























































  if( bUnlock ){
    int rcwork = LSM_BUSY;
    lsmFinishWork(pDb, 0, &rcwork);
  }
  return rc;
}







<







 







|







 







>
|
<







 







>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>













<







 







|
<
<
<
<
<
<
<



<







 







>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>







705
706
707
708
709
710
711

712
713
714
715
716
717
718
....
1080
1081
1082
1083
1084
1085
1086
1087
1088
1089
1090
1091
1092
1093
1094
....
2000
2001
2002
2003
2004
2005
2006
2007
2008

2009
2010
2011
2012
2013
2014
2015
....
2071
2072
2073
2074
2075
2076
2077
2078
2079
2080
2081
2082
2083
2084
2085
2086
2087
2088
2089
2090
2091
2092
2093
2094
2095
2096
2097
2098
2099
2100
2101
2102
2103
2104
2105
2106

2107
2108
2109
2110
2111
2112
2113
....
2115
2116
2117
2118
2119
2120
2121
2122







2123
2124
2125

2126
2127
2128
2129
2130
2131
2132
....
2140
2141
2142
2143
2144
2145
2146
2147
2148
2149
2150
2151
2152
2153
2154
2155
2156
2157
2158
2159
2160
2161
2162
2163
2164
2165
2166
2167
2168
2169
2170
2171
2172
2173
2174
2175
2176
2177
2178
2179
2180
2181
2182
2183
2184
2185
2186
2187
2188
2189
2190
2191
2192
2193
2194
2195
2196
2197
2198
2199
2200
2201
2202
2203
2204
2205
2206
2207
2208
2209
2210
2211
2212
2213
2214
** Return true if page iPg is the first page on its block.
**
** This function is only called in non-compressed database mode.
*/
static int fsIsFirst(FileSystem *pFS, Pgno iPg){
  const int nPagePerBlock = (pFS->nBlocksize / pFS->nPagesize);
  assert( !pFS->pCompress );

  return ( (iPg % nPagePerBlock)==1
        || (iPg<nPagePerBlock && iPg==fsFirstPageOnBlock(pFS, 1))
  );
}

/*
** Given a page reference, return a pointer to the in-memory buffer of the
................................................................................
        }
        if( rc==LSM_OK ){
          int n = pFS->nPagesize;
          rc = p->xUncompress(p->pCtx, 
              (char *)pPg->aData, &n, 
              (const char *)pFS->aIBuffer, pPg->nCompress
          );
          if( rc==LSM_OK && n!=pPg->pFS->nPagesize ){
            rc = LSM_CORRUPT_BKPT;
          }
        }
      }
    }
  }
  return rc;
................................................................................
    assert( pPg->nRef>0 );
    pPg->nRef--;
    if( pPg->nRef==0 && pPg->iPg!=0 ){
      FileSystem *pFS = pPg->pFS;
      rc = lsmFsPagePersist(pPg);
      pFS->nOut--;

      assert( pPg->pFS->pCompress 
           || fsIsFirst(pPg->pFS, pPg->iPg)==0 

           || (pPg->flags & PAGE_HASPREV)
      );
      pPg->aData -= (pPg->flags & PAGE_HASPREV);
      pPg->flags &= ~PAGE_HASPREV;

      if( pFS->bUseMmap ){
        pPg->pHashNext = pFS->pFree;
................................................................................

/*
** Helper function for lsmInfoArrayStructure().
*/
static Segment *startsWith(Segment *pRun, Pgno iFirst){
  return (iFirst==pRun->iFirst) ? pRun : 0;
}

static Segment *findSegment(Snapshot *pWorker, Pgno iFirst){
  Level *pLvl;                    /* Used to iterate through db levels */
  Segment *pSeg = 0;              /* Pointer to segment to return */

  for(pLvl=lsmDbSnapshotLevel(pWorker); pLvl && pSeg==0; pLvl=pLvl->pNext){
    if( 0==(pSeg = startsWith(&pLvl->lhs, iFirst)) ){
      int i;
      for(i=0; i<pLvl->nRight; i++){
        if( (pSeg = startsWith(&pLvl->aRhs[i], iFirst)) ) break;
      }
    }
  }

  return pSeg;
}

/*
** This function implements the lsm_info(LSM_INFO_ARRAY_STRUCTURE) request.
** If successful, *pzOut is set to point to a nul-terminated string 
** containing the array structure and LSM_OK is returned. The caller should
** eventually free the string using lsmFree().
**
** If an error occurs, *pzOut is set to NULL and an LSM error code returned.
*/
int lsmInfoArrayStructure(lsm_db *pDb, Pgno iFirst, char **pzOut){
  int rc = LSM_OK;
  Snapshot *pWorker;              /* Worker snapshot */
  Segment *pArray = 0;            /* Array to report on */

  int bUnlock = 0;

  *pzOut = 0;
  if( iFirst==0 ) return LSM_ERROR;

  /* Obtain the worker snapshot */
  pWorker = pDb->pWorker;
................................................................................
    rc = lsmBeginWork(pDb);
    if( rc!=LSM_OK ) return rc;
    pWorker = pDb->pWorker;
    bUnlock = 1;
  }

  /* Search for the array that starts on page iFirst */
  pArray = findSegment(pWorker, iFirst);








  if( pArray==0 ){
    /* Could not find the requested array. This is an error. */

    rc = LSM_ERROR;
  }else{
    FileSystem *pFS = pDb->pFS;
    LsmString str;
    int iBlk;
    int iLastBlk;
   
................................................................................
      fsBlockNext(pFS, iBlk, &iBlk);
      lsmStringAppendf(&str, " %d", fsFirstPageOnBlock(pFS, iBlk));
    }
    lsmStringAppendf(&str, " %d", pArray->iLastPg);

    *pzOut = str.z;
  }

  if( bUnlock ){
    int rcwork = LSM_BUSY;
    lsmFinishWork(pDb, 0, &rcwork);
  }
  return rc;
}

/*
** This function implements the lsm_info(LSM_INFO_ARRAY_PAGES) request.
** If successful, *pzOut is set to point to a nul-terminated string 
** containing the array structure and LSM_OK is returned. The caller should
** eventually free the string using lsmFree().
**
** If an error occurs, *pzOut is set to NULL and an LSM error code returned.
*/
int lsmInfoArrayPages(lsm_db *pDb, Pgno iFirst, char **pzOut){
  int rc = LSM_OK;
  Snapshot *pWorker;              /* Worker snapshot */
  Segment *pArray = 0;            /* Array to report on */
  int bUnlock = 0;

  *pzOut = 0;
  if( iFirst==0 ) return LSM_ERROR;

  /* Obtain the worker snapshot */
  pWorker = pDb->pWorker;
  if( !pWorker ){
    rc = lsmBeginWork(pDb);
    if( rc!=LSM_OK ) return rc;
    pWorker = pDb->pWorker;
    bUnlock = 1;
  }

  /* Search for the array that starts on page iFirst */
  pArray = findSegment(pWorker, iFirst);

  if( pArray==0 ){
    /* Could not find the requested array. This is an error. */
    rc = LSM_ERROR;
  }else{
    Page *pPg = 0;
    FileSystem *pFS = pDb->pFS;
    LsmString str;

    lsmStringInit(&str, pDb->pEnv);
    rc = lsmFsDbPageGet(pFS, iFirst, &pPg);
    while( rc==LSM_OK && pPg ){
      Page *pNext = 0;
      lsmStringAppendf(&str, " %lld", lsmFsPageNumber(pPg));
      rc = lsmFsDbPageNext(pArray, pPg, 1, &pNext);
      lsmFsPageRelease(pPg);
      pPg = pNext;
    }

    if( rc!=LSM_OK ){
      lsmFree(pDb->pEnv, str.z);
    }else{
      *pzOut = str.z;
    }
  }

  if( bUnlock ){
    int rcwork = LSM_BUSY;
    lsmFinishWork(pDb, 0, &rcwork);
  }
  return rc;
}

Changes to src/lsm_main.c.

462
463
464
465
466
467
468







469
470
471
472
473
474
475

    case LSM_INFO_ARRAY_STRUCTURE: {
      Pgno pgno = va_arg(ap, Pgno);
      char **pzVal = va_arg(ap, char **);
      rc = lsmInfoArrayStructure(pDb, pgno, pzVal);
      break;
    }








    case LSM_INFO_PAGE_HEX_DUMP:
    case LSM_INFO_PAGE_ASCII_DUMP: {
      Pgno pgno = va_arg(ap, Pgno);
      char **pzVal = va_arg(ap, char **);
      rc = lsmInfoPageDump(pDb, pgno, (eParam==LSM_INFO_PAGE_HEX_DUMP), pzVal);
      break;







>
>
>
>
>
>
>







462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482

    case LSM_INFO_ARRAY_STRUCTURE: {
      Pgno pgno = va_arg(ap, Pgno);
      char **pzVal = va_arg(ap, char **);
      rc = lsmInfoArrayStructure(pDb, pgno, pzVal);
      break;
    }

    case LSM_INFO_ARRAY_PAGES: {
      Pgno pgno = va_arg(ap, Pgno);
      char **pzVal = va_arg(ap, char **);
      rc = lsmInfoArrayPages(pDb, pgno, pzVal);
      break;
    }

    case LSM_INFO_PAGE_HEX_DUMP:
    case LSM_INFO_PAGE_ASCII_DUMP: {
      Pgno pgno = va_arg(ap, Pgno);
      char **pzVal = va_arg(ap, char **);
      rc = lsmInfoPageDump(pDb, pgno, (eParam==LSM_INFO_PAGE_HEX_DUMP), pzVal);
      break;

Changes to tool/lsmview.tcl.

363
364
365
366
367
368
369
370
371
372
373
374
375

376
377
378
379
380






















381
382




383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
...
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469

470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
...
489
490
491
492
493
494
495
496
497
498
499
500

501


502
503
504
505
proc static_redraw {C} {
  link_varset $C myText myDb myData
  $C delete all
  draw_canvas_content $C $myData [list static_select_callback $C]
}

proc static_select_callback {C segment} {
  link_varset $C myText myDb myData myTree
  foreach {iFirst iLast iRoot nSize $segment} $segment {}

  set data [string trim [exec_lsmtest_show $myDb array $iFirst]]
  $myText delete 0.0 end


  $myTree delete [$myTree children {}]

  foreach {first last} $data {
    set nPg [expr {$last - $first + 1}]
    set caption "${first}..${last} ($nPg pages)"






















    set id [$myTree insert {} end -text $caption]
    for {set i $first} {$i <= $last} {incr i} {




      set item [$myTree insert $id end -text $i]
      if {$i == $iRoot} { 
        set rootitem $item 
        set rootparent $id 
      }
    }
  }

  if {[info exists rootitem]} {
    $myTree item $rootparent -open 1
    $myTree selection set $rootitem
  } else {
    $myTree item $id -open 1
    $myTree selection set [lindex [$myTree children $id] 0]
  }
}

proc static_page_callback {C pgno} {
  link_varset $C myText myDb myData myPgno myMode
  set myPgno $pgno
  set data [string trim [exec_lsmtest_show $myDb $myMode $myPgno]]
  $myText delete 0.0 end
  $myText insert 0.0 $data
}

proc static_treeview_select {C} {
  link_varset $C myText myDb myData myTree
  set sel [lindex [$myTree selection] 0]
................................................................................
  puts $fd [$t get 1.0 end]
  flush $fd
  close $fd
  exec $::env(VISUAL) $fn &
  after 1000 [subst {catch {file delete -force $fn}}]
}

proc static_setup {zDb} {

  panedwindow .pan -orient horizontal
  frame .pan.c
  set C [scrollable canvas .pan.c.c -background white -width 400 -height 600]
  label .pan.c.info -border 2 -relief sunken -height 2
  pack .pan.c.info -side bottom -fill x 
  pack .pan.c.c -side top -fill both -expand 1

  link_varset $C myText myDb myData myTree mySelected myMode myModeButton
  set myDb $zDb

  frame .pan.t
  frame .pan.t.f
  set myModeButton [button .pan.t.f.pagemode]
  $myModeButton configure -command [list static_toggle_pagemode $C]
  $myModeButton configure -width 20
  $myModeButton configure -text "Switch to HEX"
  set myMode page-ascii


  set myText [scrollable text .pan.t.text -background white -width 80]
  button .pan.t.f.view -command [list show_text_in_editor $myText] \
                       -text Export
  pack .pan.t.f.pagemode .pan.t.f.view -side right
  pack .pan.t.f -fill x
  pack .pan.t.text -expand 1 -fill both

  set myTree [scrollable ::ttk::treeview .pan.tree]
  set myData [string trim [exec_lsmtest_show $zDb]]

  $myText configure -wrap none
  bind $C <KeyPress-q> exit
  focus $C

  .pan add .pan.c .pan.tree .pan.t
  pack .pan -fill both -expand 1
................................................................................
  static_redraw $C
  bind $C <Configure>  [list static_redraw $C]
  bind $myTree <<TreeviewSelect>> [list static_treeview_select $C]

  static_select_callback $C [lindex $myData 0 0]
}

if {[llength $argv] > 1} {
  puts stderr "Usage: $argv0 ?DATABASE?"
  exit -1
}
if {[llength $argv]==1} {

  static_setup [lindex $argv 0]


} else {
  dynamic_setup
  fileevent stdin readable [list dynamic_input $C $S]
}







|


|


>


<
|
|
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
|
<
>
>
>
>
|
|
|
|
<













|

|







 







|








|









>









|







 







|
|


|
>
|
>
>




363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378

379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403

404
405
406
407
408
409
410
411

412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
...
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
...
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
proc static_redraw {C} {
  link_varset $C myText myDb myData
  $C delete all
  draw_canvas_content $C $myData [list static_select_callback $C]
}

proc static_select_callback {C segment} {
  link_varset $C myText myDb myData myTree myCfg
  foreach {iFirst iLast iRoot nSize $segment} $segment {}

  set data [string trim [exec_lsmtest_show -c $myCfg $myDb array-pages $iFirst]]
  $myText delete 0.0 end

  # Delete the existing tree entries.
  $myTree delete [$myTree children {}]


  set nBlksz [exec_lsmtest_show -c $myCfg $myDb blocksize]
  set nPgsz  [exec_lsmtest_show -c $myCfg $myDb pagesize]

  if {[regexp {c=1} $myCfg]          || [regexp {co=1} $myCfg]
   || [regexp {com=1} $myCfg]        || [regexp {comp=1} $myCfg]
   || [regexp {compr=1} $myCfg]      || [regexp {compres=1} $myCfg]
   || [regexp {compress=1} $myCfg]   || [regexp {compressi=1} $myCfg]
   || [regexp {compressio=1} $myCfg] || [regexp {compression=1} $myCfg]
  } {
    set nBlkPg $nBlksz
  } else {
    set nBlkPg [expr ($nBlksz / $nPgsz)]
  }

  foreach pg $data {
    set blk [expr 1 + ($pg / $nBlkPg)]
    if {[info exists block($blk)]} {
      incr block($blk) 1
    } else {
      set block($blk) 1
    }
  }
  foreach blk [lsort -integer [array names block]] {
    set nPg $block($blk)
    set blkid($blk) [$myTree insert {} end -text "block $blk ($nPg pages)"]

  }
  foreach pg $data {
    set blk [expr 1 + ($pg / $nBlkPg)]
    set id $blkid($blk)
    set item [$myTree insert $id end -text $pg]
    if {$pg == $iRoot} {
      set rootitem $item
      set rootparent $id

    }
  }

  if {[info exists rootitem]} {
    $myTree item $rootparent -open 1
    $myTree selection set $rootitem
  } else {
    $myTree item $id -open 1
    $myTree selection set [lindex [$myTree children $id] 0]
  }
}

proc static_page_callback {C pgno} {
  link_varset $C myText myDb myData myPgno myMode myCfg
  set myPgno $pgno
  set data [string trim [exec_lsmtest_show -c $myCfg $myDb $myMode $myPgno]]
  $myText delete 0.0 end
  $myText insert 0.0 $data
}

proc static_treeview_select {C} {
  link_varset $C myText myDb myData myTree
  set sel [lindex [$myTree selection] 0]
................................................................................
  puts $fd [$t get 1.0 end]
  flush $fd
  close $fd
  exec $::env(VISUAL) $fn &
  after 1000 [subst {catch {file delete -force $fn}}]
}

proc static_setup {zConfig zDb} {

  panedwindow .pan -orient horizontal
  frame .pan.c
  set C [scrollable canvas .pan.c.c -background white -width 400 -height 600]
  label .pan.c.info -border 2 -relief sunken -height 2
  pack .pan.c.info -side bottom -fill x 
  pack .pan.c.c -side top -fill both -expand 1

  link_varset $C myText myDb myData myTree mySelected myMode myModeButton myCfg
  set myDb $zDb

  frame .pan.t
  frame .pan.t.f
  set myModeButton [button .pan.t.f.pagemode]
  $myModeButton configure -command [list static_toggle_pagemode $C]
  $myModeButton configure -width 20
  $myModeButton configure -text "Switch to HEX"
  set myMode page-ascii
  set myCfg $zConfig

  set myText [scrollable text .pan.t.text -background white -width 80]
  button .pan.t.f.view -command [list show_text_in_editor $myText] \
                       -text Export
  pack .pan.t.f.pagemode .pan.t.f.view -side right
  pack .pan.t.f -fill x
  pack .pan.t.text -expand 1 -fill both

  set myTree [scrollable ::ttk::treeview .pan.tree]
  set myData [string trim [exec_lsmtest_show -c $myCfg $zDb]]

  $myText configure -wrap none
  bind $C <KeyPress-q> exit
  focus $C

  .pan add .pan.c .pan.tree .pan.t
  pack .pan -fill both -expand 1
................................................................................
  static_redraw $C
  bind $C <Configure>  [list static_redraw $C]
  bind $myTree <<TreeviewSelect>> [list static_treeview_select $C]

  static_select_callback $C [lindex $myData 0 0]
}

if {[llength $argv] > 2} {
  puts stderr "Usage: $argv0 ?CONFIG? ?DATABASE?"
  exit -1
}
if {[llength $argv]>0} {
  set zConfig ""
  set zDb [lindex $argv end]
  if {[llength $argv]>1} {set zConfig [lindex $argv 0]}
  static_setup $zConfig $zDb
} else {
  dynamic_setup
  fileevent stdin readable [list dynamic_input $C $S]
}