Update .fkey_missing_indexes to use the built-in pragma vtabs.
FossilOrigin-Name: 3ab05987b0cc12af64bf32d885d04aff45d7a77c
This commit is contained in:
@@ -1,5 +1,5 @@
|
||||
C Merge\sthe\spragma-as-vtab\schange\sinto\sthis\sbranch.
|
||||
D 2016-12-16T16:13:45.218
|
||||
C Update\s.fkey_missing_indexes\sto\suse\sthe\sbuilt-in\spragma\svtabs.
|
||||
D 2016-12-16T16:44:27.209
|
||||
F Makefile.in c194b58fe00c370a48ac6ae6945e92a7781db1c8
|
||||
F Makefile.linux-gcc 7bc79876b875010e8c8f9502eb935ca92aa3c434
|
||||
F Makefile.msc b8ca53350ae545e3562403d5da2a69cec79308da
|
||||
@@ -389,7 +389,7 @@ F src/random.c 80f5d666f23feb3e6665a6ce04c7197212a88384
|
||||
F src/resolve.c bb070cf5f23611c44ab7e4788803684e385fc3fb
|
||||
F src/rowset.c 7b7e7e479212e65b723bf40128c7b36dc5afdfac
|
||||
F src/select.c 204491a5e09a66380a067943d8651af8bda1d358
|
||||
F src/shell.c 331be7c5d82477baff3ab584f1dc679e9676e0ef
|
||||
F src/shell.c 667382b94e1ba301960cbf34db96d25afe219892
|
||||
F src/sqlite.h.in e8e2d108d82647f0a812fdb74accf91c1ec08ddc
|
||||
F src/sqlite3.rc 5121c9e10c3964d5755191c80dd1180c122fc3a8
|
||||
F src/sqlite3ext.h 8648034aa702469afb553231677306cc6492a1ae
|
||||
@@ -1537,7 +1537,7 @@ F vsixtest/vsixtest.tcl 6a9a6ab600c25a91a7acc6293828957a386a8a93
|
||||
F vsixtest/vsixtest.vcxproj.data 2ed517e100c66dc455b492e1a33350c1b20fbcdc
|
||||
F vsixtest/vsixtest.vcxproj.filters 37e51ffedcdb064aad6ff33b6148725226cd608e
|
||||
F vsixtest/vsixtest_TemporaryKey.pfx e5b1b036facdb453873e7084e1cae9102ccc67a0
|
||||
P 976c51b4836dfba2ce9b246334a85bda08ac526f 546821e29edb0282a4b1d8f49512e36027a6bf6d
|
||||
R 30e9fb459cb16eb6392de3bdb60521ee
|
||||
P 4ba45e722371ca4343e3563e7e1c2896b48c9a87
|
||||
R 5944ad7aef550b0c7873733cf5d30c76
|
||||
U dan
|
||||
Z 8d92568d6acf97185670c4652b437304
|
||||
Z 1d6e2ebe082c25e976b4f2822f56282f
|
||||
|
||||
+1
-1
@@ -1 +1 @@
|
||||
4ba45e722371ca4343e3563e7e1c2896b48c9a87
|
||||
3ab05987b0cc12af64bf32d885d04aff45d7a77c
|
||||
+64
-306
@@ -3258,285 +3258,12 @@ int shellDeleteFile(const char *zFilename){
|
||||
/**************************************************************************
|
||||
** Beginning of implementation of .fkey_missing_indexes
|
||||
*/
|
||||
typedef struct PragmaVtab PragmaVtab;
|
||||
typedef struct PragmaCursor PragmaCursor;
|
||||
typedef struct PragmaConfig PragmaConfig;
|
||||
|
||||
/*
|
||||
** Table structure for "pragma" eponymous virtual tables.
|
||||
*/
|
||||
struct PragmaVtab {
|
||||
sqlite3_vtab base;
|
||||
PragmaConfig *pConfig;
|
||||
sqlite3 *db;
|
||||
};
|
||||
|
||||
/*
|
||||
** Cursor structure for "pragma" eponymous virtual tables.
|
||||
*/
|
||||
struct PragmaCursor {
|
||||
sqlite3_vtab_cursor base;
|
||||
sqlite3_stmt *pSelect;
|
||||
sqlite3_stmt *pPragma;
|
||||
sqlite_int64 iRowid;
|
||||
};
|
||||
|
||||
struct PragmaConfig {
|
||||
const char *zModule;
|
||||
const char *zSchema;
|
||||
const char *zPragma;
|
||||
};
|
||||
|
||||
/*
|
||||
** Pragma virtual table module xConnect method.
|
||||
*/
|
||||
static int shellPragmaConnect(
|
||||
sqlite3 *db,
|
||||
void *pAux,
|
||||
int argc, const char *const*argv,
|
||||
sqlite3_vtab **ppVtab,
|
||||
char **pzErr
|
||||
){
|
||||
PragmaConfig *pConfig = (PragmaConfig*)pAux;
|
||||
PragmaVtab *pTab = 0;
|
||||
int rc;
|
||||
|
||||
rc = sqlite3_declare_vtab(db, pConfig->zSchema);
|
||||
if( rc==SQLITE_OK ){
|
||||
pTab = (PragmaVtab*)sqlite3_malloc(sizeof(PragmaVtab));
|
||||
if( pTab==0 ){
|
||||
rc = SQLITE_NOMEM;
|
||||
}else{
|
||||
memset(pTab, 0, sizeof(PragmaVtab));
|
||||
pTab->db = db;
|
||||
pTab->pConfig = pConfig;
|
||||
}
|
||||
}else{
|
||||
*pzErr = sqlite3_mprintf("%s", sqlite3_errmsg(db));
|
||||
}
|
||||
|
||||
*ppVtab = (sqlite3_vtab*)pTab;
|
||||
return rc;
|
||||
}
|
||||
|
||||
/*
|
||||
** Pragma virtual table module xDisconnect method.
|
||||
*/
|
||||
static int shellPragmaDisconnect(sqlite3_vtab *pVtab){
|
||||
PragmaVtab *pTab = (PragmaVtab*)pVtab;
|
||||
sqlite3_free(pTab);
|
||||
return SQLITE_OK;
|
||||
}
|
||||
|
||||
/*
|
||||
** Pragma virtual table module xBestIndex method.
|
||||
*/
|
||||
static int shellPragmaBestIndex(sqlite3_vtab *tab, sqlite3_index_info *pIdxInfo){
|
||||
return SQLITE_OK;
|
||||
}
|
||||
|
||||
/*
|
||||
** Pragma virtual table module xOpen method.
|
||||
*/
|
||||
static int shellPragmaOpen(sqlite3_vtab *pVtab, sqlite3_vtab_cursor **ppCursor){
|
||||
PragmaCursor *pCsr;
|
||||
|
||||
pCsr = (PragmaCursor*)sqlite3_malloc(sizeof(PragmaCursor));
|
||||
if( pCsr==0 ) return SQLITE_NOMEM;
|
||||
memset(pCsr, 0, sizeof(PragmaCursor));
|
||||
pCsr->base.pVtab = pVtab;
|
||||
|
||||
*ppCursor = (sqlite3_vtab_cursor*)pCsr;
|
||||
return SQLITE_OK;
|
||||
}
|
||||
|
||||
/*
|
||||
** Pragma virtual table module xClose method.
|
||||
*/
|
||||
static int shellPragmaClose(sqlite3_vtab_cursor *cur){
|
||||
return SQLITE_OK;
|
||||
}
|
||||
|
||||
/*
|
||||
** Pragma virtual table module xNext method.
|
||||
*/
|
||||
static int shellPragmaNext(sqlite3_vtab_cursor *pVtabCursor){
|
||||
PragmaCursor *pCsr = (PragmaCursor*)pVtabCursor;
|
||||
PragmaVtab *pTab = (PragmaVtab*)(pVtabCursor->pVtab);
|
||||
sqlite3 *db = pTab->db;
|
||||
int rc = SQLITE_OK;
|
||||
|
||||
/* Increment the xRowid value */
|
||||
pCsr->iRowid++;
|
||||
|
||||
while( 1 ){
|
||||
assert( rc==SQLITE_OK );
|
||||
|
||||
if( pCsr->pPragma ){
|
||||
if( SQLITE_ROW!=sqlite3_step(pCsr->pPragma) ){
|
||||
rc = sqlite3_finalize(pCsr->pPragma);
|
||||
pCsr->pPragma = 0;
|
||||
}
|
||||
}
|
||||
|
||||
if( rc==SQLITE_OK && pCsr->pPragma==0 ){
|
||||
if( SQLITE_ROW!=sqlite3_step(pCsr->pSelect) ){
|
||||
rc = sqlite3_finalize(pCsr->pSelect);
|
||||
pCsr->pSelect = 0;
|
||||
}else{
|
||||
const char *zName = (const char*)sqlite3_column_text(pCsr->pSelect, 0);
|
||||
char *zSql = sqlite3_mprintf(pTab->pConfig->zPragma, zName);
|
||||
if( zSql==0 ){
|
||||
rc = SQLITE_NOMEM;
|
||||
}else{
|
||||
rc = sqlite3_prepare_v2(db, zSql, -1, &pCsr->pPragma, 0);
|
||||
if( rc!=SQLITE_OK ){
|
||||
pTab->base.zErrMsg = sqlite3_mprintf("%s", sqlite3_errmsg(db));
|
||||
}else{
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
/*
|
||||
** Pragma virtual table module xFilter method.
|
||||
*/
|
||||
static int shellPragmaFilter(
|
||||
sqlite3_vtab_cursor *pVtabCursor,
|
||||
int idxNum, const char *idxStr,
|
||||
int argc, sqlite3_value **argv
|
||||
){
|
||||
PragmaCursor *pCsr = (PragmaCursor*)pVtabCursor;
|
||||
PragmaVtab *pTab = (PragmaVtab*)(pVtabCursor->pVtab);
|
||||
int rc;
|
||||
|
||||
sqlite3_finalize(pCsr->pSelect);
|
||||
sqlite3_finalize(pCsr->pPragma);
|
||||
pCsr->pSelect = 0;
|
||||
pCsr->pPragma = 0;
|
||||
pCsr->iRowid = 0;
|
||||
|
||||
rc = sqlite3_prepare_v2(pTab->db,
|
||||
"SELECT name FROM sqlite_master WHERE type = 'table' AND rootpage>0",
|
||||
-1, &pCsr->pSelect, 0
|
||||
);
|
||||
if( rc!=SQLITE_OK ){
|
||||
pTab->base.zErrMsg = sqlite3_mprintf("%s", sqlite3_errmsg(pTab->db));
|
||||
return rc;
|
||||
}
|
||||
|
||||
return shellPragmaNext(pVtabCursor);
|
||||
}
|
||||
|
||||
/*
|
||||
** Pragma virtual table module xEof method.
|
||||
*/
|
||||
static int shellPragmaEof(sqlite3_vtab_cursor *pVtabCursor){
|
||||
PragmaCursor *pCsr = (PragmaCursor*)pVtabCursor;
|
||||
return (pCsr->pSelect==0);
|
||||
}
|
||||
|
||||
/*
|
||||
** Pragma virtual table module xColumn method.
|
||||
*/
|
||||
static int shellPragmaColumn(
|
||||
sqlite3_vtab_cursor *pVtabCursor,
|
||||
sqlite3_context *ctx,
|
||||
int i
|
||||
){
|
||||
PragmaCursor *pCsr = (PragmaCursor*)pVtabCursor;
|
||||
switch( i ){
|
||||
case 0: /* "database" */
|
||||
sqlite3_result_text(ctx, "main", -1, SQLITE_STATIC);
|
||||
break;
|
||||
|
||||
case 1: /* "child"/"table" */
|
||||
sqlite3_result_value(ctx, sqlite3_column_value(pCsr->pSelect, 0));
|
||||
break;
|
||||
|
||||
default:
|
||||
sqlite3_result_value(ctx, sqlite3_column_value(pCsr->pPragma, i-2));
|
||||
break;
|
||||
}
|
||||
|
||||
return SQLITE_OK;
|
||||
}
|
||||
|
||||
/*
|
||||
** Pragma virtual table module xRowid method.
|
||||
*/
|
||||
static int shellPragmaRowid(sqlite3_vtab_cursor *pVtabCursor, sqlite_int64 *p){
|
||||
PragmaCursor *pCsr = (PragmaCursor*)pVtabCursor;
|
||||
*p = pCsr->iRowid;
|
||||
return SQLITE_OK;
|
||||
}
|
||||
|
||||
/*
|
||||
** Register the virtual table module with the supplied database handle.
|
||||
*/
|
||||
static int shellPragmaRegister(sqlite3 *db){
|
||||
static sqlite3_module shellPragmaModule = {
|
||||
0, /* iVersion */
|
||||
shellPragmaConnect, /* xCreate - create a table */
|
||||
shellPragmaConnect, /* xConnect - connect to an existing table */
|
||||
shellPragmaBestIndex, /* xBestIndex - Determine search strategy */
|
||||
shellPragmaDisconnect, /* xDisconnect - Disconnect from a table */
|
||||
shellPragmaDisconnect, /* xDestroy - Drop a table */
|
||||
shellPragmaOpen, /* xOpen - open a cursor */
|
||||
shellPragmaClose, /* xClose - close a cursor */
|
||||
shellPragmaFilter, /* xFilter - configure scan constraints */
|
||||
shellPragmaNext, /* xNext - advance a cursor */
|
||||
shellPragmaEof, /* xEof */
|
||||
shellPragmaColumn, /* xColumn - read data */
|
||||
shellPragmaRowid, /* xRowid - read data */
|
||||
0, /* xUpdate - write data */
|
||||
0, /* xBegin - begin transaction */
|
||||
0, /* xSync - sync transaction */
|
||||
0, /* xCommit - commit transaction */
|
||||
0, /* xRollback - rollback transaction */
|
||||
0, /* xFindFunction - function overloading */
|
||||
0, /* xRename - rename the table */
|
||||
0, /* xSavepoint */
|
||||
0, /* xRelease */
|
||||
0 /* xRollbackTo */
|
||||
};
|
||||
int rc = SQLITE_OK;
|
||||
int i;
|
||||
|
||||
static PragmaConfig aConfig[] = {
|
||||
{ "pragma_foreign_key_list",
|
||||
"CREATE TABLE x(database, child, "
|
||||
"id, seq, parent, child_col, parent_col, on_update, on_delete, match)",
|
||||
"PRAGMA foreign_key_list = %Q"
|
||||
},
|
||||
{ "pragma_table_info",
|
||||
"CREATE TABLE x(database, tbl, "
|
||||
"cid, name, type, not_null, dflt_value, pk)",
|
||||
"PRAGMA table_info = %Q"
|
||||
}
|
||||
};
|
||||
|
||||
for(i=0; rc==SQLITE_OK && i<sizeof(aConfig)/sizeof(aConfig[0]); i++){
|
||||
rc = sqlite3_create_module_v2(
|
||||
db, aConfig[i].zModule, &shellPragmaModule, (void*)&aConfig[i], 0
|
||||
);
|
||||
}
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
/*
|
||||
** The implementation of SQL scalar function fkey_collate_clause(). This
|
||||
** scalar function is always called with four arguments - the parent
|
||||
** table name, the parent column name, the child table name and the child
|
||||
** column name.
|
||||
** The implementation of SQL scalar function fkey_collate_clause(), used
|
||||
** by the ".fkey_missing_indexes" command. This scalar function is always
|
||||
** called with four arguments - the parent table name, the parent column name,
|
||||
** the child table name and the child column name.
|
||||
**
|
||||
** fkey_collate_clause('parent-tab', 'parent-col', 'child-tab', 'child-col')
|
||||
**
|
||||
@@ -3593,40 +3320,76 @@ static int shellFkeyMissingIndexes(
|
||||
char **azArg, /* Array of arguments passed to dot command */
|
||||
int nArg /* Number of entries in azArg[] */
|
||||
){
|
||||
sqlite3 *db = pState->db;
|
||||
FILE *out = pState->out;
|
||||
sqlite3 *db = pState->db; /* Database handle to query "main" db of */
|
||||
FILE *out = pState->out; /* Stream to write non-error output to */
|
||||
int bVerbose = 0; /* If -verbose is present */
|
||||
int bGroupByParent = 0; /* If -groupbyparent is present */
|
||||
int i;
|
||||
const char *zIndent = "";
|
||||
int i; /* To iterate through azArg[] */
|
||||
const char *zIndent = ""; /* How much to indent CREATE INDEX by */
|
||||
int rc; /* Return code */
|
||||
sqlite3_stmt *pSql = 0; /* Compiled version of SQL statement below */
|
||||
|
||||
int rc;
|
||||
sqlite3_stmt *pSql = 0;
|
||||
/*
|
||||
** This SELECT statement returns one row for each foreign key constraint
|
||||
** in the schema of the main database. The column values are:
|
||||
**
|
||||
** 0. The text of an SQL statement similar to:
|
||||
**
|
||||
** "EXPLAIN QUERY PLAN SELECT rowid FROM child_table WHERE child_key=?"
|
||||
**
|
||||
** This is the same SELECT that the foreign keys implementation needs
|
||||
** to run internally on child tables. If there is an index that can
|
||||
** be used to optimize this query, then it can also be used by the FK
|
||||
** implementation to optimize DELETE or UPDATE statements on the parent
|
||||
** table.
|
||||
**
|
||||
** 1. A GLOB pattern suitable for sqlite3_strglob(). If the plan output by
|
||||
** the EXPLAIN QUERY PLAN command matches this pattern, then the schema
|
||||
** contains an index that can be used to optimize the query.
|
||||
**
|
||||
** 2. Human readable text that describes the child table and columns. e.g.
|
||||
**
|
||||
** "child_table(child_key1, child_key2)"
|
||||
**
|
||||
** 3. Human readable text that describes the parent table and columns. e.g.
|
||||
**
|
||||
** "parent_table(parent_key1, parent_key2)"
|
||||
**
|
||||
** 4. A full CREATE INDEX statement for an index that could be used to
|
||||
** optimize DELETE or UPDATE statements on the parent table. e.g.
|
||||
**
|
||||
** "CREATE INDEX child_table_child_key ON child_table(child_key)"
|
||||
**
|
||||
** 5. The name of the parent table.
|
||||
**
|
||||
** These six values are used by the C logic below to generate the report.
|
||||
*/
|
||||
const char *zSql =
|
||||
"SELECT "
|
||||
" 'EXPLAIN QUERY PLAN SELECT rowid FROM ' || quote(child) || ' WHERE ' "
|
||||
" || group_concat(quote(child) || '.' || quote(child_col) || '=?' || "
|
||||
" fkey_collate_clause(parent, parent_col, child, child_col),' AND ')"
|
||||
"SELECT "
|
||||
" 'EXPLAIN QUERY PLAN SELECT rowid FROM ' || quote(s.name) || ' WHERE '"
|
||||
" || group_concat(quote(s.name) || '.' || quote(f.[from]) || '=?' "
|
||||
" || fkey_collate_clause(f.[table], f.[to], s.name, f.[from]),' AND ')"
|
||||
", "
|
||||
" 'SEARCH TABLE ' || child || ' USING COVERING INDEX*('"
|
||||
" 'SEARCH TABLE ' || s.name || ' USING COVERING INDEX*('"
|
||||
" || group_concat('*=?', ' AND ') || ')'"
|
||||
", "
|
||||
" child || '(' || group_concat(child_col, ', ') || ')'"
|
||||
" s.name || '(' || group_concat(f.[from], ', ') || ')'"
|
||||
", "
|
||||
" parent || '(' || group_concat(COALESCE(parent_col, "
|
||||
" (SELECT name FROM pragma_table_info WHERE tbl=parent AND pk=seq+1)"
|
||||
" f.[table] || '(' || group_concat(COALESCE(f.[to], "
|
||||
" (SELECT name FROM pragma_table_info(f.[table]) WHERE pk=seq+1)"
|
||||
" )) || ')'"
|
||||
", "
|
||||
" 'CREATE INDEX ' || quote(child ||'_'|| group_concat(child_col, '_'))"
|
||||
" || ' ON ' || quote(child) || '('"
|
||||
" || group_concat(quote(child_col) ||"
|
||||
" fkey_collate_clause(parent, parent_col, child, child_col), ', ')"
|
||||
" 'CREATE INDEX ' || quote(s.name ||'_'|| group_concat(f.[from], '_'))"
|
||||
" || ' ON ' || quote(s.name) || '('"
|
||||
" || group_concat(quote(f.[from]) ||"
|
||||
" fkey_collate_clause(f.[table], f.[to], s.name, f.[from]), ', ')"
|
||||
" || ');'"
|
||||
", "
|
||||
" parent "
|
||||
" f.[table] "
|
||||
|
||||
"FROM pragma_foreign_key_list GROUP BY child, id "
|
||||
"ORDER BY (CASE WHEN ? THEN parent ELSE CHILD END)"
|
||||
"FROM sqlite_master AS s, pragma_foreign_key_list(s.name) AS f "
|
||||
"GROUP BY s.name, f.id "
|
||||
"ORDER BY (CASE WHEN ? THEN f.[table] ELSE s.name END)"
|
||||
;
|
||||
|
||||
for(i=1; i<nArg; i++){
|
||||
@@ -3643,16 +3406,11 @@ static int shellFkeyMissingIndexes(
|
||||
return SQLITE_ERROR;
|
||||
}
|
||||
}
|
||||
|
||||
/* Register the pragma eponymous virtual tables */
|
||||
rc = shellPragmaRegister(db);
|
||||
|
||||
/* Register the fkey_collate_clause() SQL function */
|
||||
if( rc==SQLITE_OK ){
|
||||
rc = sqlite3_create_function(db, "fkey_collate_clause", 4, SQLITE_UTF8,
|
||||
0, shellFkeyCollateClause, 0, 0
|
||||
);
|
||||
}
|
||||
rc = sqlite3_create_function(db, "fkey_collate_clause", 4, SQLITE_UTF8,
|
||||
0, shellFkeyCollateClause, 0, 0
|
||||
);
|
||||
|
||||
|
||||
if( rc==SQLITE_OK ){
|
||||
|
||||
Reference in New Issue
Block a user