1a43c8c8aSdrh /*
2a43c8c8aSdrh ** 2017-10-11
3a43c8c8aSdrh **
4a43c8c8aSdrh ** The author disclaims copyright to this source code. In place of
5a43c8c8aSdrh ** a legal notice, here is a blessing:
6a43c8c8aSdrh **
7a43c8c8aSdrh ** May you do good and not evil.
8a43c8c8aSdrh ** May you find forgiveness for yourself and forgive others.
9a43c8c8aSdrh ** May you share freely, never taking more than you give.
10a43c8c8aSdrh **
11a43c8c8aSdrh ******************************************************************************
12a43c8c8aSdrh **
13a43c8c8aSdrh ** This file contains an implementation of the "sqlite_dbpage" virtual table.
14a43c8c8aSdrh **
15a43c8c8aSdrh ** The sqlite_dbpage virtual table is used to read or write whole raw
16a43c8c8aSdrh ** pages of the database file. The pager interface is used so that
17a43c8c8aSdrh ** uncommitted changes and changes recorded in the WAL file are correctly
18a43c8c8aSdrh ** retrieved.
1934d0b1acSdrh **
2034d0b1acSdrh ** Usage example:
2134d0b1acSdrh **
2234d0b1acSdrh ** SELECT data FROM sqlite_dbpage('aux1') WHERE pgno=123;
2334d0b1acSdrh **
2434d0b1acSdrh ** This is an eponymous virtual table so it does not need to be created before
2534d0b1acSdrh ** use. The optional argument to the sqlite_dbpage() table name is the
2634d0b1acSdrh ** schema for the database file that is to be read. The default schema is
2734d0b1acSdrh ** "main".
2834d0b1acSdrh **
2934d0b1acSdrh ** The data field of sqlite_dbpage table can be updated. The new
3034d0b1acSdrh ** value must be a BLOB which is the correct page size, otherwise the
3134d0b1acSdrh ** update fails. Rows may not be deleted or inserted.
32a43c8c8aSdrh */
33a43c8c8aSdrh
34a43c8c8aSdrh #include "sqliteInt.h" /* Requires access to internal data structures */
35a43c8c8aSdrh #if (defined(SQLITE_ENABLE_DBPAGE_VTAB) || defined(SQLITE_TEST)) \
36a43c8c8aSdrh && !defined(SQLITE_OMIT_VIRTUALTABLE)
37a43c8c8aSdrh
38a43c8c8aSdrh typedef struct DbpageTable DbpageTable;
39a43c8c8aSdrh typedef struct DbpageCursor DbpageCursor;
40a43c8c8aSdrh
41a43c8c8aSdrh struct DbpageCursor {
42a43c8c8aSdrh sqlite3_vtab_cursor base; /* Base class. Must be first */
43a43c8c8aSdrh int pgno; /* Current page number */
4434d0b1acSdrh int mxPgno; /* Last page to visit on this scan */
453cd8aaa7Sdrh Pager *pPager; /* Pager being read/written */
463cd8aaa7Sdrh DbPage *pPage1; /* Page 1 of the database */
473cd8aaa7Sdrh int iDb; /* Index of database to analyze */
483cd8aaa7Sdrh int szPage; /* Size of each page in bytes */
49a43c8c8aSdrh };
50a43c8c8aSdrh
51a43c8c8aSdrh struct DbpageTable {
52a43c8c8aSdrh sqlite3_vtab base; /* Base class. Must be first */
53a43c8c8aSdrh sqlite3 *db; /* The database */
54a43c8c8aSdrh };
55a43c8c8aSdrh
563cd8aaa7Sdrh /* Columns */
573cd8aaa7Sdrh #define DBPAGE_COLUMN_PGNO 0
583cd8aaa7Sdrh #define DBPAGE_COLUMN_DATA 1
593cd8aaa7Sdrh #define DBPAGE_COLUMN_SCHEMA 2
603cd8aaa7Sdrh
613cd8aaa7Sdrh
623cd8aaa7Sdrh
63a43c8c8aSdrh /*
64a43c8c8aSdrh ** Connect to or create a dbpagevfs virtual table.
65a43c8c8aSdrh */
dbpageConnect(sqlite3 * db,void * pAux,int argc,const char * const * argv,sqlite3_vtab ** ppVtab,char ** pzErr)66a43c8c8aSdrh static int dbpageConnect(
67a43c8c8aSdrh sqlite3 *db,
68a43c8c8aSdrh void *pAux,
69a43c8c8aSdrh int argc, const char *const*argv,
70a43c8c8aSdrh sqlite3_vtab **ppVtab,
71a43c8c8aSdrh char **pzErr
72a43c8c8aSdrh ){
73a43c8c8aSdrh DbpageTable *pTab = 0;
74a43c8c8aSdrh int rc = SQLITE_OK;
75a43c8c8aSdrh
762b1c2aadSdrh sqlite3_vtab_config(db, SQLITE_VTAB_DIRECTONLY);
77a43c8c8aSdrh rc = sqlite3_declare_vtab(db,
7834d0b1acSdrh "CREATE TABLE x(pgno INTEGER PRIMARY KEY, data BLOB, schema HIDDEN)");
79a43c8c8aSdrh if( rc==SQLITE_OK ){
80a43c8c8aSdrh pTab = (DbpageTable *)sqlite3_malloc64(sizeof(DbpageTable));
81a43c8c8aSdrh if( pTab==0 ) rc = SQLITE_NOMEM_BKPT;
82a43c8c8aSdrh }
83a43c8c8aSdrh
84a43c8c8aSdrh assert( rc==SQLITE_OK || pTab==0 );
85a43c8c8aSdrh if( rc==SQLITE_OK ){
86a43c8c8aSdrh memset(pTab, 0, sizeof(DbpageTable));
87a43c8c8aSdrh pTab->db = db;
88a43c8c8aSdrh }
89a43c8c8aSdrh
90a43c8c8aSdrh *ppVtab = (sqlite3_vtab*)pTab;
91a43c8c8aSdrh return rc;
92a43c8c8aSdrh }
93a43c8c8aSdrh
94a43c8c8aSdrh /*
95a43c8c8aSdrh ** Disconnect from or destroy a dbpagevfs virtual table.
96a43c8c8aSdrh */
dbpageDisconnect(sqlite3_vtab * pVtab)97a43c8c8aSdrh static int dbpageDisconnect(sqlite3_vtab *pVtab){
98a43c8c8aSdrh sqlite3_free(pVtab);
99a43c8c8aSdrh return SQLITE_OK;
100a43c8c8aSdrh }
101a43c8c8aSdrh
102a43c8c8aSdrh /*
103a43c8c8aSdrh ** idxNum:
104a43c8c8aSdrh **
1053cd8aaa7Sdrh ** 0 schema=main, full table scan
1063cd8aaa7Sdrh ** 1 schema=main, pgno=?1
1073cd8aaa7Sdrh ** 2 schema=?1, full table scan
1083cd8aaa7Sdrh ** 3 schema=?1, pgno=?2
109a43c8c8aSdrh */
dbpageBestIndex(sqlite3_vtab * tab,sqlite3_index_info * pIdxInfo)110a43c8c8aSdrh static int dbpageBestIndex(sqlite3_vtab *tab, sqlite3_index_info *pIdxInfo){
11134d0b1acSdrh int i;
1123cd8aaa7Sdrh int iPlan = 0;
1133cd8aaa7Sdrh
1143cd8aaa7Sdrh /* If there is a schema= constraint, it must be honored. Report a
1153cd8aaa7Sdrh ** ridiculously large estimated cost if the schema= constraint is
1163cd8aaa7Sdrh ** unavailable
1173cd8aaa7Sdrh */
1183cd8aaa7Sdrh for(i=0; i<pIdxInfo->nConstraint; i++){
1193cd8aaa7Sdrh struct sqlite3_index_constraint *p = &pIdxInfo->aConstraint[i];
1203cd8aaa7Sdrh if( p->iColumn!=DBPAGE_COLUMN_SCHEMA ) continue;
1213cd8aaa7Sdrh if( p->op!=SQLITE_INDEX_CONSTRAINT_EQ ) continue;
1223cd8aaa7Sdrh if( !p->usable ){
12320b3fc4dSdrh /* No solution. */
12420b3fc4dSdrh return SQLITE_CONSTRAINT;
1253cd8aaa7Sdrh }
1263cd8aaa7Sdrh iPlan = 2;
1273cd8aaa7Sdrh pIdxInfo->aConstraintUsage[i].argvIndex = 1;
1283cd8aaa7Sdrh pIdxInfo->aConstraintUsage[i].omit = 1;
1293cd8aaa7Sdrh break;
1303cd8aaa7Sdrh }
1313cd8aaa7Sdrh
1323cd8aaa7Sdrh /* If we reach this point, it means that either there is no schema=
1333cd8aaa7Sdrh ** constraint (in which case we use the "main" schema) or else the
1343cd8aaa7Sdrh ** schema constraint was accepted. Lower the estimated cost accordingly
1353cd8aaa7Sdrh */
1363cd8aaa7Sdrh pIdxInfo->estimatedCost = 1.0e6;
1373cd8aaa7Sdrh
1383cd8aaa7Sdrh /* Check for constraints against pgno */
13934d0b1acSdrh for(i=0; i<pIdxInfo->nConstraint; i++){
14034d0b1acSdrh struct sqlite3_index_constraint *p = &pIdxInfo->aConstraint[i];
14134d0b1acSdrh if( p->usable && p->iColumn<=0 && p->op==SQLITE_INDEX_CONSTRAINT_EQ ){
14234d0b1acSdrh pIdxInfo->estimatedRows = 1;
14334d0b1acSdrh pIdxInfo->idxFlags = SQLITE_INDEX_SCAN_UNIQUE;
14434d0b1acSdrh pIdxInfo->estimatedCost = 1.0;
1453cd8aaa7Sdrh pIdxInfo->aConstraintUsage[i].argvIndex = iPlan ? 2 : 1;
14634d0b1acSdrh pIdxInfo->aConstraintUsage[i].omit = 1;
1473cd8aaa7Sdrh iPlan |= 1;
14834d0b1acSdrh break;
14934d0b1acSdrh }
15034d0b1acSdrh }
1513cd8aaa7Sdrh pIdxInfo->idxNum = iPlan;
1523cd8aaa7Sdrh
15334d0b1acSdrh if( pIdxInfo->nOrderBy>=1
15434d0b1acSdrh && pIdxInfo->aOrderBy[0].iColumn<=0
15534d0b1acSdrh && pIdxInfo->aOrderBy[0].desc==0
15634d0b1acSdrh ){
15734d0b1acSdrh pIdxInfo->orderByConsumed = 1;
15834d0b1acSdrh }
1597d0ae003Sdrh sqlite3VtabUsesAllSchemas(pIdxInfo);
160a43c8c8aSdrh return SQLITE_OK;
161a43c8c8aSdrh }
162a43c8c8aSdrh
163a43c8c8aSdrh /*
164a43c8c8aSdrh ** Open a new dbpagevfs cursor.
165a43c8c8aSdrh */
dbpageOpen(sqlite3_vtab * pVTab,sqlite3_vtab_cursor ** ppCursor)166a43c8c8aSdrh static int dbpageOpen(sqlite3_vtab *pVTab, sqlite3_vtab_cursor **ppCursor){
167a43c8c8aSdrh DbpageCursor *pCsr;
168a43c8c8aSdrh
169a43c8c8aSdrh pCsr = (DbpageCursor *)sqlite3_malloc64(sizeof(DbpageCursor));
170a43c8c8aSdrh if( pCsr==0 ){
171a43c8c8aSdrh return SQLITE_NOMEM_BKPT;
172a43c8c8aSdrh }else{
173a43c8c8aSdrh memset(pCsr, 0, sizeof(DbpageCursor));
174a43c8c8aSdrh pCsr->base.pVtab = pVTab;
175a43c8c8aSdrh pCsr->pgno = -1;
176a43c8c8aSdrh }
177a43c8c8aSdrh
178a43c8c8aSdrh *ppCursor = (sqlite3_vtab_cursor *)pCsr;
179a43c8c8aSdrh return SQLITE_OK;
180a43c8c8aSdrh }
181a43c8c8aSdrh
182a43c8c8aSdrh /*
183a43c8c8aSdrh ** Close a dbpagevfs cursor.
184a43c8c8aSdrh */
dbpageClose(sqlite3_vtab_cursor * pCursor)185a43c8c8aSdrh static int dbpageClose(sqlite3_vtab_cursor *pCursor){
186a43c8c8aSdrh DbpageCursor *pCsr = (DbpageCursor *)pCursor;
1873cd8aaa7Sdrh if( pCsr->pPage1 ) sqlite3PagerUnrefPageOne(pCsr->pPage1);
188a43c8c8aSdrh sqlite3_free(pCsr);
189a43c8c8aSdrh return SQLITE_OK;
190a43c8c8aSdrh }
191a43c8c8aSdrh
192a43c8c8aSdrh /*
193a43c8c8aSdrh ** Move a dbpagevfs cursor to the next entry in the file.
194a43c8c8aSdrh */
dbpageNext(sqlite3_vtab_cursor * pCursor)195a43c8c8aSdrh static int dbpageNext(sqlite3_vtab_cursor *pCursor){
196a43c8c8aSdrh int rc = SQLITE_OK;
197a43c8c8aSdrh DbpageCursor *pCsr = (DbpageCursor *)pCursor;
198a43c8c8aSdrh pCsr->pgno++;
199a43c8c8aSdrh return rc;
200a43c8c8aSdrh }
201a43c8c8aSdrh
dbpageEof(sqlite3_vtab_cursor * pCursor)202a43c8c8aSdrh static int dbpageEof(sqlite3_vtab_cursor *pCursor){
203a43c8c8aSdrh DbpageCursor *pCsr = (DbpageCursor *)pCursor;
20434d0b1acSdrh return pCsr->pgno > pCsr->mxPgno;
205a43c8c8aSdrh }
206a43c8c8aSdrh
2073cd8aaa7Sdrh /*
2083cd8aaa7Sdrh ** idxNum:
2093cd8aaa7Sdrh **
2103cd8aaa7Sdrh ** 0 schema=main, full table scan
2113cd8aaa7Sdrh ** 1 schema=main, pgno=?1
2123cd8aaa7Sdrh ** 2 schema=?1, full table scan
2133cd8aaa7Sdrh ** 3 schema=?1, pgno=?2
2143cd8aaa7Sdrh **
2153cd8aaa7Sdrh ** idxStr is not used
2163cd8aaa7Sdrh */
dbpageFilter(sqlite3_vtab_cursor * pCursor,int idxNum,const char * idxStr,int argc,sqlite3_value ** argv)217a43c8c8aSdrh static int dbpageFilter(
218a43c8c8aSdrh sqlite3_vtab_cursor *pCursor,
219a43c8c8aSdrh int idxNum, const char *idxStr,
220a43c8c8aSdrh int argc, sqlite3_value **argv
221a43c8c8aSdrh ){
222a43c8c8aSdrh DbpageCursor *pCsr = (DbpageCursor *)pCursor;
223a43c8c8aSdrh DbpageTable *pTab = (DbpageTable *)pCursor->pVtab;
2240503f2acSdrh int rc;
2253cd8aaa7Sdrh sqlite3 *db = pTab->db;
2263cd8aaa7Sdrh Btree *pBt;
227a43c8c8aSdrh
2283cd8aaa7Sdrh /* Default setting is no rows of result */
2293cd8aaa7Sdrh pCsr->pgno = 1;
2303cd8aaa7Sdrh pCsr->mxPgno = 0;
2313cd8aaa7Sdrh
2323cd8aaa7Sdrh if( idxNum & 2 ){
2333cd8aaa7Sdrh const char *zSchema;
2343cd8aaa7Sdrh assert( argc>=1 );
2353cd8aaa7Sdrh zSchema = (const char*)sqlite3_value_text(argv[0]);
2363cd8aaa7Sdrh pCsr->iDb = sqlite3FindDbName(db, zSchema);
2373cd8aaa7Sdrh if( pCsr->iDb<0 ) return SQLITE_OK;
2383cd8aaa7Sdrh }else{
2393cd8aaa7Sdrh pCsr->iDb = 0;
2403cd8aaa7Sdrh }
2413cd8aaa7Sdrh pBt = db->aDb[pCsr->iDb].pBt;
2423cd8aaa7Sdrh if( pBt==0 ) return SQLITE_OK;
2433cd8aaa7Sdrh pCsr->pPager = sqlite3BtreePager(pBt);
2443cd8aaa7Sdrh pCsr->szPage = sqlite3BtreeGetPageSize(pBt);
2453cd8aaa7Sdrh pCsr->mxPgno = sqlite3BtreeLastPage(pBt);
2463cd8aaa7Sdrh if( idxNum & 1 ){
2473cd8aaa7Sdrh assert( argc>(idxNum>>1) );
2483cd8aaa7Sdrh pCsr->pgno = sqlite3_value_int(argv[idxNum>>1]);
2493cd8aaa7Sdrh if( pCsr->pgno<1 || pCsr->pgno>pCsr->mxPgno ){
25034d0b1acSdrh pCsr->pgno = 1;
25134d0b1acSdrh pCsr->mxPgno = 0;
25234d0b1acSdrh }else{
25334d0b1acSdrh pCsr->mxPgno = pCsr->pgno;
25434d0b1acSdrh }
25534d0b1acSdrh }else{
2563cd8aaa7Sdrh assert( pCsr->pgno==1 );
25734d0b1acSdrh }
2580503f2acSdrh if( pCsr->pPage1 ) sqlite3PagerUnrefPageOne(pCsr->pPage1);
2593cd8aaa7Sdrh rc = sqlite3PagerGet(pCsr->pPager, 1, &pCsr->pPage1, 0);
260a43c8c8aSdrh return rc;
261a43c8c8aSdrh }
262a43c8c8aSdrh
dbpageColumn(sqlite3_vtab_cursor * pCursor,sqlite3_context * ctx,int i)263a43c8c8aSdrh static int dbpageColumn(
264a43c8c8aSdrh sqlite3_vtab_cursor *pCursor,
265a43c8c8aSdrh sqlite3_context *ctx,
266a43c8c8aSdrh int i
267a43c8c8aSdrh ){
268a43c8c8aSdrh DbpageCursor *pCsr = (DbpageCursor *)pCursor;
269a43c8c8aSdrh int rc = SQLITE_OK;
270a43c8c8aSdrh switch( i ){
271a43c8c8aSdrh case 0: { /* pgno */
272a43c8c8aSdrh sqlite3_result_int(ctx, pCsr->pgno);
273a43c8c8aSdrh break;
274a43c8c8aSdrh }
275a43c8c8aSdrh case 1: { /* data */
276a43c8c8aSdrh DbPage *pDbPage = 0;
2777985e05aSdan if( pCsr->pgno==((PENDING_BYTE/pCsr->szPage)+1) ){
2787985e05aSdan /* The pending byte page. Assume it is zeroed out. Attempting to
2797985e05aSdan ** request this page from the page is an SQLITE_CORRUPT error. */
2807985e05aSdan sqlite3_result_zeroblob(ctx, pCsr->szPage);
2817985e05aSdan }else{
2823cd8aaa7Sdrh rc = sqlite3PagerGet(pCsr->pPager, pCsr->pgno, (DbPage**)&pDbPage, 0);
283a43c8c8aSdrh if( rc==SQLITE_OK ){
2843cd8aaa7Sdrh sqlite3_result_blob(ctx, sqlite3PagerGetData(pDbPage), pCsr->szPage,
285a43c8c8aSdrh SQLITE_TRANSIENT);
286a43c8c8aSdrh }
287a43c8c8aSdrh sqlite3PagerUnref(pDbPage);
2887985e05aSdan }
289a43c8c8aSdrh break;
290a43c8c8aSdrh }
291a43c8c8aSdrh default: { /* schema */
292a43c8c8aSdrh sqlite3 *db = sqlite3_context_db_handle(ctx);
2933cd8aaa7Sdrh sqlite3_result_text(ctx, db->aDb[pCsr->iDb].zDbSName, -1, SQLITE_STATIC);
294a43c8c8aSdrh break;
295a43c8c8aSdrh }
296a43c8c8aSdrh }
2975ca0b386Sdan return rc;
298a43c8c8aSdrh }
299a43c8c8aSdrh
dbpageRowid(sqlite3_vtab_cursor * pCursor,sqlite_int64 * pRowid)300a43c8c8aSdrh static int dbpageRowid(sqlite3_vtab_cursor *pCursor, sqlite_int64 *pRowid){
301a43c8c8aSdrh DbpageCursor *pCsr = (DbpageCursor *)pCursor;
302a43c8c8aSdrh *pRowid = pCsr->pgno;
303a43c8c8aSdrh return SQLITE_OK;
304a43c8c8aSdrh }
305a43c8c8aSdrh
dbpageUpdate(sqlite3_vtab * pVtab,int argc,sqlite3_value ** argv,sqlite_int64 * pRowid)30634d0b1acSdrh static int dbpageUpdate(
30734d0b1acSdrh sqlite3_vtab *pVtab,
30834d0b1acSdrh int argc,
30934d0b1acSdrh sqlite3_value **argv,
31034d0b1acSdrh sqlite_int64 *pRowid
31134d0b1acSdrh ){
31234d0b1acSdrh DbpageTable *pTab = (DbpageTable *)pVtab;
313aca84e6aSmistachkin Pgno pgno;
31434d0b1acSdrh DbPage *pDbPage = 0;
31534d0b1acSdrh int rc = SQLITE_OK;
31634d0b1acSdrh char *zErr = 0;
3173cd8aaa7Sdrh const char *zSchema;
3183cd8aaa7Sdrh int iDb;
3193cd8aaa7Sdrh Btree *pBt;
3203cd8aaa7Sdrh Pager *pPager;
3213cd8aaa7Sdrh int szPage;
32234d0b1acSdrh
323a296cda0Sdrh if( pTab->db->flags & SQLITE_Defensive ){
324a296cda0Sdrh zErr = "read-only";
325a296cda0Sdrh goto update_fail;
326a296cda0Sdrh }
32734d0b1acSdrh if( argc==1 ){
32834d0b1acSdrh zErr = "cannot delete";
32934d0b1acSdrh goto update_fail;
33034d0b1acSdrh }
33134d0b1acSdrh pgno = sqlite3_value_int(argv[0]);
332aca84e6aSmistachkin if( (Pgno)sqlite3_value_int(argv[1])!=pgno ){
33334d0b1acSdrh zErr = "cannot insert";
33434d0b1acSdrh goto update_fail;
33534d0b1acSdrh }
3363cd8aaa7Sdrh zSchema = (const char*)sqlite3_value_text(argv[4]);
3373cd8aaa7Sdrh iDb = zSchema ? sqlite3FindDbName(pTab->db, zSchema) : -1;
3383cd8aaa7Sdrh if( iDb<0 ){
3393cd8aaa7Sdrh zErr = "no such schema";
3403cd8aaa7Sdrh goto update_fail;
3413cd8aaa7Sdrh }
3423cd8aaa7Sdrh pBt = pTab->db->aDb[iDb].pBt;
343e684ac6fSdrh if( pgno<1 || pBt==0 || pgno>sqlite3BtreeLastPage(pBt) ){
3443cd8aaa7Sdrh zErr = "bad page number";
3453cd8aaa7Sdrh goto update_fail;
3463cd8aaa7Sdrh }
3473cd8aaa7Sdrh szPage = sqlite3BtreeGetPageSize(pBt);
34834d0b1acSdrh if( sqlite3_value_type(argv[3])!=SQLITE_BLOB
3493cd8aaa7Sdrh || sqlite3_value_bytes(argv[3])!=szPage
35034d0b1acSdrh ){
35134d0b1acSdrh zErr = "bad page value";
35234d0b1acSdrh goto update_fail;
35334d0b1acSdrh }
3543cd8aaa7Sdrh pPager = sqlite3BtreePager(pBt);
3553cd8aaa7Sdrh rc = sqlite3PagerGet(pPager, pgno, (DbPage**)&pDbPage, 0);
35634d0b1acSdrh if( rc==SQLITE_OK ){
357*c7dd9b60Sdrh const void *pData = sqlite3_value_blob(argv[3]);
358*c7dd9b60Sdrh assert( pData!=0 || pTab->db->mallocFailed );
359*c7dd9b60Sdrh if( pData
360*c7dd9b60Sdrh && (rc = sqlite3PagerWrite(pDbPage))==SQLITE_OK
361*c7dd9b60Sdrh ){
362*c7dd9b60Sdrh memcpy(sqlite3PagerGetData(pDbPage), pData, szPage);
36334d0b1acSdrh }
36434d0b1acSdrh }
36534d0b1acSdrh sqlite3PagerUnref(pDbPage);
36634d0b1acSdrh return rc;
36734d0b1acSdrh
36834d0b1acSdrh update_fail:
36934d0b1acSdrh sqlite3_free(pVtab->zErrMsg);
37034d0b1acSdrh pVtab->zErrMsg = sqlite3_mprintf("%s", zErr);
37134d0b1acSdrh return SQLITE_ERROR;
37234d0b1acSdrh }
37334d0b1acSdrh
3743cd8aaa7Sdrh /* Since we do not know in advance which database files will be
3753cd8aaa7Sdrh ** written by the sqlite_dbpage virtual table, start a write transaction
3763cd8aaa7Sdrh ** on them all.
3773cd8aaa7Sdrh */
dbpageBegin(sqlite3_vtab * pVtab)3783cd8aaa7Sdrh static int dbpageBegin(sqlite3_vtab *pVtab){
3793cd8aaa7Sdrh DbpageTable *pTab = (DbpageTable *)pVtab;
3803cd8aaa7Sdrh sqlite3 *db = pTab->db;
3813cd8aaa7Sdrh int i;
382e36281fcSdan int rc = SQLITE_OK;
383e36281fcSdan for(i=0; rc==SQLITE_OK && i<db->nDb; i++){
3843cd8aaa7Sdrh Btree *pBt = db->aDb[i].pBt;
385e36281fcSdan if( pBt ) rc = sqlite3BtreeBeginTrans(pBt, 1, 0);
3863cd8aaa7Sdrh }
387e36281fcSdan return rc;
3883cd8aaa7Sdrh }
3893cd8aaa7Sdrh
3903cd8aaa7Sdrh
391a43c8c8aSdrh /*
392a43c8c8aSdrh ** Invoke this routine to register the "dbpage" virtual table module
393a43c8c8aSdrh */
sqlite3DbpageRegister(sqlite3 * db)394a43c8c8aSdrh int sqlite3DbpageRegister(sqlite3 *db){
395a43c8c8aSdrh static sqlite3_module dbpage_module = {
396a43c8c8aSdrh 0, /* iVersion */
397a43c8c8aSdrh dbpageConnect, /* xCreate */
398a43c8c8aSdrh dbpageConnect, /* xConnect */
399a43c8c8aSdrh dbpageBestIndex, /* xBestIndex */
400a43c8c8aSdrh dbpageDisconnect, /* xDisconnect */
401a43c8c8aSdrh dbpageDisconnect, /* xDestroy */
402a43c8c8aSdrh dbpageOpen, /* xOpen - open a cursor */
403a43c8c8aSdrh dbpageClose, /* xClose - close a cursor */
404a43c8c8aSdrh dbpageFilter, /* xFilter - configure scan constraints */
405a43c8c8aSdrh dbpageNext, /* xNext - advance a cursor */
406a43c8c8aSdrh dbpageEof, /* xEof - check for end of scan */
407a43c8c8aSdrh dbpageColumn, /* xColumn - read data */
408a43c8c8aSdrh dbpageRowid, /* xRowid - read data */
40934d0b1acSdrh dbpageUpdate, /* xUpdate */
4103cd8aaa7Sdrh dbpageBegin, /* xBegin */
411a43c8c8aSdrh 0, /* xSync */
412a43c8c8aSdrh 0, /* xCommit */
413a43c8c8aSdrh 0, /* xRollback */
414a43c8c8aSdrh 0, /* xFindMethod */
415a43c8c8aSdrh 0, /* xRename */
416a43c8c8aSdrh 0, /* xSavepoint */
417a43c8c8aSdrh 0, /* xRelease */
418a43c8c8aSdrh 0, /* xRollbackTo */
41984c501baSdrh 0 /* xShadowName */
420a43c8c8aSdrh };
421a43c8c8aSdrh return sqlite3_create_module(db, "sqlite_dbpage", &dbpage_module, 0);
422a43c8c8aSdrh }
423a43c8c8aSdrh #elif defined(SQLITE_ENABLE_DBPAGE_VTAB)
sqlite3DbpageRegister(sqlite3 * db)424a43c8c8aSdrh int sqlite3DbpageRegister(sqlite3 *db){ return SQLITE_OK; }
425a43c8c8aSdrh #endif /* SQLITE_ENABLE_DBSTAT_VTAB */
426