Team Ai
Modelpublic

AryaWu/sqlite

sourceHugging Faceupdated 10mo agoView on Hugging Face
0likes
dbpage.c503 linesDownload Raw Back to src
1/*2** 2017-10-113**4** The author disclaims copyright to this source code.  In place of5** 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 raw16** pages of the database file.  The pager interface is used so that 17** uncommitted changes and changes recorded in the WAL file are correctly18** 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 before25** use.  The optional argument to the sqlite_dbpage() table name is the26** schema for the database file that is to be read.  The default schema is27** "main".28**29** The data field of sqlite_dbpage table can be updated.  The new30** value must be a BLOB which is the correct page size, otherwise the31** update fails.  INSERT operations also work, and operate as if they32** where REPLACE.  The size of the database can be extended by INSERT-ing33** new pages on the end.34**35** Rows may not be deleted.  However, doing an INSERT to page number N36** with NULL page data causes the N-th page and all subsequent pages to be37** deleted and the database to be truncated.38*/39 40#include "sqliteInt.h"   /* Requires access to internal data structures */41#if (defined(SQLITE_ENABLE_DBPAGE_VTAB) || defined(SQLITE_TEST)) \42    && !defined(SQLITE_OMIT_VIRTUALTABLE)43 44typedef struct DbpageTable DbpageTable;45typedef struct DbpageCursor DbpageCursor;46 47struct DbpageCursor {48  sqlite3_vtab_cursor base;       /* Base class.  Must be first */49  Pgno pgno;                      /* Current page number */50  Pgno mxPgno;                    /* Last page to visit on this scan */51  Pager *pPager;                  /* Pager being read/written */52  DbPage *pPage1;                 /* Page 1 of the database */53  int iDb;                        /* Index of database to analyze */54  int szPage;                     /* Size of each page in bytes */55};56 57struct DbpageTable {58  sqlite3_vtab base;              /* Base class.  Must be first */59  sqlite3 *db;                    /* The database */60  int iDbTrunc;                   /* Database to truncate */61  Pgno pgnoTrunc;                 /* Size to truncate to */62};63 64/* Columns */65#define DBPAGE_COLUMN_PGNO    066#define DBPAGE_COLUMN_DATA    167#define DBPAGE_COLUMN_SCHEMA  268 69 70/*71** Connect to or create a dbpagevfs virtual table.72*/73static int dbpageConnect(74  sqlite3 *db,75  void *pAux,76  int argc, const char *const*argv,77  sqlite3_vtab **ppVtab,78  char **pzErr79){80  DbpageTable *pTab = 0;81  int rc = SQLITE_OK;82  (void)pAux;83  (void)argc;84  (void)argv;85  (void)pzErr;86 87  sqlite3_vtab_config(db, SQLITE_VTAB_DIRECTONLY);88  sqlite3_vtab_config(db, SQLITE_VTAB_USES_ALL_SCHEMAS);89  rc = sqlite3_declare_vtab(db, 90          "CREATE TABLE x(pgno INTEGER PRIMARY KEY, data BLOB, schema HIDDEN)");91  if( rc==SQLITE_OK ){92    pTab = (DbpageTable *)sqlite3_malloc64(sizeof(DbpageTable));93    if( pTab==0 ) rc = SQLITE_NOMEM_BKPT;94  }95 96  assert( rc==SQLITE_OK || pTab==0 );97  if( rc==SQLITE_OK ){98    memset(pTab, 0, sizeof(DbpageTable));99    pTab->db = db;100  }101 102  *ppVtab = (sqlite3_vtab*)pTab;103  return rc;104}105 106/*107** Disconnect from or destroy a dbpagevfs virtual table.108*/109static int dbpageDisconnect(sqlite3_vtab *pVtab){110  sqlite3_free(pVtab);111  return SQLITE_OK;112}113 114/*115** idxNum:116**117**     0     schema=main, full table scan118**     1     schema=main, pgno=?1119**     2     schema=?1, full table scan120**     3     schema=?1, pgno=?2121*/122static int dbpageBestIndex(sqlite3_vtab *tab, sqlite3_index_info *pIdxInfo){123  int i;124  int iPlan = 0;125  (void)tab;126 127  /* If there is a schema= constraint, it must be honored.  Report a128  ** ridiculously large estimated cost if the schema= constraint is129  ** unavailable130  */131  for(i=0; i<pIdxInfo->nConstraint; i++){132    struct sqlite3_index_constraint *p = &pIdxInfo->aConstraint[i];133    if( p->iColumn!=DBPAGE_COLUMN_SCHEMA ) continue;134    if( p->op!=SQLITE_INDEX_CONSTRAINT_EQ ) continue;135    if( !p->usable ){136      /* No solution. */137      return SQLITE_CONSTRAINT;138    }139    iPlan = 2;140    pIdxInfo->aConstraintUsage[i].argvIndex = 1;141    pIdxInfo->aConstraintUsage[i].omit = 1;142    break;143  }144 145  /* If we reach this point, it means that either there is no schema=146  ** constraint (in which case we use the "main" schema) or else the147  ** schema constraint was accepted.  Lower the estimated cost accordingly148  */149  pIdxInfo->estimatedCost = 1.0e6;150 151  /* Check for constraints against pgno */152  for(i=0; i<pIdxInfo->nConstraint; i++){153    struct sqlite3_index_constraint *p = &pIdxInfo->aConstraint[i];154    if( p->usable && p->iColumn<=0 && p->op==SQLITE_INDEX_CONSTRAINT_EQ ){155      pIdxInfo->estimatedRows = 1;156      pIdxInfo->idxFlags = SQLITE_INDEX_SCAN_UNIQUE;157      pIdxInfo->estimatedCost = 1.0;158      pIdxInfo->aConstraintUsage[i].argvIndex = iPlan ? 2 : 1;159      pIdxInfo->aConstraintUsage[i].omit = 1;160      iPlan |= 1;161      break;162    }163  }164  pIdxInfo->idxNum = iPlan;165 166  if( pIdxInfo->nOrderBy>=1167   && pIdxInfo->aOrderBy[0].iColumn<=0168   && pIdxInfo->aOrderBy[0].desc==0169  ){170    pIdxInfo->orderByConsumed = 1;171  }172  return SQLITE_OK;173}174 175/*176** Open a new dbpagevfs cursor.177*/178static int dbpageOpen(sqlite3_vtab *pVTab, sqlite3_vtab_cursor **ppCursor){179  DbpageCursor *pCsr;180 181  pCsr = (DbpageCursor *)sqlite3_malloc64(sizeof(DbpageCursor));182  if( pCsr==0 ){183    return SQLITE_NOMEM_BKPT;184  }else{185    memset(pCsr, 0, sizeof(DbpageCursor));186    pCsr->base.pVtab = pVTab;187    pCsr->pgno = 0;188  }189 190  *ppCursor = (sqlite3_vtab_cursor *)pCsr;191  return SQLITE_OK;192}193 194/*195** Close a dbpagevfs cursor.196*/197static int dbpageClose(sqlite3_vtab_cursor *pCursor){198  DbpageCursor *pCsr = (DbpageCursor *)pCursor;199  if( pCsr->pPage1 ) sqlite3PagerUnrefPageOne(pCsr->pPage1);200  sqlite3_free(pCsr);201  return SQLITE_OK;202}203 204/*205** Move a dbpagevfs cursor to the next entry in the file.206*/207static int dbpageNext(sqlite3_vtab_cursor *pCursor){208  int rc = SQLITE_OK;209  DbpageCursor *pCsr = (DbpageCursor *)pCursor;210  pCsr->pgno++;211  return rc;212}213 214static int dbpageEof(sqlite3_vtab_cursor *pCursor){215  DbpageCursor *pCsr = (DbpageCursor *)pCursor;216  return pCsr->pgno > pCsr->mxPgno;217}218 219/*220** idxNum:221**222**     0     schema=main, full table scan223**     1     schema=main, pgno=?1224**     2     schema=?1, full table scan225**     3     schema=?1, pgno=?2226**227** idxStr is not used228*/229static int dbpageFilter(230  sqlite3_vtab_cursor *pCursor,231  int idxNum, const char *idxStr,232  int argc, sqlite3_value **argv233){234  DbpageCursor *pCsr = (DbpageCursor *)pCursor;235  DbpageTable *pTab = (DbpageTable *)pCursor->pVtab;236  int rc;237  sqlite3 *db = pTab->db;238  Btree *pBt;239 240  UNUSED_PARAMETER(idxStr);241  UNUSED_PARAMETER(argc);242 243  /* Default setting is no rows of result */244  pCsr->pgno = 1;245  pCsr->mxPgno = 0;246 247  if( idxNum & 2 ){248    const char *zSchema;249    assert( argc>=1 );250    zSchema = (const char*)sqlite3_value_text(argv[0]);251    pCsr->iDb = sqlite3FindDbName(db, zSchema);252    if( pCsr->iDb<0 ) return SQLITE_OK;253  }else{254    pCsr->iDb = 0;255  }256  pBt = db->aDb[pCsr->iDb].pBt;257  if( NEVER(pBt==0) ) return SQLITE_OK;258  pCsr->pPager = sqlite3BtreePager(pBt);259  pCsr->szPage = sqlite3BtreeGetPageSize(pBt);260  pCsr->mxPgno = sqlite3BtreeLastPage(pBt);261  if( idxNum & 1 ){262    assert( argc>(idxNum>>1) );263    pCsr->pgno = sqlite3_value_int(argv[idxNum>>1]);264    if( pCsr->pgno<1 || pCsr->pgno>pCsr->mxPgno ){265      pCsr->pgno = 1;266      pCsr->mxPgno = 0;267    }else{268      pCsr->mxPgno = pCsr->pgno;269    }270  }else{271    assert( pCsr->pgno==1 );272  }273  if( pCsr->pPage1 ) sqlite3PagerUnrefPageOne(pCsr->pPage1);274  rc = sqlite3PagerGet(pCsr->pPager, 1, &pCsr->pPage1, 0);275  return rc;276}277 278static int dbpageColumn(279  sqlite3_vtab_cursor *pCursor,280  sqlite3_context *ctx,281  int i282){283  DbpageCursor *pCsr = (DbpageCursor *)pCursor;284  int rc = SQLITE_OK;285  switch( i ){286    case 0: {           /* pgno */287      sqlite3_result_int64(ctx, (sqlite3_int64)pCsr->pgno);288      break;289    }290    case 1: {           /* data */291      DbPage *pDbPage = 0;292      if( pCsr->pgno==(Pgno)((PENDING_BYTE/pCsr->szPage)+1) ){293        /* The pending byte page. Assume it is zeroed out. Attempting to294        ** request this page from the page is an SQLITE_CORRUPT error. */295        sqlite3_result_zeroblob(ctx, pCsr->szPage);296      }else{297        rc = sqlite3PagerGet(pCsr->pPager, pCsr->pgno, (DbPage**)&pDbPage, 0);298        if( rc==SQLITE_OK ){299          sqlite3_result_blob(ctx, sqlite3PagerGetData(pDbPage), pCsr->szPage,300              SQLITE_TRANSIENT);301        }302        sqlite3PagerUnref(pDbPage);303      }304      break;305    }306    default: {          /* schema */307      sqlite3 *db = sqlite3_context_db_handle(ctx);308      sqlite3_result_text(ctx, db->aDb[pCsr->iDb].zDbSName, -1, SQLITE_STATIC);309      break;310    }311  }312  return rc;313}314 315static int dbpageRowid(sqlite3_vtab_cursor *pCursor, sqlite_int64 *pRowid){316  DbpageCursor *pCsr = (DbpageCursor *)pCursor;317  *pRowid = pCsr->pgno;318  return SQLITE_OK;319}320 321/* 322** Open write transactions. Since we do not know in advance which database323** files will be written by the sqlite_dbpage virtual table, start a write324** transaction on them all.325**326** Return SQLITE_OK if successful, or an SQLite error code otherwise.327*/328static int dbpageBeginTrans(DbpageTable *pTab){329  sqlite3 *db = pTab->db;330  int rc = SQLITE_OK;331  int i;332  for(i=0; rc==SQLITE_OK && i<db->nDb; i++){333    Btree *pBt = db->aDb[i].pBt;334    if( pBt ) rc = sqlite3BtreeBeginTrans(pBt, 1, 0);335  }336  return rc;337}338 339static int dbpageUpdate(340  sqlite3_vtab *pVtab,341  int argc,342  sqlite3_value **argv,343  sqlite_int64 *pRowid344){345  DbpageTable *pTab = (DbpageTable *)pVtab;346  Pgno pgno;347  DbPage *pDbPage = 0;348  int rc = SQLITE_OK;349  char *zErr = 0;350  int iDb;351  Btree *pBt;352  Pager *pPager;353  int szPage;354  int isInsert;355 356  (void)pRowid;357  if( pTab->db->flags & SQLITE_Defensive ){358    zErr = "read-only";359    goto update_fail;360  }361  if( argc==1 ){362    zErr = "cannot delete";363    goto update_fail;364  }365  if( sqlite3_value_type(argv[0])==SQLITE_NULL ){366    pgno = (Pgno)sqlite3_value_int64(argv[2]);367    isInsert = 1;368  }else{369    pgno = (Pgno)sqlite3_value_int64(argv[0]);370    if( (Pgno)sqlite3_value_int(argv[1])!=pgno ){371      zErr = "cannot insert";372      goto update_fail;373    }374    isInsert = 0;375  }376  if( sqlite3_value_type(argv[4])==SQLITE_NULL ){377    iDb = 0;378  }else{379    const char *zSchema = (const char*)sqlite3_value_text(argv[4]);380    iDb = sqlite3FindDbName(pTab->db, zSchema);381    if( iDb<0 ){382      zErr = "no such schema";383      goto update_fail;384    }385  }386  pBt = pTab->db->aDb[iDb].pBt;387  if( pgno<1 || NEVER(pBt==0) ){388    zErr = "bad page number";389    goto update_fail;390  }391  szPage = sqlite3BtreeGetPageSize(pBt);392  if( sqlite3_value_type(argv[3])!=SQLITE_BLOB 393   || sqlite3_value_bytes(argv[3])!=szPage394  ){395    if( sqlite3_value_type(argv[3])==SQLITE_NULL && isInsert && pgno>1 ){396      /* "INSERT INTO dbpage($PGNO,NULL)" causes page number $PGNO and397      ** all subsequent pages to be deleted. */398      pTab->iDbTrunc = iDb;399      pTab->pgnoTrunc = pgno-1;400      pgno = 1;401    }else{402      zErr = "bad page value";403      goto update_fail;404    }405  }406 407  if( dbpageBeginTrans(pTab)!=SQLITE_OK ){408    zErr = "failed to open transaction";409    goto update_fail;410  }411 412  pPager = sqlite3BtreePager(pBt);413  rc = sqlite3PagerGet(pPager, pgno, (DbPage**)&pDbPage, 0);414  if( rc==SQLITE_OK ){415    const void *pData = sqlite3_value_blob(argv[3]);416    if( (rc = sqlite3PagerWrite(pDbPage))==SQLITE_OK && pData ){417      unsigned char *aPage = sqlite3PagerGetData(pDbPage);418      memcpy(aPage, pData, szPage);419      pTab->pgnoTrunc = 0;420    }421  }422  if( rc!=SQLITE_OK ){423    pTab->pgnoTrunc = 0;424  }425  sqlite3PagerUnref(pDbPage);426  return rc;427 428update_fail:429  pTab->pgnoTrunc = 0;430  sqlite3_free(pVtab->zErrMsg);431  pVtab->zErrMsg = sqlite3_mprintf("%s", zErr);432  return SQLITE_ERROR;433}434 435static int dbpageBegin(sqlite3_vtab *pVtab){436  DbpageTable *pTab = (DbpageTable *)pVtab;437  pTab->pgnoTrunc = 0;438  return SQLITE_OK;439}440 441/* Invoke sqlite3PagerTruncate() as necessary, just prior to COMMIT442*/443static int dbpageSync(sqlite3_vtab *pVtab){444  DbpageTable *pTab = (DbpageTable *)pVtab;445  if( pTab->pgnoTrunc>0 ){446    Btree *pBt = pTab->db->aDb[pTab->iDbTrunc].pBt;447    Pager *pPager = sqlite3BtreePager(pBt);448    sqlite3BtreeEnter(pBt);449    if( pTab->pgnoTrunc<sqlite3BtreeLastPage(pBt) ){450      sqlite3PagerTruncateImage(pPager, pTab->pgnoTrunc);451    }452    sqlite3BtreeLeave(pBt);453  }454  pTab->pgnoTrunc = 0;455  return SQLITE_OK;456}457 458/* Cancel any pending truncate.459*/460static int dbpageRollbackTo(sqlite3_vtab *pVtab, int notUsed1){461  DbpageTable *pTab = (DbpageTable *)pVtab;462  pTab->pgnoTrunc = 0;463  (void)notUsed1;464  return SQLITE_OK;465}466 467/*468** Invoke this routine to register the "dbpage" virtual table module469*/470int sqlite3DbpageRegister(sqlite3 *db){471  static sqlite3_module dbpage_module = {472    2,                            /* iVersion */473    dbpageConnect,                /* xCreate */474    dbpageConnect,                /* xConnect */475    dbpageBestIndex,              /* xBestIndex */476    dbpageDisconnect,             /* xDisconnect */477    dbpageDisconnect,             /* xDestroy */478    dbpageOpen,                   /* xOpen - open a cursor */479    dbpageClose,                  /* xClose - close a cursor */480    dbpageFilter,                 /* xFilter - configure scan constraints */481    dbpageNext,                   /* xNext - advance a cursor */482    dbpageEof,                    /* xEof - check for end of scan */483    dbpageColumn,                 /* xColumn - read data */484    dbpageRowid,                  /* xRowid - read data */485    dbpageUpdate,                 /* xUpdate */486    dbpageBegin,                  /* xBegin */487    dbpageSync,                   /* xSync */488    0,                            /* xCommit */489    0,                            /* xRollback */490    0,                            /* xFindMethod */491    0,                            /* xRename */492    0,                            /* xSavepoint */493    0,                            /* xRelease */494    dbpageRollbackTo,             /* xRollbackTo */495    0,                            /* xShadowName */496    0                             /* xIntegrity */497  };498  return sqlite3_create_module(db, "sqlite_dbpage", &dbpage_module, 0);499}500#elif defined(SQLITE_ENABLE_DBPAGE_VTAB)501int sqlite3DbpageRegister(sqlite3 *db){ return SQLITE_OK; }502#endif /* SQLITE_ENABLE_DBSTAT_VTAB */503