/ Check-in [eaeeb09d]
Login

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

Overview
Comment:Add the sqlite_dbpage virtual table (enabled using SQLITE_ENABLE_DBPAGE_VTAB). Make that virtual table and dbstat available to the command-line shell.
Downloads: Tarball | ZIP archive | SQL archive
Timelines: family | ancestors | descendants | both | trunk
Files: files | file ages | folders
SHA3-256: eaeeb09d4aa1dbccdd2488af8461e2a8c8a53d92c63fd56330be041ad72a9e4a
User & Date: drh 2017-10-12 20:37:20
Context
2017-10-13
16:19
Move a bunch of unrelated test code out of tclsqlite.c and into test_*.c files. There is still some test code in tclsqlite.c, but the amount is greatly reduced. check-in: a9c4bc88 user: drh tags: trunk
15:06
Move some test logic out of tclsqlite.c and into auxiliary test_*.c files. This is a work in progress. check-in: 95b7687f user: drh tags: tclsqlite-cleanup
2017-10-12
20:37
Add the sqlite_dbpage virtual table (enabled using SQLITE_ENABLE_DBPAGE_VTAB). Make that virtual table and dbstat available to the command-line shell. check-in: eaeeb09d user: drh tags: trunk
19:50
Create the new ext/repair folder and move checkfreelist.c there. Remove checkfreelist.c from the command-line shell (undoing check-in [48418f2e]). Closed-Leaf check-in: dfdebd12 user: drh tags: dbpage
13:47
The src/shell.c file is now generated from src/shell.c.in, so remove shell.c from version control and update the makefiles to build it automatically. check-in: 36acc0a9 user: drh tags: trunk
Changes
Hide Diffs Side-by-Side Diffs Ignore Whitespace Patch

Changes to Makefile.in.

   162    162   
   163    163   USE_AMALGAMATION = @USE_AMALGAMATION@
   164    164   
   165    165   # Object files for the SQLite library (non-amalgamation).
   166    166   #
   167    167   LIBOBJS0 = alter.lo analyze.lo attach.lo auth.lo \
   168    168            backup.lo bitvec.lo btmutex.lo btree.lo build.lo \
   169         -         callback.lo complete.lo ctime.lo date.lo dbstat.lo delete.lo \
          169  +         callback.lo complete.lo ctime.lo \
          170  +         date.lo dbpage.lo dbstat.lo delete.lo \
   170    171            expr.lo fault.lo fkey.lo \
   171    172            fts3.lo fts3_aux.lo fts3_expr.lo fts3_hash.lo fts3_icu.lo \
   172    173            fts3_porter.lo fts3_snippet.lo fts3_tokenizer.lo fts3_tokenizer1.lo \
   173    174            fts3_tokenize_vtab.lo \
   174    175            fts3_unicode.lo fts3_unicode2.lo fts3_write.lo \
   175    176   	 fts5.lo \
   176    177            func.lo global.lo hash.lo \
................................................................................
   211    212     $(TOP)/src/btree.h \
   212    213     $(TOP)/src/btreeInt.h \
   213    214     $(TOP)/src/build.c \
   214    215     $(TOP)/src/callback.c \
   215    216     $(TOP)/src/complete.c \
   216    217     $(TOP)/src/ctime.c \
   217    218     $(TOP)/src/date.c \
          219  +  $(TOP)/src/dbpage.c \
   218    220     $(TOP)/src/dbstat.c \
   219    221     $(TOP)/src/delete.c \
   220    222     $(TOP)/src/expr.c \
   221    223     $(TOP)/src/fault.c \
   222    224     $(TOP)/src/fkey.c \
   223    225     $(TOP)/src/func.c \
   224    226     $(TOP)/src/global.c \
................................................................................
   446    448     $(TOP)/src/attach.c \
   447    449     $(TOP)/src/backup.c \
   448    450     $(TOP)/src/bitvec.c \
   449    451     $(TOP)/src/btree.c \
   450    452     $(TOP)/src/build.c \
   451    453     $(TOP)/src/ctime.c \
   452    454     $(TOP)/src/date.c \
          455  +  $(TOP)/src/dbpage.c \
   453    456     $(TOP)/src/dbstat.c \
   454    457     $(TOP)/src/expr.c \
   455    458     $(TOP)/src/func.c \
   456    459     $(TOP)/src/insert.c \
   457    460     $(TOP)/src/wal.c \
   458    461     $(TOP)/src/main.c \
   459    462     $(TOP)/src/mem5.c \
................................................................................
   566    569   # Extra compiler options for various shell tools
   567    570   #
   568    571   SHELL_OPT = -DSQLITE_ENABLE_JSON1 -DSQLITE_ENABLE_FTS4
   569    572   # SHELL_OPT += -DSQLITE_ENABLE_FTS5
   570    573   SHELL_OPT += -DSQLITE_ENABLE_EXPLAIN_COMMENTS
   571    574   SHELL_OPT += -DSQLITE_ENABLE_UNKNOWN_SQL_FUNCTION
   572    575   SHELL_OPT += -DSQLITE_ENABLE_STMTVTAB
          576  +SHELL_OPT += -DSQLITE_ENABLE_DBPAGE_VTAB
          577  +SHELL_OPT += -DSQLITE_ENABLE_DBSTAT_VTAB
   573    578   FUZZERSHELL_OPT = -DSQLITE_ENABLE_JSON1
   574    579   FUZZCHECK_OPT = -DSQLITE_ENABLE_JSON1 -DSQLITE_ENABLE_MEMSYS5 -DSQLITE_OSS_FUZZ
   575    580   FUZZCHECK_OPT += -DSQLITE_MAX_MEMORY=50000000
   576    581   FUZZCHECK_SRC = $(TOP)/test/fuzzcheck.c $(TOP)/test/ossfuzz.c
   577    582   DBFUZZ_OPT = 
   578    583   
   579    584   # This is the default Makefile target.  The objects listed here
................................................................................
   749    754   
   750    755   ctime.lo:	$(TOP)/src/ctime.c $(HDR)
   751    756   	$(LTCOMPILE) $(TEMP_STORE) -c $(TOP)/src/ctime.c
   752    757   
   753    758   date.lo:	$(TOP)/src/date.c $(HDR)
   754    759   	$(LTCOMPILE) $(TEMP_STORE) -c $(TOP)/src/date.c
   755    760   
          761  +dbpage.lo:	$(TOP)/src/dbpage.c $(HDR)
          762  +	$(LTCOMPILE) $(TEMP_STORE) -c $(TOP)/src/dbpage.c
          763  +
   756    764   dbstat.lo:	$(TOP)/src/dbstat.c $(HDR)
   757    765   	$(LTCOMPILE) $(TEMP_STORE) -c $(TOP)/src/dbstat.c
   758    766   
   759    767   delete.lo:	$(TOP)/src/delete.c $(HDR)
   760    768   	$(LTCOMPILE) $(TEMP_STORE) -c $(TOP)/src/delete.c
   761    769   
   762    770   expr.lo:	$(TOP)/src/expr.c $(HDR)

