blob: 9378dd4fc40b4d0faee584493c2ebc992076ea93 [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;
drh3547e492022-12-23 14:49:24 +000075 (void)pAux;
76 (void)argc;
77 (void)argv;
78 (void)pzErr;
drha43c8c82017-10-11 13:48:11 +000079
drh2b1c2aa2020-01-07 19:45:40 +000080 sqlite3_vtab_config(db, SQLITE_VTAB_DIRECTONLY);
drha43c8c82017-10-11 13:48:11 +000081 rc = sqlite3_declare_vtab(db,
drh34d0b1a2017-10-11 15:02:53 +000082 "CREATE TABLE x(pgno INTEGER PRIMARY KEY, data BLOB, schema HIDDEN)");
drha43c8c82017-10-11 13:48:11 +000083 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 ){
drha43c8c82017-10-11 13:48:11 +000090 memset(pTab, 0, sizeof(DbpageTable));
91 pTab->db = db;
drha43c8c82017-10-11 13:48:11 +000092 }
93
94 *ppVtab = (sqlite3_vtab*)pTab;
95 return rc;
96}
97
98/*
99** Disconnect from or destroy a dbpagevfs virtual table.
100*/
101static int dbpageDisconnect(sqlite3_vtab *pVtab){
102 sqlite3_free(pVtab);
103 return SQLITE_OK;
104}
105
106/*
107** idxNum:
108**
drh3cd8aaa2017-10-25 19:18:33 +0000109** 0 schema=main, full table scan
110** 1 schema=main, pgno=?1
111** 2 schema=?1, full table scan
112** 3 schema=?1, pgno=?2
drha43c8c82017-10-11 13:48:11 +0000113*/
114static int dbpageBestIndex(sqlite3_vtab *tab, sqlite3_index_info *pIdxInfo){
drh34d0b1a2017-10-11 15:02:53 +0000115 int i;
drh3cd8aaa2017-10-25 19:18:33 +0000116 int iPlan = 0;
drh3547e492022-12-23 14:49:24 +0000117 (void)tab;
drh3cd8aaa2017-10-25 19:18:33 +0000118
119 /* If there is a schema= constraint, it must be honored. Report a
120 ** ridiculously large estimated cost if the schema= constraint is
121 ** unavailable
122 */
123 for(i=0; i<pIdxInfo->nConstraint; i++){
124 struct sqlite3_index_constraint *p = &pIdxInfo->aConstraint[i];
125 if( p->iColumn!=DBPAGE_COLUMN_SCHEMA ) continue;
126 if( p->op!=SQLITE_INDEX_CONSTRAINT_EQ ) continue;
127 if( !p->usable ){
drh20b3fc42018-11-16 20:18:07 +0000128 /* No solution. */
129 return SQLITE_CONSTRAINT;
drh3cd8aaa2017-10-25 19:18:33 +0000130 }
131 iPlan = 2;
132 pIdxInfo->aConstraintUsage[i].argvIndex = 1;
133 pIdxInfo->aConstraintUsage[i].omit = 1;
134 break;
135 }
136
137 /* If we reach this point, it means that either there is no schema=
138 ** constraint (in which case we use the "main" schema) or else the
139 ** schema constraint was accepted. Lower the estimated cost accordingly
140 */
141 pIdxInfo->estimatedCost = 1.0e6;
142
143 /* Check for constraints against pgno */
drh34d0b1a2017-10-11 15:02:53 +0000144 for(i=0; i<pIdxInfo->nConstraint; i++){
145 struct sqlite3_index_constraint *p = &pIdxInfo->aConstraint[i];
146 if( p->usable && p->iColumn<=0 && p->op==SQLITE_INDEX_CONSTRAINT_EQ ){
147 pIdxInfo->estimatedRows = 1;
148 pIdxInfo->idxFlags = SQLITE_INDEX_SCAN_UNIQUE;
149 pIdxInfo->estimatedCost = 1.0;
drh3cd8aaa2017-10-25 19:18:33 +0000150 pIdxInfo->aConstraintUsage[i].argvIndex = iPlan ? 2 : 1;
drh34d0b1a2017-10-11 15:02:53 +0000151 pIdxInfo->aConstraintUsage[i].omit = 1;
drh3cd8aaa2017-10-25 19:18:33 +0000152 iPlan |= 1;
drh34d0b1a2017-10-11 15:02:53 +0000153 break;
154 }
155 }
drh3cd8aaa2017-10-25 19:18:33 +0000156 pIdxInfo->idxNum = iPlan;
157
drh34d0b1a2017-10-11 15:02:53 +0000158 if( pIdxInfo->nOrderBy>=1
159 && pIdxInfo->aOrderBy[0].iColumn<=0
160 && pIdxInfo->aOrderBy[0].desc==0
161 ){
162 pIdxInfo->orderByConsumed = 1;
163 }
drh7d0ae002022-04-08 17:01:29 +0000164 sqlite3VtabUsesAllSchemas(pIdxInfo);
drha43c8c82017-10-11 13:48:11 +0000165 return SQLITE_OK;
166}
167
168/*
169** Open a new dbpagevfs cursor.
170*/
171static int dbpageOpen(sqlite3_vtab *pVTab, sqlite3_vtab_cursor **ppCursor){
172 DbpageCursor *pCsr;
173
174 pCsr = (DbpageCursor *)sqlite3_malloc64(sizeof(DbpageCursor));
175 if( pCsr==0 ){
176 return SQLITE_NOMEM_BKPT;
177 }else{
178 memset(pCsr, 0, sizeof(DbpageCursor));
179 pCsr->base.pVtab = pVTab;
180 pCsr->pgno = -1;
181 }
182
183 *ppCursor = (sqlite3_vtab_cursor *)pCsr;
184 return SQLITE_OK;
185}
186
187/*
188** Close a dbpagevfs cursor.
189*/
190static int dbpageClose(sqlite3_vtab_cursor *pCursor){
191 DbpageCursor *pCsr = (DbpageCursor *)pCursor;
drh3cd8aaa2017-10-25 19:18:33 +0000192 if( pCsr->pPage1 ) sqlite3PagerUnrefPageOne(pCsr->pPage1);
drha43c8c82017-10-11 13:48:11 +0000193 sqlite3_free(pCsr);
194 return SQLITE_OK;
195}
196
197/*
198** Move a dbpagevfs cursor to the next entry in the file.
199*/
200static int dbpageNext(sqlite3_vtab_cursor *pCursor){
201 int rc = SQLITE_OK;
202 DbpageCursor *pCsr = (DbpageCursor *)pCursor;
203 pCsr->pgno++;
204 return rc;
205}
206
207static int dbpageEof(sqlite3_vtab_cursor *pCursor){
208 DbpageCursor *pCsr = (DbpageCursor *)pCursor;
drh34d0b1a2017-10-11 15:02:53 +0000209 return pCsr->pgno > pCsr->mxPgno;
drha43c8c82017-10-11 13:48:11 +0000210}
211
drh3cd8aaa2017-10-25 19:18:33 +0000212/*
213** idxNum:
214**
215** 0 schema=main, full table scan
216** 1 schema=main, pgno=?1
217** 2 schema=?1, full table scan
218** 3 schema=?1, pgno=?2
219**
220** idxStr is not used
221*/
drha43c8c82017-10-11 13:48:11 +0000222static int dbpageFilter(
223 sqlite3_vtab_cursor *pCursor,
224 int idxNum, const char *idxStr,
225 int argc, sqlite3_value **argv
226){
227 DbpageCursor *pCsr = (DbpageCursor *)pCursor;
228 DbpageTable *pTab = (DbpageTable *)pCursor->pVtab;
drh0503f2a2017-10-27 18:24:11 +0000229 int rc;
drh3cd8aaa2017-10-25 19:18:33 +0000230 sqlite3 *db = pTab->db;
231 Btree *pBt;
drha43c8c82017-10-11 13:48:11 +0000232
drh3547e492022-12-23 14:49:24 +0000233 (void)idxStr;
234
drh3cd8aaa2017-10-25 19:18:33 +0000235 /* Default setting is no rows of result */
236 pCsr->pgno = 1;
237 pCsr->mxPgno = 0;
238
239 if( idxNum & 2 ){
240 const char *zSchema;
241 assert( argc>=1 );
242 zSchema = (const char*)sqlite3_value_text(argv[0]);
243 pCsr->iDb = sqlite3FindDbName(db, zSchema);
244 if( pCsr->iDb<0 ) return SQLITE_OK;
245 }else{
246 pCsr->iDb = 0;
247 }
248 pBt = db->aDb[pCsr->iDb].pBt;
249 if( pBt==0 ) return SQLITE_OK;
250 pCsr->pPager = sqlite3BtreePager(pBt);
251 pCsr->szPage = sqlite3BtreeGetPageSize(pBt);
252 pCsr->mxPgno = sqlite3BtreeLastPage(pBt);
253 if( idxNum & 1 ){
254 assert( argc>(idxNum>>1) );
255 pCsr->pgno = sqlite3_value_int(argv[idxNum>>1]);
256 if( pCsr->pgno<1 || pCsr->pgno>pCsr->mxPgno ){
drh34d0b1a2017-10-11 15:02:53 +0000257 pCsr->pgno = 1;
258 pCsr->mxPgno = 0;
259 }else{
260 pCsr->mxPgno = pCsr->pgno;
261 }
262 }else{
drh3cd8aaa2017-10-25 19:18:33 +0000263 assert( pCsr->pgno==1 );
drh34d0b1a2017-10-11 15:02:53 +0000264 }
drh0503f2a2017-10-27 18:24:11 +0000265 if( pCsr->pPage1 ) sqlite3PagerUnrefPageOne(pCsr->pPage1);
drh3cd8aaa2017-10-25 19:18:33 +0000266 rc = sqlite3PagerGet(pCsr->pPager, 1, &pCsr->pPage1, 0);
drha43c8c82017-10-11 13:48:11 +0000267 return rc;
268}
269
270static int dbpageColumn(
271 sqlite3_vtab_cursor *pCursor,
272 sqlite3_context *ctx,
273 int i
274){
275 DbpageCursor *pCsr = (DbpageCursor *)pCursor;
drha43c8c82017-10-11 13:48:11 +0000276 int rc = SQLITE_OK;
277 switch( i ){
278 case 0: { /* pgno */
279 sqlite3_result_int(ctx, pCsr->pgno);
280 break;
281 }
282 case 1: { /* data */
283 DbPage *pDbPage = 0;
dan7985e052022-09-13 18:08:24 +0000284 if( pCsr->pgno==((PENDING_BYTE/pCsr->szPage)+1) ){
285 /* The pending byte page. Assume it is zeroed out. Attempting to
286 ** request this page from the page is an SQLITE_CORRUPT error. */
287 sqlite3_result_zeroblob(ctx, pCsr->szPage);
288 }else{
289 rc = sqlite3PagerGet(pCsr->pPager, pCsr->pgno, (DbPage**)&pDbPage, 0);
290 if( rc==SQLITE_OK ){
291 sqlite3_result_blob(ctx, sqlite3PagerGetData(pDbPage), pCsr->szPage,
292 SQLITE_TRANSIENT);
293 }
294 sqlite3PagerUnref(pDbPage);
drha43c8c82017-10-11 13:48:11 +0000295 }
drha43c8c82017-10-11 13:48:11 +0000296 break;
297 }
298 default: { /* schema */
299 sqlite3 *db = sqlite3_context_db_handle(ctx);
drh3cd8aaa2017-10-25 19:18:33 +0000300 sqlite3_result_text(ctx, db->aDb[pCsr->iDb].zDbSName, -1, SQLITE_STATIC);
drha43c8c82017-10-11 13:48:11 +0000301 break;
302 }
303 }
dan5ca0b382022-09-12 20:02:33 +0000304 return rc;
drha43c8c82017-10-11 13:48:11 +0000305}
306
307static int dbpageRowid(sqlite3_vtab_cursor *pCursor, sqlite_int64 *pRowid){
308 DbpageCursor *pCsr = (DbpageCursor *)pCursor;
309 *pRowid = pCsr->pgno;
310 return SQLITE_OK;
311}
312
drh34d0b1a2017-10-11 15:02:53 +0000313static int dbpageUpdate(
314 sqlite3_vtab *pVtab,
315 int argc,
316 sqlite3_value **argv,
317 sqlite_int64 *pRowid
318){
319 DbpageTable *pTab = (DbpageTable *)pVtab;
mistachkinaca84e62017-11-10 12:41:21 +0000320 Pgno pgno;
drh34d0b1a2017-10-11 15:02:53 +0000321 DbPage *pDbPage = 0;
322 int rc = SQLITE_OK;
323 char *zErr = 0;
drh3cd8aaa2017-10-25 19:18:33 +0000324 const char *zSchema;
325 int iDb;
326 Btree *pBt;
327 Pager *pPager;
328 int szPage;
drh34d0b1a2017-10-11 15:02:53 +0000329
drh3547e492022-12-23 14:49:24 +0000330 (void)pRowid;
drha296cda2018-11-03 16:09:59 +0000331 if( pTab->db->flags & SQLITE_Defensive ){
332 zErr = "read-only";
333 goto update_fail;
334 }
drh34d0b1a2017-10-11 15:02:53 +0000335 if( argc==1 ){
336 zErr = "cannot delete";
337 goto update_fail;
338 }
339 pgno = sqlite3_value_int(argv[0]);
mistachkinaca84e62017-11-10 12:41:21 +0000340 if( (Pgno)sqlite3_value_int(argv[1])!=pgno ){
drh34d0b1a2017-10-11 15:02:53 +0000341 zErr = "cannot insert";
342 goto update_fail;
343 }
drh3cd8aaa2017-10-25 19:18:33 +0000344 zSchema = (const char*)sqlite3_value_text(argv[4]);
345 iDb = zSchema ? sqlite3FindDbName(pTab->db, zSchema) : -1;
346 if( iDb<0 ){
347 zErr = "no such schema";
348 goto update_fail;
349 }
350 pBt = pTab->db->aDb[iDb].pBt;
drhe684ac62022-03-08 13:59:46 +0000351 if( pgno<1 || pBt==0 || pgno>sqlite3BtreeLastPage(pBt) ){
drh3cd8aaa2017-10-25 19:18:33 +0000352 zErr = "bad page number";
353 goto update_fail;
354 }
355 szPage = sqlite3BtreeGetPageSize(pBt);
drh34d0b1a2017-10-11 15:02:53 +0000356 if( sqlite3_value_type(argv[3])!=SQLITE_BLOB
drh3cd8aaa2017-10-25 19:18:33 +0000357 || sqlite3_value_bytes(argv[3])!=szPage
drh34d0b1a2017-10-11 15:02:53 +0000358 ){
359 zErr = "bad page value";
360 goto update_fail;
361 }
drh3cd8aaa2017-10-25 19:18:33 +0000362 pPager = sqlite3BtreePager(pBt);
363 rc = sqlite3PagerGet(pPager, pgno, (DbPage**)&pDbPage, 0);
drh34d0b1a2017-10-11 15:02:53 +0000364 if( rc==SQLITE_OK ){
drhc7dd9b62022-10-31 18:01:05 +0000365 const void *pData = sqlite3_value_blob(argv[3]);
366 assert( pData!=0 || pTab->db->mallocFailed );
367 if( pData
368 && (rc = sqlite3PagerWrite(pDbPage))==SQLITE_OK
369 ){
370 memcpy(sqlite3PagerGetData(pDbPage), pData, szPage);
drh34d0b1a2017-10-11 15:02:53 +0000371 }
372 }
373 sqlite3PagerUnref(pDbPage);
374 return rc;
375
376update_fail:
377 sqlite3_free(pVtab->zErrMsg);
378 pVtab->zErrMsg = sqlite3_mprintf("%s", zErr);
379 return SQLITE_ERROR;
380}
381
drh3cd8aaa2017-10-25 19:18:33 +0000382/* Since we do not know in advance which database files will be
383** written by the sqlite_dbpage virtual table, start a write transaction
384** on them all.
385*/
386static int dbpageBegin(sqlite3_vtab *pVtab){
387 DbpageTable *pTab = (DbpageTable *)pVtab;
388 sqlite3 *db = pTab->db;
389 int i;
dane36281f2022-07-06 13:59:45 +0000390 int rc = SQLITE_OK;
391 for(i=0; rc==SQLITE_OK && i<db->nDb; i++){
drh3cd8aaa2017-10-25 19:18:33 +0000392 Btree *pBt = db->aDb[i].pBt;
dane36281f2022-07-06 13:59:45 +0000393 if( pBt ) rc = sqlite3BtreeBeginTrans(pBt, 1, 0);
drh3cd8aaa2017-10-25 19:18:33 +0000394 }
dane36281f2022-07-06 13:59:45 +0000395 return rc;
drh3cd8aaa2017-10-25 19:18:33 +0000396}
397
398
drha43c8c82017-10-11 13:48:11 +0000399/*
400** Invoke this routine to register the "dbpage" virtual table module
401*/
402int sqlite3DbpageRegister(sqlite3 *db){
403 static sqlite3_module dbpage_module = {
404 0, /* iVersion */
405 dbpageConnect, /* xCreate */
406 dbpageConnect, /* xConnect */
407 dbpageBestIndex, /* xBestIndex */
408 dbpageDisconnect, /* xDisconnect */
409 dbpageDisconnect, /* xDestroy */
410 dbpageOpen, /* xOpen - open a cursor */
411 dbpageClose, /* xClose - close a cursor */
412 dbpageFilter, /* xFilter - configure scan constraints */
413 dbpageNext, /* xNext - advance a cursor */
414 dbpageEof, /* xEof - check for end of scan */
415 dbpageColumn, /* xColumn - read data */
416 dbpageRowid, /* xRowid - read data */
drh34d0b1a2017-10-11 15:02:53 +0000417 dbpageUpdate, /* xUpdate */
drh3cd8aaa2017-10-25 19:18:33 +0000418 dbpageBegin, /* xBegin */
drha43c8c82017-10-11 13:48:11 +0000419 0, /* xSync */
420 0, /* xCommit */
421 0, /* xRollback */
422 0, /* xFindMethod */
423 0, /* xRename */
424 0, /* xSavepoint */
425 0, /* xRelease */
426 0, /* xRollbackTo */
drh84c501b2018-11-05 23:01:45 +0000427 0 /* xShadowName */
drha43c8c82017-10-11 13:48:11 +0000428 };
429 return sqlite3_create_module(db, "sqlite_dbpage", &dbpage_module, 0);
430}
431#elif defined(SQLITE_ENABLE_DBPAGE_VTAB)
432int sqlite3DbpageRegister(sqlite3 *db){ return SQLITE_OK; }
433#endif /* SQLITE_ENABLE_DBSTAT_VTAB */