modernc.org/cc@v1.0.1/v2/testdata/_sqlite/ext/repair/checkfreelist.c (about)

     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  }