Changes to Makefile.msc.

  1087   1087   ###############################################################################
  1088   1088   
  1089   1089   # <<mark>>
  1090   1090   # Object files for the SQLite library (non-amalgamation).
  1091   1091   #
  1092   1092   LIBOBJS0 = vdbe.lo parse.lo alter.lo analyze.lo attach.lo auth.lo \
  1093   1093            backup.lo bitvec.lo btmutex.lo btree.lo build.lo \
  1094         -         callback.lo complete.lo ctime.lo date.lo dbstat.lo delete.lo \
         1094  +         callback.lo complete.lo ctime.lo \
         1095  +         date.lo dbpage.lo dbstat.lo delete.lo \
  1095   1096            expr.lo fault.lo fkey.lo \
  1096   1097            fts3.lo fts3_aux.lo fts3_expr.lo fts3_hash.lo fts3_icu.lo \
  1097   1098            fts3_porter.lo fts3_snippet.lo fts3_tokenizer.lo fts3_tokenizer1.lo \
  1098   1099            fts3_tokenize_vtab.lo fts3_unicode.lo fts3_unicode2.lo fts3_write.lo \
  1099   1100            fts5.lo \
  1100   1101            func.lo global.lo hash.lo \
  1101   1102            icu.lo insert.lo legacy.lo loadext.lo \
................................................................................
  1150   1151     $(TOP)\src\btmutex.c \
  1151   1152     $(TOP)\src\btree.c \
  1152   1153     $(TOP)\src\build.c \
  1153   1154     $(TOP)\src\callback.c \
  1154   1155     $(TOP)\src\complete.c \
  1155   1156     $(TOP)\src\ctime.c \
  1156   1157     $(TOP)\src\date.c \
         1158  +  $(TOP)\src\dbpage.c \
  1157   1159     $(TOP)\src\dbstat.c \
  1158   1160     $(TOP)\src\delete.c \
  1159   1161     $(TOP)\src\expr.c \
  1160   1162     $(TOP)\src\fault.c \
  1161   1163     $(TOP)\src\fkey.c \
  1162   1164     $(TOP)\src\func.c \
  1163   1165     $(TOP)\src\global.c \
................................................................................
  1501   1503   # <</mark>>
  1502   1504   
  1503   1505   # Additional compiler options for the shell.  These are only effective
  1504   1506   # when the shell is not being dynamically linked.
  1505   1507   #
  1506   1508   !IF $(DYNAMIC_SHELL)==0 && $(FOR_WIN10)==0
  1507   1509   SHELL_COMPILE_OPTS = $(SHELL_COMPILE_OPTS) -DSQLITE_ENABLE_JSON1 -DSQLITE_ENABLE_FTS4 -DSQLITE_ENABLE_EXPLAIN_COMMENTS -DSQLITE_ENABLE_STMTVTAB
         1510  +SHELL_COMPILE_OPTS = $(SHELL_COMPILE_OPTS) -DSQLITE_ENABLE_DBPAGE_VTAB -DSQLITE_ENABLE_DBSTAT_VTAB
  1508   1511   !ENDIF
  1509   1512   
  1510   1513   # <<mark>>
  1511   1514   # Extra compiler options for various test tools.
  1512   1515   #
  1513   1516   MPTESTER_COMPILE_OPTS = -DSQLITE_ENABLE_JSON1 -DSQLITE_ENABLE_FTS5
  1514   1517   FUZZERSHELL_COMPILE_OPTS = -DSQLITE_ENABLE_JSON1
................................................................................
  1738   1741   
  1739   1742   ctime.lo:	$(TOP)\src\ctime.c $(HDR)
  1740   1743   	$(LTCOMPILE) $(CORE_COMPILE_OPTS) -c $(TOP)\src\ctime.c
  1741   1744   
  1742   1745   date.lo:	$(TOP)\src\date.c $(HDR)
  1743   1746   	$(LTCOMPILE) $(CORE_COMPILE_OPTS) -c $(TOP)\src\date.c
  1744   1747   
  1745         -dbstat.lo:	$(TOP)\src\date.c $(HDR)
         1748  +dbpage.lo:	$(TOP)\src\dbpage.c $(HDR)
         1749  +	$(LTCOMPILE) $(CORE_COMPILE_OPTS) -c $(TOP)\src\dbpage.c
         1750  +
         1751  +dbstat.lo:	$(TOP)\src\dbstat.c $(HDR)
  1746   1752   	$(LTCOMPILE) $(CORE_COMPILE_OPTS) -c $(TOP)\src\dbstat.c
  1747   1753   
  1748   1754   delete.lo:	$(TOP)\src\delete.c $(HDR)
  1749   1755   	$(LTCOMPILE) $(CORE_COMPILE_OPTS) -c $(TOP)\src\delete.c
  1750   1756   
  1751   1757   expr.lo:	$(TOP)\src\expr.c $(HDR)
  1752   1758   	$(LTCOMPILE) $(CORE_COMPILE_OPTS) -c $(TOP)\src\expr.c

Added ext/repair/README.md.

            1  +This folder contains extensions and utility programs intended to analyze
            2  +live database files, detect problems, and possibly fix them.
            3  +
            4  +As SQLite is being used on larger and larger databases, database sizes
            5  +are growing into the terabyte range.  At that size, hardware malfunctions
            6  +and/or cosmic rays will occasionally corrupt a database file.  Detecting 
            7  +problems and fixing errors a terabyte-sized databases can take hours or days,
            8  +and it is undesirable to take applications that depend on the databases 
            9  +off-line for such a long time.
           10  +The utilities in the folder are intended to provide mechanisms for
           11  +detecting and fixing problems in large databases while those databases
           12  +are in active use.
           13  +
           14  +The utilities and extensions in this folder are experimental and under
           15  +active development at the time of this writing (2017-10-12).  If and when
           16  +they stabilize, this README will be updated to reflect that fact.

