drh | a43c8c8 | 2017-10-11 13:48:11 +0000 | [diff] [blame] | 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. |
drh | 34d0b1a | 2017-10-11 15:02:53 +0000 | [diff] [blame] | 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. |
drh | a43c8c8 | 2017-10-11 13:48:11 +0000 | [diff] [blame] | 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 */ |
drh | 34d0b1a | 2017-10-11 15:02:53 +0000 | [diff] [blame] | 44 | int mxPgno; /* Last page to visit on this scan */ |
drh | 3cd8aaa | 2017-10-25 19:18:33 +0000 | [diff] [blame] | 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 */ |
drh | a43c8c8 | 2017-10-11 13:48:11 +0000 | [diff] [blame] | 49 | }; |
| 50 | |
| 51 | struct DbpageTable { |
| 52 | sqlite3_vtab base; /* Base class. Must be first */ |
| 53 | sqlite3 *db; /* The database */ |
drh | a43c8c8 | 2017-10-11 13:48:11 +0000 | [diff] [blame] | 54 | }; |
| 55 | |
drh | 3cd8aaa | 2017-10-25 19:18:33 +0000 | [diff] [blame] | 56 | /* Columns */ |
| 57 | #define DBPAGE_COLUMN_PGNO 0 |
| 58 | #define DBPAGE_COLUMN_DATA 1 |
| 59 | #define DBPAGE_COLUMN_SCHEMA 2 |
| 60 | |
| 61 | |
| 62 | |
drh | a43c8c8 | 2017-10-11 13:48:11 +0000 | [diff] [blame] | 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; |
drh | 3547e49 | 2022-12-23 14:49:24 +0000 | [diff] [blame] | 75 | (void)pAux; |
| 76 | (void)argc; |
| 77 | (void)argv; |
| 78 | (void)pzErr; |
drh | a43c8c8 | 2017-10-11 13:48:11 +0000 | [diff] [blame] | 79 | |
drh | 2b1c2aa | 2020-01-07 19:45:40 +0000 | [diff] [blame] | 80 | sqlite3_vtab_config(db, SQLITE_VTAB_DIRECTONLY); |
drh | a43c8c8 | 2017-10-11 13:48:11 +0000 | [diff] [blame] | 81 | rc = sqlite3_declare_vtab(db, |
drh | 34d0b1a | 2017-10-11 15:02:53 +0000 | [diff] [blame] | 82 | "CREATE TABLE x(pgno INTEGER PRIMARY KEY, data BLOB, schema HIDDEN)"); |
drh | a43c8c8 | 2017-10-11 13:48:11 +0000 | [diff] [blame] | 83 | 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 ){ |
drh | a43c8c8 | 2017-10-11 13:48:11 +0000 | [diff] [blame] | 90 | memset(pTab, 0, sizeof(DbpageTable)); |
| 91 | pTab->db = db; |
drh | a43c8c8 | 2017-10-11 13:48:11 +0000 | [diff] [blame] | 92 | } |
| 93 | |
| 94 | *ppVtab = (sqlite3_vtab*)pTab; |
| 95 | return rc; |
| 96 | } |
| 97 | |
| 98 | /* |
| 99 | ** Disconnect from or destroy a dbpagevfs virtual table. |
| 100 | */ |
| 101 | static int dbpageDisconnect(sqlite3_vtab *pVtab){ |
| 102 | sqlite3_free(pVtab); |
| 103 | return SQLITE_OK; |
| 104 | } |
| 105 | |
| 106 | /* |
| 107 | ** idxNum: |
| 108 | ** |
drh | 3cd8aaa | 2017-10-25 19:18:33 +0000 | [diff] [blame] | 109 | ** 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 |
drh | a43c8c8 | 2017-10-11 13:48:11 +0000 | [diff] [blame] | 113 | */ |
| 114 | static int dbpageBestIndex(sqlite3_vtab *tab, sqlite3_index_info *pIdxInfo){ |
drh | 34d0b1a | 2017-10-11 15:02:53 +0000 | [diff] [blame] | 115 | int i; |
drh | 3cd8aaa | 2017-10-25 19:18:33 +0000 | [diff] [blame] | 116 | int iPlan = 0; |
drh | 3547e49 | 2022-12-23 14:49:24 +0000 | [diff] [blame] | 117 | (void)tab; |
drh | 3cd8aaa | 2017-10-25 19:18:33 +0000 | [diff] [blame] | 118 | |
| 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 ){ |
drh | 20b3fc4 | 2018-11-16 20:18:07 +0000 | [diff] [blame] | 128 | /* No solution. */ |
| 129 | return SQLITE_CONSTRAINT; |
drh | 3cd8aaa | 2017-10-25 19:18:33 +0000 | [diff] [blame] | 130 | } |
| 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 */ |
drh | 34d0b1a | 2017-10-11 15:02:53 +0000 | [diff] [blame] | 144 | 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; |
drh | 3cd8aaa | 2017-10-25 19:18:33 +0000 | [diff] [blame] | 150 | pIdxInfo->aConstraintUsage[i].argvIndex = iPlan ? 2 : 1; |
drh | 34d0b1a | 2017-10-11 15:02:53 +0000 | [diff] [blame] | 151 | pIdxInfo->aConstraintUsage[i].omit = 1; |
drh | 3cd8aaa | 2017-10-25 19:18:33 +0000 | [diff] [blame] | 152 | iPlan |= 1; |
drh | 34d0b1a | 2017-10-11 15:02:53 +0000 | [diff] [blame] | 153 | break; |
| 154 | } |
| 155 | } |
drh | 3cd8aaa | 2017-10-25 19:18:33 +0000 | [diff] [blame] | 156 | pIdxInfo->idxNum = iPlan; |
| 157 | |
drh | 34d0b1a | 2017-10-11 15:02:53 +0000 | [diff] [blame] | 158 | if( pIdxInfo->nOrderBy>=1 |
| 159 | && pIdxInfo->aOrderBy[0].iColumn<=0 |
| 160 | && pIdxInfo->aOrderBy[0].desc==0 |
| 161 | ){ |
| 162 | pIdxInfo->orderByConsumed = 1; |
| 163 | } |
drh | 7d0ae00 | 2022-04-08 17:01:29 +0000 | [diff] [blame] | 164 | sqlite3VtabUsesAllSchemas(pIdxInfo); |
drh | a43c8c8 | 2017-10-11 13:48:11 +0000 | [diff] [blame] | 165 | return SQLITE_OK; |
| 166 | } |
| 167 | |
| 168 | /* |
| 169 | ** Open a new dbpagevfs cursor. |
| 170 | */ |
| 171 | static 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 | */ |
| 190 | static int dbpageClose(sqlite3_vtab_cursor *pCursor){ |
| 191 | DbpageCursor *pCsr = (DbpageCursor *)pCursor; |
drh | 3cd8aaa | 2017-10-25 19:18:33 +0000 | [diff] [blame] | 192 | if( pCsr->pPage1 ) sqlite3PagerUnrefPageOne(pCsr->pPage1); |
drh | a43c8c8 | 2017-10-11 13:48:11 +0000 | [diff] [blame] | 193 | sqlite3_free(pCsr); |
| 194 | return SQLITE_OK; |
| 195 | } |
| 196 | |
| 197 | /* |
| 198 | ** Move a dbpagevfs cursor to the next entry in the file. |
| 199 | */ |
| 200 | static int dbpageNext(sqlite3_vtab_cursor *pCursor){ |
| 201 | int rc = SQLITE_OK; |
| 202 | DbpageCursor *pCsr = (DbpageCursor *)pCursor; |
| 203 | pCsr->pgno++; |
| 204 | return rc; |
| 205 | } |
| 206 | |
| 207 | static int dbpageEof(sqlite3_vtab_cursor *pCursor){ |
| 208 | DbpageCursor *pCsr = (DbpageCursor *)pCursor; |
drh | 34d0b1a | 2017-10-11 15:02:53 +0000 | [diff] [blame] | 209 | return pCsr->pgno > pCsr->mxPgno; |
drh | a43c8c8 | 2017-10-11 13:48:11 +0000 | [diff] [blame] | 210 | } |
| 211 | |
drh | 3cd8aaa | 2017-10-25 19:18:33 +0000 | [diff] [blame] | 212 | /* |
| 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 | */ |
drh | a43c8c8 | 2017-10-11 13:48:11 +0000 | [diff] [blame] | 222 | static 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; |
drh | 0503f2a | 2017-10-27 18:24:11 +0000 | [diff] [blame] | 229 | int rc; |
drh | 3cd8aaa | 2017-10-25 19:18:33 +0000 | [diff] [blame] | 230 | sqlite3 *db = pTab->db; |
| 231 | Btree *pBt; |
drh | a43c8c8 | 2017-10-11 13:48:11 +0000 | [diff] [blame] | 232 | |
drh | 3547e49 | 2022-12-23 14:49:24 +0000 | [diff] [blame] | 233 | (void)idxStr; |
| 234 | |
drh | 3cd8aaa | 2017-10-25 19:18:33 +0000 | [diff] [blame] | 235 | /* 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 ){ |
drh | 34d0b1a | 2017-10-11 15:02:53 +0000 | [diff] [blame] | 257 | pCsr->pgno = 1; |
| 258 | pCsr->mxPgno = 0; |
| 259 | }else{ |
| 260 | pCsr->mxPgno = pCsr->pgno; |
| 261 | } |
| 262 | }else{ |
drh | 3cd8aaa | 2017-10-25 19:18:33 +0000 | [diff] [blame] | 263 | assert( pCsr->pgno==1 ); |
drh | 34d0b1a | 2017-10-11 15:02:53 +0000 | [diff] [blame] | 264 | } |
drh | 0503f2a | 2017-10-27 18:24:11 +0000 | [diff] [blame] | 265 | if( pCsr->pPage1 ) sqlite3PagerUnrefPageOne(pCsr->pPage1); |
drh | 3cd8aaa | 2017-10-25 19:18:33 +0000 | [diff] [blame] | 266 | rc = sqlite3PagerGet(pCsr->pPager, 1, &pCsr->pPage1, 0); |
drh | a43c8c8 | 2017-10-11 13:48:11 +0000 | [diff] [blame] | 267 | return rc; |
| 268 | } |
| 269 | |
| 270 | static int dbpageColumn( |
| 271 | sqlite3_vtab_cursor *pCursor, |
| 272 | sqlite3_context *ctx, |
| 273 | int i |
| 274 | ){ |
| 275 | DbpageCursor *pCsr = (DbpageCursor *)pCursor; |
drh | a43c8c8 | 2017-10-11 13:48:11 +0000 | [diff] [blame] | 276 | 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; |
dan | 7985e05 | 2022-09-13 18:08:24 +0000 | [diff] [blame] | 284 | 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); |
drh | a43c8c8 | 2017-10-11 13:48:11 +0000 | [diff] [blame] | 295 | } |
drh | a43c8c8 | 2017-10-11 13:48:11 +0000 | [diff] [blame] | 296 | break; |
| 297 | } |
| 298 | default: { /* schema */ |
| 299 | sqlite3 *db = sqlite3_context_db_handle(ctx); |
drh | 3cd8aaa | 2017-10-25 19:18:33 +0000 | [diff] [blame] | 300 | sqlite3_result_text(ctx, db->aDb[pCsr->iDb].zDbSName, -1, SQLITE_STATIC); |
drh | a43c8c8 | 2017-10-11 13:48:11 +0000 | [diff] [blame] | 301 | break; |
| 302 | } |
| 303 | } |
dan | 5ca0b38 | 2022-09-12 20:02:33 +0000 | [diff] [blame] | 304 | return rc; |
drh | a43c8c8 | 2017-10-11 13:48:11 +0000 | [diff] [blame] | 305 | } |
| 306 | |
| 307 | static int dbpageRowid(sqlite3_vtab_cursor *pCursor, sqlite_int64 *pRowid){ |
| 308 | DbpageCursor *pCsr = (DbpageCursor *)pCursor; |
| 309 | *pRowid = pCsr->pgno; |
| 310 | return SQLITE_OK; |
| 311 | } |
| 312 | |
drh | 34d0b1a | 2017-10-11 15:02:53 +0000 | [diff] [blame] | 313 | static int dbpageUpdate( |
| 314 | sqlite3_vtab *pVtab, |
| 315 | int argc, |
| 316 | sqlite3_value **argv, |
| 317 | sqlite_int64 *pRowid |
| 318 | ){ |
| 319 | DbpageTable *pTab = (DbpageTable *)pVtab; |
mistachkin | aca84e6 | 2017-11-10 12:41:21 +0000 | [diff] [blame] | 320 | Pgno pgno; |
drh | 34d0b1a | 2017-10-11 15:02:53 +0000 | [diff] [blame] | 321 | DbPage *pDbPage = 0; |
| 322 | int rc = SQLITE_OK; |
| 323 | char *zErr = 0; |
drh | 3cd8aaa | 2017-10-25 19:18:33 +0000 | [diff] [blame] | 324 | const char *zSchema; |
| 325 | int iDb; |
| 326 | Btree *pBt; |
| 327 | Pager *pPager; |
| 328 | int szPage; |
drh | 34d0b1a | 2017-10-11 15:02:53 +0000 | [diff] [blame] | 329 | |
drh | 3547e49 | 2022-12-23 14:49:24 +0000 | [diff] [blame] | 330 | (void)pRowid; |
drh | a296cda | 2018-11-03 16:09:59 +0000 | [diff] [blame] | 331 | if( pTab->db->flags & SQLITE_Defensive ){ |
| 332 | zErr = "read-only"; |
| 333 | goto update_fail; |
| 334 | } |
drh | 34d0b1a | 2017-10-11 15:02:53 +0000 | [diff] [blame] | 335 | if( argc==1 ){ |
| 336 | zErr = "cannot delete"; |
| 337 | goto update_fail; |
| 338 | } |
| 339 | pgno = sqlite3_value_int(argv[0]); |
mistachkin | aca84e6 | 2017-11-10 12:41:21 +0000 | [diff] [blame] | 340 | if( (Pgno)sqlite3_value_int(argv[1])!=pgno ){ |
drh | 34d0b1a | 2017-10-11 15:02:53 +0000 | [diff] [blame] | 341 | zErr = "cannot insert"; |
| 342 | goto update_fail; |
| 343 | } |
drh | 3cd8aaa | 2017-10-25 19:18:33 +0000 | [diff] [blame] | 344 | 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; |
drh | e684ac6 | 2022-03-08 13:59:46 +0000 | [diff] [blame] | 351 | if( pgno<1 || pBt==0 || pgno>sqlite3BtreeLastPage(pBt) ){ |
drh | 3cd8aaa | 2017-10-25 19:18:33 +0000 | [diff] [blame] | 352 | zErr = "bad page number"; |
| 353 | goto update_fail; |
| 354 | } |
| 355 | szPage = sqlite3BtreeGetPageSize(pBt); |
drh | 34d0b1a | 2017-10-11 15:02:53 +0000 | [diff] [blame] | 356 | if( sqlite3_value_type(argv[3])!=SQLITE_BLOB |
drh | 3cd8aaa | 2017-10-25 19:18:33 +0000 | [diff] [blame] | 357 | || sqlite3_value_bytes(argv[3])!=szPage |
drh | 34d0b1a | 2017-10-11 15:02:53 +0000 | [diff] [blame] | 358 | ){ |
| 359 | zErr = "bad page value"; |
| 360 | goto update_fail; |
| 361 | } |
drh | 3cd8aaa | 2017-10-25 19:18:33 +0000 | [diff] [blame] | 362 | pPager = sqlite3BtreePager(pBt); |
| 363 | rc = sqlite3PagerGet(pPager, pgno, (DbPage**)&pDbPage, 0); |
drh | 34d0b1a | 2017-10-11 15:02:53 +0000 | [diff] [blame] | 364 | if( rc==SQLITE_OK ){ |
drh | c7dd9b6 | 2022-10-31 18:01:05 +0000 | [diff] [blame] | 365 | 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); |
drh | 34d0b1a | 2017-10-11 15:02:53 +0000 | [diff] [blame] | 371 | } |
| 372 | } |
| 373 | sqlite3PagerUnref(pDbPage); |
| 374 | return rc; |
| 375 | |
| 376 | update_fail: |
| 377 | sqlite3_free(pVtab->zErrMsg); |
| 378 | pVtab->zErrMsg = sqlite3_mprintf("%s", zErr); |
| 379 | return SQLITE_ERROR; |
| 380 | } |
| 381 | |
drh | 3cd8aaa | 2017-10-25 19:18:33 +0000 | [diff] [blame] | 382 | /* 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 | */ |
| 386 | static int dbpageBegin(sqlite3_vtab *pVtab){ |
| 387 | DbpageTable *pTab = (DbpageTable *)pVtab; |
| 388 | sqlite3 *db = pTab->db; |
| 389 | int i; |
dan | e36281f | 2022-07-06 13:59:45 +0000 | [diff] [blame] | 390 | int rc = SQLITE_OK; |
| 391 | for(i=0; rc==SQLITE_OK && i<db->nDb; i++){ |
drh | 3cd8aaa | 2017-10-25 19:18:33 +0000 | [diff] [blame] | 392 | Btree *pBt = db->aDb[i].pBt; |
dan | e36281f | 2022-07-06 13:59:45 +0000 | [diff] [blame] | 393 | if( pBt ) rc = sqlite3BtreeBeginTrans(pBt, 1, 0); |
drh | 3cd8aaa | 2017-10-25 19:18:33 +0000 | [diff] [blame] | 394 | } |
dan | e36281f | 2022-07-06 13:59:45 +0000 | [diff] [blame] | 395 | return rc; |
drh | 3cd8aaa | 2017-10-25 19:18:33 +0000 | [diff] [blame] | 396 | } |
| 397 | |
| 398 | |
drh | a43c8c8 | 2017-10-11 13:48:11 +0000 | [diff] [blame] | 399 | /* |
| 400 | ** Invoke this routine to register the "dbpage" virtual table module |
| 401 | */ |
| 402 | int 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 */ |
drh | 34d0b1a | 2017-10-11 15:02:53 +0000 | [diff] [blame] | 417 | dbpageUpdate, /* xUpdate */ |
drh | 3cd8aaa | 2017-10-25 19:18:33 +0000 | [diff] [blame] | 418 | dbpageBegin, /* xBegin */ |
drh | a43c8c8 | 2017-10-11 13:48:11 +0000 | [diff] [blame] | 419 | 0, /* xSync */ |
| 420 | 0, /* xCommit */ |
| 421 | 0, /* xRollback */ |
| 422 | 0, /* xFindMethod */ |
| 423 | 0, /* xRename */ |
| 424 | 0, /* xSavepoint */ |
| 425 | 0, /* xRelease */ |
| 426 | 0, /* xRollbackTo */ |
drh | 84c501b | 2018-11-05 23:01:45 +0000 | [diff] [blame] | 427 | 0 /* xShadowName */ |
drh | a43c8c8 | 2017-10-11 13:48:11 +0000 | [diff] [blame] | 428 | }; |
| 429 | return sqlite3_create_module(db, "sqlite_dbpage", &dbpage_module, 0); |
| 430 | } |
| 431 | #elif defined(SQLITE_ENABLE_DBPAGE_VTAB) |
| 432 | int sqlite3DbpageRegister(sqlite3 *db){ return SQLITE_OK; } |
| 433 | #endif /* SQLITE_ENABLE_DBSTAT_VTAB */ |