1
0
mirror of https://github.com/sqlite/sqlite.git synced 2025-08-01 06:27:03 +03:00

Enhancements to sqlite_dbpage() so that it accepts INSERT statements that can

extend or truncate the database.  Add the sqlite3-rsync utility program that
make a copy of a live database over SSH.

FossilOrigin-Name: b7a8ce4c8c5fc6a3b4744d412d96f99d2452eb4086ad84472511da3b4d6afec6
This commit is contained in:
drh
2024-09-14 16:39:02 +00:00
9 changed files with 1985 additions and 33 deletions

View File

@ -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 nTrunc; /* Entries in aTrunc[] */
Pgno *aTrunc; /* Truncation size for each database */
};
/* Columns */
@ -59,7 +67,6 @@ struct DbpageTable {
#define DBPAGE_COLUMN_SCHEMA 2
/*
** Connect to or create a dbpagevfs virtual table.
*/
@ -100,6 +107,8 @@ static int dbpageConnect(
** 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;
}
@ -325,6 +334,7 @@ static int dbpageUpdate(
Btree *pBt;
Pager *pPager;
int szPage;
int isInsert;
(void)pRowid;
if( pTab->db->flags & SQLITE_Defensive ){
@ -337,18 +347,20 @@ static int dbpageUpdate(
}
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 = zSchema ? sqlite3FindDbName(pTab->db, zSchema) : -1;
iDb = sqlite3FindDbName(pTab->db, zSchema);
if( iDb<0 ){
zErr = "no such schema";
goto update_fail;
@ -363,18 +375,31 @@ static int dbpageUpdate(
if( sqlite3_value_type(argv[3])!=SQLITE_BLOB
|| sqlite3_value_bytes(argv[3])!=szPage
){
zErr = "bad page value";
goto update_fail;
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]);
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);
}
}
sqlite3PagerUnref(pDbPage);
@ -398,6 +423,26 @@ static int dbpageBegin(sqlite3_vtab *pVtab){
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;
}
@ -422,7 +467,7 @@ int sqlite3DbpageRegister(sqlite3 *db){
dbpageRowid, /* xRowid - read data */
dbpageUpdate, /* xUpdate */
dbpageBegin, /* xBegin */
0, /* xSync */
dbpageSync, /* xSync */
0, /* xCommit */
0, /* xRollback */
0, /* xFindMethod */

View File

@ -1209,6 +1209,7 @@ INCLUDE test_windirent.c
INCLUDE ../ext/misc/memtrace.c
INCLUDE ../ext/misc/pcachetrace.c
INCLUDE ../ext/misc/shathree.c
INCLUDE ../ext/misc/sha1.c
INCLUDE ../ext/misc/uint.c
INCLUDE ../ext/misc/decimal.c
INCLUDE ../ext/misc/percentile.c
@ -5392,6 +5393,7 @@ static void open_db(ShellState *p, int openFlags){
#ifndef SQLITE_OMIT_LOAD_EXTENSION
sqlite3_enable_load_extension(p->db, 1);
#endif
sqlite3_sha_init(p->db, 0, 0);
sqlite3_shathree_init(p->db, 0, 0);
sqlite3_uint_init(p->db, 0, 0);
sqlite3_stmtrand_init(p->db, 0, 0);