Added ext/repair/checkfreelist.c.

            1  +/*
            2  +** 2017 October 11
            3  +**
            4  +** The author disclaims copyright to this source code.  In place of
            5  +** a legal notice, here is a blessing:
            6  +**
            7  +**    May you do good and not evil.
            8  +**    May you find forgiveness for yourself and forgive others.
            9  +**    May you share freely, never taking more than you give.
           10  +**
           11  +*************************************************************************
           12  +**
           13  +** This module exports a single C function:
           14  +**
           15  +**   int sqlite3_check_freelist(sqlite3 *db, const char *zDb);
           16  +**
           17  +** This function checks the free-list in database zDb (one of "main", 
           18  +** "temp", etc.) and reports any errors by invoking the sqlite3_log()
           19  +** function. It returns SQLITE_OK if successful, or an SQLite error
           20  +** code otherwise. It is not an error if the free-list is corrupted but
           21  +** no IO or OOM errors occur.
           22  +**
           23  +** If this file is compiled and loaded as an SQLite loadable extension,
           24  +** it adds an SQL function "checkfreelist" to the database handle, to
           25  +** be invoked as follows:
           26  +**
           27  +**   SELECT checkfreelist(<database-name>);
           28  +**
           29  +** This function performs the same checks as sqlite3_check_freelist(),
           30  +** except that it returns all error messages as a single text value,
           31  +** separated by newline characters. If the freelist is not corrupted
           32  +** in any way, an empty string is returned.
           33  +**
           34  +** To compile this module for use as an SQLite loadable extension:
           35  +**
           36  +**   gcc -Os -fPIC -shared checkfreelist.c -o checkfreelist.so
           37  +*/
           38  +
           39  +#include "sqlite3ext.h"
           40  +SQLITE_EXTENSION_INIT1
           41  +
           42  +#ifndef SQLITE_AMALGAMATION
           43  +# include <string.h>
           44  +# include <stdio.h>
           45  +# include <stdlib.h>
           46  +# include <assert.h>
           47  +# define ALWAYS(X)  1
           48  +# define NEVER(X)   0
           49  +  typedef unsigned char u8;
           50  +  typedef unsigned short u16;
           51  +  typedef unsigned int u32;
           52  +#define get4byte(x) (        \
           53  +    ((u32)((x)[0])<<24) +    \
           54  +    ((u32)((x)[1])<<16) +    \
           55  +    ((u32)((x)[2])<<8) +     \
           56  +    ((u32)((x)[3]))          \
           57  +)
           58  +#endif
           59  +
           60  +/*
           61  +** Execute a single PRAGMA statement and return the integer value returned
           62  +** via output parameter (*pnOut).
           63  +**
           64  +** The SQL statement passed as the third argument should be a printf-style
           65  +** format string containing a single "%s" which will be replace by the
           66  +** value passed as the second argument. e.g.
           67  +**
           68  +**   sqlGetInteger(db, "main", "PRAGMA %s.page_count", pnOut)
           69  +**
           70  +** executes "PRAGMA main.page_count" and stores the results in (*pnOut).
           71  +*/
           72  +static int sqlGetInteger(
           73  +  sqlite3 *db,                    /* Database handle */
           74  +  const char *zDb,                /* Database name ("main", "temp" etc.) */
           75  +  const char *zFmt,               /* SQL statement format */
           76  +  u32 *pnOut                      /* OUT: Integer value */
           77  +){
           78  +  int rc, rc2;
           79  +  char *zSql;
           80  +  sqlite3_stmt *pStmt = 0;
           81  +  int bOk = 0;
           82  +
           83  +  zSql = sqlite3_mprintf(zFmt, zDb);
           84  +  if( zSql==0 ){
           85  +    rc = SQLITE_NOMEM;
           86  +  }else{
           87  +    rc = sqlite3_prepare_v2(db, zSql, -1, &pStmt, 0);
           88  +    sqlite3_free(zSql);
           89  +  }
           90  +
           91  +  if( rc==SQLITE_OK && SQLITE_ROW==sqlite3_step(pStmt) ){
           92  +    *pnOut = (u32)sqlite3_column_int(pStmt, 0);
           93  +    bOk = 1;
           94  +  }
           95  +
           96  +  rc2 = sqlite3_finalize(pStmt);
           97  +  if( rc==SQLITE_OK ) rc = rc2;
           98  +  if( rc==SQLITE_OK && bOk==0 ) rc = SQLITE_ERROR;
           99  +  return rc;
          100  +}
          101  +
          102  +/*
          103  +** Argument zFmt must be a printf-style format string and must be 
          104  +** followed by its required arguments. If argument pzOut is NULL, 
          105  +** then the results of printf()ing the format string are passed to
          106  +** sqlite3_log(). Otherwise, they are appended to the string
          107  +** at (*pzOut).
          108  +*/
          109  +static int checkFreelistError(char **pzOut, const char *zFmt, ...){
          110  +  int rc = SQLITE_OK;
          111  +  char *zErr = 0;
          112  +  va_list ap;
          113  +
          114  +  va_start(ap, zFmt);
          115  +  zErr = sqlite3_vmprintf(zFmt, ap);
          116  +  if( zErr==0 ){
          117  +    rc = SQLITE_NOMEM;
          118  +  }else{
          119  +    if( pzOut ){
          120  +      *pzOut = sqlite3_mprintf("%s%z%s", *pzOut?"\n":"", *pzOut, zErr);
          121  +      if( *pzOut==0 ) rc = SQLITE_NOMEM;
          122  +    }else{
          123  +      sqlite3_log(SQLITE_ERROR, "checkfreelist: %s", zErr);
          124  +    }
          125  +    sqlite3_free(zErr);
          126  +  }
          127  +  va_end(ap);
          128  +  return rc;
          129  +}
          130  +
          131  +static int checkFreelist(
          132  +  sqlite3 *db, 
          133  +  const char *zDb,
          134  +  char **pzOut
          135  +){
          136  +  /* This query returns one row for each page on the free list. Each row has
          137  +  ** two columns - the page number and page content.  */
          138  +  const char *zTrunk = 
          139  +    "WITH freelist_trunk(i, d, n) AS ("
          140  +      "SELECT 1, NULL, sqlite_readint32(data, 32) "
          141  +      "FROM sqlite_dbpage(:1) WHERE pgno=1 "
          142  +        "UNION ALL "
          143  +      "SELECT n, data, sqlite_readint32(data) "
          144  +      "FROM freelist_trunk, sqlite_dbpage(:1) WHERE pgno=n "
          145  +    ")"
          146  +    "SELECT i, d FROM freelist_trunk WHERE i!=1;";
          147  +
          148  +  int rc, rc2;                    /* Return code */
          149  +  sqlite3_stmt *pTrunk = 0;       /* Compilation of zTrunk */
          150  +  u32 nPage = 0;                  /* Number of pages in db */
          151  +  u32 nExpected = 0;              /* Expected number of free pages */
          152  +  u32 nFree = 0;                  /* Number of pages on free list */
          153  +
          154  +  if( zDb==0 ) zDb = "main";
          155  +
          156  +  if( (rc = sqlGetInteger(db, zDb, "PRAGMA %s.page_count", &nPage))
          157  +   || (rc = sqlGetInteger(db, zDb, "PRAGMA %s.freelist_count", &nExpected))
          158  +  ){
          159  +    return rc;
          160  +  }
          161  +
          162  +  rc = sqlite3_prepare_v2(db, zTrunk, -1, &pTrunk, 0);
          163  +  if( rc!=SQLITE_OK ) return rc;
          164  +  sqlite3_bind_text(pTrunk, 1, zDb, -1, SQLITE_STATIC);
          165  +  while( rc==SQLITE_OK && sqlite3_step(pTrunk)==SQLITE_ROW ){
          166  +    u32 i;
          167  +    u32 iTrunk = (u32)sqlite3_column_int(pTrunk, 0);
          168  +    const u8 *aData = (const u8*)sqlite3_column_blob(pTrunk, 1);
          169  +    int nData = sqlite3_column_bytes(pTrunk, 1);
          170  +    u32 iNext = get4byte(&aData[0]);
          171  +    u32 nLeaf = get4byte(&aData[4]);
          172  +
          173  +    if( nLeaf>((nData/4)-2-6) ){
          174  +      rc = checkFreelistError(pzOut, 
          175  +          "leaf count out of range (%d) on trunk page %d", 
          176  +          (int)nLeaf, (int)iTrunk
          177  +      );
          178  +      nLeaf = (nData/4) - 2 - 6;
          179  +    }
          180  +
          181  +    nFree += 1+nLeaf;
          182  +    if( iNext>nPage ){
          183  +      rc = checkFreelistError(pzOut, 
          184  +          "trunk page %d is out of range", (int)iNext
          185  +      );
          186  +    }
          187  +
          188  +    for(i=0; rc==SQLITE_OK && i<nLeaf; i++){
          189  +      u32 iLeaf = get4byte(&aData[8 + 4*i]);
          190  +      if( iLeaf==0 || iLeaf>nPage ){
          191  +        rc = checkFreelistError(pzOut,
          192  +            "leaf page %d is out of range (child %d of trunk page %d)", 
          193  +            (int)iLeaf, (int)i, (int)iTrunk
          194  +        );
          195  +      }
          196  +    }
          197  +  }
          198  +
          199  +  if( rc==SQLITE_OK && nFree!=nExpected ){
          200  +    rc = checkFreelistError(pzOut,
          201  +        "free-list count mismatch: actual=%d header=%d", 
          202  +        (int)nFree, (int)nExpected
          203  +    );
          204  +  }
          205  +
          206  +  rc2 = sqlite3_finalize(pTrunk);
          207  +  if( rc==SQLITE_OK ) rc = rc2;
          208  +  return rc;
          209  +}
          210  +
          211  +int sqlite3_check_freelist(sqlite3 *db, const char *zDb){
          212  +  return checkFreelist(db, zDb, 0);
          213  +}
          214  +
          215  +static void checkfreelist_function(
          216  +  sqlite3_context *pCtx,
          217  +  int nArg,
          218  +  sqlite3_value **apArg
          219  +){
          220  +  const char *zDb;
          221  +  int rc;
          222  +  char *zOut = 0;
          223  +  sqlite3 *db = sqlite3_context_db_handle(pCtx);
          224  +
          225  +  assert( nArg==1 );
          226  +  zDb = (const char*)sqlite3_value_text(apArg[0]);
          227  +  rc = checkFreelist(db, zDb, &zOut);
          228  +  if( rc==SQLITE_OK ){
          229  +    sqlite3_result_text(pCtx, zOut?zOut:"ok", -1, SQLITE_TRANSIENT);
          230  +  }else{
          231  +    sqlite3_result_error_code(pCtx, rc);
          232  +  }
          233  +
          234  +  sqlite3_free(zOut);
          235  +}
          236  +
          237  +/*
          238  +** An SQL function invoked as follows:
          239  +**
          240  +**   sqlite_readint32(BLOB)           -- Decode 32-bit integer from start of blob
          241  +*/
          242  +static void readint_function(
          243  +  sqlite3_context *pCtx,
          244  +  int nArg,
          245  +  sqlite3_value **apArg
          246  +){
          247  +  const u8 *zBlob;
          248  +  int nBlob;
          249  +  int iOff = 0;
          250  +  u32 iRet = 0;
          251  +
          252  +  if( nArg!=1 && nArg!=2 ){
          253  +    sqlite3_result_error(
          254  +        pCtx, "wrong number of arguments to function sqlite_readint32()", -1
          255  +    );
          256  +    return;
          257  +  }
          258  +  if( nArg==2 ){
          259  +    iOff = sqlite3_value_int(apArg[1]);
          260  +  }
          261  +
          262  +  zBlob = sqlite3_value_blob(apArg[0]);
          263  +  nBlob = sqlite3_value_bytes(apArg[0]);
          264  +
          265  +  if( nBlob>=(iOff+4) ){
          266  +    iRet = get4byte(&zBlob[iOff]);
          267  +  }
          268  +
          269  +  sqlite3_result_int64(pCtx, (sqlite3_int64)iRet);
          270  +}
          271  +
          272  +/*
          273  +** Register the SQL functions.
          274  +*/
          275  +static int cflRegister(sqlite3 *db){
          276  +  int rc = sqlite3_create_function(
          277  +      db, "sqlite_readint32", -1, SQLITE_UTF8, 0, readint_function, 0, 0
          278  +  );
          279  +  if( rc!=SQLITE_OK ) return rc;
          280  +  rc = sqlite3_create_function(
          281  +      db, "checkfreelist", 1, SQLITE_UTF8, 0, checkfreelist_function, 0, 0
          282  +  );
          283  +  return rc;
          284  +}
          285  +
          286  +/*
          287  +** Extension load function.
          288  +*/
          289  +#ifdef _WIN32
          290  +__declspec(dllexport)
          291  +#endif
          292  +int sqlite3_checkfreelist_init(
          293  +  sqlite3 *db, 
          294  +  char **pzErrMsg, 
          295  +  const sqlite3_api_routines *pApi
          296  +){
          297  +  SQLITE_EXTENSION_INIT2(pApi);
          298  +  return cflRegister(db);
          299  +}

