1
0
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:
dan
2024-10-12 18:00:22 +00:00
parent 589ce93e9e
commit d5a780fddf
4 changed files with 165 additions and 17 deletions

View File

@ -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);
}