blob: 27c962d14414d69420e45cf28c15f10439c6511b [file] [log] [blame]
drha43c8c82017-10-11 13:48:11 +00001/*
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.
drh34d0b1a2017-10-11 15:02:53 +000019**
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.
drha43c8c82017-10-11 13:48:11 +000032*/
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 */
drh34d0b1a2017-10-11 15:02:53 +000044 int mxPgno; /* Last page to visit on this scan */
drh3cd8aaa2017-10-25 19:18:33 +000045 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 */
drha43c8c82017-10-11 13:48:11 +000049};
50
51struct DbpageTable {
52 sqlite3_vtab base; /* Base class. Must be first */
53 sqlite3 *db; /* The database */
drha43c8c82017-10-11 13:48:11 +000054};
55
drh3cd8aaa2017-10-25 19:18:33 +000056/* Columns */
57#define DBPAGE_COLUMN_PGNO 0
58#define DBPAGE_COLUMN_DATA 1
59#define DBPAGE_COLUMN_SCHEMA 2
60
61
62
drha43c8c82017-10-11 13:48:11 +000063/*
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;
drha43c8c82017-10-11 13:48:11 +000075
drha43c8c82017-10-11 13:48:11 +000076 rc = sqlite3_declare_vtab(db,
drh34d0b1a2017-10-11 15:02:53 +000077 "CREATE TABLE x(pgno INTEGER PRIMARY KEY, data BLOB, schema HIDDEN)");
drha43c8c82017-10-11 13:48:11 +000078 if( rc==SQLITE_OK ){
79 pTab = (DbpageTable *)sqlite3_malloc64(sizeof(DbpageTable));
80 if( pTab==0 ) rc = SQLITE_NOMEM_BKPT;
81 }
82
83 assert( rc==SQLITE_OK || pTab==0 );
84 if( rc==SQLITE_OK ){
drha43c8c82017-10-11 13:48:11 +000085 memset(pTab, 0, sizeof(DbpageTable));
86 pTab->db = db;
drha43c8c82017-10-11 13:48:11 +000087 }
88
89 *ppVtab = (sqlite3_vtab*)pTab;
90 return rc;
91}
92
93/*
94** Disconnect from or destroy a dbpagevfs virtual table.
95*/
96static int dbpageDisconnect(sqlite3_vtab *pVtab){
97 sqlite3_free(pVtab);
98 return SQLITE_OK;
99}
100
101/*
102** idxNum:
103**
drh3cd8aaa2017-10-25 19:18:33 +0000104** 0 schema=main, full table scan
105** 1 schema=main, pgno=?1
106** 2 schema=?1, full table scan
107** 3 schema=?1, pgno=?2
drha43c8c82017-10-11 13:48:11 +0000108*/
109static int dbpageBestIndex(sqlite3_vtab *tab, sqlite3_index_info *pIdxInfo){
drh34d0b1a2017-10-11 15:02:53 +0000110 int i;
drh3cd8aaa2017-10-25 19:18:33 +0000111 int iPlan = 0;
112
113 /* If there is a schema= constraint, it must be honored. Report a
114 ** ridiculously large estimated cost if the schema= constraint is
115 ** unavailable
116 */
117 for(i=0; i<pIdxInfo->nConstraint; i++){
118 struct sqlite3_index_constraint *p = &pIdxInfo->aConstraint[i];
119 if( p->iColumn!=DBPAGE_COLUMN_SCHEMA ) continue;
120 if( p->op!=SQLITE_INDEX_CONSTRAINT_EQ ) continue;
121 if( !p->usable ){
drh20b3fc42018-11-16 20:18:07 +0000122 /* No solution. */
123 return SQLITE_CONSTRAINT;
drh3cd8aaa2017-10-25 19:18:33 +0000124 }
125 iPlan = 2;
126 pIdxInfo->aConstraintUsage[i].argvIndex = 1;
127 pIdxInfo->aConstraintUsage[i].omit = 1;
128 break;
129 }
130
131 /* If we reach this point, it means that either there is no schema=
132 ** constraint (in which case we use the "main" schema) or else the
133 ** schema constraint was accepted. Lower the estimated cost accordingly
134 */
135 pIdxInfo->estimatedCost = 1.0e6;
136
137 /* Check for constraints against pgno */
drh34d0b1a2017-10-11 15:02:53 +0000138 for(i=0; i<pIdxInfo->nConstraint; i++){
139 struct sqlite3_index_constraint *p = &pIdxInfo->aConstraint[i];
140 if( p->usable && p->iColumn<=0 && p->op==SQLITE_INDEX_CONSTRAINT_EQ ){
141 pIdxInfo->estimatedRows = 1;
142 pIdxInfo->idxFlags = SQLITE_INDEX_SCAN_UNIQUE;
143 pIdxInfo->estimatedCost = 1.0;
drh3cd8aaa2017-10-25 19:18:33 +0000144 pIdxInfo->aConstraintUsage[i].argvIndex = iPlan ? 2 : 1;
drh34d0b1a2017-10-11 15:02:53 +0000145 pIdxInfo->aConstraintUsage[i].omit = 1;
drh3cd8aaa2017-10-25 19:18:33 +0000146 iPlan |= 1;
drh34d0b1a2017-10-11 15:02:53 +0000147 break;
148 }
149 }
drh3cd8aaa2017-10-25 19:18:33 +0000150 pIdxInfo->idxNum = iPlan;
151
drh34d0b1a2017-10-11 15:02:53 +0000152 if( pIdxInfo->nOrderBy>=1
153 && pIdxInfo->aOrderBy[0].iColumn<=0
154 && pIdxInfo->aOrderBy[0].desc==0
155 ){
156 pIdxInfo->orderByConsumed = 1;
157 }
drha43c8c82017-10-11 13:48:11 +0000158 return SQLITE_OK;
159}
160
161/*
162** Open a new dbpagevfs cursor.
163*/
164static int dbpageOpen(sqlite3_vtab *pVTab, sqlite3_vtab_cursor **ppCursor){
165 DbpageCursor *pCsr;
166
167 pCsr = (DbpageCursor *)sqlite3_malloc64(sizeof(DbpageCursor));
168 if( pCsr==0 ){
169 return SQLITE_NOMEM_BKPT;
170 }else{
171 memset(pCsr, 0, sizeof(DbpageCursor));
172 pCsr->base.pVtab = pVTab;
173 pCsr->pgno = -1;
174 }
175
176 *ppCursor = (sqlite3_vtab_cursor *)pCsr;
177 return SQLITE_OK;
178}
179
180/*
181** Close a dbpagevfs cursor.
182*/
183static int dbpageClose(sqlite3_vtab_cursor *pCursor){
184 DbpageCursor *pCsr = (DbpageCursor *)pCursor;
drh3cd8aaa2017-10-25 19:18:33 +0000185 if( pCsr->pPage1 ) sqlite3PagerUnrefPageOne(pCsr->pPage1);
drha43c8c82017-10-11 13:48:11 +0000186 sqlite3_free(pCsr);
187 return SQLITE_OK;
188}
189
190/*
191** Move a dbpagevfs cursor to the next entry in the file.
192*/
193static int dbpageNext(sqlite3_vtab_cursor *pCursor){
194 int rc = SQLITE_OK;
195 DbpageCursor *pCsr = (DbpageCursor *)pCursor;
196 pCsr->pgno++;
197 return rc;
198}
199
200static int dbpageEof(sqlite3_vtab_cursor *pCursor){
201 DbpageCursor *pCsr = (DbpageCursor *)pCursor;
drh34d0b1a2017-10-11 15:02:53 +0000202 return pCsr->pgno > pCsr->mxPgno;
drha43c8c82017-10-11 13:48:11 +0000203}
204
drh3cd8aaa2017-10-25 19:18:33 +0000205/*
206** idxNum:
207**
208** 0 schema=main, full table scan
209** 1 schema=main, pgno=?1
210** 2 schema=?1, full table scan
211** 3 schema=?1, pgno=?2
212**
213** idxStr is not used
214*/
drha43c8c82017-10-11 13:48:11 +0000215static int dbpageFilter(
216 sqlite3_vtab_cursor *pCursor,
217 int idxNum, const char *idxStr,
218 int argc, sqlite3_value **argv
219){
220 DbpageCursor *pCsr = (DbpageCursor *)pCursor;
221 DbpageTable *pTab = (DbpageTable *)pCursor->pVtab;
drh0503f2a2017-10-27 18:24:11 +0000222 int rc;
drh3cd8aaa2017-10-25 19:18:33 +0000223 sqlite3 *db = pTab->db;
224 Btree *pBt;
drha43c8c82017-10-11 13:48:11 +0000225
drh3cd8aaa2017-10-25 19:18:33 +0000226 /* Default setting is no rows of result */
227 pCsr->pgno = 1;
228 pCsr->mxPgno = 0;
229
230 if( idxNum & 2 ){
231 const char *zSchema;
232 assert( argc>=1 );
233 zSchema = (const char*)sqlite3_value_text(argv[0]);
234 pCsr->iDb = sqlite3FindDbName(db, zSchema);
235 if( pCsr->iDb<0 ) return SQLITE_OK;
236 }else{
237 pCsr->iDb = 0;
238 }
239 pBt = db->aDb[pCsr->iDb].pBt;
240 if( pBt==0 ) return SQLITE_OK;
241 pCsr->pPager = sqlite3BtreePager(pBt);
242 pCsr->szPage = sqlite3BtreeGetPageSize(pBt);
243 pCsr->mxPgno = sqlite3BtreeLastPage(pBt);
244 if( idxNum & 1 ){
245 assert( argc>(idxNum>>1) );
246 pCsr->pgno = sqlite3_value_int(argv[idxNum>>1]);
247 if( pCsr->pgno<1 || pCsr->pgno>pCsr->mxPgno ){
drh34d0b1a2017-10-11 15:02:53 +0000248 pCsr->pgno = 1;
249 pCsr->mxPgno = 0;
250 }else{
251 pCsr->mxPgno = pCsr->pgno;
252 }
253 }else{
drh3cd8aaa2017-10-25 19:18:33 +0000254 assert( pCsr->pgno==1 );
drh34d0b1a2017-10-11 15:02:53 +0000255 }
drh0503f2a2017-10-27 18:24:11 +0000256 if( pCsr->pPage1 ) sqlite3PagerUnrefPageOne(pCsr->pPage1);
drh3cd8aaa2017-10-25 19:18:33 +0000257 rc = sqlite3PagerGet(pCsr->pPager, 1, &pCsr->pPage1, 0);
drha43c8c82017-10-11 13:48:11 +0000258 return rc;
259}
260
261static int dbpageColumn(
262 sqlite3_vtab_cursor *pCursor,
263 sqlite3_context *ctx,
264 int i
265){
266 DbpageCursor *pCsr = (DbpageCursor *)pCursor;
drha43c8c82017-10-11 13:48:11 +0000267 int rc = SQLITE_OK;
268 switch( i ){
269 case 0: { /* pgno */
270 sqlite3_result_int(ctx, pCsr->pgno);
271 break;
272 }
273 case 1: { /* data */
274 DbPage *pDbPage = 0;
drh3cd8aaa2017-10-25 19:18:33 +0000275 rc = sqlite3PagerGet(pCsr->pPager, pCsr->pgno, (DbPage**)&pDbPage, 0);
drha43c8c82017-10-11 13:48:11 +0000276 if( rc==SQLITE_OK ){
drh3cd8aaa2017-10-25 19:18:33 +0000277 sqlite3_result_blob(ctx, sqlite3PagerGetData(pDbPage), pCsr->szPage,
drha43c8c82017-10-11 13:48:11 +0000278 SQLITE_TRANSIENT);
279 }
280 sqlite3PagerUnref(pDbPage);
281 break;
282 }
283 default: { /* schema */
284 sqlite3 *db = sqlite3_context_db_handle(ctx);
drh3cd8aaa2017-10-25 19:18:33 +0000285 sqlite3_result_text(ctx, db->aDb[pCsr->iDb].zDbSName, -1, SQLITE_STATIC);
drha43c8c82017-10-11 13:48:11 +0000286 break;
287 }
288 }
289 return SQLITE_OK;
290}
291
292static int dbpageRowid(sqlite3_vtab_cursor *pCursor, sqlite_int64 *pRowid){
293 DbpageCursor *pCsr = (DbpageCursor *)pCursor;
294 *pRowid = pCsr->pgno;
295 return SQLITE_OK;
296}
297
drh34d0b1a2017-10-11 15:02:53 +0000298static int dbpageUpdate(
299 sqlite3_vtab *pVtab,
300 int argc,
301 sqlite3_value **argv,
302 sqlite_int64 *pRowid
303){
304 DbpageTable *pTab = (DbpageTable *)pVtab;
mistachkinaca84e62017-11-10 12:41:21 +0000305 Pgno pgno;
drh34d0b1a2017-10-11 15:02:53 +0000306 DbPage *pDbPage = 0;
307 int rc = SQLITE_OK;
308 char *zErr = 0;
drh3cd8aaa2017-10-25 19:18:33 +0000309 const char *zSchema;
310 int iDb;
311 Btree *pBt;
312 Pager *pPager;
313 int szPage;
drh34d0b1a2017-10-11 15:02:53 +0000314
drha296cda2018-11-03 16:09:59 +0000315 if( pTab->db->flags & SQLITE_Defensive ){
316 zErr = "read-only";
317 goto update_fail;
318 }
drh34d0b1a2017-10-11 15:02:53 +0000319 if( argc==1 ){
320 zErr = "cannot delete";
321 goto update_fail;
322 }
323 pgno = sqlite3_value_int(argv[0]);
mistachkinaca84e62017-11-10 12:41:21 +0000324 if( (Pgno)sqlite3_value_int(argv[1])!=pgno ){
drh34d0b1a2017-10-11 15:02:53 +0000325 zErr = "cannot insert";
326 goto update_fail;
327 }
drh3cd8aaa2017-10-25 19:18:33 +0000328 zSchema = (const char*)sqlite3_value_text(argv[4]);
329 iDb = zSchema ? sqlite3FindDbName(pTab->db, zSchema) : -1;
330 if( iDb<0 ){
331 zErr = "no such schema";
332 goto update_fail;
333 }
334 pBt = pTab->db->aDb[iDb].pBt;
drha48a2902017-11-01 19:58:25 +0000335 if( pgno<1 || pBt==0 || pgno>(int)sqlite3BtreeLastPage(pBt) ){
drh3cd8aaa2017-10-25 19:18:33 +0000336 zErr = "bad page number";
337 goto update_fail;
338 }
339 szPage = sqlite3BtreeGetPageSize(pBt);
drh34d0b1a2017-10-11 15:02:53 +0000340 if( sqlite3_value_type(argv[3])!=SQLITE_BLOB
drh3cd8aaa2017-10-25 19:18:33 +0000341 || sqlite3_value_bytes(argv[3])!=szPage
drh34d0b1a2017-10-11 15:02:53 +0000342 ){
343 zErr = "bad page value";
344 goto update_fail;
345 }
drh3cd8aaa2017-10-25 19:18:33 +0000346 pPager = sqlite3BtreePager(pBt);
347 rc = sqlite3PagerGet(pPager, pgno, (DbPage**)&pDbPage, 0);
drh34d0b1a2017-10-11 15:02:53 +0000348 if( rc==SQLITE_OK ){
349 rc = sqlite3PagerWrite(pDbPage);
350 if( rc==SQLITE_OK ){
351 memcpy(sqlite3PagerGetData(pDbPage),
352 sqlite3_value_blob(argv[3]),
drh3cd8aaa2017-10-25 19:18:33 +0000353 szPage);
drh34d0b1a2017-10-11 15:02:53 +0000354 }
355 }
356 sqlite3PagerUnref(pDbPage);
357 return rc;
358
359update_fail:
360 sqlite3_free(pVtab->zErrMsg);
361 pVtab->zErrMsg = sqlite3_mprintf("%s", zErr);
362 return SQLITE_ERROR;
363}
364
drh3cd8aaa2017-10-25 19:18:33 +0000365/* Since we do not know in advance which database files will be
366** written by the sqlite_dbpage virtual table, start a write transaction
367** on them all.
368*/
369static int dbpageBegin(sqlite3_vtab *pVtab){
370 DbpageTable *pTab = (DbpageTable *)pVtab;
371 sqlite3 *db = pTab->db;
372 int i;
373 for(i=0; i<db->nDb; i++){
374 Btree *pBt = db->aDb[i].pBt;
drhbb2d9b12018-06-06 16:28:40 +0000375 if( pBt ) sqlite3BtreeBeginTrans(pBt, 1, 0);
drh3cd8aaa2017-10-25 19:18:33 +0000376 }
377 return SQLITE_OK;
378}
379
380
drha43c8c82017-10-11 13:48:11 +0000381/*
382** Invoke this routine to register the "dbpage" virtual table module
383*/
384int sqlite3DbpageRegister(sqlite3 *db){
385 static sqlite3_module dbpage_module = {
386 0, /* iVersion */
387 dbpageConnect, /* xCreate */
388 dbpageConnect, /* xConnect */
389 dbpageBestIndex, /* xBestIndex */
390 dbpageDisconnect, /* xDisconnect */
391 dbpageDisconnect, /* xDestroy */
392 dbpageOpen, /* xOpen - open a cursor */
393 dbpageClose, /* xClose - close a cursor */
394 dbpageFilter, /* xFilter - configure scan constraints */
395 dbpageNext, /* xNext - advance a cursor */
396 dbpageEof, /* xEof - check for end of scan */
397 dbpageColumn, /* xColumn - read data */
398 dbpageRowid, /* xRowid - read data */
drh34d0b1a2017-10-11 15:02:53 +0000399 dbpageUpdate, /* xUpdate */
drh3cd8aaa2017-10-25 19:18:33 +0000400 dbpageBegin, /* xBegin */
drha43c8c82017-10-11 13:48:11 +0000401 0, /* xSync */
402 0, /* xCommit */
403 0, /* xRollback */
404 0, /* xFindMethod */
405 0, /* xRename */
406 0, /* xSavepoint */
407 0, /* xRelease */
408 0, /* xRollbackTo */
drh84c501b2018-11-05 23:01:45 +0000409 0 /* xShadowName */
drha43c8c82017-10-11 13:48:11 +0000410 };
411 return sqlite3_create_module(db, "sqlite_dbpage", &dbpage_module, 0);
412}
413#elif defined(SQLITE_ENABLE_DBPAGE_VTAB)
414int sqlite3DbpageRegister(sqlite3 *db){ return SQLITE_OK; }
415#endif /* SQLITE_ENABLE_DBSTAT_VTAB */