Changes to main.mk.

    51     51   THREADLIB += $(LIBS)
    52     52   
    53     53   # Object files for the SQLite library.
    54     54   #
    55     55   LIBOBJ+= vdbe.o parse.o \
    56     56            alter.o analyze.o attach.o auth.o \
    57     57            backup.o bitvec.o btmutex.o btree.o build.o \
    58         -         callback.o complete.o ctime.o date.o dbstat.o delete.o expr.o \
           58  +         callback.o complete.o ctime.o \
           59  +         date.o dbpage.o dbstat.o delete.o expr.o \
    59     60   	 fault.o fkey.o \
    60     61            fts3.o fts3_aux.o fts3_expr.o fts3_hash.o fts3_icu.o fts3_porter.o \
    61     62            fts3_snippet.o fts3_tokenizer.o fts3_tokenizer1.o \
    62     63            fts3_tokenize_vtab.o \
    63     64   	 fts3_unicode.o fts3_unicode2.o \
    64     65            fts3_write.o fts5.o func.o global.o hash.o \
    65     66            icu.o insert.o json1.o legacy.o loadext.o \
................................................................................
    92     93     $(TOP)/src/btree.h \
    93     94     $(TOP)/src/btreeInt.h \
    94     95     $(TOP)/src/build.c \
    95     96     $(TOP)/src/callback.c \
    96     97     $(TOP)/src/complete.c \
    97     98     $(TOP)/src/ctime.c \
    98     99     $(TOP)/src/date.c \
          100  +  $(TOP)/src/dbpage.c \
    99    101     $(TOP)/src/dbstat.c \
   100    102     $(TOP)/src/delete.c \
   101    103     $(TOP)/src/expr.c \
   102    104     $(TOP)/src/fault.c \
   103    105     $(TOP)/src/fkey.c \
   104    106     $(TOP)/src/func.c \
   105    107     $(TOP)/src/global.c \
................................................................................
   356    358   
   357    359   TESTSRC2 = \
   358    360     $(TOP)/src/attach.c \
   359    361     $(TOP)/src/backup.c \
   360    362     $(TOP)/src/btree.c \
   361    363     $(TOP)/src/build.c \
   362    364     $(TOP)/src/date.c \
          365  +  $(TOP)/src/dbpage.c \
   363    366     $(TOP)/src/dbstat.c \
   364    367     $(TOP)/src/expr.c \
   365    368     $(TOP)/src/func.c \
   366    369     $(TOP)/src/insert.c \
   367    370     $(TOP)/src/wal.c \
   368    371     $(TOP)/src/main.c \
   369    372     $(TOP)/src/mem5.c \
