aboutsummaryrefslogtreecommitdiff
path: root/src/dbpage.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/dbpage.c')
-rw-r--r--src/dbpage.c144
1 files changed, 105 insertions, 39 deletions
diff --git a/src/dbpage.c b/src/dbpage.c
index 73c31f0da..4e2addad9 100644
--- a/src/dbpage.c
+++ b/src/dbpage.c
@@ -28,7 +28,13 @@
**
** 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.
+** 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 */
@@ -51,6 +57,8 @@ struct DbpageCursor {
struct DbpageTable {
sqlite3_vtab base; /* Base class. Must be first */
sqlite3 *db; /* The database */
+ int iDbTrunc; /* Database to truncate */
+ Pgno pgnoTrunc; /* Size to truncate to */
};
/* Columns */
@@ -59,7 +67,6 @@ struct DbpageTable {
#define DBPAGE_COLUMN_SCHEMA 2
-
/*
** Connect to or create a dbpagevfs virtual table.
*/
@@ -220,7 +227,7 @@ static int dbpageEof(sqlite3_vtab_cursor *pCursor){
** idxStr is not used
*/
static int dbpageFilter(
- sqlite3_vtab_cursor *pCursor,
+ sqlite3_vtab_cursor *pCursor,
int idxNum, const char *idxStr,
int argc, sqlite3_value **argv
){
@@ -230,10 +237,11 @@ static int dbpageFilter(
sqlite3 *db = pTab->db;
Btree *pBt;
- (void)idxStr;
-
+ UNUSED_PARAMETER(idxStr);
+ UNUSED_PARAMETER(argc);
+
/* Default setting is no rows of result */
- pCsr->pgno = 1;
+ pCsr->pgno = 1;
pCsr->mxPgno = 0;
if( idxNum & 2 ){
@@ -268,8 +276,8 @@ static int dbpageFilter(
}
static int dbpageColumn(
- sqlite3_vtab_cursor *pCursor,
- sqlite3_context *ctx,
+ sqlite3_vtab_cursor *pCursor,
+ sqlite3_context *ctx,
int i
){
DbpageCursor *pCsr = (DbpageCursor *)pCursor;
@@ -310,6 +318,24 @@ static int dbpageRowid(sqlite3_vtab_cursor *pCursor, sqlite_int64 *pRowid){
return SQLITE_OK;
}
+/*
+** Open write transactions. 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.
+**
+** Return SQLITE_OK if successful, or an SQLite error code otherwise.
+*/
+static int dbpageBeginTrans(DbpageTable *pTab){
+ sqlite3 *db = pTab->db;
+ int rc = SQLITE_OK;
+ int i;
+ for(i=0; rc==SQLITE_OK && i<db->nDb; i++){
+ Btree *pBt = db->aDb[i].pBt;
+ if( pBt ) rc = sqlite3BtreeBeginTrans(pBt, 1, 0);
+ }
+ return rc;
+}
+
static int dbpageUpdate(
sqlite3_vtab *pVtab,
int argc,
@@ -321,11 +347,11 @@ static int dbpageUpdate(
DbPage *pDbPage = 0;
int rc = SQLITE_OK;
char *zErr = 0;
- const char *zSchema;
int iDb;
Btree *pBt;
Pager *pPager;
int szPage;
+ int isInsert;
(void)pRowid;
if( pTab->db->flags & SQLITE_Defensive ){
@@ -336,21 +362,29 @@ static int dbpageUpdate(
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;
+ 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;
}
- 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;
+ 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( NEVER(pgno<1) || NEVER(pBt==0) || NEVER(pgno>sqlite3BtreeLastPage(pBt)) ){
+ if( pgno<1 || NEVER(pBt==0) ){
zErr = "bad page number";
goto update_fail;
}
@@ -358,51 +392,83 @@ static int dbpageUpdate(
if( sqlite3_value_type(argv[3])!=SQLITE_BLOB
|| sqlite3_value_bytes(argv[3])!=szPage
){
- zErr = "bad page value";
+ if( sqlite3_value_type(argv[3])==SQLITE_NULL && isInsert && pgno>1 ){
+ /* "INSERT INTO dbpage($PGNO,NULL)" causes page number $PGNO and
+ ** all subsequent pages to be deleted. */
+ pTab->iDbTrunc = iDb;
+ pTab->pgnoTrunc = pgno-1;
+ pgno = 1;
+ }else{
+ zErr = "bad page value";
+ goto update_fail;
+ }
+ }
+
+ if( dbpageBeginTrans(pTab)!=SQLITE_OK ){
+ zErr = "failed to open transaction";
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);
+ if( (rc = sqlite3PagerWrite(pDbPage))==SQLITE_OK && pData ){
+ unsigned char *aPage = sqlite3PagerGetData(pDbPage);
+ memcpy(aPage, pData, szPage);
+ pTab->pgnoTrunc = 0;
}
+ }else{
+ pTab->pgnoTrunc = 0;
}
sqlite3PagerUnref(pDbPage);
return rc;
update_fail:
+ pTab->pgnoTrunc = 0;
sqlite3_free(pVtab->zErrMsg);
pVtab->zErrMsg = sqlite3_mprintf("%s", zErr);
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 ) (void)sqlite3BtreeBeginTrans(pBt, 1, 0);
+ pTab->pgnoTrunc = 0;
+ return SQLITE_OK;
+}
+
+/* Invoke sqlite3PagerTruncate() as necessary, just prior to COMMIT
+*/
+static int dbpageSync(sqlite3_vtab *pVtab){
+ DbpageTable *pTab = (DbpageTable *)pVtab;
+ if( pTab->pgnoTrunc>0 ){
+ Btree *pBt = pTab->db->aDb[pTab->iDbTrunc].pBt;
+ Pager *pPager = sqlite3BtreePager(pBt);
+ sqlite3BtreeEnter(pBt);
+ if( pTab->pgnoTrunc<sqlite3BtreeLastPage(pBt) ){
+ sqlite3PagerTruncateImage(pPager, pTab->pgnoTrunc);
+ }
+ sqlite3BtreeLeave(pBt);
}
+ pTab->pgnoTrunc = 0;
return SQLITE_OK;
}
+/* Cancel any pending truncate.
+*/
+static int dbpageRollbackTo(sqlite3_vtab *pVtab, int notUsed1){
+ DbpageTable *pTab = (DbpageTable *)pVtab;
+ pTab->pgnoTrunc = 0;
+ (void)notUsed1;
+ return SQLITE_OK;
+}
/*
** Invoke this routine to register the "dbpage" virtual table module
*/
int sqlite3DbpageRegister(sqlite3 *db){
static sqlite3_module dbpage_module = {
- 0, /* iVersion */
+ 2, /* iVersion */
dbpageConnect, /* xCreate */
dbpageConnect, /* xConnect */
dbpageBestIndex, /* xBestIndex */
@@ -417,14 +483,14 @@ int sqlite3DbpageRegister(sqlite3 *db){
dbpageRowid, /* xRowid - read data */
dbpageUpdate, /* xUpdate */
dbpageBegin, /* xBegin */
- 0, /* xSync */
+ dbpageSync, /* xSync */
0, /* xCommit */
0, /* xRollback */
0, /* xFindMethod */
0, /* xRename */
0, /* xSavepoint */
0, /* xRelease */
- 0, /* xRollbackTo */
+ dbpageRollbackTo, /* xRollbackTo */
0, /* xShadowName */
0 /* xIntegrity */
};