AryaWu/sqlite
0
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 