SQLite

Changes On Branch dbpage
Login

Many hyperlinks are disabled.
Use anonymous login to enable hyperlinks.

Changes In Branch dbpage Excluding Merge-Ins

This is equivalent to a diff from fe0d67e72d to 6aa9c8e79b

2024-09-13
23:41
Merge sqlite_dbpage fixes into the sqlite3-rsync branch. (check-in: dff76b7a34 user: drh tags: sqlite3-rsync)
21:47
Bug fix in the enhanced sqlite_dbpage for when truncating two or more ATTACH-ed databases within the same transaction. (Leaf check-in: 6aa9c8e79b user: drh tags: dbpage)
2024-09-10
12:09
Add the ability for sqlite_dbpage to truncate the database file by writing a NULL page. Experimental. (check-in: eb3c89ee2e user: drh tags: dbpage)
2024-09-09
19:12
Fix an OOM-handling problem affecting locale=1 fts5 tables. (check-in: d8103684f6 user: dan tags: trunk)
18:45
Generalize the sqlite3_dbpage virtual table so that it is able to write new pages onto the end of the database file using INSERT. (check-in: fe0d67e72d user: drh tags: trunk)
15:39
Move the vfstrace extension out of src/ and into ext/misc/. Make it a standard part of the CLI. Improve its output. Also fix some unrelated comment typos. (check-in: 123cb1f579 user: drh tags: trunk)

Changes to src/dbpage.c.
24
25
26
27
28
29
30




31


32
33
34
35
36
37
38
** This is an eponymous virtual table so it does not need to be created before
** use.  The optional argument to the sqlite_dbpage() table name is the
** schema for the database file that is to be read.  The default schema is
** "main".
**
** The data field of sqlite_dbpage table can be updated.  The new
** value must be a BLOB which is the correct page size, otherwise the




** update fails.  Rows may not be deleted or inserted.


*/

#include "sqliteInt.h"   /* Requires access to internal data structures */
#if (defined(SQLITE_ENABLE_DBPAGE_VTAB) || defined(SQLITE_TEST)) \
    && !defined(SQLITE_OMIT_VIRTUALTABLE)

typedef struct DbpageTable DbpageTable;







>
>
>
>
|
>
>







24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
** This is an eponymous virtual table so it does not need to be created before
** use.  The optional argument to the sqlite_dbpage() table name is the
** schema for the database file that is to be read.  The default schema is
** "main".
**
** The data field of sqlite_dbpage table can be updated.  The new
** value must be a BLOB which is the correct page size, otherwise the
** update fails.  INSERT operations also work, and operate as if they
** where REPLACE.  The size of the database can be extended by INSERT-ing
** new pages on the end.
**
** Rows may not be deleted.  However, doing an INSERT to page number N
** with NULL page data causes the N-th page and all subsequent pages to be
** deleted and the database to be truncated.
*/

#include "sqliteInt.h"   /* Requires access to internal data structures */
#if (defined(SQLITE_ENABLE_DBPAGE_VTAB) || defined(SQLITE_TEST)) \
    && !defined(SQLITE_OMIT_VIRTUALTABLE)

typedef struct DbpageTable DbpageTable;
47
48
49
50
51
52
53


54
55
56
57
58
59
60
61
62
63
64
65
66
67
  int iDb;                        /* Index of database to analyze */
  int szPage;                     /* Size of each page in bytes */
};

struct DbpageTable {
  sqlite3_vtab base;              /* Base class.  Must be first */
  sqlite3 *db;                    /* The database */


};

/* Columns */
#define DBPAGE_COLUMN_PGNO    0
#define DBPAGE_COLUMN_DATA    1
#define DBPAGE_COLUMN_SCHEMA  2



/*
** Connect to or create a dbpagevfs virtual table.
*/
static int dbpageConnect(
  sqlite3 *db,







>
>






<







53
54
55
56
57
58
59
60
61
62
63
64
65
66
67

68
69
70
71
72
73
74
  int iDb;                        /* Index of database to analyze */
  int szPage;                     /* Size of each page in bytes */
};

struct DbpageTable {
  sqlite3_vtab base;              /* Base class.  Must be first */
  sqlite3 *db;                    /* The database */
  int nTrunc;                     /* Entries in aTrunc[] */
  Pgno *aTrunc;                   /* Truncation size for each database */
};

/* Columns */
#define DBPAGE_COLUMN_PGNO    0
#define DBPAGE_COLUMN_DATA    1
#define DBPAGE_COLUMN_SCHEMA  2



/*
** Connect to or create a dbpagevfs virtual table.
*/
static int dbpageConnect(
  sqlite3 *db,
96
97
98
99
100
101
102


103
104
105
106
107
108
109
  return rc;
}

