mirror of
https://github.com/sqlite/sqlite.git
synced 2025-07-29 08:01:23 +03:00
Allow the ".expert" command to analyze statements that use built-in virtual tables.
FossilOrigin-Name: a201906cd3c85080f9b739c2d347c51348ebebd3dc9b647d33d8dcae4b6e5850
This commit is contained in:
@ -1392,6 +1392,66 @@ static int idxProcessTriggers(sqlite3expert *p, char **pzErr){
|
||||
return rc;
|
||||
}
|
||||
|
||||
/*
|
||||
** This function tests if the schema of the main database of database handle
|
||||
** db contains an object named zTab. Assuming no error occurs, output parameter
|
||||
** (*pbContains) is set to true if zTab exists, or false if it does not.
|
||||
**
|
||||
** Or, if an error occurs, an SQLite error code is returned. The final value
|
||||
** of (*pbContains) is undefined in this case.
|
||||
*/
|
||||
static int expertDbContainsObject(
|
||||
sqlite3 *db,
|
||||
const char *zTab,
|
||||
int *pbContains /* OUT: True if object exists */
|
||||
){
|
||||
const char *zSql = "SELECT 1 FROM sqlite_schema WHERE name = ?";
|
||||
sqlite3_stmt *pSql = 0;
|
||||
int rc = SQLITE_OK;
|
||||
int ret = 0;
|
||||
|
||||
rc = sqlite3_prepare_v2(db, zSql, -1, &pSql, 0);
|
||||
if( rc==SQLITE_OK ){
|
||||
sqlite3_bind_text(pSql, 1, zTab, -1, SQLITE_STATIC);
|
||||
if( SQLITE_ROW==sqlite3_step(pSql) ){
|
||||
ret = 1;
|
||||
}
|
||||
rc = sqlite3_finalize(pSql);
|
||||
}
|
||||
|
||||
*pbContains = ret;
|
||||
return rc;
|
||||
}
|
||||
|
||||
/*
|
||||
** Execute SQL command zSql using database handle db. If no error occurs,
|
||||
** set (*pzErr) to NULL and return SQLITE_OK.
|
||||
**
|
||||
** If an error does occur, return an SQLite error code and set (*pzErr) to
|
||||
** point to a buffer containing an English language error message. Except,
|
||||
** if the error message begins with "no such module:", then ignore the
|
||||
** error and return as if the SQL statement had succeeded.
|
||||
**
|
||||
** This is used to copy as much of the database schema as possible while
|
||||
** ignoring any errors related to missing virtual table modules.
|
||||
*/
|
||||
static int expertSchemaSql(sqlite3 *db, const char *zSql, char **pzErr){
|
||||
int rc = SQLITE_OK;
|
||||
char *zErr = 0;
|
||||
|
||||
rc = sqlite3_exec(db, zSql, 0, 0, &zErr);
|
||||
if( rc!=SQLITE_OK && zErr ){
|
||||
int nErr = STRLEN(zErr);
|
||||
if( nErr>=15 && memcmp(zErr, "no such module:", 15)==0 ){
|
||||
sqlite3_free(zErr);
|
||||
rc = SQLITE_OK;
|
||||
zErr = 0;
|
||||
}
|
||||
}
|
||||
|
||||
*pzErr = zErr;
|
||||
return rc;
|
||||
}
|
||||
|
||||
static int idxCreateVtabSchema(sqlite3expert *p, char **pzErrmsg){
|
||||
int rc = idxRegisterVtab(p);
|
||||
@ -1403,22 +1463,29 @@ static int idxCreateVtabSchema(sqlite3expert *p, char **pzErrmsg){
|
||||
** 2) Create the equivalent virtual table in dbv.
|
||||
*/
|
||||
rc = idxPrepareStmt(p->db, &pSchema, pzErrmsg,
|
||||
"SELECT type, name, sql, 1 FROM sqlite_schema "
|
||||
"SELECT type, name, sql, 1, sql LIKE 'create virtual%' "
|
||||
"FROM sqlite_schema "
|
||||
"WHERE type IN ('table','view') AND name NOT LIKE 'sqlite_%%' "
|
||||
" UNION ALL "
|
||||
"SELECT type, name, sql, 2 FROM sqlite_schema "
|
||||
"SELECT type, name, sql, 2, 0 FROM sqlite_schema "
|
||||
"WHERE type = 'trigger'"
|
||||
" AND tbl_name IN(SELECT name FROM sqlite_schema WHERE type = 'view') "
|
||||
"ORDER BY 4, 1"
|
||||
"ORDER BY 4, 5 DESC, 1"
|
||||
);
|
||||
while( rc==SQLITE_OK && SQLITE_ROW==sqlite3_step(pSchema) ){
|
||||
const char *zType = (const char*)sqlite3_column_text(pSchema, 0);
|
||||
const char *zName = (const char*)sqlite3_column_text(pSchema, 1);
|
||||
const char *zSql = (const char*)sqlite3_column_text(pSchema, 2);
|
||||
int bVirtual = sqlite3_column_int(pSchema, 4);
|
||||
int bExists = 0;
|
||||
|
||||
if( zType==0 || zName==0 ) continue;
|
||||
if( zType[0]=='v' || zType[1]=='r' ){
|
||||
if( zSql ) rc = sqlite3_exec(p->dbv, zSql, 0, 0, pzErrmsg);
|
||||
rc = expertDbContainsObject(p->dbv, zName, &bExists);
|
||||
if( rc || bExists ) continue;
|
||||
|
||||
if( zType[0]=='v' || zType[1]=='r' || bVirtual ){
|
||||
/* A view. Or a trigger on a view. */
|
||||
if( zSql ) rc = expertSchemaSql(p->dbv, zSql, pzErrmsg);
|
||||
}else{
|
||||
IdxTable *pTab;
|
||||
rc = idxGetTableInfo(p->db, zName, &pTab, pzErrmsg);
|
||||
@ -1957,12 +2024,18 @@ sqlite3expert *sqlite3_expert_new(sqlite3 *db, char **pzErrmsg){
|
||||
if( rc==SQLITE_OK ){
|
||||
sqlite3_stmt *pSql = 0;
|
||||
rc = idxPrintfPrepareStmt(pNew->db, &pSql, pzErrmsg,
|
||||
"SELECT sql FROM sqlite_schema WHERE name NOT LIKE 'sqlite_%%'"
|
||||
" AND sql NOT LIKE 'CREATE VIRTUAL %%' ORDER BY rowid"
|
||||
"SELECT sql, name "
|
||||
" FROM sqlite_schema WHERE name NOT LIKE 'sqlite_%%'"
|
||||
" ORDER BY rowid"
|
||||
);
|
||||
while( rc==SQLITE_OK && SQLITE_ROW==sqlite3_step(pSql) ){
|
||||
const char *zSql = (const char*)sqlite3_column_text(pSql, 0);
|
||||
if( zSql ) rc = sqlite3_exec(pNew->dbm, zSql, 0, 0, pzErrmsg);
|
||||
const char *zName = (const char*)sqlite3_column_text(pSql, 1);
|
||||
int bExists = 0;
|
||||
rc = expertDbContainsObject(pNew->dbm, zName, &bExists);
|
||||
if( rc==SQLITE_OK && zSql && bExists==0 ){
|
||||
rc = expertSchemaSql(pNew->dbm, zSql, pzErrmsg);
|
||||
}
|
||||
}
|
||||
idxFinalize(&rc, pSql);
|
||||
}
|
||||
|
Reference in New Issue
Block a user