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
38typedef struct DbpageTable DbpageTable;
39typedef struct DbpageCursor DbpageCursor;
40
41struct 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 Pager *pPager; /* Pager being read/written */
46 DbPage *pPage1; /* Page 1 of the database */
47 int iDb; /* Index of database to analyze */
48 int szPage; /* Size of each page in bytes */
49};
50
51struct DbpageTable {
52 sqlite3_vtab base; /* Base class. Must be first */
53 sqlite3 *db; /* The database */
54};
55
56/* Columns */
57#define DBPAGE_COLUMN_PGNO 0
58#define DBPAGE_COLUMN_DATA 1
59#define DBPAGE_COLUMN_SCHEMA 2
60
61
62
63/*
64** Connect to or create a dbpagevfs virtual table.
65*/
66static int dbpageConnect(
67 sqlite3 *db,
68 void *pAux,
69 int argc, const char *const*argv,
70 sqlite3_vtab **ppVtab,
71 char **pzErr
72){
73 DbpageTable *pTab = 0;
74 int rc = SQLITE_OK;
75
76 sqlite3_vtab_config(db, SQLITE_VTAB_DIRECTONLY);
77 rc = sqlite3_declare_vtab(db,
78 "CREATE TABLE x(pgno INTEGER PRIMARY KEY, data BLOB, schema HIDDEN)");
79 if( rc==SQLITE_OK ){
80 pTab = (DbpageTable *)sqlite3_malloc64(sizeof(DbpageTable));
81 if( pTab==0 ) rc = SQLITE_NOMEM_BKPT;
82 }
83
84 assert( rc==SQLITE_OK || pTab==0 );
85 if( rc==SQLITE_OK ){
86 memset(pTab, 0, sizeof(DbpageTable));
87 pTab->db = db;
88 }
89
90 *ppVtab = (sqlite3_vtab*)pTab;
91 return rc;
92}
93
94/*
95** Disconnect from or destroy a dbpagevfs virtual table.
96*/
97static int dbpageDisconnect(sqlite3_vtab *pVtab){
98 sqlite3_free(pVtab);
99 return SQLITE_OK;
100}
101
102/*
103** idxNum:
104**
105** 0 schema=main, full table scan
106** 1 schema=main, pgno=?1
107** 2 schema=?1, full table scan
108** 3 schema=?1, pgno=?2
109*/
110static int dbpageBestIndex(sqlite3_vtab *tab, sqlite3_index_info *pIdxInfo){
111 int i;
112 int iPlan = 0;
113
114 /* If there is a schema= constraint, it must be honored. Report a
115 ** ridiculously large estimated cost if the schema= constraint is
116 ** unavailable
117 */
118 for(i=0; i<pIdxInfo->nConstraint; i++){
119 struct sqlite3_index_constraint *p = &pIdxInfo->aConstraint[i];
120 if( p->iColumn!=DBPAGE_COLUMN_SCHEMA ) continue;
121 if( p->op!=SQLITE_INDEX_CONSTRAINT_EQ ) continue;
122 if( !p->usable ){
123 /* No solution. */
124 return SQLITE_CONSTRAINT;
125 }
126 iPlan = 2;
127 pIdxInfo->aConstraintUsage[i].argvIndex = 1;
128 pIdxInfo->aConstraintUsage[i].omit = 1;
129 break;
130 }
131
132 /* If we reach this point, it means that either there is no schema=
133 ** constraint (in which case we use the "main" schema) or else the
134 ** schema constraint was accepted. Lower the estimated cost accordingly
135 */
136 pIdxInfo->estimatedCost = 1.0e6;
137
138 /* Check for constraints against pgno */
139 for(i=0; i<pIdxInfo->nConstraint; i++){
140 struct sqlite3_index_constraint *p = &pIdxInfo->aConstraint[i];
141 if( p->usable && p->iColumn<=0 && p->op==SQLITE_INDEX_CONSTRAINT_EQ ){
142 pIdxInfo->estimatedRows = 1;
143 pIdxInfo->idxFlags = SQLITE_INDEX_SCAN_UNIQUE;
144 pIdxInfo->estimatedCost = 1.0;
145 pIdxInfo->aConstraintUsage[i].argvIndex = iPlan ? 2 : 1;
146 pIdxInfo->aConstraintUsage[i].omit = 1;
147 iPlan |= 1;
148 break;
149 }
150 }
151 pIdxInfo->idxNum = iPlan;
152
153 if( pIdxInfo->nOrderBy>=1
154 && pIdxInfo->aOrderBy[0].iColumn<=0
155 && pIdxInfo->aOrderBy[0].desc==0
156 ){
157 pIdxInfo->orderByConsumed = 1;
158 }
159 sqlite3VtabUsesAllSchemas(pIdxInfo);
160 return SQLITE_OK;
161}
162
163/*
164** Open a new dbpagevfs cursor.
165*/
166static int dbpageOpen(sqlite3_vtab *pVTab, sqlite3_vtab_cursor **ppCursor){
167 DbpageCursor *pCsr;
168
169 pCsr = (DbpageCursor *)sqlite3_malloc64(sizeof(DbpageCursor));
170 if( pCsr==0 ){
171 return SQLITE_NOMEM_BKPT;
172 }else{
173 memset(pCsr, 0, sizeof(DbpageCursor));
174 pCsr->base.pVtab = pVTab;
175 pCsr->pgno = -1;
176 }
177
178 *ppCursor = (sqlite3_vtab_cursor *)pCsr;
179 return SQLITE_OK;
180}
181
182/*
183** Close a dbpagevfs cursor.
184*/
185static int dbpageClose(sqlite3_vtab_cursor *pCursor){
186 DbpageCursor *pCsr = (DbpageCursor *)pCursor;
187 if( pCsr->pPage1 ) sqlite3PagerUnrefPageOne(pCsr->pPage1);
188 sqlite3_free(pCsr);
189 return SQLITE_OK;
190}
191
192/*
193** Move a dbpagevfs cursor to the next entry in the file.
194*/
195static int dbpageNext(sqlite3_vtab_cursor *pCursor){
196 int rc = SQLITE_OK;
197 DbpageCursor *pCsr = (DbpageCursor *)pCursor;
198 pCsr->pgno++;
199 return rc;
200}
201
202static int dbpageEof(sqlite3_vtab_cursor *pCursor){
203 DbpageCursor *pCsr = (DbpageCursor *)pCursor;
204 return pCsr->pgno > pCsr->mxPgno;
205}
206
207/*
208** idxNum:
209**
210** 0 schema=main, full table scan
211** 1 schema=main, pgno=?1
212** 2 schema=?1, full table scan
213** 3 schema=?1, pgno=?2
214**
215** idxStr is not used
216*/
217static int dbpageFilter(
218 sqlite3_vtab_cursor *pCursor,
219 int idxNum, const char *idxStr,
220 int argc, sqlite3_value **argv
221){
222 DbpageCursor *pCsr = (DbpageCursor *)pCursor;
223 DbpageTable *pTab = (DbpageTable *)pCursor->pVtab;
224 int rc;
225 sqlite3 *db = pTab->db;
226 Btree *pBt;
227
228 /* Default setting is no rows of result */
229 pCsr->pgno = 1;
230 pCsr->mxPgno = 0;
231
232 if( idxNum & 2 ){
233 const char *zSchema;
234 assert( argc>=1 );
235 zSchema = (const char*)sqlite3_value_text(argv[0]);
236 pCsr->iDb = sqlite3FindDbName(db, zSchema);
237 if( pCsr->iDb<0 ) return SQLITE_OK;
238 }else{
239 pCsr->iDb = 0;
240 }
241 pBt = db->aDb[pCsr->iDb].pBt;
242 if( pBt==0 ) return SQLITE_OK;
243 pCsr->pPager = sqlite3BtreePager(pBt);
244 pCsr->szPage = sqlite3BtreeGetPageSize(pBt);
245 pCsr->mxPgno = sqlite3BtreeLastPage(pBt);
246 if( idxNum & 1 ){
247 assert( argc>(idxNum>>1) );
248 pCsr->pgno = sqlite3_value_int(argv[idxNum>>1]);
249 if( pCsr->pgno<1 || pCsr->pgno>pCsr->mxPgno ){
250 pCsr->pgno = 1;
251 pCsr->mxPgno = 0;
252 }else{
253 pCsr->mxPgno = pCsr->pgno;
254 }
255 }else{
256 assert( pCsr->pgno==1 );
257 }
258 if( pCsr->pPage1 ) sqlite3PagerUnrefPageOne(pCsr->pPage1);
259 rc = sqlite3PagerGet(pCsr->pPager, 1, &pCsr->pPage1, 0);
260 return rc;
261}
262
263static int dbpageColumn(
264 sqlite3_vtab_cursor *pCursor,
265 sqlite3_context *ctx,
266 int i
267){
268 DbpageCursor *pCsr = (DbpageCursor *)pCursor;
269 int rc = SQLITE_OK;
270 switch( i ){
271 case 0: { /* pgno */
272 sqlite3_result_int(ctx, pCsr->pgno);
273 break;
274 }
275 case 1: { /* data */
276 DbPage *pDbPage = 0;
277 if( pCsr->pgno==((PENDING_BYTE/pCsr->szPage)+1) ){
278 /* The pending byte page. Assume it is zeroed out. Attempting to
279 ** request this page from the page is an SQLITE_CORRUPT error. */
280 sqlite3_result_zeroblob(ctx, pCsr->szPage);
281 }else{
282 rc = sqlite3PagerGet(pCsr->pPager, pCsr->pgno, (DbPage**)&pDbPage, 0);
283 if( rc==SQLITE_OK ){
284 sqlite3_result_blob(ctx, sqlite3PagerGetData(pDbPage), pCsr->szPage,
285 SQLITE_TRANSIENT);
286 }
287 sqlite3PagerUnref(pDbPage);
288 }
289 break;
290 }
291 default: { /* schema */
292 sqlite3 *db = sqlite3_context_db_handle(ctx);
293 sqlite3_result_text(ctx, db->aDb[pCsr->iDb].zDbSName, -1, SQLITE_STATIC);
294 break;
295 }
296 }
297 return rc;
298}
299
300static int dbpageRowid(sqlite3_vtab_cursor *pCursor, sqlite_int64 *pRowid){
301 DbpageCursor *pCsr = (DbpageCursor *)pCursor;
302 *pRowid = pCsr->pgno;
303 return SQLITE_OK;
304}
305
306static int dbpageUpdate(
307 sqlite3_vtab *pVtab,
308 int argc,
309 sqlite3_value **argv,
310 sqlite_int64 *pRowid
311){
312 DbpageTable *pTab = (DbpageTable *)pVtab;
313 Pgno pgno;
314 DbPage *pDbPage = 0;
315 int rc = SQLITE_OK;
316 char *zErr = 0;
317 const char *zSchema;
318 int iDb;
319 Btree *pBt;
320 Pager *pPager;
321 int szPage;
322
323 if( pTab->db->flags & SQLITE_Defensive ){
324 zErr = "read-only";
325 goto update_fail;
326 }
327 if( argc==1 ){
328 zErr = "cannot delete";
329 goto update_fail;
330 }
331 pgno = sqlite3_value_int(argv[0]);
332 if( (Pgno)sqlite3_value_int(argv[1])!=pgno ){
333 zErr = "cannot insert";
334 goto update_fail;
335 }
336 zSchema = (const char*)sqlite3_value_text(argv[4]);
337 iDb = zSchema ? sqlite3FindDbName(pTab->db, zSchema) : -1;
338 if( iDb<0 ){
339 zErr = "no such schema";
340 goto update_fail;
341 }
342 pBt = pTab->db->aDb[iDb].pBt;
343 if( pgno<1 || pBt==0 || pgno>sqlite3BtreeLastPage(pBt) ){
344 zErr = "bad page number";
345 goto update_fail;
346 }
347 szPage = sqlite3BtreeGetPageSize(pBt);
348 if( sqlite3_value_type(argv[3])!=SQLITE_BLOB
349 || sqlite3_value_bytes(argv[3])!=szPage
350 ){
351 zErr = "bad page value";
352 goto update_fail;
353 }
354 pPager = sqlite3BtreePager(pBt);
355 rc = sqlite3PagerGet(pPager, pgno, (DbPage**)&pDbPage, 0);
356 if( rc==SQLITE_OK ){
357 const void *pData = sqlite3_value_blob(argv[3]);
358 assert( pData!=0 || pTab->db->mallocFailed );
359 if( pData
360 && (rc = sqlite3PagerWrite(pDbPage))==SQLITE_OK
361 ){
362 memcpy(sqlite3PagerGetData(pDbPage), pData, szPage);
363 }
364 }
365 sqlite3PagerUnref(pDbPage);
366 return rc;
367
368update_fail:
369 sqlite3_free(pVtab->zErrMsg);
370 pVtab->zErrMsg = sqlite3_mprintf("%s", zErr);
371 return SQLITE_ERROR;
372}
373
374/* Since we do not know in advance which database files will be
375** written by the sqlite_dbpage virtual table, start a write transaction
376** on them all.
377*/
378static int dbpageBegin(sqlite3_vtab *pVtab){
379 DbpageTable *pTab = (DbpageTable *)pVtab;
380 sqlite3 *db = pTab->db;
381 int i;
382 int rc = SQLITE_OK;
383 for(i=0; rc==SQLITE_OK && i<db->nDb; i++){
384 Btree *pBt = db->aDb[i].pBt;
385 if( pBt ) rc = sqlite3BtreeBeginTrans(pBt, 1, 0);
386 }
387 return rc;
388}
389
390
391/*
392** Invoke this routine to register the "dbpage" virtual table module
393*/
394int sqlite3DbpageRegister(sqlite3 *db){
395 static sqlite3_module dbpage_module = {
396 0, /* iVersion */
397 dbpageConnect, /* xCreate */
398 dbpageConnect, /* xConnect */
399 dbpageBestIndex, /* xBestIndex */
400 dbpageDisconnect, /* xDisconnect */
401 dbpageDisconnect, /* xDestroy */
402 dbpageOpen, /* xOpen - open a cursor */
403 dbpageClose, /* xClose - close a cursor */
404 dbpageFilter, /* xFilter - configure scan constraints */
405 dbpageNext, /* xNext - advance a cursor */
406 dbpageEof, /* xEof - check for end of scan */
407 dbpageColumn, /* xColumn - read data */
408 dbpageRowid, /* xRowid - read data */
409 dbpageUpdate, /* xUpdate */
410 dbpageBegin, /* xBegin */
411 0, /* xSync */
412 0, /* xCommit */
413 0, /* xRollback */
414 0, /* xFindMethod */
415 0, /* xRename */
416 0, /* xSavepoint */
417 0, /* xRelease */
418 0, /* xRollbackTo */
419 0 /* xShadowName */
420 };
421 return sqlite3_create_module(db, "sqlite_dbpage", &dbpage_module, 0);
422}
423#elif defined(SQLITE_ENABLE_DBPAGE_VTAB)
424int sqlite3DbpageRegister(sqlite3 *db){ return SQLITE_OK; }
425#endif /* SQLITE_ENABLE_DBSTAT_VTAB */
426