................................................................................
   477    480   
   478    481   # Extra compiler options for various shell tools
   479    482   #
   480    483   SHELL_OPT += -DSQLITE_ENABLE_JSON1 -DSQLITE_ENABLE_FTS4 -DSQLITE_ENABLE_FTS5
   481    484   SHELL_OPT += -DSQLITE_ENABLE_EXPLAIN_COMMENTS
   482    485   SHELL_OPT += -DSQLITE_ENABLE_UNKNOWN_SQL_FUNCTION
   483    486   SHELL_OPT += -DSQLITE_ENABLE_STMTVTAB
          487  +SHELL_OPT += -DSQLITE_ENABLE_DBPAGE_VTAB
          488  +SHELL_OPT += -DSQLITE_ENABLE_DBSTAT_VTAB
   484    489   FUZZERSHELL_OPT = -DSQLITE_ENABLE_JSON1
   485    490   FUZZCHECK_OPT = -DSQLITE_ENABLE_JSON1 -DSQLITE_ENABLE_MEMSYS5
   486    491   FUZZCHECK_OPT += -DSQLITE_MAX_MEMORY=50000000
   487    492   DBFUZZ_OPT =
   488    493   KV_OPT = -DSQLITE_THREADSAFE=0 -DSQLITE_DIRECT_OVERFLOW_READ
   489    494   ST_OPT = -DSQLITE_THREADSAFE=0
   490    495   