/*
** Disconnect from or destroy a dbpagevfs virtual table.
*/
static int dbpageDisconnect(sqlite3_vtab *pVtab){


  sqlite3_free(pVtab);
  return SQLITE_OK;
}

/*
** idxNum:
**







>
>







103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
  return rc;
}

/*
** Disconnect from or destroy a dbpagevfs virtual table.
*/
static int dbpageDisconnect(sqlite3_vtab *pVtab){
  DbpageTable *pTab = (DbpageTable *)pVtab;
  sqlite3_free(pTab->aTrunc);
  sqlite3_free(pVtab);
  return SQLITE_OK;
}

/*
** idxNum:
**
321
322
323
324
325
326
327

328
329
330
331
332
333
334
335
336
337
338
339

340
341
342
343
344
345

346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365














366
367

368
369
370
371
372
373
374
375
376
377

378
379
380
381
382
383
384
  DbPage *pDbPage = 0;
  int rc = SQLITE_OK;
  char *zErr = 0;
  int iDb;
  Btree *pBt;
  Pager *pPager;
  int szPage;


  (void)pRowid;
  if( pTab->db->flags & SQLITE_Defensive ){
    zErr = "read-only";
    goto update_fail;
  }
  if( argc==1 ){
    zErr = "cannot delete";
    goto update_fail;
  }
  if( sqlite3_value_type(argv[0])==SQLITE_NULL ){
    pgno = (Pgno)sqlite3_value_int(argv[2]);

  }else{
    pgno = sqlite3_value_int(argv[0]);
    if( (Pgno)sqlite3_value_int(argv[1])!=pgno ){
      zErr = "cannot insert";
      goto update_fail;
    }

  }
  if( sqlite3_value_type(argv[4])==SQLITE_NULL ){
    iDb = 0;
  }else{
    const char *zSchema = (const char*)sqlite3_value_text(argv[4]);
    iDb = zSchema ? sqlite3FindDbName(pTab->db, zSchema) : -1;
    if( iDb<0 ){
      zErr = "no such schema";
      goto update_fail;
    }
  }
  pBt = pTab->db->aDb[iDb].pBt;
  if( pgno<1 || NEVER(pBt==0) ){
    zErr = "bad page number";
    goto update_fail;
  }
  szPage = sqlite3BtreeGetPageSize(pBt);
  if( sqlite3_value_type(argv[3])!=SQLITE_BLOB 
   || sqlite3_value_bytes(argv[3])!=szPage
  ){














    zErr = "bad page value";
    goto update_fail;

  }
  pPager = sqlite3BtreePager(pBt);
  rc = sqlite3PagerGet(pPager, pgno, (DbPage**)&pDbPage, 0);
  if( rc==SQLITE_OK ){
    const void *pData = sqlite3_value_blob(argv[3]);
    assert( pData!=0 || pTab->db->mallocFailed );
    if( pData
     && (rc = sqlite3PagerWrite(pDbPage))==SQLITE_OK
    ){
      memcpy(sqlite3PagerGetData(pDbPage), pData, szPage);

    }
  }
  sqlite3PagerUnref(pDbPage);
  return rc;

update_fail:
  sqlite3_free(pVtab->zErrMsg);







>












>






>





|














>
>
>
>
>
>
>
>
>
>
>
>
>
>
|
|
>





<
<
|
<
|
>







330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399


400

401
402
403
404
405
406
407
408
409
  DbPage *pDbPage = 0;
  int rc = SQLITE_OK;
  char *zErr = 0;
  int iDb;
  Btree *pBt;
  Pager *pPager;
  int szPage;
  int isInsert;

  (void)pRowid;
  if( pTab->db->flags & SQLITE_Defensive ){
    zErr = "read-only";
    goto update_fail;
  }
  if( argc==1 ){
    zErr = "cannot delete";
    goto update_fail;
  }
  if( sqlite3_value_type(argv[0])==SQLITE_NULL ){
    pgno = (Pgno)sqlite3_value_int(argv[2]);
    isInsert = 1;
  }else{
    pgno = sqlite3_value_int(argv[0]);
    if( (Pgno)sqlite3_value_int(argv[1])!=pgno ){
      zErr = "cannot insert";
      goto update_fail;
    }
    isInsert = 0;
  }
  if( sqlite3_value_type(argv[4])==SQLITE_NULL ){
    iDb = 0;
  }else{
    const char *zSchema = (const char*)sqlite3_value_text(argv[4]);
    iDb = sqlite3FindDbName(pTab->db, zSchema);
    if( iDb<0 ){
      zErr = "no such schema";
      goto update_fail;
    }
  }
  pBt = pTab->db->aDb[iDb].pBt;
  if( pgno<1 || NEVER(pBt==0) ){
    zErr = "bad page number";
    goto update_fail;
  }
  szPage = sqlite3BtreeGetPageSize(pBt);
  if( sqlite3_value_type(argv[3])!=SQLITE_BLOB 
   || sqlite3_value_bytes(argv[3])!=szPage
  ){
    if( sqlite3_value_type(argv[3])==SQLITE_NULL && isInsert ){
      if( iDb>=pTab->nTrunc ){
        testcase( pTab->aTrunc!=0 );
        pTab->aTrunc = sqlite3_realloc(pTab->aTrunc, (iDb+1)*sizeof(Pgno));
        if( pTab->aTrunc ){
          int j;
          for(j=pTab->nTrunc; j<iDb; j++) pTab->aTrunc[j] = 0;
          pTab->nTrunc = iDb+1;
        }else{
          return SQLITE_NOMEM;
        }
      }
      pTab->aTrunc[iDb] = pgno;
    }else{
      zErr = "bad page value";
      goto update_fail;
    }
  }
  pPager = sqlite3BtreePager(pBt);
  rc = sqlite3PagerGet(pPager, pgno, (DbPage**)&pDbPage, 0);
  if( rc==SQLITE_OK ){
    const void *pData = sqlite3_value_blob(argv[3]);


    if( (rc = sqlite3PagerWrite(pDbPage))==SQLITE_OK && pData ){

      unsigned char *aPage = sqlite3PagerGetData(pDbPage);
      memcpy(aPage, pData, szPage);
    }
  }
  sqlite3PagerUnref(pDbPage);
  return rc;

update_fail:
  sqlite3_free(pVtab->zErrMsg);
393
394
395
396
397
398
399




















400
401
402
403
404
405
406
static int dbpageBegin(sqlite3_vtab *pVtab){
  DbpageTable *pTab = (DbpageTable *)pVtab;
  sqlite3 *db = pTab->db;
  int i;
  for(i=0; i<db->nDb; i++){
    Btree *pBt = db->aDb[i].pBt;
    if( pBt ) (void)sqlite3BtreeBeginTrans(pBt, 1, 0);




















  }
  return SQLITE_OK;
}


/*
** Invoke this routine to register the "dbpage" virtual table module







>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>







418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
static int dbpageBegin(sqlite3_vtab *pVtab){
  DbpageTable *pTab = (DbpageTable *)pVtab;
  sqlite3 *db = pTab->db;
  int i;
  for(i=0; i<db->nDb; i++){
    Btree *pBt = db->aDb[i].pBt;
    if( pBt ) (void)sqlite3BtreeBeginTrans(pBt, 1, 0);
  }
  if( pTab->nTrunc>0 ){
    memset(pTab->aTrunc, 0, sizeof(pTab->aTrunc[0])*pTab->nTrunc);
  }
  return SQLITE_OK;
}

/* Invoke sqlite3PagerTruncate() as necessary, just prior to COMMIT
*/
static int dbpageSync(sqlite3_vtab *pVtab){
  int iDb;
  DbpageTable *pTab = (DbpageTable *)pVtab;

  for(iDb=0; iDb<pTab->nTrunc; iDb++){
    if( pTab->aTrunc[iDb]>0 ){
      Btree *pBt = pTab->db->aDb[iDb].pBt;
      Pager *pPager = sqlite3BtreePager(pBt);
      sqlite3PagerTruncateImage(pPager, pTab->aTrunc[iDb]);
      pTab->aTrunc[iDb] = 0;
    }
  }
  return SQLITE_OK;
}


