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 | 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 | |
51 | struct 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 | */ |
66 | static 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 | */ |
97 | static 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 | */ |
110 | static 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 | */ |
166 | static 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 | */ |
185 | static 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 | */ |
195 | static int dbpageNext(sqlite3_vtab_cursor *pCursor){ |
196 | int rc = SQLITE_OK; |
197 | DbpageCursor *pCsr = (DbpageCursor *)pCursor; |
198 | pCsr->pgno++; |
199 | return rc; |
200 | } |
201 | |
202 | static 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 | */ |
217 | static 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 | |
263 | static 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 | |
300 | static int dbpageRowid(sqlite3_vtab_cursor *pCursor, sqlite_int64 *pRowid){ |
301 | DbpageCursor *pCsr = (DbpageCursor *)pCursor; |
302 | *pRowid = pCsr->pgno; |
303 | return SQLITE_OK; |
304 | } |
305 | |
306 | static 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 | |
368 | update_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 | */ |
378 | static 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 | */ |
394 | int 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) |
424 | int sqlite3DbpageRegister(sqlite3 *db){ return SQLITE_OK; } |
425 | #endif /* SQLITE_ENABLE_DBSTAT_VTAB */ |
426 | |