Added src/dbpage.c.

            1  +/*
            2  +** 2017-10-11
            3  +**
            4  +** The author disclaims copyright to this source code.  In place of
            5  +** a legal notice, here is a blessing:
            6  +**
            7  +**    May you do good and not evil.
            8  +**    May you find forgiveness for yourself and forgive others.
            9  +**    May you share freely, never taking more than you give.
           10  +**
           11  +******************************************************************************
           12  +**
           13  +** This file contains an implementation of the "sqlite_dbpage" virtual table.
           14  +**
           15  +** The sqlite_dbpage virtual table is used to read or write whole raw
           16  +** pages of the database file.  The pager interface is used so that 
           17  +** uncommitted changes and changes recorded in the WAL file are correctly
           18  +** retrieved.
           19  +**
           20  +** Usage example:
           21  +**
           22  +**    SELECT data FROM sqlite_dbpage('aux1') WHERE pgno=123;
           23  +**
           24  +** This is an eponymous virtual table so it does not need to be created before
           25  +** use.  The optional argument to the sqlite_dbpage() table name is the
           26  +** schema for the database file that is to be read.  The default schema is
           27  +** "main".
           28  +**
           29  +** The data field of sqlite_dbpage table can be updated.  The new
           30  +** value must be a BLOB which is the correct page size, otherwise the
           31  +** update fails.  Rows may not be deleted or inserted.
           32  +*/
           33  +
           34  +#include "sqliteInt.h"   /* Requires access to internal data structures */
           35  +#if (defined(SQLITE_ENABLE_DBPAGE_VTAB) || defined(SQLITE_TEST)) \
           36  +    && !defined(SQLITE_OMIT_VIRTUALTABLE)
           37  +
           38  +typedef struct DbpageTable DbpageTable;
           39  +typedef struct DbpageCursor DbpageCursor;
           40  +
           41  +struct DbpageCursor {
           42  +  sqlite3_vtab_cursor base;       /* Base class.  Must be first */
           43  +  int pgno;                       /* Current page number */
           44  +  int mxPgno;                     /* Last page to visit on this scan */
           45  +};
           46  +
           47  +struct DbpageTable {
           48  +  sqlite3_vtab base;              /* Base class.  Must be first */
           49  +  sqlite3 *db;                    /* The database */
           50  +  Pager *pPager;                  /* Pager being read/written */
           51  +  int iDb;                        /* Index of database to analyze */
           52  +  int szPage;                     /* Size of each page in bytes */
           53  +  int nPage;                      /* Number of pages in the file */
           54  +};
           55  +
           56  +/*
           57  +** Connect to or create a dbpagevfs virtual table.
           58  +*/
           59  +static int dbpageConnect(
           60  +  sqlite3 *db,
           61  +  void *pAux,
           62  +  int argc, const char *const*argv,
           63  +  sqlite3_vtab **ppVtab,
           64  +  char **pzErr
           65  +){
           66  +  DbpageTable *pTab = 0;
           67  +  int rc = SQLITE_OK;
           68  +  int iDb;
           69  +
           70  +  if( argc>=4 ){
           71  +    Token nm;
           72  +    sqlite3TokenInit(&nm, (char*)argv[3]);
           73  +    iDb = sqlite3FindDb(db, &nm);
           74  +    if( iDb<0 ){
           75  +      *pzErr = sqlite3_mprintf("no such schema: %s", argv[3]);
           76  +      return SQLITE_ERROR;
           77  +    }
           78  +  }else{
           79  +    iDb = 0;
           80  +  }
           81  +  rc = sqlite3_declare_vtab(db, 
           82  +          "CREATE TABLE x(pgno INTEGER PRIMARY KEY, data BLOB, schema HIDDEN)");
           83  +  if( rc==SQLITE_OK ){
           84  +    pTab = (DbpageTable *)sqlite3_malloc64(sizeof(DbpageTable));
           85  +    if( pTab==0 ) rc = SQLITE_NOMEM_BKPT;
           86  +  }
           87  +
           88  +  assert( rc==SQLITE_OK || pTab==0 );
           89  +  if( rc==SQLITE_OK ){
           90  +    Btree *pBt = db->aDb[iDb].pBt;
           91  +    memset(pTab, 0, sizeof(DbpageTable));
           92  +    pTab->db = db;
           93  +    pTab->iDb = iDb;
           94  +    pTab->pPager = pBt ? sqlite3BtreePager(pBt) : 0;
           95  +  }
           96  +
           97  +  *ppVtab = (sqlite3_vtab*)pTab;
           98  +  return rc;
           99  +}
          100  +
          101  +/*
          102  +** Disconnect from or destroy a dbpagevfs virtual table.
          103  +*/
          104  +static int dbpageDisconnect(sqlite3_vtab *pVtab){
          105  +  sqlite3_free(pVtab);
          106  +  return SQLITE_OK;
          107  +}
          108  +
          109  +/*
          110  +** idxNum:
          111  +**
          112  +**     0     full table scan
          113  +**     1     pgno=?1
          114  +*/
          115  +static int dbpageBestIndex(sqlite3_vtab *tab, sqlite3_index_info *pIdxInfo){
          116  +  int i;
          117  +  pIdxInfo->estimatedCost = 1.0e6;  /* Initial cost estimate */
          118  +  for(i=0; i<pIdxInfo->nConstraint; i++){
          119  +    struct sqlite3_index_constraint *p = &pIdxInfo->aConstraint[i];
          120  +    if( p->usable && p->iColumn<=0 && p->op==SQLITE_INDEX_CONSTRAINT_EQ ){
          121  +      pIdxInfo->estimatedRows = 1;
          122  +      pIdxInfo->idxFlags = SQLITE_INDEX_SCAN_UNIQUE;
          123  +      pIdxInfo->estimatedCost = 1.0;
          124  +      pIdxInfo->idxNum = 1;
          125  +      pIdxInfo->aConstraintUsage[i].argvIndex = 1;
          126  +      pIdxInfo->aConstraintUsage[i].omit = 1;
          127  +      break;
          128  +    }
          129  +  }
          130  +  if( pIdxInfo->nOrderBy>=1
          131  +   && pIdxInfo->aOrderBy[0].iColumn<=0
          132  +   && pIdxInfo->aOrderBy[0].desc==0
          133  +  ){
          134  +    pIdxInfo->orderByConsumed = 1;
          135  +  }
          136  +  return SQLITE_OK;
          137  +}
          138  +
          139  +/*
          140  +** Open a new dbpagevfs cursor.
          141  +*/
          142  +static int dbpageOpen(sqlite3_vtab *pVTab, sqlite3_vtab_cursor **ppCursor){
          143  +  DbpageCursor *pCsr;
          144  +
          145  +  pCsr = (DbpageCursor *)sqlite3_malloc64(sizeof(DbpageCursor));
          146  +  if( pCsr==0 ){
          147  +    return SQLITE_NOMEM_BKPT;
          148  +  }else{
          149  +    memset(pCsr, 0, sizeof(DbpageCursor));
          150  +    pCsr->base.pVtab = pVTab;
          151  +    pCsr->pgno = -1;
          152  +  }
          153  +
          154  +  *ppCursor = (sqlite3_vtab_cursor *)pCsr;
          155  +  return SQLITE_OK;
          156  +}
          157  +
          158  +/*
          159  +** Close a dbpagevfs cursor.
          160  +*/
          161  +static int dbpageClose(sqlite3_vtab_cursor *pCursor){
          162  +  DbpageCursor *pCsr = (DbpageCursor *)pCursor;
          163  +  sqlite3_free(pCsr);
          164  +  return SQLITE_OK;
          165  +}
          166  +
          167  +/*
          168  +** Move a dbpagevfs cursor to the next entry in the file.
          169  +*/
          170  +static int dbpageNext(sqlite3_vtab_cursor *pCursor){
          171  +  int rc = SQLITE_OK;
          172  +  DbpageCursor *pCsr = (DbpageCursor *)pCursor;
          173  +  pCsr->pgno++;
          174  +  return rc;
          175  +}
          176  +
          177  +static int dbpageEof(sqlite3_vtab_cursor *pCursor){
          178  +  DbpageCursor *pCsr = (DbpageCursor *)pCursor;
          179  +  return pCsr->pgno > pCsr->mxPgno;
          180  +}
          181  +
          182  +static int dbpageFilter(
          183  +  sqlite3_vtab_cursor *pCursor, 
          184  +  int idxNum, const char *idxStr,
          185  +  int argc, sqlite3_value **argv
          186  +){
          187  +  DbpageCursor *pCsr = (DbpageCursor *)pCursor;
          188  +  DbpageTable *pTab = (DbpageTable *)pCursor->pVtab;
          189  +  int rc = SQLITE_OK;
          190  +  Btree *pBt = pTab->db->aDb[pTab->iDb].pBt;
          191  +
          192  +  pTab->szPage = sqlite3BtreeGetPageSize(pBt);
          193  +  pTab->nPage = sqlite3BtreeLastPage(pBt);
          194  +  if( idxNum==1 ){
          195  +    pCsr->pgno = sqlite3_value_int(argv[0]);
          196  +    if( pCsr->pgno<1 || pCsr->pgno>pTab->nPage ){
          197  +      pCsr->pgno = 1;
          198  +      pCsr->mxPgno = 0;
          199  +    }else{
          200  +      pCsr->mxPgno = pCsr->pgno;
          201  +    }
          202  +  }else{
          203  +    pCsr->pgno = 1;
          204  +    pCsr->mxPgno = pTab->nPage;
          205  +  }
          206  +  return rc;
          207  +}
          208  +
          209  +static int dbpageColumn(
          210  +  sqlite3_vtab_cursor *pCursor, 
          211  +  sqlite3_context *ctx, 
          212  +  int i
          213  +){
          214  +  DbpageCursor *pCsr = (DbpageCursor *)pCursor;
          215  +  DbpageTable *pTab = (DbpageTable *)pCursor->pVtab;
          216  +  int rc = SQLITE_OK;
          217  +  switch( i ){
          218  +    case 0: {           /* pgno */
          219  +      sqlite3_result_int(ctx, pCsr->pgno);
          220  +      break;
          221  +    }
          222  +    case 1: {           /* data */
          223  +      DbPage *pDbPage = 0;
          224  +      rc = sqlite3PagerGet(pTab->pPager, pCsr->pgno, (DbPage**)&pDbPage, 0);
          225  +      if( rc==SQLITE_OK ){
          226  +        sqlite3_result_blob(ctx, sqlite3PagerGetData(pDbPage), pTab->szPage,
          227  +                            SQLITE_TRANSIENT);
          228  +      }
          229  +      sqlite3PagerUnref(pDbPage);
          230  +      break;
          231  +    }
          232  +    default: {          /* schema */
          233  +      sqlite3 *db = sqlite3_context_db_handle(ctx);
          234  +      sqlite3_result_text(ctx, db->aDb[pTab->iDb].zDbSName, -1, SQLITE_STATIC);
          235  +      break;
          236  +    }
          237  +  }
          238  +  return SQLITE_OK;
          239  +}
          240  +
          241  +static int dbpageRowid(sqlite3_vtab_cursor *pCursor, sqlite_int64 *pRowid){
          242  +  DbpageCursor *pCsr = (DbpageCursor *)pCursor;
          243  +  *pRowid = pCsr->pgno;
          244  +  return SQLITE_OK;
          245  +}
          246  +
          247  +static int dbpageUpdate(
          248  +  sqlite3_vtab *pVtab,
          249  +  int argc,
          250  +  sqlite3_value **argv,
          251  +  sqlite_int64 *pRowid
          252  +){
          253  +  DbpageTable *pTab = (DbpageTable *)pVtab;
          254  +  int pgno;
          255  +  DbPage *pDbPage = 0;
          256  +  int rc = SQLITE_OK;
          257  +  char *zErr = 0;
          258  +
          259  +  if( argc==1 ){
          260  +    zErr = "cannot delete";
          261  +    goto update_fail;
          262  +  }
          263  +  pgno = sqlite3_value_int(argv[0]);
          264  +  if( pgno<1 || pgno>pTab->nPage ){
          265  +    zErr = "bad page number";
          266  +    goto update_fail;
          267  +  }
          268  +  if( sqlite3_value_int(argv[1])!=pgno ){
          269  +    zErr = "cannot insert";
          270  +    goto update_fail;
          271  +  }
          272  +  if( sqlite3_value_type(argv[3])!=SQLITE_BLOB 
          273  +   || sqlite3_value_bytes(argv[3])!=pTab->szPage 
          274  +  ){
          275  +    zErr = "bad page value";
          276  +    goto update_fail;
          277  +  }
          278  +  rc = sqlite3PagerGet(pTab->pPager, pgno, (DbPage**)&pDbPage, 0);
          279  +  if( rc==SQLITE_OK ){
          280  +    rc = sqlite3PagerWrite(pDbPage);
          281  +    if( rc==SQLITE_OK ){
          282  +      memcpy(sqlite3PagerGetData(pDbPage),
          283  +             sqlite3_value_blob(argv[3]),
          284  +             pTab->szPage);
          285  +    }
          286  +  }
          287  +  sqlite3PagerUnref(pDbPage);
          288  +  return rc;
          289  +
          290  +update_fail:
          291  +  sqlite3_free(pVtab->zErrMsg);
          292  +  pVtab->zErrMsg = sqlite3_mprintf("%s", zErr);
          293  +  return SQLITE_ERROR;
          294  +}
          295  +
          296  +/*
          297  +** Invoke this routine to register the "dbpage" virtual table module
          298  +*/
          299  +int sqlite3DbpageRegister(sqlite3 *db){
          300  +  static sqlite3_module dbpage_module = {
          301  +    0,                            /* iVersion */
          302  +    dbpageConnect,                /* xCreate */
          303  +    dbpageConnect,                /* xConnect */
          304  +    dbpageBestIndex,              /* xBestIndex */
          305  +    dbpageDisconnect,             /* xDisconnect */
          306  +    dbpageDisconnect,             /* xDestroy */
          307  +    dbpageOpen,                   /* xOpen - open a cursor */
          308  +    dbpageClose,                  /* xClose - close a cursor */
          309  +    dbpageFilter,                 /* xFilter - configure scan constraints */
          310  +    dbpageNext,                   /* xNext - advance a cursor */
          311  +    dbpageEof,                    /* xEof - check for end of scan */
          312  +    dbpageColumn,                 /* xColumn - read data */
          313  +    dbpageRowid,                  /* xRowid - read data */
          314  +    dbpageUpdate,                 /* xUpdate */
          315  +    0,                            /* xBegin */
          316  +    0,                            /* xSync */
          317  +    0,                            /* xCommit */
          318  +    0,                            /* xRollback */
          319  +    0,                            /* xFindMethod */
          320  +    0,                            /* xRename */
          321  +    0,                            /* xSavepoint */
          322  +    0,                            /* xRelease */
          323  +    0,                            /* xRollbackTo */
          324  +  };
          325  +  return sqlite3_create_module(db, "sqlite_dbpage", &dbpage_module, 0);
          326  +}
          327  +#elif defined(SQLITE_ENABLE_DBPAGE_VTAB)
          328  +int sqlite3DbpageRegister(sqlite3 *db){ return SQLITE_OK; }
          329  +#endif /* SQLITE_ENABLE_DBSTAT_VTAB */