/*
** Invoke this routine to register the "dbpage" virtual table module
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
    dbpageFilter,                 /* xFilter - configure scan constraints */
    dbpageNext,                   /* xNext - advance a cursor */
    dbpageEof,                    /* xEof - check for end of scan */
    dbpageColumn,                 /* xColumn - read data */
    dbpageRowid,                  /* xRowid - read data */
    dbpageUpdate,                 /* xUpdate */
    dbpageBegin,                  /* xBegin */
    0,                            /* xSync */
    0,                            /* xCommit */
    0,                            /* xRollback */
    0,                            /* xFindMethod */
    0,                            /* xRename */
    0,                            /* xSavepoint */
    0,                            /* xRelease */
    0,                            /* xRollbackTo */







|







463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
    dbpageFilter,                 /* xFilter - configure scan constraints */
    dbpageNext,                   /* xNext - advance a cursor */
    dbpageEof,                    /* xEof - check for end of scan */
    dbpageColumn,                 /* xColumn - read data */
    dbpageRowid,                  /* xRowid - read data */
    dbpageUpdate,                 /* xUpdate */
    dbpageBegin,                  /* xBegin */
    dbpageSync,                   /* xSync */
    0,                            /* xCommit */
    0,                            /* xRollback */
    0,                            /* xFindMethod */
    0,                            /* xRename */
    0,                            /* xSavepoint */
    0,                            /* xRelease */
    0,                            /* xRollbackTo */