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 */ 330