Changes to src/main.c.

  3049   3049   #endif
  3050   3050   
  3051   3051   #ifdef SQLITE_ENABLE_RTREE
  3052   3052     if( !db->mallocFailed && rc==SQLITE_OK){
  3053   3053       rc = sqlite3RtreeInit(db);
  3054   3054     }
  3055   3055   #endif
         3056  +
         3057  +#ifdef SQLITE_ENABLE_DBPAGE_VTAB
         3058  +  if( !db->mallocFailed && rc==SQLITE_OK){
         3059  +    rc = sqlite3DbpageRegister(db);
         3060  +  }
         3061  +#endif
  3056   3062   
  3057   3063   #ifdef SQLITE_ENABLE_DBSTAT_VTAB
  3058   3064     if( !db->mallocFailed && rc==SQLITE_OK){
  3059   3065       rc = sqlite3DbstatRegister(db);
  3060   3066     }
  3061   3067   #endif
  3062   3068   

Changes to src/shell.c.in.

  3607   3607        { "number of triggers:",
  3608   3608          "SELECT count(*) FROM %s WHERE type='trigger'" },
  3609   3609        { "number of views:",
  3610   3610          "SELECT count(*) FROM %s WHERE type='view'" },
  3611   3611        { "schema size:",
  3612   3612          "SELECT total(length(sql)) FROM %s" },
  3613   3613     };
  3614         -  sqlite3_file *pFile = 0;
  3615   3614     int i;
  3616   3615     char *zSchemaTab;
  3617   3616     char *zDb = nArg>=2 ? azArg[1] : "main";
         3617  +  sqlite3_stmt *pStmt = 0;
  3618   3618     unsigned char aHdr[100];
  3619   3619     open_db(p, 0);
  3620   3620     if( p->db==0 ) return 1;
  3621         -  sqlite3_file_control(p->db, zDb, SQLITE_FCNTL_FILE_POINTER, &pFile);
  3622         -  if( pFile==0 || pFile->pMethods==0 || pFile->pMethods->xRead==0 ){
  3623         -    return 1;
  3624         -  }
  3625         -  i = pFile->pMethods->xRead(pFile, aHdr, 100, 0);
  3626         -  if( i!=SQLITE_OK ){
         3621  +  sqlite3_prepare_v2(p->db,"SELECT data FROM sqlite_dbpage(?1) WHERE pgno=1",
         3622  +                     -1, &pStmt, 0);
         3623  +  sqlite3_bind_text(pStmt, 1, zDb, -1, SQLITE_STATIC);
         3624  +  if( sqlite3_step(pStmt)==SQLITE_ROW
         3625  +   && sqlite3_column_bytes(pStmt,0)>100
         3626  +  ){
         3627  +    memcpy(aHdr, sqlite3_column_blob(pStmt,0), 100);
         3628  +    sqlite3_finalize(pStmt);
         3629  +  }else{
  3627   3630       raw_printf(stderr, "unable to read database header\n");
         3631  +    sqlite3_finalize(pStmt);
  3628   3632       return 1;
  3629   3633     }
  3630   3634     i = get2byteInt(aHdr+16);
  3631   3635     if( i==1 ) i = 65536;
  3632   3636     utf8_printf(p->out, "%-20s %d\n", "database page size:", i);
  3633   3637     utf8_printf(p->out, "%-20s %d\n", "write format:", aHdr[18]);
  3634   3638     utf8_printf(p->out, "%-20s %d\n", "read format:", aHdr[19]);

Changes to src/sqliteInt.h.

  4396   4396   ** Threading interface
  4397   4397   */
  4398   4398   #if SQLITE_MAX_WORKER_THREADS>0
  4399   4399   int sqlite3ThreadCreate(SQLiteThread**,void*(*)(void*),void*);
  4400   4400   int sqlite3ThreadJoin(SQLiteThread*, void**);
  4401   4401   #endif
  4402   4402   
         4403  +#if defined(SQLITE_ENABLE_DBPAGE_VTAB) || defined(SQLITE_TEST)
         4404  +int sqlite3DbpageRegister(sqlite3*);
         4405  +#endif
  4403   4406   #if defined(SQLITE_ENABLE_DBSTAT_VTAB) || defined(SQLITE_TEST)
  4404   4407   int sqlite3DbstatRegister(sqlite3*);
  4405   4408   #endif
  4406   4409   
  4407   4410   int sqlite3ExprVectorSize(Expr *pExpr);
  4408   4411   int sqlite3ExprIsVector(Expr *pExpr);
  4409   4412   Expr *sqlite3VectorFieldSubexpr(Expr*, int);

Added test/checkfreelist.test.

            1  +# 2017-10-11
            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  +# This file implements regression tests for SQLite library.  The
           12  +# focus of this file is testing the checkfreelist extension.
           13  +#
           14  +
           15  +set testdir [file dirname $argv0]
           16  +source $testdir/tester.tcl
           17  +set testprefix checkfreelist
           18  +
           19  +ifcapable !vtab||!compound {
           20  +  finish_test
           21  +  return
           22  +}
           23  +
           24  +if {[file exists ../checkfreelist.so]==0} {
           25  +  finish_test
           26  +  return
           27  +}
           28  +
           29  +do_execsql_test 1.0 {
           30  +  CREATE TABLE t1(a, b);
           31  +}
           32  +
           33  +db enable_load_extension 1
           34  +do_execsql_test 1.1 {
           35  +  SELECT load_extension('../checkfreelist.so');
           36  +} {{}}
           37  +
           38  +do_execsql_test 1.2 { SELECT checkfreelist('main') } {ok}
           39  +do_execsql_test 1.3 {
           40  +  WITH s(i) AS (
           41  +    SELECT 1 UNION ALL SELECT i+1 FROM s WHERE i<10000
           42  +  )
           43  +  INSERT INTO t1 SELECT randomblob(400), randomblob(400) FROM s;
           44  +  DELETE FROM t1 WHERE rowid%3;
           45  +  PRAGMA freelist_count;
           46  +} {6726}
           47  +
           48  +do_execsql_test 1.4 { SELECT checkfreelist('main') } {ok}
           49  +do_execsql_test 1.5 {
           50  +  WITH freelist_trunk(i, d, n) AS (
           51  +    SELECT 1, NULL, sqlite_readint32(data, 32) FROM sqlite_dbpage WHERE pgno=1
           52  +      UNION ALL
           53  +    SELECT n, data, sqlite_readint32(data) 
           54  +    FROM freelist_trunk, sqlite_dbpage WHERE pgno=n
           55  +  )
           56  +  SELECT i FROM freelist_trunk WHERE i!=1;
           57  +} {
           58  +  10010 9716 9344 8970 8596 8223 7848 7475 7103 6728 6355 5983 5609 5235
           59  +  4861 4488 4113 3741 3368 2993 2620 2248 1873 1500 1126 753 378 5
           60  +}
           61  +
           62  +do_execsql_test 1.6 { SELECT checkfreelist('main') } {ok}
           63  +
           64  +proc set_int {blob idx newval} {
           65  +  binary scan $blob I* ints
           66  +  lset ints $idx $newval
           67  +  binary format I* $ints
           68  +}
           69  +db func set_int set_int
           70  +
           71  +proc get_int {blob idx} {
           72  +  binary scan $blob I* ints
           73  +  lindex $ints $idx
           74  +}
           75  +db func get_int get_int
           76  +
           77  +do_execsql_test 1.7 {
           78  +  BEGIN;
           79  +    UPDATE sqlite_dbpage 
           80  +      SET data = set_int(data, 1, get_int(data, 1)-1) 
           81  +      WHERE pgno=4861;
           82  +    SELECT checkfreelist('main');
           83  +  ROLLBACK;
           84  +} {{free-list count mismatch: actual=6725 header=6726}}
           85  +
           86  +do_execsql_test 1.8 {
           87  +  BEGIN;
           88  +    UPDATE sqlite_dbpage 
           89  +      SET data = set_int(data, 5, (SELECT * FROM pragma_page_count)+1)
           90  +      WHERE pgno=4861;
           91  +    SELECT checkfreelist('main');
           92  +  ROLLBACK;
           93  +} {{leaf page 10093 is out of range (child 3 of trunk page 4861)}}
           94  +
           95  +do_execsql_test 1.9 {
           96  +  BEGIN;
           97  +    UPDATE sqlite_dbpage 
           98  +      SET data = set_int(data, 5, 0)
           99  +      WHERE pgno=4861;
          100  +    SELECT checkfreelist('main');
          101  +  ROLLBACK;
          102  +} {{leaf page 0 is out of range (child 3 of trunk page 4861)}}
          103  +
          104  +do_execsql_test 1.10 {
          105  +  BEGIN;
          106  +    UPDATE sqlite_dbpage 
          107  +      SET data = set_int(data, get_int(data, 1)+1, 0)
          108  +      WHERE pgno=5;
          109  +    SELECT checkfreelist('main');
          110  +  ROLLBACK;
          111  +} {{leaf page 0 is out of range (child 247 of trunk page 5)}}
          112  +
          113  +do_execsql_test 1.11 {
          114  +  BEGIN;
          115  +    UPDATE sqlite_dbpage 
          116  +      SET data = set_int(data, 1, 249)
          117  +      WHERE pgno=5;
          118  +    SELECT checkfreelist('main');
          119  +  ROLLBACK;
          120  +} {{leaf count out of range (249) on trunk page 5}}
          121  +
          122  +finish_test
          123  +

Added test/dbpage.test.

            1  +# 2017-10-11
            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  +# This file implements regression tests for SQLite library.  The
           12  +# focus of this file is testing the sqlite_dbpage virtual table.
           13  +#
           14  +
           15  +set testdir [file dirname $argv0]
           16  +source $testdir/tester.tcl
           17  +set testprefix dbpage
           18  +
           19  +ifcapable !vtab||!compound {
           20  +  finish_test
           21  +  return
           22  +}
           23  +
           24  +do_execsql_test 100 {
           25  +  PRAGMA page_size=4096;
           26  +  PRAGMA journal_mode=WAL;
           27  +  CREATE TABLE t1(a,b);
           28  +  WITH RECURSIVE c(x) AS (VALUES(1) UNION ALL SELECT x+1 FROM c WHERE x<100)
           29  +  INSERT INTO t1(a,b) SELECT x, printf('%d-x%.*c',x,x,'x') FROM c;
           30  +  PRAGMA integrity_check;
           31  +} {wal ok}
           32  +do_execsql_test 110 {
           33  +  SELECT pgno, quote(substr(data,1,5)) FROM sqlite_dbpage('main') ORDER BY pgno;
           34  +} {1 X'53514C6974' 2 X'0500000001' 3 X'0D0000004E' 4 X'0D00000016'}
           35  +do_execsql_test 120 {
           36  +  SELECT pgno, quote(substr(data,1,5)) FROM sqlite_dbpage WHERE pgno=2;
           37  +} {2 X'0500000001'}
           38  +do_execsql_test 130 {
           39  +  SELECT pgno, quote(substr(data,1,5)) FROM sqlite_dbpage WHERE pgno=4;
           40  +} {4 X'0D00000016'}
           41  +do_execsql_test 140 {
           42  +  SELECT pgno, quote(substr(data,1,5)) FROM sqlite_dbpage WHERE pgno=5;
           43  +} {}
           44  +do_execsql_test 150 {
           45  +  SELECT pgno, quote(substr(data,1,5)) FROM sqlite_dbpage WHERE pgno=0;
           46  +} {}
           47  +
           48  +do_execsql_test 200 {
           49  +  CREATE TEMP TABLE saved_content(x);
           50  +  INSERT INTO saved_content(x) SELECT data FROM sqlite_dbpage WHERE pgno=4;
           51  +  UPDATE sqlite_dbpage SET data=zeroblob(4096) WHERE pgno=4;
           52  +} {}
           53  +do_catchsql_test 210 {
           54  +  PRAGMA integrity_check;
           55  +} {1 {database disk image is malformed}}
           56  +do_execsql_test 220 {
           57  +  SELECT pgno, quote(substr(data,1,5)) FROM sqlite_dbpage('main') ORDER BY pgno;
           58  +} {1 X'53514C6974' 2 X'0500000001' 3 X'0D0000004E' 4 X'0000000000'}
           59  +do_execsql_test 230 {
           60  +  UPDATE sqlite_dbpage SET data=(SELECT x FROM saved_content) WHERE pgno=4;
           61  +} {}
           62  +do_catchsql_test 230 {
           63  +  PRAGMA integrity_check;
           64  +} {0 ok}
           65  +
           66  +
           67  +
           68  +
           69  +finish_test

Changes to tool/mksqlite3c.tcl.

   390    390      fts3_unicode2.c
   391    391   
   392    392      rtree.c
   393    393      icu.c
   394    394      fts3_icu.c
   395    395      sqlite3rbu.c
   396    396      dbstat.c
          397  +   dbpage.c
   397    398      sqlite3session.c
   398    399      json1.c
   399    400      fts5.c
   400    401      stmt.c
   401    402   } {
   402    403     copy_file tsrc/$file
   403    404   }