Fix the sqlite3_dbpage virtual table so that it can read and write from
any attached database. FossilOrigin-Name: d4f893e1ae53a0445939ea2920af87d21dd36270494381028b2eaebe5c188f18
This commit is contained in:
parent
7e2b38c53b
commit
3cd8aaa748
16
manifest
16
manifest
@ -1,5 +1,5 @@
|
||||
C Add\stests\scases\sand\sfix\sminor\sissues\sin\sthe\srtreecheck()\sfunction.
|
||||
D 2017-10-25T18:17:24.834
|
||||
C Fix\sthe\ssqlite3_dbpage\svirtual\stable\sso\sthat\sit\scan\sread\sand\swrite\sfrom\nany\sattached\sdatabase.
|
||||
D 2017-10-25T19:18:33.009
|
||||
F Makefile.in e016061b23e60ac9ec27c65cb577292b6bde0307ca55abd874ab3487b3b1beb2
|
||||
F Makefile.linux-gcc 7bc79876b875010e8c8f9502eb935ca92aa3c434
|
||||
F Makefile.msc 37740aba9c4bb359c627eadccf1cfd7be4f5f847078723777ea7763969e533b1
|
||||
@ -413,7 +413,7 @@ F src/callback.c 28a8ede982fde4129b828350f78f2c01fe7d12c74d1a0a05d7108ab36f30868
|
||||
F src/complete.c a3634ab1e687055cd002e11b8f43eb75c17da23e
|
||||
F src/ctime.c ff1be3eed7bdd75aaca61ca8dc848f7c9f850ef2fb9cb56f2734e922a098f9c0
|
||||
F src/date.c 48f743d88bbe88f848532d333cca84f26e52a4f217e86f86be7fc1b919c33d74
|
||||
F src/dbpage.c c625a0bd605d4cea9a3258b8db49a5474a04976e95a9fe380cdaf74e8eb6736d
|
||||
F src/dbpage.c 196a072dffda3ad50bb078da86bb39f1d07cabe4f88e2b12482f4f5ffce73352
|
||||
F src/dbstat.c 7a4ba8518b6369ef3600c49cf9c918ad979acba610b2aebef1b656d649b96720
|
||||
F src/delete.c 21a5f1812fdb599e9f7afb9f650bdabab60a3afd51d7e94e539c982f647b0023
|
||||
F src/expr.c 755caeafc43e3cd31e1d810795712641f6e19f7e7e9575faece4c77381fd8304
|
||||
@ -715,7 +715,7 @@ F test/cursorhint2.test 8457e93d97f665f23f97cdbc8477d16e3480331b
|
||||
F test/date.test 9b73bbeb1b82d9c1f44dec5cf563bf7da58d2373
|
||||
F test/date2.test 74c234bece1b016e94dd4ef9c8cc7a199a8806c0e2291cab7ba64bace6350b10
|
||||
F test/dbfuzz.c 73047c920d6210e5912c87cdffd9a1c281d4252e
|
||||
F test/dbpage.test 10b9e91d07b0892444fff4578706648e955b5fb260218298f838da74f0d9d211
|
||||
F test/dbpage.test ace7a8d2d66b0ad043dacc61d206991184c9916de6862bf4ff165a7906a624af
|
||||
F test/dbstatus.test 73149851b3aff14fc6db478e58f9083a66422cf5
|
||||
F test/dbstatus2.test e93ab03bfae6d62d4d935f20de928c19ca0ed0ab
|
||||
F test/default.test 0cb49b1c315a0d81c81d775e407f66906a2a604d
|
||||
@ -1666,7 +1666,7 @@ F vsixtest/vsixtest.tcl 6a9a6ab600c25a91a7acc6293828957a386a8a93
|
||||
F vsixtest/vsixtest.vcxproj.data 2ed517e100c66dc455b492e1a33350c1b20fbcdc
|
||||
F vsixtest/vsixtest.vcxproj.filters 37e51ffedcdb064aad6ff33b6148725226cd608e
|
||||
F vsixtest/vsixtest_TemporaryKey.pfx e5b1b036facdb453873e7084e1cae9102ccc67a0
|
||||
P 7d26498063bbc4525976ace698fafcf161d939908f4ffb36d7a4df8fb1bce1fb
|
||||
R b18b38b834db7af5e4754d71c435762f
|
||||
U dan
|
||||
Z f91c79bfd0a67ab59612bc897b82c53b
|
||||
P d6130cd226c0ca95e02f0cbabfdc27071acdcf83e0d0cb0eaa47d992479ed9a1
|
||||
R de06ad2d264283eba96aba95f64ee6f5
|
||||
U drh
|
||||
Z 3b4d77a61659a96790923a30bda00b59
|
||||
|
@ -1 +1 @@
|
||||
d6130cd226c0ca95e02f0cbabfdc27071acdcf83e0d0cb0eaa47d992479ed9a1
|
||||
d4f893e1ae53a0445939ea2920af87d21dd36270494381028b2eaebe5c188f18
|
169
src/dbpage.c
169
src/dbpage.c
@ -42,17 +42,24 @@ struct DbpageCursor {
|
||||
sqlite3_vtab_cursor base; /* Base class. Must be first */
|
||||
int pgno; /* Current page number */
|
||||
int mxPgno; /* Last page to visit on this scan */
|
||||
Pager *pPager; /* Pager being read/written */
|
||||
DbPage *pPage1; /* Page 1 of the database */
|
||||
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 */
|
||||
Pager *pPager; /* Pager being read/written */
|
||||
int iDb; /* Index of database to analyze */
|
||||
int szPage; /* Size of each page in bytes */
|
||||
int nPage; /* Number of pages in the file */
|
||||
};
|
||||
|
||||
/* Columns */
|
||||
#define DBPAGE_COLUMN_PGNO 0
|
||||
#define DBPAGE_COLUMN_DATA 1
|
||||
#define DBPAGE_COLUMN_SCHEMA 2
|
||||
|
||||
|
||||
|
||||
/*
|
||||
** Connect to or create a dbpagevfs virtual table.
|
||||
*/
|
||||
@ -65,19 +72,7 @@ static int dbpageConnect(
|
||||
){
|
||||
DbpageTable *pTab = 0;
|
||||
int rc = SQLITE_OK;
|
||||
int iDb;
|
||||
|
||||
if( argc>=4 ){
|
||||
Token nm;
|
||||
sqlite3TokenInit(&nm, (char*)argv[3]);
|
||||
iDb = sqlite3FindDb(db, &nm);
|
||||
if( iDb<0 ){
|
||||
*pzErr = sqlite3_mprintf("no such schema: %s", argv[3]);
|
||||
return SQLITE_ERROR;
|
||||
}
|
||||
}else{
|
||||
iDb = 0;
|
||||
}
|
||||
rc = sqlite3_declare_vtab(db,
|
||||
"CREATE TABLE x(pgno INTEGER PRIMARY KEY, data BLOB, schema HIDDEN)");
|
||||
if( rc==SQLITE_OK ){
|
||||
@ -87,11 +82,8 @@ static int dbpageConnect(
|
||||
|
||||
assert( rc==SQLITE_OK || pTab==0 );
|
||||
if( rc==SQLITE_OK ){
|
||||
Btree *pBt = db->aDb[iDb].pBt;
|
||||
memset(pTab, 0, sizeof(DbpageTable));
|
||||
pTab->db = db;
|
||||
pTab->iDb = iDb;
|
||||
pTab->pPager = pBt ? sqlite3BtreePager(pBt) : 0;
|
||||
}
|
||||
|
||||
*ppVtab = (sqlite3_vtab*)pTab;
|
||||
@ -109,24 +101,55 @@ static int dbpageDisconnect(sqlite3_vtab *pVtab){
|
||||
/*
|
||||
** idxNum:
|
||||
**
|
||||
** 0 full table scan
|
||||
** 1 pgno=?1
|
||||
** 0 schema=main, full table scan
|
||||
** 1 schema=main, pgno=?1
|
||||
** 2 schema=?1, full table scan
|
||||
** 3 schema=?1, pgno=?2
|
||||
*/
|
||||
static int dbpageBestIndex(sqlite3_vtab *tab, sqlite3_index_info *pIdxInfo){
|
||||
int i;
|
||||
pIdxInfo->estimatedCost = 1.0e6; /* Initial cost estimate */
|
||||
int iPlan = 0;
|
||||
|
||||
/* If there is a schema= constraint, it must be honored. Report a
|
||||
** ridiculously large estimated cost if the schema= constraint is
|
||||
** unavailable
|
||||
*/
|
||||
for(i=0; i<pIdxInfo->nConstraint; i++){
|
||||
struct sqlite3_index_constraint *p = &pIdxInfo->aConstraint[i];
|
||||
if( p->iColumn!=DBPAGE_COLUMN_SCHEMA ) continue;
|
||||
if( p->op!=SQLITE_INDEX_CONSTRAINT_EQ ) continue;
|
||||
if( !p->usable ){
|
||||
/* No solution. Use the default SQLITE_BIG_DBL cost */
|
||||
pIdxInfo->estimatedRows = 0x7fffffff;
|
||||
return SQLITE_OK;
|
||||
}
|
||||
iPlan = 2;
|
||||
pIdxInfo->aConstraintUsage[i].argvIndex = 1;
|
||||
pIdxInfo->aConstraintUsage[i].omit = 1;
|
||||
break;
|
||||
}
|
||||
|
||||
/* If we reach this point, it means that either there is no schema=
|
||||
** constraint (in which case we use the "main" schema) or else the
|
||||
** schema constraint was accepted. Lower the estimated cost accordingly
|
||||
*/
|
||||
pIdxInfo->estimatedCost = 1.0e6;
|
||||
|
||||
/* Check for constraints against pgno */
|
||||
for(i=0; i<pIdxInfo->nConstraint; i++){
|
||||
struct sqlite3_index_constraint *p = &pIdxInfo->aConstraint[i];
|
||||
if( p->usable && p->iColumn<=0 && p->op==SQLITE_INDEX_CONSTRAINT_EQ ){
|
||||
pIdxInfo->estimatedRows = 1;
|
||||
pIdxInfo->idxFlags = SQLITE_INDEX_SCAN_UNIQUE;
|
||||
pIdxInfo->estimatedCost = 1.0;
|
||||
pIdxInfo->idxNum = 1;
|
||||
pIdxInfo->aConstraintUsage[i].argvIndex = 1;
|
||||
pIdxInfo->aConstraintUsage[i].argvIndex = iPlan ? 2 : 1;
|
||||
pIdxInfo->aConstraintUsage[i].omit = 1;
|
||||
iPlan |= 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
pIdxInfo->idxNum = iPlan;
|
||||
|
||||
if( pIdxInfo->nOrderBy>=1
|
||||
&& pIdxInfo->aOrderBy[0].iColumn<=0
|
||||
&& pIdxInfo->aOrderBy[0].desc==0
|
||||
@ -160,6 +183,7 @@ static int dbpageOpen(sqlite3_vtab *pVTab, sqlite3_vtab_cursor **ppCursor){
|
||||
*/
|
||||
static int dbpageClose(sqlite3_vtab_cursor *pCursor){
|
||||
DbpageCursor *pCsr = (DbpageCursor *)pCursor;
|
||||
if( pCsr->pPage1 ) sqlite3PagerUnrefPageOne(pCsr->pPage1);
|
||||
sqlite3_free(pCsr);
|
||||
return SQLITE_OK;
|
||||
}
|
||||
@ -179,6 +203,16 @@ static int dbpageEof(sqlite3_vtab_cursor *pCursor){
|
||||
return pCsr->pgno > pCsr->mxPgno;
|
||||
}
|
||||
|
||||
/*
|
||||
** idxNum:
|
||||
**
|
||||
** 0 schema=main, full table scan
|
||||
** 1 schema=main, pgno=?1
|
||||
** 2 schema=?1, full table scan
|
||||
** 3 schema=?1, pgno=?2
|
||||
**
|
||||
** idxStr is not used
|
||||
*/
|
||||
static int dbpageFilter(
|
||||
sqlite3_vtab_cursor *pCursor,
|
||||
int idxNum, const char *idxStr,
|
||||
@ -187,22 +221,40 @@ static int dbpageFilter(
|
||||
DbpageCursor *pCsr = (DbpageCursor *)pCursor;
|
||||
DbpageTable *pTab = (DbpageTable *)pCursor->pVtab;
|
||||
int rc = SQLITE_OK;
|
||||
Btree *pBt = pTab->db->aDb[pTab->iDb].pBt;
|
||||
sqlite3 *db = pTab->db;
|
||||
Btree *pBt;
|
||||
|
||||
pTab->szPage = sqlite3BtreeGetPageSize(pBt);
|
||||
pTab->nPage = sqlite3BtreeLastPage(pBt);
|
||||
if( idxNum==1 ){
|
||||
pCsr->pgno = sqlite3_value_int(argv[0]);
|
||||
if( pCsr->pgno<1 || pCsr->pgno>pTab->nPage ){
|
||||
/* Default setting is no rows of result */
|
||||
pCsr->pgno = 1;
|
||||
pCsr->mxPgno = 0;
|
||||
|
||||
if( idxNum & 2 ){
|
||||
const char *zSchema;
|
||||
assert( argc>=1 );
|
||||
zSchema = (const char*)sqlite3_value_text(argv[0]);
|
||||
pCsr->iDb = sqlite3FindDbName(db, zSchema);
|
||||
if( pCsr->iDb<0 ) return SQLITE_OK;
|
||||
}else{
|
||||
pCsr->iDb = 0;
|
||||
}
|
||||
pBt = db->aDb[pCsr->iDb].pBt;
|
||||
if( pBt==0 ) return SQLITE_OK;
|
||||
pCsr->pPager = sqlite3BtreePager(pBt);
|
||||
pCsr->szPage = sqlite3BtreeGetPageSize(pBt);
|
||||
pCsr->mxPgno = sqlite3BtreeLastPage(pBt);
|
||||
if( idxNum & 1 ){
|
||||
assert( argc>(idxNum>>1) );
|
||||
pCsr->pgno = sqlite3_value_int(argv[idxNum>>1]);
|
||||
if( pCsr->pgno<1 || pCsr->pgno>pCsr->mxPgno ){
|
||||
pCsr->pgno = 1;
|
||||
pCsr->mxPgno = 0;
|
||||
}else{
|
||||
pCsr->mxPgno = pCsr->pgno;
|
||||
}
|
||||
}else{
|
||||
pCsr->pgno = 1;
|
||||
pCsr->mxPgno = pTab->nPage;
|
||||
assert( pCsr->pgno==1 );
|
||||
}
|
||||
rc = sqlite3PagerGet(pCsr->pPager, 1, &pCsr->pPage1, 0);
|
||||
return rc;
|
||||
}
|
||||
|
||||
@ -212,7 +264,6 @@ static int dbpageColumn(
|
||||
int i
|
||||
){
|
||||
DbpageCursor *pCsr = (DbpageCursor *)pCursor;
|
||||
DbpageTable *pTab = (DbpageTable *)pCursor->pVtab;
|
||||
int rc = SQLITE_OK;
|
||||
switch( i ){
|
||||
case 0: { /* pgno */
|
||||
@ -221,9 +272,9 @@ static int dbpageColumn(
|
||||
}
|
||||
case 1: { /* data */
|
||||
DbPage *pDbPage = 0;
|
||||
rc = sqlite3PagerGet(pTab->pPager, pCsr->pgno, (DbPage**)&pDbPage, 0);
|
||||
rc = sqlite3PagerGet(pCsr->pPager, pCsr->pgno, (DbPage**)&pDbPage, 0);
|
||||
if( rc==SQLITE_OK ){
|
||||
sqlite3_result_blob(ctx, sqlite3PagerGetData(pDbPage), pTab->szPage,
|
||||
sqlite3_result_blob(ctx, sqlite3PagerGetData(pDbPage), pCsr->szPage,
|
||||
SQLITE_TRANSIENT);
|
||||
}
|
||||
sqlite3PagerUnref(pDbPage);
|
||||
@ -231,7 +282,7 @@ static int dbpageColumn(
|
||||
}
|
||||
default: { /* schema */
|
||||
sqlite3 *db = sqlite3_context_db_handle(ctx);
|
||||
sqlite3_result_text(ctx, db->aDb[pTab->iDb].zDbSName, -1, SQLITE_STATIC);
|
||||
sqlite3_result_text(ctx, db->aDb[pCsr->iDb].zDbSName, -1, SQLITE_STATIC);
|
||||
break;
|
||||
}
|
||||
}
|
||||
@ -255,33 +306,47 @@ static int dbpageUpdate(
|
||||
DbPage *pDbPage = 0;
|
||||
int rc = SQLITE_OK;
|
||||
char *zErr = 0;
|
||||
const char *zSchema;
|
||||
int iDb;
|
||||
Btree *pBt;
|
||||
Pager *pPager;
|
||||
int szPage;
|
||||
|
||||
if( argc==1 ){
|
||||
zErr = "cannot delete";
|
||||
goto update_fail;
|
||||
}
|
||||
pgno = sqlite3_value_int(argv[0]);
|
||||
if( pgno<1 || pgno>pTab->nPage ){
|
||||
zErr = "bad page number";
|
||||
goto update_fail;
|
||||
}
|
||||
if( sqlite3_value_int(argv[1])!=pgno ){
|
||||
zErr = "cannot insert";
|
||||
goto update_fail;
|
||||
}
|
||||
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 || pBt==0 || pgno>sqlite3BtreeLastPage(pBt) ){
|
||||
zErr = "bad page number";
|
||||
goto update_fail;
|
||||
}
|
||||
szPage = sqlite3BtreeGetPageSize(pBt);
|
||||
if( sqlite3_value_type(argv[3])!=SQLITE_BLOB
|
||||
|| sqlite3_value_bytes(argv[3])!=pTab->szPage
|
||||
|| sqlite3_value_bytes(argv[3])!=szPage
|
||||
){
|
||||
zErr = "bad page value";
|
||||
goto update_fail;
|
||||
}
|
||||
rc = sqlite3PagerGet(pTab->pPager, pgno, (DbPage**)&pDbPage, 0);
|
||||
pPager = sqlite3BtreePager(pBt);
|
||||
rc = sqlite3PagerGet(pPager, pgno, (DbPage**)&pDbPage, 0);
|
||||
if( rc==SQLITE_OK ){
|
||||
rc = sqlite3PagerWrite(pDbPage);
|
||||
if( rc==SQLITE_OK ){
|
||||
memcpy(sqlite3PagerGetData(pDbPage),
|
||||
sqlite3_value_blob(argv[3]),
|
||||
pTab->szPage);
|
||||
szPage);
|
||||
}
|
||||
}
|
||||
sqlite3PagerUnref(pDbPage);
|
||||
@ -293,6 +358,22 @@ update_fail:
|
||||
return SQLITE_ERROR;
|
||||
}
|
||||
|
||||
/* Since we do not know in advance which database files will be
|
||||
** written by the sqlite_dbpage virtual table, start a write transaction
|
||||
** on them all.
|
||||
*/
|
||||
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 ) sqlite3BtreeBeginTrans(pBt, 1);
|
||||
}
|
||||
return SQLITE_OK;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
** Invoke this routine to register the "dbpage" virtual table module
|
||||
*/
|
||||
@ -312,7 +393,7 @@ int sqlite3DbpageRegister(sqlite3 *db){
|
||||
dbpageColumn, /* xColumn - read data */
|
||||
dbpageRowid, /* xRowid - read data */
|
||||
dbpageUpdate, /* xUpdate */
|
||||
0, /* xBegin */
|
||||
dbpageBegin, /* xBegin */
|
||||
0, /* xSync */
|
||||
0, /* xCommit */
|
||||
0, /* xRollback */
|
||||
|
@ -49,6 +49,13 @@ do_execsql_test 140 {
|
||||
do_execsql_test 150 {
|
||||
SELECT pgno, quote(substr(data,1,5)) FROM sqlite_dbpage WHERE pgno=0;
|
||||
} {}
|
||||
do_execsql_test 160 {
|
||||
ATTACH ':memory:' AS aux1;
|
||||
PRAGMA aux1.page_size=4096;
|
||||
CREATE TABLE aux1.t2(a,b,c);
|
||||
INSERT INTO t2 VALUES(11,12,13);
|
||||
SELECT pgno, quote(substr(data,1,5)) FROM sqlite_dbpage('aux1');
|
||||
} {1 X'53514C6974' 2 X'0D00000001'}
|
||||
|
||||
do_execsql_test 200 {
|
||||
CREATE TEMP TABLE saved_content(x);
|
||||
@ -67,8 +74,23 @@ do_execsql_test 230 {
|
||||
do_catchsql_test 230 {
|
||||
PRAGMA integrity_check;
|
||||
} {0 ok}
|
||||
|
||||
|
||||
|
||||
do_execsql_test 240 {
|
||||
DELETE FROM saved_content;
|
||||
INSERT INTO saved_content(x)
|
||||
SELECT data FROM sqlite_dbpage WHERE schema='aux1' AND pgno=2;
|
||||
} {}
|
||||
do_execsql_test 241 {
|
||||
UPDATE sqlite_dbpage SET data=zeroblob(4096) WHERE pgno=2 AND schema='aux1';
|
||||
} {}
|
||||
do_catchsql_test 250 {
|
||||
PRAGMA aux1.integrity_check;
|
||||
} {1 {database disk image is malformed}}
|
||||
do_execsql_test 260 {
|
||||
UPDATE sqlite_dbpage SET data=(SELECT x FROM saved_content)
|
||||
WHERE pgno=2 AND schema='aux1';
|
||||
} {}
|
||||
do_catchsql_test 270 {
|
||||
PRAGMA aux1.integrity_check;
|
||||
} {0 ok}
|
||||
|
||||
finish_test
|
||||
|
Loading…
x
Reference in New Issue
Block a user