#include "sqliteInt.h"
#if (defined(SQLITE_ENABLE_DBPAGE_VTAB) || defined(SQLITE_TEST)) \
&& !defined(SQLITE_OMIT_VIRTUALTABLE)
typedef struct DbpageTable DbpageTable;
typedef struct DbpageCursor DbpageCursor;
struct DbpageCursor {
sqlite3_vtab_cursor base;
int pgno;
int mxPgno;
Pager *pPager;
DbPage *pPage1;
int iDb;
int szPage;
};
struct DbpageTable {
sqlite3_vtab base;
sqlite3 *db;
};
#define DBPAGE_COLUMN_PGNO 0
#define DBPAGE_COLUMN_DATA 1
#define DBPAGE_COLUMN_SCHEMA 2
static int dbpageConnect(
sqlite3 *db,
void *pAux,
int argc, const char *const*argv,
sqlite3_vtab **ppVtab,
char **pzErr
){
DbpageTable *pTab = 0;
int rc = SQLITE_OK;
(void)pAux;
(void)argc;
(void)argv;
(void)pzErr;
sqlite3_vtab_config(db, SQLITE_VTAB_DIRECTONLY);
sqlite3_vtab_config(db, SQLITE_VTAB_USES_ALL_SCHEMAS);
rc = sqlite3_declare_vtab(db,
"CREATE TABLE x(pgno INTEGER PRIMARY KEY, data BLOB, schema HIDDEN)");
if( rc==SQLITE_OK ){
pTab = (DbpageTable *)sqlite3_malloc64(sizeof(DbpageTable));
if( pTab==0 ) rc = SQLITE_NOMEM_BKPT;
}
assert( rc==SQLITE_OK || pTab==0 );
if( rc==SQLITE_OK ){
memset(pTab, 0, sizeof(DbpageTable));
pTab->db = db;
}
*ppVtab = (sqlite3_vtab*)pTab;
return rc;
}
static int dbpageDisconnect(sqlite3_vtab *pVtab){
sqlite3_free(pVtab);
return SQLITE_OK;
}
static int dbpageBestIndex(sqlite3_vtab *tab, sqlite3_index_info *pIdxInfo){
int i;
int iPlan = 0;
(void)tab;
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 ){
return SQLITE_CONSTRAINT;
}
iPlan = 2;
pIdxInfo->aConstraintUsage[i].argvIndex = 1;
pIdxInfo->aConstraintUsage[i].omit = 1;
break;
}
pIdxInfo->estimatedCost = 1.0e6;
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->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
){
pIdxInfo->orderByConsumed = 1;
}
return SQLITE_OK;
}
static int dbpageOpen(sqlite3_vtab *pVTab, sqlite3_vtab_cursor **ppCursor){
DbpageCursor *pCsr;
pCsr = (DbpageCursor *)sqlite3_malloc64(sizeof(DbpageCursor));
if( pCsr==0 ){
return SQLITE_NOMEM_BKPT;
}else{
memset(pCsr, 0, sizeof(DbpageCursor));
pCsr->base.pVtab = pVTab;
pCsr->pgno = -1;
}
*ppCursor = (sqlite3_vtab_cursor *)pCsr;
return SQLITE_OK;
}
static int dbpageClose(sqlite3_vtab_cursor *pCursor){
DbpageCursor *pCsr = (DbpageCursor *)pCursor;
if( pCsr->pPage1 ) sqlite3PagerUnrefPageOne(pCsr->pPage1);
sqlite3_free(pCsr);
return SQLITE_OK;
}
static int dbpageNext(sqlite3_vtab_cursor *pCursor){
int rc = SQLITE_OK;
DbpageCursor *pCsr = (DbpageCursor *)pCursor;
pCsr->pgno++;
return rc;
}
static int dbpageEof(sqlite3_vtab_cursor *pCursor){
DbpageCursor *pCsr = (DbpageCursor *)pCursor;
return pCsr->pgno > pCsr->mxPgno;
}
static int dbpageFilter(
sqlite3_vtab_cursor *pCursor,
int idxNum, const char *idxStr,
int argc, sqlite3_value **argv
){
DbpageCursor *pCsr = (DbpageCursor *)pCursor;
DbpageTable *pTab = (DbpageTable *)pCursor->pVtab;
int rc;
sqlite3 *db = pTab->db;
Btree *pBt;
(void)idxStr;
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( NEVER(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{
assert( pCsr->pgno==1 );
}
if( pCsr->pPage1 ) sqlite3PagerUnrefPageOne(pCsr->pPage1);
rc = sqlite3PagerGet(pCsr->pPager, 1, &pCsr->pPage1, 0);
return rc;
}
static int dbpageColumn(
sqlite3_vtab_cursor *pCursor,
sqlite3_context *ctx,
int i
){
DbpageCursor *pCsr = (DbpageCursor *)pCursor;
int rc = SQLITE_OK;
switch( i ){
case 0: {
sqlite3_result_int(ctx, pCsr->pgno);
break;
}
case 1: {
DbPage *pDbPage = 0;
if( pCsr->pgno==((PENDING_BYTE/pCsr->szPage)+1) ){
sqlite3_result_zeroblob(ctx, pCsr->szPage);
}else{
rc = sqlite3PagerGet(pCsr->pPager, pCsr->pgno, (DbPage**)&pDbPage, 0);
if( rc==SQLITE_OK ){
sqlite3_result_blob(ctx, sqlite3PagerGetData(pDbPage), pCsr->szPage,
SQLITE_TRANSIENT);
}
sqlite3PagerUnref(pDbPage);
}
break;
}
default: {
sqlite3 *db = sqlite3_context_db_handle(ctx);
sqlite3_result_text(ctx, db->aDb[pCsr->iDb].zDbSName, -1, SQLITE_STATIC);
break;
}
}
return rc;
}
static int dbpageRowid(sqlite3_vtab_cursor *pCursor, sqlite_int64 *pRowid){
DbpageCursor *pCsr = (DbpageCursor *)pCursor;
*pRowid = pCsr->pgno;
return SQLITE_OK;
}
static int dbpageUpdate(
sqlite3_vtab *pVtab,
int argc,
sqlite3_value **argv,
sqlite_int64 *pRowid
){
DbpageTable *pTab = (DbpageTable *)pVtab;
Pgno pgno;
DbPage *pDbPage = 0;
int rc = SQLITE_OK;
char *zErr = 0;
const char *zSchema;
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;
}
pgno = sqlite3_value_int(argv[0]);
if( sqlite3_value_type(argv[0])==SQLITE_NULL
|| (Pgno)sqlite3_value_int(argv[1])!=pgno
){
zErr = "cannot insert";
goto update_fail;
}
zSchema = (const char*)sqlite3_value_text(argv[4]);
iDb = ALWAYS(zSchema) ? sqlite3FindDbName(pTab->db, zSchema) : -1;
if( NEVER(iDb<0) ){
zErr = "no such schema";
goto update_fail;
}
pBt = pTab->db->aDb[iDb].pBt;
if( NEVER(pgno<1) || NEVER(pBt==0) || NEVER(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])!=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);
pVtab->zErrMsg = sqlite3_mprintf("%s", zErr);
return SQLITE_ERROR;
}
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;
}
int sqlite3DbpageRegister(sqlite3 *db){
static sqlite3_module dbpage_module = {
0,
dbpageConnect,
dbpageConnect,
dbpageBestIndex,
dbpageDisconnect,
dbpageDisconnect,
dbpageOpen,
dbpageClose,
dbpageFilter,
dbpageNext,
dbpageEof,
dbpageColumn,
dbpageRowid,
dbpageUpdate,
dbpageBegin,
0,
0,
0,
0,
0,
0,
0,
0,
0
};
return sqlite3_create_module(db, "sqlite_dbpage", &dbpage_module, 0);
}
#elif defined(SQLITE_ENABLE_DBPAGE_VTAB)
int sqlite3DbpageRegister(sqlite3 *db){ return SQLITE_OK; }
#endif