]> git.ipfire.org Git - thirdparty/sqlite.git/commitdiff
Update .fkey_missing_indexes to use the built-in pragma vtabs.
authordan <dan@noemail.net>
Fri, 16 Dec 2016 16:44:27 +0000 (16:44 +0000)
committerdan <dan@noemail.net>
Fri, 16 Dec 2016 16:44:27 +0000 (16:44 +0000)
FossilOrigin-Name: 3ab05987b0cc12af64bf32d885d04aff45d7a77c

manifest
manifest.uuid
src/shell.c

index fd8c4d7b2f22d478d4337cbcfe18096a01b4cae2..9287923a08fcafacc170ad4d38b36361e16fe3d9 100644 (file)
--- a/manifest
+++ b/manifest
@@ -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
index f61f9ac10023f2a5207ef6d8a8b2e55a39f37168..eae7fa71fba2a552374dd8ab98d8c968a29ee0d1 100644 (file)
@@ -1 +1 @@
-4ba45e722371ca4343e3563e7e1c2896b48c9a87
\ No newline at end of file
+3ab05987b0cc12af64bf32d885d04aff45d7a77c
\ No newline at end of file
index b0bd9c6ece163d775a743bfc92b9a23ac18f3fba..164d2b0d6766479d20bdfd14c02ab8c94a5c8409 100644 (file)
@@ -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 rc;
-  sqlite3_stmt *pSql = 0;
+  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 */
+
+  /*
+  ** 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 ){