mirror of
https://github.com/sqlite/sqlite.git
synced 2025-07-30 19:03:16 +03:00
Add the "--lost-and-found" option to the ".recover" command. For setting the name of the orphaned rows table.
FossilOrigin-Name: 67bb88e24c74d02ae0c4ac6ff2f873f6b0035ccefe5cccfc71c5686cbc76b4c3
This commit is contained in:
@ -10,10 +10,14 @@
|
|||||||
**
|
**
|
||||||
******************************************************************************
|
******************************************************************************
|
||||||
**
|
**
|
||||||
** This file contains an implementation of the eponymous "sqlite_dbdata"
|
** This file contains an implementation of two eponymous virtual tables,
|
||||||
** virtual table. sqlite_dbdata is used to extract data directly from a
|
** "sqlite_dbdata" and "sqlite_dbptr". Both modules require that the
|
||||||
** database b-tree page and its associated overflow pages, bypassing the b-tree
|
** "sqlite_dbpage" eponymous virtual table be available.
|
||||||
** layer. The table schema is equivalent to:
|
**
|
||||||
|
** SQLITE_DBDATA:
|
||||||
|
** sqlite_dbdata is used to extract data directly from a database b-tree
|
||||||
|
** page and its associated overflow pages, bypassing the b-tree layer.
|
||||||
|
** The table schema is equivalent to:
|
||||||
**
|
**
|
||||||
** CREATE TABLE sqlite_dbdata(
|
** CREATE TABLE sqlite_dbdata(
|
||||||
** pgno INTEGER,
|
** pgno INTEGER,
|
||||||
@ -23,23 +27,25 @@
|
|||||||
** schema TEXT HIDDEN
|
** schema TEXT HIDDEN
|
||||||
** );
|
** );
|
||||||
**
|
**
|
||||||
** IMPORTANT: THE VIRTUAL TABLE SCHEMA ABOVE IS SUBJECT TO CHANGE. IN THE
|
** IMPORTANT: THE VIRTUAL TABLE SCHEMA ABOVE IS SUBJECT TO CHANGE. IN THE
|
||||||
** FUTURE NEW NON-HIDDEN COLUMNS MAY BE ADDED BETWEEN "value" AND "schema".
|
** FUTURE NEW NON-HIDDEN COLUMNS MAY BE ADDED BETWEEN "value" AND
|
||||||
|
** "schema".
|
||||||
**
|
**
|
||||||
** Each page of the database is inspected. If it cannot be interpreted as a
|
** Each page of the database is inspected. If it cannot be interpreted as
|
||||||
** b-tree page, or if it is a b-tree page containing 0 entries, the
|
** a b-tree page, or if it is a b-tree page containing 0 entries, the
|
||||||
** sqlite_dbdata table contains no rows for that page. Otherwise, the table
|
** sqlite_dbdata table contains no rows for that page. Otherwise, the
|
||||||
** contains one row for each field in the record associated with each
|
** table contains one row for each field in the record associated with
|
||||||
** cell on the page. For intkey b-trees, the key value is stored in field -1.
|
** each cell on the page. For intkey b-trees, the key value is stored in
|
||||||
|
** field -1.
|
||||||
**
|
**
|
||||||
** For example, for the database:
|
** For example, for the database:
|
||||||
**
|
**
|
||||||
** CREATE TABLE t1(a, b); -- root page is page 2
|
** CREATE TABLE t1(a, b); -- root page is page 2
|
||||||
** INSERT INTO t1(rowid, a, b) VALUES(5, 'v', 'five');
|
** INSERT INTO t1(rowid, a, b) VALUES(5, 'v', 'five');
|
||||||
** INSERT INTO t1(rowid, a, b) VALUES(10, 'x', 'ten');
|
** INSERT INTO t1(rowid, a, b) VALUES(10, 'x', 'ten');
|
||||||
**
|
**
|
||||||
** the sqlite_dbdata table contains, as well as from entries related to
|
** the sqlite_dbdata table contains, as well as from entries related to
|
||||||
** page 1, content equivalent to:
|
** page 1, content equivalent to:
|
||||||
**
|
**
|
||||||
** INSERT INTO sqlite_dbdata(pgno, cell, field, value) VALUES
|
** INSERT INTO sqlite_dbdata(pgno, cell, field, value) VALUES
|
||||||
** (2, 0, -1, 5 ),
|
** (2, 0, -1, 5 ),
|
||||||
@ -49,19 +55,21 @@
|
|||||||
** (2, 1, 0, 'x' ),
|
** (2, 1, 0, 'x' ),
|
||||||
** (2, 1, 1, 'ten' );
|
** (2, 1, 1, 'ten' );
|
||||||
**
|
**
|
||||||
** If database corruption is encountered, this module does not report an
|
** If database corruption is encountered, this module does not report an
|
||||||
** error. Instead, it attempts to extract as much data as possible and
|
** error. Instead, it attempts to extract as much data as possible and
|
||||||
** ignores the corruption.
|
** ignores the corruption.
|
||||||
**
|
|
||||||
** This module requires that the "sqlite_dbpage" eponymous virtual table be
|
|
||||||
** available.
|
|
||||||
**
|
**
|
||||||
|
** SQLITE_DBPTR:
|
||||||
|
** The sqlite_dbptr table has the following schema:
|
||||||
**
|
**
|
||||||
** CREATE TABLE sqlite_dbptr(
|
** CREATE TABLE sqlite_dbptr(
|
||||||
** pgno INTEGER,
|
** pgno INTEGER,
|
||||||
** child INTEGER,
|
** child INTEGER,
|
||||||
** schema TEXT HIDDEN
|
** schema TEXT HIDDEN
|
||||||
** );
|
** );
|
||||||
|
**
|
||||||
|
** It contains one entry for each b-tree pointer between a parent and
|
||||||
|
** child page in the database.
|
||||||
*/
|
*/
|
||||||
#if !defined(SQLITEINT_H)
|
#if !defined(SQLITEINT_H)
|
||||||
#include "sqlite3ext.h"
|
#include "sqlite3ext.h"
|
||||||
@ -77,8 +85,7 @@ SQLITE_EXTENSION_INIT1
|
|||||||
typedef struct DbdataTable DbdataTable;
|
typedef struct DbdataTable DbdataTable;
|
||||||
typedef struct DbdataCursor DbdataCursor;
|
typedef struct DbdataCursor DbdataCursor;
|
||||||
|
|
||||||
|
/* Cursor object */
|
||||||
/* A cursor for the sqlite_dbdata table */
|
|
||||||
struct DbdataCursor {
|
struct DbdataCursor {
|
||||||
sqlite3_vtab_cursor base; /* Base class. Must be first */
|
sqlite3_vtab_cursor base; /* Base class. Must be first */
|
||||||
sqlite3_stmt *pStmt; /* For fetching database pages */
|
sqlite3_stmt *pStmt; /* For fetching database pages */
|
||||||
@ -103,7 +110,7 @@ struct DbdataCursor {
|
|||||||
sqlite3_int64 iIntkey; /* Integer key value */
|
sqlite3_int64 iIntkey; /* Integer key value */
|
||||||
};
|
};
|
||||||
|
|
||||||
/* The sqlite_dbdata table */
|
/* Table object */
|
||||||
struct DbdataTable {
|
struct DbdataTable {
|
||||||
sqlite3_vtab base; /* Base class. Must be first */
|
sqlite3_vtab base; /* Base class. Must be first */
|
||||||
sqlite3 *db; /* The database connection */
|
sqlite3 *db; /* The database connection */
|
||||||
@ -111,16 +118,12 @@ struct DbdataTable {
|
|||||||
int bPtr; /* True for sqlite3_dbptr table */
|
int bPtr; /* True for sqlite3_dbptr table */
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/* Column and schema definitions for sqlite_dbdata */
|
||||||
#define DBDATA_COLUMN_PGNO 0
|
#define DBDATA_COLUMN_PGNO 0
|
||||||
#define DBDATA_COLUMN_CELL 1
|
#define DBDATA_COLUMN_CELL 1
|
||||||
#define DBDATA_COLUMN_FIELD 2
|
#define DBDATA_COLUMN_FIELD 2
|
||||||
#define DBDATA_COLUMN_VALUE 3
|
#define DBDATA_COLUMN_VALUE 3
|
||||||
#define DBDATA_COLUMN_SCHEMA 4
|
#define DBDATA_COLUMN_SCHEMA 4
|
||||||
|
|
||||||
#define DBPTR_COLUMN_PGNO 0
|
|
||||||
#define DBPTR_COLUMN_CHILD 1
|
|
||||||
#define DBPTR_COLUMN_SCHEMA 2
|
|
||||||
|
|
||||||
#define DBDATA_SCHEMA \
|
#define DBDATA_SCHEMA \
|
||||||
"CREATE TABLE x(" \
|
"CREATE TABLE x(" \
|
||||||
" pgno INTEGER," \
|
" pgno INTEGER," \
|
||||||
@ -130,6 +133,10 @@ struct DbdataTable {
|
|||||||
" schema TEXT HIDDEN" \
|
" schema TEXT HIDDEN" \
|
||||||
")"
|
")"
|
||||||
|
|
||||||
|
/* Column and schema definitions for sqlite_dbptr */
|
||||||
|
#define DBPTR_COLUMN_PGNO 0
|
||||||
|
#define DBPTR_COLUMN_CHILD 1
|
||||||
|
#define DBPTR_COLUMN_SCHEMA 2
|
||||||
#define DBPTR_SCHEMA \
|
#define DBPTR_SCHEMA \
|
||||||
"CREATE TABLE x(" \
|
"CREATE TABLE x(" \
|
||||||
" pgno INTEGER," \
|
" pgno INTEGER," \
|
||||||
@ -138,7 +145,8 @@ struct DbdataTable {
|
|||||||
")"
|
")"
|
||||||
|
|
||||||
/*
|
/*
|
||||||
** Connect to the sqlite_dbdata virtual table.
|
** Connect to an sqlite_dbdata (pAux==0) or sqlite_dbptr (pAux!=0) virtual
|
||||||
|
** table.
|
||||||
*/
|
*/
|
||||||
static int dbdataConnect(
|
static int dbdataConnect(
|
||||||
sqlite3 *db,
|
sqlite3 *db,
|
||||||
@ -166,7 +174,7 @@ static int dbdataConnect(
|
|||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
** Disconnect from or destroy a dbdata virtual table.
|
** Disconnect from or destroy a sqlite_dbdata or sqlite_dbptr virtual table.
|
||||||
*/
|
*/
|
||||||
static int dbdataDisconnect(sqlite3_vtab *pVtab){
|
static int dbdataDisconnect(sqlite3_vtab *pVtab){
|
||||||
DbdataTable *pTab = (DbdataTable*)pVtab;
|
DbdataTable *pTab = (DbdataTable*)pVtab;
|
||||||
@ -178,7 +186,6 @@ static int dbdataDisconnect(sqlite3_vtab *pVtab){
|
|||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
**
|
|
||||||
** This function interprets two types of constraints:
|
** This function interprets two types of constraints:
|
||||||
**
|
**
|
||||||
** schema=?
|
** schema=?
|
||||||
@ -239,7 +246,7 @@ static int dbdataBestIndex(sqlite3_vtab *tab, sqlite3_index_info *pIdx){
|
|||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
** Open a new dbdata cursor.
|
** Open a new sqlite_dbdata or sqlite_dbptr cursor.
|
||||||
*/
|
*/
|
||||||
static int dbdataOpen(sqlite3_vtab *pVTab, sqlite3_vtab_cursor **ppCursor){
|
static int dbdataOpen(sqlite3_vtab *pVTab, sqlite3_vtab_cursor **ppCursor){
|
||||||
DbdataCursor *pCsr;
|
DbdataCursor *pCsr;
|
||||||
@ -256,6 +263,10 @@ static int dbdataOpen(sqlite3_vtab *pVTab, sqlite3_vtab_cursor **ppCursor){
|
|||||||
return SQLITE_OK;
|
return SQLITE_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
** Restore a cursor object to the state it was in when first allocated
|
||||||
|
** by dbdataOpen().
|
||||||
|
*/
|
||||||
static void dbdataResetCursor(DbdataCursor *pCsr){
|
static void dbdataResetCursor(DbdataCursor *pCsr){
|
||||||
DbdataTable *pTab = (DbdataTable*)(pCsr->base.pVtab);
|
DbdataTable *pTab = (DbdataTable*)(pCsr->base.pVtab);
|
||||||
if( pTab->pStmt==0 ){
|
if( pTab->pStmt==0 ){
|
||||||
@ -271,7 +282,7 @@ static void dbdataResetCursor(DbdataCursor *pCsr){
|
|||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
** Close a dbdata cursor.
|
** Close an sqlite_dbdata or sqlite_dbptr cursor.
|
||||||
*/
|
*/
|
||||||
static int dbdataClose(sqlite3_vtab_cursor *pCursor){
|
static int dbdataClose(sqlite3_vtab_cursor *pCursor){
|
||||||
DbdataCursor *pCsr = (DbdataCursor*)pCursor;
|
DbdataCursor *pCsr = (DbdataCursor*)pCursor;
|
||||||
@ -280,8 +291,9 @@ static int dbdataClose(sqlite3_vtab_cursor *pCursor){
|
|||||||
return SQLITE_OK;
|
return SQLITE_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
/* Decode big-endian integers */
|
** Utility methods to decode 16 and 32-bit big-endian unsigned integers.
|
||||||
|
*/
|
||||||
static unsigned int get_uint16(unsigned char *a){
|
static unsigned int get_uint16(unsigned char *a){
|
||||||
return (a[0]<<8)|a[1];
|
return (a[0]<<8)|a[1];
|
||||||
}
|
}
|
||||||
@ -289,11 +301,21 @@ static unsigned int get_uint32(unsigned char *a){
|
|||||||
return (a[0]<<24)|(a[1]<<16)|(a[2]<<8)|a[3];
|
return (a[0]<<24)|(a[1]<<16)|(a[2]<<8)|a[3];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
** Load page pgno from the database via the sqlite_dbpage virtual table.
|
||||||
|
** If successful, set (*ppPage) to point to a buffer containing the page
|
||||||
|
** data, (*pnPage) to the size of that buffer in bytes and return
|
||||||
|
** SQLITE_OK. In this case it is the responsibility of the caller to
|
||||||
|
** eventually free the buffer using sqlite3_free().
|
||||||
|
**
|
||||||
|
** Or, if an error occurs, set both (*ppPage) and (*pnPage) to 0 and
|
||||||
|
** return an SQLite error code.
|
||||||
|
*/
|
||||||
static int dbdataLoadPage(
|
static int dbdataLoadPage(
|
||||||
DbdataCursor *pCsr,
|
DbdataCursor *pCsr, /* Cursor object */
|
||||||
u32 pgno,
|
u32 pgno, /* Page number of page to load */
|
||||||
u8 **ppPage,
|
u8 **ppPage, /* OUT: pointer to page buffer */
|
||||||
int *pnPage
|
int *pnPage /* OUT: Size of (*ppPage) in bytes */
|
||||||
){
|
){
|
||||||
int rc2;
|
int rc2;
|
||||||
int rc = SQLITE_OK;
|
int rc = SQLITE_OK;
|
||||||
@ -338,6 +360,10 @@ static int dbdataGetVarint(const u8 *z, sqlite3_int64 *pVal){
|
|||||||
return 9;
|
return 9;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
** Return the number of bytes of space used by an SQLite value of type
|
||||||
|
** eType.
|
||||||
|
*/
|
||||||
static int dbdataValueBytes(int eType){
|
static int dbdataValueBytes(int eType){
|
||||||
switch( eType ){
|
switch( eType ){
|
||||||
case 0: case 8: case 9:
|
case 0: case 8: case 9:
|
||||||
@ -361,6 +387,10 @@ static int dbdataValueBytes(int eType){
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
** Load a value of type eType from buffer pData and use it to set the
|
||||||
|
** result of context object pCtx.
|
||||||
|
*/
|
||||||
static void dbdataValue(sqlite3_context *pCtx, int eType, u8 *pData){
|
static void dbdataValue(sqlite3_context *pCtx, int eType, u8 *pData){
|
||||||
switch( eType ){
|
switch( eType ){
|
||||||
case 0:
|
case 0:
|
||||||
@ -411,7 +441,7 @@ static void dbdataValue(sqlite3_context *pCtx, int eType, u8 *pData){
|
|||||||
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
** Move a dbdata cursor to the next entry in the file.
|
** Move an sqlite_dbdata or sqlite_dbptr cursor to the next entry.
|
||||||
*/
|
*/
|
||||||
static int dbdataNext(sqlite3_vtab_cursor *pCursor){
|
static int dbdataNext(sqlite3_vtab_cursor *pCursor){
|
||||||
DbdataCursor *pCsr = (DbdataCursor*)pCursor;
|
DbdataCursor *pCsr = (DbdataCursor*)pCursor;
|
||||||
@ -575,14 +605,20 @@ static int dbdataNext(sqlite3_vtab_cursor *pCursor){
|
|||||||
return SQLITE_OK;
|
return SQLITE_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* We have reached EOF if previous sqlite3_step() returned
|
/*
|
||||||
** anything other than SQLITE_ROW;
|
** Return true if the cursor is at EOF.
|
||||||
*/
|
*/
|
||||||
static int dbdataEof(sqlite3_vtab_cursor *pCursor){
|
static int dbdataEof(sqlite3_vtab_cursor *pCursor){
|
||||||
DbdataCursor *pCsr = (DbdataCursor*)pCursor;
|
DbdataCursor *pCsr = (DbdataCursor*)pCursor;
|
||||||
return pCsr->aPage==0;
|
return pCsr->aPage==0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
** Determine the size in pages of database zSchema (where zSchema is
|
||||||
|
** "main", "temp" or the name of an attached database) and set
|
||||||
|
** pCsr->szDb accordingly. If successful, return SQLITE_OK. Otherwise,
|
||||||
|
** an SQLite error code.
|
||||||
|
*/
|
||||||
static int dbdataDbsize(DbdataCursor *pCsr, const char *zSchema){
|
static int dbdataDbsize(DbdataCursor *pCsr, const char *zSchema){
|
||||||
DbdataTable *pTab = (DbdataTable*)pCsr->base.pVtab;
|
DbdataTable *pTab = (DbdataTable*)pCsr->base.pVtab;
|
||||||
char *zSql = 0;
|
char *zSql = 0;
|
||||||
@ -601,7 +637,8 @@ static int dbdataDbsize(DbdataCursor *pCsr, const char *zSchema){
|
|||||||
return rc;
|
return rc;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Position a cursor back to the beginning.
|
/*
|
||||||
|
** xFilter method for sqlite_dbdata and sqlite_dbptr.
|
||||||
*/
|
*/
|
||||||
static int dbdataFilter(
|
static int dbdataFilter(
|
||||||
sqlite3_vtab_cursor *pCursor,
|
sqlite3_vtab_cursor *pCursor,
|
||||||
@ -648,7 +685,9 @@ static int dbdataFilter(
|
|||||||
return rc;
|
return rc;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Return a column for the sqlite_dbdata table */
|
/*
|
||||||
|
** Return a column for the sqlite_dbdata or sqlite_dbptr table.
|
||||||
|
*/
|
||||||
static int dbdataColumn(
|
static int dbdataColumn(
|
||||||
sqlite3_vtab_cursor *pCursor,
|
sqlite3_vtab_cursor *pCursor,
|
||||||
sqlite3_context *ctx,
|
sqlite3_context *ctx,
|
||||||
@ -699,7 +738,9 @@ static int dbdataColumn(
|
|||||||
return SQLITE_OK;
|
return SQLITE_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Return the ROWID for the sqlite_dbdata table */
|
/*
|
||||||
|
** Return the rowid for an sqlite_dbdata or sqlite_dptr table.
|
||||||
|
*/
|
||||||
static int dbdataRowid(sqlite3_vtab_cursor *pCursor, sqlite_int64 *pRowid){
|
static int dbdataRowid(sqlite3_vtab_cursor *pCursor, sqlite_int64 *pRowid){
|
||||||
DbdataCursor *pCsr = (DbdataCursor*)pCursor;
|
DbdataCursor *pCsr = (DbdataCursor*)pCursor;
|
||||||
*pRowid = pCsr->iRowid;
|
*pRowid = pCsr->iRowid;
|
||||||
|
16
manifest
16
manifest
@ -1,5 +1,5 @@
|
|||||||
C Fix\sa\sproblem\sin\sthe\s.recover\scommand\swith\srecovering\sWITHOUT\sROWID\stables\swhere\sthe\sPK\scolumns\sare\snot\sthe\sleftmost\sin\sthe\sCREATE\sTABLE\sstatement.
|
C Add\sthe\s"--lost-and-found"\soption\sto\sthe\s".recover"\scommand.\sFor\ssetting\sthe\sname\sof\sthe\sorphaned\srows\stable.
|
||||||
D 2019-04-27T15:35:45.828
|
D 2019-04-27T18:47:03.466
|
||||||
F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1
|
F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1
|
||||||
F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea
|
F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea
|
||||||
F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724
|
F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724
|
||||||
@ -284,7 +284,7 @@ F ext/misc/closure.c dbfd8543b2a017ae6b1a5843986b22ddf99ff126ec9634a2f4047cd14c8
|
|||||||
F ext/misc/completion.c cec672d40604075bb341a7f11ac48393efdcd90a979269b8fe7977ea62d0547f
|
F ext/misc/completion.c cec672d40604075bb341a7f11ac48393efdcd90a979269b8fe7977ea62d0547f
|
||||||
F ext/misc/compress.c dd4f8a6d0baccff3c694757db5b430f3bbd821d8686d1fc24df55cf9f035b189
|
F ext/misc/compress.c dd4f8a6d0baccff3c694757db5b430f3bbd821d8686d1fc24df55cf9f035b189
|
||||||
F ext/misc/csv.c 7f047aeb68f5802e7ce6639292095d622a488bb43526ed04810e0649faa71ceb
|
F ext/misc/csv.c 7f047aeb68f5802e7ce6639292095d622a488bb43526ed04810e0649faa71ceb
|
||||||
F ext/misc/dbdata.c b7547f43906f9296e43be807ca78e2ef3f335ca26d6e91d178df30cd2fd46572
|
F ext/misc/dbdata.c fe978dad2df13dd4b377b5d38f4883282801b18711220a229d0fd266a5deab26
|
||||||
F ext/misc/dbdump.c baf6e37447c9d6968417b1cd34cbedb0b0ab3f91b5329501d8a8d5be3287c336
|
F ext/misc/dbdump.c baf6e37447c9d6968417b1cd34cbedb0b0ab3f91b5329501d8a8d5be3287c336
|
||||||
F ext/misc/eval.c 4b4757592d00fd32e44c7a067e6a0e4839c81a4d57abc4131ee7806d1be3104e
|
F ext/misc/eval.c 4b4757592d00fd32e44c7a067e6a0e4839c81a4d57abc4131ee7806d1be3104e
|
||||||
F ext/misc/explain.c d5c12962d79913ef774b297006872af1fccda388f61a11d37758f9179a09551f
|
F ext/misc/explain.c d5c12962d79913ef774b297006872af1fccda388f61a11d37758f9179a09551f
|
||||||
@ -520,7 +520,7 @@ F src/random.c 80f5d666f23feb3e6665a6ce04c7197212a88384
|
|||||||
F src/resolve.c 567888ee3faec14dae06519b4306201771058364a37560186a3e0e755ebc4cb8
|
F src/resolve.c 567888ee3faec14dae06519b4306201771058364a37560186a3e0e755ebc4cb8
|
||||||
F src/rowset.c d977b011993aaea002cab3e0bb2ce50cf346000dff94e944d547b989f4b1fe93
|
F src/rowset.c d977b011993aaea002cab3e0bb2ce50cf346000dff94e944d547b989f4b1fe93
|
||||||
F src/select.c b7304d2f491c11a03a7fbdf34bc218282ac54052377809d4dc3b4b1e7f4bfc93
|
F src/select.c b7304d2f491c11a03a7fbdf34bc218282ac54052377809d4dc3b4b1e7f4bfc93
|
||||||
F src/shell.c.in 3701177f3821330c8eb2af96f60123245cf42273abdae472bcb96bb120dcba8f
|
F src/shell.c.in 51f027f6b48fab39e0745915d979747880b7c35827798535726ac44366a291f1
|
||||||
F src/sqlite.h.in 38390767acc1914d58930e03149595ee4710afa4e3c43ab6c3a8aea3f1a6b8cd
|
F src/sqlite.h.in 38390767acc1914d58930e03149595ee4710afa4e3c43ab6c3a8aea3f1a6b8cd
|
||||||
F src/sqlite3.rc 5121c9e10c3964d5755191c80dd1180c122fc3a8
|
F src/sqlite3.rc 5121c9e10c3964d5755191c80dd1180c122fc3a8
|
||||||
F src/sqlite3ext.h 9ecc93b8493bd20c0c07d52e2ac0ed8bab9b549c7f7955b59869597b650dd8b5
|
F src/sqlite3ext.h 9ecc93b8493bd20c0c07d52e2ac0ed8bab9b549c7f7955b59869597b650dd8b5
|
||||||
@ -1226,7 +1226,7 @@ F test/randexpr1.tcl 40dec52119ed3a2b8b2a773bce24b63a3a746459
|
|||||||
F test/randexpr1.test eda062a97e60f9c38ae8d806b03b0ddf23d796df
|
F test/randexpr1.test eda062a97e60f9c38ae8d806b03b0ddf23d796df
|
||||||
F test/rbu.test 168573d353cd0fd10196b87b0caa322c144ef736
|
F test/rbu.test 168573d353cd0fd10196b87b0caa322c144ef736
|
||||||
F test/rdonly.test 64e2696c322e3538df0b1ed624e21f9a23ed9ff8
|
F test/rdonly.test 64e2696c322e3538df0b1ed624e21f9a23ed9ff8
|
||||||
F test/recover.test bfeb5ab4574f9a264b3893ce0e41f04c2052b72b174e5dd9d847b6e7b8f4d15c
|
F test/recover.test 52609c8cc24e72d3d8a20fb8bc32ba2ce8ca2093a7f4573bd4f2969f78f6d2b4
|
||||||
F test/regexp1.test 497ea812f264d12b6198d6e50a76be4a1973a9d8
|
F test/regexp1.test 497ea812f264d12b6198d6e50a76be4a1973a9d8
|
||||||
F test/regexp2.test 40e894223b3d6672655481493f1be12012f2b33c
|
F test/regexp2.test 40e894223b3d6672655481493f1be12012f2b33c
|
||||||
F test/reindex.test 44edd3966b474468b823d481eafef0c305022254
|
F test/reindex.test 44edd3966b474468b823d481eafef0c305022254
|
||||||
@ -1821,7 +1821,7 @@ F vsixtest/vsixtest.tcl 6a9a6ab600c25a91a7acc6293828957a386a8a93
|
|||||||
F vsixtest/vsixtest.vcxproj.data 2ed517e100c66dc455b492e1a33350c1b20fbcdc
|
F vsixtest/vsixtest.vcxproj.data 2ed517e100c66dc455b492e1a33350c1b20fbcdc
|
||||||
F vsixtest/vsixtest.vcxproj.filters 37e51ffedcdb064aad6ff33b6148725226cd608e
|
F vsixtest/vsixtest.vcxproj.filters 37e51ffedcdb064aad6ff33b6148725226cd608e
|
||||||
F vsixtest/vsixtest_TemporaryKey.pfx e5b1b036facdb453873e7084e1cae9102ccc67a0
|
F vsixtest/vsixtest_TemporaryKey.pfx e5b1b036facdb453873e7084e1cae9102ccc67a0
|
||||||
P 7221f6e33ed6a5a96ec61e25f2a1f70b84aae66e503d897eb7b7ff1aec42355d
|
P 91df4b8e0386105d01614921e8410994b621404a3d46ec4af8687b8743c52d52
|
||||||
R e71b5bc56d773e53b87820c68d2be836
|
R a9542f620433fc5d37a56caa32ea75c3
|
||||||
U dan
|
U dan
|
||||||
Z d098c243e647f537ccde95cbf714168f
|
Z 07c2469e7eeced685ce45289494bc94e
|
||||||
|
@ -1 +1 @@
|
|||||||
91df4b8e0386105d01614921e8410994b621404a3d46ec4af8687b8743c52d52
|
67bb88e24c74d02ae0c4ac6ff2f873f6b0035ccefe5cccfc71c5686cbc76b4c3
|
150
src/shell.c.in
150
src/shell.c.in
@ -1104,7 +1104,6 @@ struct ShellState {
|
|||||||
#define SHFLG_Newlines 0x00000010 /* .dump --newline flag */
|
#define SHFLG_Newlines 0x00000010 /* .dump --newline flag */
|
||||||
#define SHFLG_CountChanges 0x00000020 /* .changes setting */
|
#define SHFLG_CountChanges 0x00000020 /* .changes setting */
|
||||||
#define SHFLG_Echo 0x00000040 /* .echo or --echo setting */
|
#define SHFLG_Echo 0x00000040 /* .echo or --echo setting */
|
||||||
#define SHFLG_Recover 0x00000080 /* .dump is --recover */
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
** Macros for testing and setting shellFlgs
|
** Macros for testing and setting shellFlgs
|
||||||
@ -3577,6 +3576,7 @@ static const char *(azHelp[]) = {
|
|||||||
".prompt MAIN CONTINUE Replace the standard prompts",
|
".prompt MAIN CONTINUE Replace the standard prompts",
|
||||||
".quit Exit this program",
|
".quit Exit this program",
|
||||||
".read FILE Read input from FILE",
|
".read FILE Read input from FILE",
|
||||||
|
".recover Recover as much data as possible from corrupt db.",
|
||||||
".restore ?DB? FILE Restore content of DB (default \"main\") from FILE",
|
".restore ?DB? FILE Restore content of DB (default \"main\") from FILE",
|
||||||
".save FILE Write in-memory database into FILE",
|
".save FILE Write in-memory database into FILE",
|
||||||
".scanstats on|off Turn sqlite3_stmt_scanstatus() metrics on or off",
|
".scanstats on|off Turn sqlite3_stmt_scanstatus() metrics on or off",
|
||||||
@ -6153,6 +6153,12 @@ end_ar_command:
|
|||||||
**********************************************************************************/
|
**********************************************************************************/
|
||||||
#endif /* !defined(SQLITE_OMIT_VIRTUALTABLE) && defined(SQLITE_HAVE_ZLIB) */
|
#endif /* !defined(SQLITE_OMIT_VIRTUALTABLE) && defined(SQLITE_HAVE_ZLIB) */
|
||||||
|
|
||||||
|
/*
|
||||||
|
** If (*pRc) is not SQLITE_OK when this function is called, it is a no-op.
|
||||||
|
** Otherwise, the SQL statement or statements in zSql are executed using
|
||||||
|
** database connection db and the error code written to *pRc before
|
||||||
|
** this function returns.
|
||||||
|
*/
|
||||||
static void shellExec(sqlite3 *db, int *pRc, const char *zSql){
|
static void shellExec(sqlite3 *db, int *pRc, const char *zSql){
|
||||||
int rc = *pRc;
|
int rc = *pRc;
|
||||||
if( rc==SQLITE_OK ){
|
if( rc==SQLITE_OK ){
|
||||||
@ -6165,6 +6171,9 @@ static void shellExec(sqlite3 *db, int *pRc, const char *zSql){
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
** Like shellExec(), except that zFmt is a printf() style format string.
|
||||||
|
*/
|
||||||
static void shellExecPrintf(sqlite3 *db, int *pRc, const char *zFmt, ...){
|
static void shellExecPrintf(sqlite3 *db, int *pRc, const char *zFmt, ...){
|
||||||
char *z = 0;
|
char *z = 0;
|
||||||
if( *pRc==SQLITE_OK ){
|
if( *pRc==SQLITE_OK ){
|
||||||
@ -6181,6 +6190,12 @@ static void shellExecPrintf(sqlite3 *db, int *pRc, const char *zFmt, ...){
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
** If *pRc is not SQLITE_OK when this function is called, it is a no-op.
|
||||||
|
** Otherwise, an attempt is made to allocate, zero and return a pointer
|
||||||
|
** to a buffer nByte bytes in size. If an OOM error occurs, *pRc is set
|
||||||
|
** to SQLITE_NOMEM and NULL returned.
|
||||||
|
*/
|
||||||
static void *shellMalloc(int *pRc, sqlite3_int64 nByte){
|
static void *shellMalloc(int *pRc, sqlite3_int64 nByte){
|
||||||
void *pRet = 0;
|
void *pRet = 0;
|
||||||
if( *pRc==SQLITE_OK ){
|
if( *pRc==SQLITE_OK ){
|
||||||
@ -6194,6 +6209,17 @@ static void *shellMalloc(int *pRc, sqlite3_int64 nByte){
|
|||||||
return pRet;
|
return pRet;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
** If *pRc is not SQLITE_OK when this function is called, it is a no-op.
|
||||||
|
** Otherwise, zFmt is treated as a printf() style string. The result of
|
||||||
|
** formatting it along with any trailing arguments is written into a
|
||||||
|
** buffer obtained from sqlite3_malloc(), and pointer to which is returned.
|
||||||
|
** It is the responsibility of the caller to eventually free this buffer
|
||||||
|
** using a call to sqlite3_free().
|
||||||
|
**
|
||||||
|
** If an OOM error occurs, (*pRc) is set to SQLITE_NOMEM and a NULL
|
||||||
|
** pointer returned.
|
||||||
|
*/
|
||||||
static char *shellMPrintf(int *pRc, const char *zFmt, ...){
|
static char *shellMPrintf(int *pRc, const char *zFmt, ...){
|
||||||
char *z = 0;
|
char *z = 0;
|
||||||
if( *pRc==SQLITE_OK ){
|
if( *pRc==SQLITE_OK ){
|
||||||
@ -6208,21 +6234,25 @@ static char *shellMPrintf(int *pRc, const char *zFmt, ...){
|
|||||||
return z;
|
return z;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
** When running the ".recover" command, each output table, and the special
|
||||||
|
** orphaned row table if it is required, is represented by an instance
|
||||||
|
** of the following struct.
|
||||||
|
*/
|
||||||
typedef struct RecoverTable RecoverTable;
|
typedef struct RecoverTable RecoverTable;
|
||||||
struct RecoverTable {
|
struct RecoverTable {
|
||||||
char *zName; /* Name of table */
|
char *zQuoted; /* Quoted version of table name */
|
||||||
char *zQuoted; /* Quoted version of zName */
|
|
||||||
int nCol; /* Number of columns in table */
|
int nCol; /* Number of columns in table */
|
||||||
char **azlCol; /* Array of column lists */
|
char **azlCol; /* Array of column lists */
|
||||||
int iPk;
|
int iPk; /* Index of IPK column */
|
||||||
};
|
};
|
||||||
|
|
||||||
/*
|
/*
|
||||||
** Free a RecoverTable object allocated by recoverNewTable()
|
** Free a RecoverTable object allocated by recoverFindTable() or
|
||||||
|
** recoverOrphanTable().
|
||||||
*/
|
*/
|
||||||
static void recoverFreeTable(RecoverTable *pTab){
|
static void recoverFreeTable(RecoverTable *pTab){
|
||||||
if( pTab ){
|
if( pTab ){
|
||||||
sqlite3_free(pTab->zName);
|
|
||||||
sqlite3_free(pTab->zQuoted);
|
sqlite3_free(pTab->zQuoted);
|
||||||
if( pTab->azlCol ){
|
if( pTab->azlCol ){
|
||||||
int i;
|
int i;
|
||||||
@ -6235,7 +6265,14 @@ static void recoverFreeTable(RecoverTable *pTab){
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static RecoverTable *recoverOldTable(
|
/*
|
||||||
|
** This function is a no-op if (*pRc) is not SQLITE_OK when it is called.
|
||||||
|
** Otherwise, it allocates and returns a RecoverTable object based on the
|
||||||
|
** final four arguments passed to this function. It is the responsibility
|
||||||
|
** of the caller to eventually free the returned object using
|
||||||
|
** recoverFreeTable().
|
||||||
|
*/
|
||||||
|
static RecoverTable *recoverNewTable(
|
||||||
int *pRc, /* IN/OUT: Error code */
|
int *pRc, /* IN/OUT: Error code */
|
||||||
const char *zName, /* Name of table */
|
const char *zName, /* Name of table */
|
||||||
const char *zSql, /* CREATE TABLE statement */
|
const char *zSql, /* CREATE TABLE statement */
|
||||||
@ -6309,8 +6346,7 @@ static RecoverTable *recoverOldTable(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pTab->zName = shellMPrintf(&rc, "%s", zName);
|
pTab->zQuoted = shellMPrintf(&rc, "%Q", zName);
|
||||||
pTab->zQuoted = shellMPrintf(&rc, "%Q", pTab->zName);
|
|
||||||
pTab->azlCol = (char**)shellMalloc(&rc, sizeof(char*) * (nSqlCol+1));
|
pTab->azlCol = (char**)shellMalloc(&rc, sizeof(char*) * (nSqlCol+1));
|
||||||
pTab->nCol = nSqlCol;
|
pTab->nCol = nSqlCol;
|
||||||
|
|
||||||
@ -6349,7 +6385,7 @@ static RecoverTable *recoverOldTable(
|
|||||||
return pTab;
|
return pTab;
|
||||||
}
|
}
|
||||||
|
|
||||||
static RecoverTable *recoverNewTable(
|
static RecoverTable *recoverFindTable(
|
||||||
ShellState *pState,
|
ShellState *pState,
|
||||||
int *pRc,
|
int *pRc,
|
||||||
int iRoot,
|
int iRoot,
|
||||||
@ -6363,7 +6399,6 @@ static RecoverTable *recoverNewTable(
|
|||||||
const char *zSql = 0;
|
const char *zSql = 0;
|
||||||
const char *zName = 0;
|
const char *zName = 0;
|
||||||
|
|
||||||
|
|
||||||
/* Search the recovered schema for an object with root page iRoot. */
|
/* Search the recovered schema for an object with root page iRoot. */
|
||||||
shellPreparePrintf(pState->db, pRc, &pStmt,
|
shellPreparePrintf(pState->db, pRc, &pStmt,
|
||||||
"SELECT type, name, sql FROM recovery.schema WHERE rootpage=%d", iRoot
|
"SELECT type, name, sql FROM recovery.schema WHERE rootpage=%d", iRoot
|
||||||
@ -6377,7 +6412,7 @@ static RecoverTable *recoverNewTable(
|
|||||||
if( sqlite3_stricmp(zType, "table")==0 ){
|
if( sqlite3_stricmp(zType, "table")==0 ){
|
||||||
zName = (const char*)sqlite3_column_text(pStmt, 1);
|
zName = (const char*)sqlite3_column_text(pStmt, 1);
|
||||||
zSql = (const char*)sqlite3_column_text(pStmt, 2);
|
zSql = (const char*)sqlite3_column_text(pStmt, 2);
|
||||||
pRet = recoverOldTable(pRc, zName, zSql, bIntkey, nCol);
|
pRet = recoverNewTable(pRc, zName, zSql, bIntkey, nCol);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -6390,24 +6425,35 @@ static RecoverTable *recoverNewTable(
|
|||||||
static RecoverTable *recoverOrphanTable(
|
static RecoverTable *recoverOrphanTable(
|
||||||
ShellState *pState,
|
ShellState *pState,
|
||||||
int *pRc,
|
int *pRc,
|
||||||
|
const char *zLostAndFound,
|
||||||
int nCol
|
int nCol
|
||||||
){
|
){
|
||||||
RecoverTable *pTab = 0;
|
RecoverTable *pTab = 0;
|
||||||
if( nCol>=0 && *pRc==SQLITE_OK ){
|
if( nCol>=0 && *pRc==SQLITE_OK ){
|
||||||
int i;
|
int i;
|
||||||
raw_printf(pState->out,
|
|
||||||
"CREATE TABLE recover_orphan(rootpgno INTEGER, "
|
/* This block determines the name of the orphan table. The prefered
|
||||||
"pgno INTEGER, nfield INTEGER, id INTEGER"
|
** name is zLostAndFound. But if that clashes with another name
|
||||||
|
** in the recovered schema, try zLostAndFound_0, zLostAndFound_1
|
||||||
|
** and so on until a non-clashing name is found. */
|
||||||
|
int iTab = 0;
|
||||||
|
char *zTab = shellMPrintf(pRc, "%s", zLostAndFound);
|
||||||
|
sqlite3_stmt *pTest = 0;
|
||||||
|
shellPrepare(pState->db, pRc,
|
||||||
|
"SELECT 1 FROM recovery.schema WHERE name=?", &pTest
|
||||||
);
|
);
|
||||||
for(i=0; i<nCol; i++){
|
if( pTest ) sqlite3_bind_text(pTest, 1, zTab, -1, SQLITE_TRANSIENT);
|
||||||
raw_printf(pState->out, ", c%d", i);
|
while( *pRc==SQLITE_OK && SQLITE_ROW==sqlite3_step(pTest) ){
|
||||||
|
shellReset(pRc, pTest);
|
||||||
|
sqlite3_free(zTab);
|
||||||
|
zTab = shellMPrintf(pRc, "%s_%d", zLostAndFound, iTab++);
|
||||||
|
sqlite3_bind_text(pTest, 1, zTab, -1, SQLITE_TRANSIENT);
|
||||||
}
|
}
|
||||||
raw_printf(pState->out, ");\n");
|
shellFinalize(pRc, pTest);
|
||||||
|
|
||||||
pTab = (RecoverTable*)shellMalloc(pRc, sizeof(RecoverTable));
|
pTab = (RecoverTable*)shellMalloc(pRc, sizeof(RecoverTable));
|
||||||
if( pTab ){
|
if( pTab ){
|
||||||
pTab->zName = shellMPrintf(pRc, "%s", "recover_orphan");
|
pTab->zQuoted = shellMPrintf(pRc, "%Q", zTab);
|
||||||
pTab->zQuoted = shellMPrintf(pRc, "%Q", pTab->zName);
|
|
||||||
pTab->nCol = nCol;
|
pTab->nCol = nCol;
|
||||||
pTab->iPk = -2;
|
pTab->iPk = -2;
|
||||||
if( nCol>0 ){
|
if( nCol>0 ){
|
||||||
@ -6419,12 +6465,22 @@ static RecoverTable *recoverOrphanTable(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
if( *pRc!=SQLITE_OK ){
|
if( *pRc!=SQLITE_OK ){
|
||||||
recoverFreeTable(pTab);
|
recoverFreeTable(pTab);
|
||||||
pTab = 0;
|
pTab = 0;
|
||||||
|
}else{
|
||||||
|
raw_printf(pState->out,
|
||||||
|
"CREATE TABLE %s(rootpgno INTEGER, "
|
||||||
|
"pgno INTEGER, nfield INTEGER, id INTEGER", pTab->zQuoted
|
||||||
|
);
|
||||||
|
for(i=0; i<nCol; i++){
|
||||||
|
raw_printf(pState->out, ", c%d", i);
|
||||||
|
}
|
||||||
|
raw_printf(pState->out, ");\n");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
sqlite3_free(zTab);
|
||||||
}
|
}
|
||||||
return pTab;
|
return pTab;
|
||||||
}
|
}
|
||||||
@ -6440,6 +6496,7 @@ static int recoverDatabaseCmd(ShellState *pState, int nArg, char **azArg){
|
|||||||
sqlite3_stmt *pPages = 0; /* Loop through all pages in a group */
|
sqlite3_stmt *pPages = 0; /* Loop through all pages in a group */
|
||||||
sqlite3_stmt *pCells = 0; /* Loop through all cells in a page */
|
sqlite3_stmt *pCells = 0; /* Loop through all cells in a page */
|
||||||
const char *zRecoveryDb = ""; /* Name of "recovery" database */
|
const char *zRecoveryDb = ""; /* Name of "recovery" database */
|
||||||
|
const char *zLostAndFound = "lost_and_found";
|
||||||
int i;
|
int i;
|
||||||
int nOrphan = -1;
|
int nOrphan = -1;
|
||||||
RecoverTable *pOrphan = 0;
|
RecoverTable *pOrphan = 0;
|
||||||
@ -6452,16 +6509,21 @@ static int recoverDatabaseCmd(ShellState *pState, int nArg, char **azArg){
|
|||||||
n = strlen(z);
|
n = strlen(z);
|
||||||
if( n<=17 && memcmp("-freelist-corrupt", z, n)==0 ){
|
if( n<=17 && memcmp("-freelist-corrupt", z, n)==0 ){
|
||||||
bFreelist = 0;
|
bFreelist = 0;
|
||||||
}
|
}else
|
||||||
if( n<=12 && memcmp("-recovery-db", z, n)==0 && i<(nArg-1) ){
|
if( n<=12 && memcmp("-recovery-db", z, n)==0 && i<(nArg-1) ){
|
||||||
i++;
|
i++;
|
||||||
zRecoveryDb = azArg[i];
|
zRecoveryDb = azArg[i];
|
||||||
|
}else
|
||||||
|
if( n<=15 && memcmp("-lost-and-found", z, n)==0 && i<(nArg-1) ){
|
||||||
|
i++;
|
||||||
|
zLostAndFound = azArg[i];
|
||||||
}
|
}
|
||||||
else{
|
else{
|
||||||
raw_printf(stderr, "unexpected option: %s\n", azArg[i]);
|
raw_printf(stderr, "unexpected option: %s\n", azArg[i]);
|
||||||
raw_printf(stderr, "options are:\n");
|
raw_printf(stderr, "options are:\n");
|
||||||
raw_printf(stderr, " --freelist-corrupt\n");
|
raw_printf(stderr, " --freelist-corrupt\n");
|
||||||
raw_printf(stderr, " --recovery-db DATABASE\n");
|
raw_printf(stderr, " --recovery-db DATABASE\n");
|
||||||
|
raw_printf(stderr, " --lost-and-found TABLE-NAME\n");
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -6599,7 +6661,7 @@ static int recoverDatabaseCmd(ShellState *pState, int nArg, char **azArg){
|
|||||||
}
|
}
|
||||||
shellFinalize(&rc, pLoop);
|
shellFinalize(&rc, pLoop);
|
||||||
pLoop = 0;
|
pLoop = 0;
|
||||||
pOrphan = recoverOrphanTable(pState, &rc, nOrphan);
|
pOrphan = recoverOrphanTable(pState, &rc, zLostAndFound, nOrphan);
|
||||||
|
|
||||||
shellPrepare(pState->db, &rc,
|
shellPrepare(pState->db, &rc,
|
||||||
"SELECT pgno FROM recovery.map WHERE root=?", &pPages
|
"SELECT pgno FROM recovery.map WHERE root=?", &pPages
|
||||||
@ -6624,11 +6686,11 @@ static int recoverDatabaseCmd(ShellState *pState, int nArg, char **azArg){
|
|||||||
int bNoop = 0;
|
int bNoop = 0;
|
||||||
RecoverTable *pTab;
|
RecoverTable *pTab;
|
||||||
|
|
||||||
pTab = recoverNewTable(pState, &rc, iRoot, bIntkey, nCol, &bNoop);
|
pTab = recoverFindTable(pState, &rc, iRoot, bIntkey, nCol, &bNoop);
|
||||||
if( bNoop || rc ) continue;
|
if( bNoop || rc ) continue;
|
||||||
if( pTab==0 ) pTab = pOrphan;
|
if( pTab==0 ) pTab = pOrphan;
|
||||||
|
|
||||||
if( 0==sqlite3_stricmp(pTab->zName, "sqlite_sequence") ){
|
if( 0==sqlite3_stricmp(pTab->zQuoted, "'sqlite_sequence'") ){
|
||||||
raw_printf(pState->out, "DELETE FROM sqlite_sequence;\n");
|
raw_printf(pState->out, "DELETE FROM sqlite_sequence;\n");
|
||||||
}
|
}
|
||||||
sqlite3_bind_int(pPages, 1, iRoot);
|
sqlite3_bind_int(pPages, 1, iRoot);
|
||||||
@ -7042,30 +7104,30 @@ static int do_meta_command(char *zLine, ShellState *p){
|
|||||||
p->nErr = 0;
|
p->nErr = 0;
|
||||||
if( zLike==0 ){
|
if( zLike==0 ){
|
||||||
run_schema_dump_query(p,
|
run_schema_dump_query(p,
|
||||||
"SELECT name, type, sql FROM sqlite_master "
|
"SELECT name, type, sql FROM sqlite_master "
|
||||||
"WHERE sql NOT NULL AND type=='table' AND name!='sqlite_sequence'"
|
"WHERE sql NOT NULL AND type=='table' AND name!='sqlite_sequence'"
|
||||||
);
|
);
|
||||||
run_schema_dump_query(p,
|
run_schema_dump_query(p,
|
||||||
"SELECT name, type, sql FROM sqlite_master "
|
"SELECT name, type, sql FROM sqlite_master "
|
||||||
"WHERE name=='sqlite_sequence'"
|
"WHERE name=='sqlite_sequence'"
|
||||||
);
|
);
|
||||||
run_table_dump_query(p,
|
run_table_dump_query(p,
|
||||||
"SELECT sql FROM sqlite_master "
|
"SELECT sql FROM sqlite_master "
|
||||||
"WHERE sql NOT NULL AND type IN ('index','trigger','view')", 0
|
"WHERE sql NOT NULL AND type IN ('index','trigger','view')", 0
|
||||||
);
|
);
|
||||||
}else{
|
}else{
|
||||||
char *zSql;
|
char *zSql;
|
||||||
zSql = sqlite3_mprintf(
|
zSql = sqlite3_mprintf(
|
||||||
"SELECT name, type, sql FROM sqlite_master "
|
"SELECT name, type, sql FROM sqlite_master "
|
||||||
"WHERE tbl_name LIKE %Q AND type=='table'"
|
"WHERE tbl_name LIKE %Q AND type=='table'"
|
||||||
" AND sql NOT NULL", zLike);
|
" AND sql NOT NULL", zLike);
|
||||||
run_schema_dump_query(p,zSql);
|
run_schema_dump_query(p,zSql);
|
||||||
sqlite3_free(zSql);
|
sqlite3_free(zSql);
|
||||||
zSql = sqlite3_mprintf(
|
zSql = sqlite3_mprintf(
|
||||||
"SELECT sql FROM sqlite_master "
|
"SELECT sql FROM sqlite_master "
|
||||||
"WHERE sql NOT NULL"
|
"WHERE sql NOT NULL"
|
||||||
" AND type IN ('index','trigger','view')"
|
" AND type IN ('index','trigger','view')"
|
||||||
" AND tbl_name LIKE %Q", zLike);
|
" AND tbl_name LIKE %Q", zLike);
|
||||||
run_table_dump_query(p, zSql, 0);
|
run_table_dump_query(p, zSql, 0);
|
||||||
sqlite3_free(zSql);
|
sqlite3_free(zSql);
|
||||||
}
|
}
|
||||||
|
@ -39,7 +39,7 @@ proc compare_dbs {db1 db2} {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
proc do_recover_test {tn} {
|
proc do_recover_test {tn {tsql {}} {res {}}} {
|
||||||
set fd [open "|$::CLI test.db .recover"]
|
set fd [open "|$::CLI test.db .recover"]
|
||||||
fconfigure $fd -encoding binary
|
fconfigure $fd -encoding binary
|
||||||
fconfigure $fd -translation binary
|
fconfigure $fd -translation binary
|
||||||
@ -48,9 +48,12 @@ proc do_recover_test {tn} {
|
|||||||
|
|
||||||
forcedelete test.db2
|
forcedelete test.db2
|
||||||
sqlite3 db2 test.db2
|
sqlite3 db2 test.db2
|
||||||
breakpoint
|
|
||||||
execsql $sql db2
|
execsql $sql db2
|
||||||
uplevel [list do_test $tn [list compare_dbs db db2] {}]
|
if {$tsql==""} {
|
||||||
|
uplevel [list do_test $tn [list compare_dbs db db2] {}]
|
||||||
|
} else {
|
||||||
|
uplevel [list do_execsql_test -db db2 $tn $tsql $res]
|
||||||
|
}
|
||||||
db2 close
|
db2 close
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -96,4 +99,31 @@ do_execsql_test 2.1.0 {
|
|||||||
|
|
||||||
do_recover_test 2.1.1
|
do_recover_test 2.1.1
|
||||||
|
|
||||||
|
do_execsql_test 2.2.0 {
|
||||||
|
PRAGMA writable_schema = 1;
|
||||||
|
DELETE FROM sqlite_master WHERE name='t1';
|
||||||
|
}
|
||||||
|
do_recover_test 2.2.1 {
|
||||||
|
SELECT name FROM sqlite_master
|
||||||
|
} {lost_and_found}
|
||||||
|
|
||||||
|
do_execsql_test 2.3.0 {
|
||||||
|
CREATE TABLE lost_and_found(a, b, c);
|
||||||
|
}
|
||||||
|
do_recover_test 2.3.1 {
|
||||||
|
SELECT name FROM sqlite_master
|
||||||
|
} {lost_and_found lost_and_found_0}
|
||||||
|
|
||||||
|
do_execsql_test 2.4.0 {
|
||||||
|
CREATE TABLE lost_and_found_0(a, b, c);
|
||||||
|
}
|
||||||
|
do_recover_test 2.4.1 {
|
||||||
|
SELECT name FROM sqlite_master;
|
||||||
|
SELECT * FROM lost_and_found_1;
|
||||||
|
} {lost_and_found lost_and_found_0 lost_and_found_1
|
||||||
|
2 2 3 {} 2 3 1
|
||||||
|
2 2 3 {} 5 6 4
|
||||||
|
2 2 3 {} 8 9 7
|
||||||
|
}
|
||||||
|
|
||||||
finish_test
|
finish_test
|
||||||
|
Reference in New Issue
Block a user