]> git.ipfire.org Git - thirdparty/sqlite.git/commitdiff
First draft of the complete dbdump.c library.
authordrh <drh@noemail.net>
Mon, 13 Mar 2017 21:26:41 +0000 (21:26 +0000)
committerdrh <drh@noemail.net>
Mon, 13 Mar 2017 21:26:41 +0000 (21:26 +0000)
FossilOrigin-Name: 84ea4fcc52d0af02648c52989c2e69f4961071e1620382555ec59a39161a7a7d

ext/misc/dbdump.c
manifest
manifest.uuid

index b7fe595e2935e645fef0fd02401a7e5c659da4f6..ce51eba0a05950cc627f086ff16bcddf8e5b00ca 100644 (file)
@@ -12,7 +12,7 @@
 **
 ** This file implements a C-language subroutine that converts the content
 ** of an SQLite database into UTF-8 text SQL statements that can be used
-** to exactly recreate the original database.
+** to exactly recreate the original database.  ROWID values are preserved.
 **
 ** A prototype of the implemented subroutine is this:
 **
 ** first three arguments of a single call to the library routine.
 */
 #include "sqlite3.h"
+#include <stdarg.h>
+#include <string.h>
+#include <ctype.h>
+
+/*
+** The state of the dump process.
+*/
+typedef struct DState DState;
+struct DState {
+  sqlite3 *db;                /* The database connection */
+  int nErr;                   /* Number of errors seen so far */
+  int rc;                     /* Error code */
+  int writableSchema;                    /* True if in writable_schema mode */
+  int (*xCallback)(const char*,void*);   /* Send output here */
+  void *pArg;                            /* Argument to xCallback() */
+};
+
+/*
+** A variable length string to which one can append text.
+*/
+typedef struct DText DText;
+struct DText {
+  char *z;           /* The text */
+  int n;             /* Number of bytes of content in z[] */
+  int nAlloc;        /* Number of bytes allocated to z[] */
+};
+
+/*
+** Initialize and destroy a DText object
+*/
+static void initText(DText *p){
+  memset(p, 0, sizeof(*p));
+}
+static void freeText(DText *p){
+  sqlite3_free(p->z);
+  initText(p);
+}
+
+/* zIn is either a pointer to a NULL-terminated string in memory obtained
+** from malloc(), or a NULL pointer. The string pointed to by zAppend is
+** added to zIn, and the result returned in memory obtained from malloc().
+** zIn, if it was not NULL, is freed.
+**
+** If the third argument, quote, is not '\0', then it is used as a
+** quote character for zAppend.
+*/
+static void appendText(DText *p, char const *zAppend, char quote){
+  int len;
+  int i;
+  int nAppend = (int)(strlen(zAppend) & 0x3fffffff);
+
+  len = nAppend+p->n+1;
+  if( quote ){
+    len += 2;
+    for(i=0; i<nAppend; i++){
+      if( zAppend[i]==quote ) len++;
+    }
+  }
+
+  if( p->n+len>=p->nAlloc ){
+    char *zNew;
+    p->nAlloc = p->nAlloc*2 + len + 20;
+    zNew = sqlite3_realloc(p->z, p->nAlloc);
+    if( zNew==0 ){
+      freeText(p);
+      return;
+    }
+    p->z = zNew;
+  }
+
+  if( quote ){
+    char *zCsr = p->z+p->n;
+    *zCsr++ = quote;
+    for(i=0; i<nAppend; i++){
+      *zCsr++ = zAppend[i];
+      if( zAppend[i]==quote ) *zCsr++ = quote;
+    }
+    *zCsr++ = quote;
+    p->n = (int)(zCsr - p->z);
+    *zCsr = '\0';
+  }else{
+    memcpy(p->z+p->n, zAppend, nAppend);
+    p->n += nAppend;
+    p->z[p->n] = '\0';
+  }
+}
+
+/*
+** Attempt to determine if identifier zName needs to be quoted, either
+** because it contains non-alphanumeric characters, or because it is an
+** SQLite keyword.  Be conservative in this estimate:  When in doubt assume
+** that quoting is required.
+**
+** Return '"' if quoting is required.  Return 0 if no quoting is required.
+*/
+static char quoteChar(const char *zName){
+  /* All SQLite keywords, in alphabetical order */
+  static const char *azKeywords[] = {
+    "ABORT", "ACTION", "ADD", "AFTER", "ALL", "ALTER", "ANALYZE", "AND", "AS",
+    "ASC", "ATTACH", "AUTOINCREMENT", "BEFORE", "BEGIN", "BETWEEN", "BY",
+    "CASCADE", "CASE", "CAST", "CHECK", "COLLATE", "COLUMN", "COMMIT",
+    "CONFLICT", "CONSTRAINT", "CREATE", "CROSS", "CURRENT_DATE",
+    "CURRENT_TIME", "CURRENT_TIMESTAMP", "DATABASE", "DEFAULT", "DEFERRABLE",
+    "DEFERRED", "DELETE", "DESC", "DETACH", "DISTINCT", "DROP", "EACH",
+    "ELSE", "END", "ESCAPE", "EXCEPT", "EXCLUSIVE", "EXISTS", "EXPLAIN",
+    "FAIL", "FOR", "FOREIGN", "FROM", "FULL", "GLOB", "GROUP", "HAVING", "IF",
+    "IGNORE", "IMMEDIATE", "IN", "INDEX", "INDEXED", "INITIALLY", "INNER",
+    "INSERT", "INSTEAD", "INTERSECT", "INTO", "IS", "ISNULL", "JOIN", "KEY",
+    "LEFT", "LIKE", "LIMIT", "MATCH", "NATURAL", "NO", "NOT", "NOTNULL",
+    "NULL", "OF", "OFFSET", "ON", "OR", "ORDER", "OUTER", "PLAN", "PRAGMA",
+    "PRIMARY", "QUERY", "RAISE", "RECURSIVE", "REFERENCES", "REGEXP",
+    "REINDEX", "RELEASE", "RENAME", "REPLACE", "RESTRICT", "RIGHT",
+    "ROLLBACK", "ROW", "SAVEPOINT", "SELECT", "SET", "TABLE", "TEMP",
+    "TEMPORARY", "THEN", "TO", "TRANSACTION", "TRIGGER", "UNION", "UNIQUE",
+    "UPDATE", "USING", "VACUUM", "VALUES", "VIEW", "VIRTUAL", "WHEN", "WHERE",
+    "WITH", "WITHOUT",
+  };
+  int i, lwr, upr, mid, c;
+  if( !isalpha((unsigned char)zName[0]) && zName[0]!='_' ) return '"';
+  for(i=0; zName[i]; i++){
+    if( !isalnum((unsigned char)zName[i]) && zName[i]!='_' ) return '"';
+  }
+  lwr = 0;
+  upr = sizeof(azKeywords)/sizeof(azKeywords[0]) - 1;
+  while( lwr<=upr ){
+    mid = (lwr+upr)/2;
+    c = sqlite3_stricmp(azKeywords[mid], zName);
+    if( c==0 ) return '"';
+    if( c<0 ){
+      lwr = mid+1;
+    }else{
+      upr = mid-1;
+    }
+  }
+  return 0;
+}
+
+
+/*
+** Release memory previously allocated by tableColumnList().
+*/
+static void freeColumnList(char **azCol){
+  int i;
+  for(i=1; azCol[i]; i++){
+    sqlite3_free(azCol[i]);
+  }
+  /* azCol[0] is a static string */
+  sqlite3_free(azCol);
+}
+
+/*
+** Return a list of pointers to strings which are the names of all
+** columns in table zTab.   The memory to hold the names is dynamically
+** allocated and must be released by the caller using a subsequent call
+** to freeColumnList().
+**
+** The azCol[0] entry is usually NULL.  However, if zTab contains a rowid
+** value that needs to be preserved, then azCol[0] is filled in with the
+** name of the rowid column.
+**
+** The first regular column in the table is azCol[1].  The list is terminated
+** by an entry with azCol[i]==0.
+*/
+static char **tableColumnList(DState *p, const char *zTab){
+  char **azCol = 0;
+  sqlite3_stmt *pStmt = 0;
+  char *zSql;
+  int nCol = 0;
+  int nAlloc = 0;
+  int nPK = 0;       /* Number of PRIMARY KEY columns seen */
+  int isIPK = 0;     /* True if one PRIMARY KEY column of type INTEGER */
+  int preserveRowid = 1;
+  int rc;
+
+  zSql = sqlite3_mprintf("PRAGMA table_info=%Q", zTab);
+  if( zSql==0 ) return 0;
+  rc = sqlite3_prepare_v2(p->db, zSql, -1, &pStmt, 0);
+  sqlite3_free(zSql);
+  if( rc ) return 0;
+  azCol[0] = 0;
+  while( sqlite3_step(pStmt)==SQLITE_ROW ){
+    if( nCol>=nAlloc-2 ){
+      char **azNew;
+      nAlloc = nAlloc*2 + nCol + 10;
+      azNew = sqlite3_realloc(azCol, nAlloc*sizeof(azCol[0]));
+      if( azNew==0 ) goto col_oom;
+      azCol = azNew;
+    }
+    azCol[++nCol] = sqlite3_mprintf("%s", sqlite3_column_text(pStmt, 1));
+    if( azCol[nCol]==0 ) goto col_oom;
+    if( sqlite3_column_int(pStmt, 5) ){
+      nPK++;
+      if( nPK==1
+       && sqlite3_stricmp((const char*)sqlite3_column_text(pStmt,2),
+                          "INTEGER")==0 
+      ){
+        isIPK = 1;
+      }else{
+        isIPK = 0;
+      }
+    }
+  }
+  sqlite3_finalize(pStmt);
+  pStmt = 0;
+  azCol[nCol+1] = 0;
+
+  /* The decision of whether or not a rowid really needs to be preserved
+  ** is tricky.  We never need to preserve a rowid for a WITHOUT ROWID table
+  ** or a table with an INTEGER PRIMARY KEY.  We are unable to preserve
+  ** rowids on tables where the rowid is inaccessible because there are other
+  ** columns in the table named "rowid", "_rowid_", and "oid".
+  */
+  if( isIPK ){
+    /* If a single PRIMARY KEY column with type INTEGER was seen, then it
+    ** might be an alise for the ROWID.  But it might also be a WITHOUT ROWID
+    ** table or a INTEGER PRIMARY KEY DESC column, neither of which are
+    ** ROWID aliases.  To distinguish these cases, check to see if
+    ** there is a "pk" entry in "PRAGMA index_list".  There will be
+    ** no "pk" index if the PRIMARY KEY really is an alias for the ROWID.
+    */
+    zSql = sqlite3_mprintf("SELECT 1 FROM pragma_index_list(%Q)"
+                           " WHERE origin='pk'", zTab);
+    if( zSql==0 ) goto col_oom;
+    rc = sqlite3_prepare_v2(p->db, zSql, -1, &pStmt, 0);
+    sqlite3_free(zSql);
+    if( rc ){
+      freeColumnList(azCol);
+      return 0;
+    }
+    rc = sqlite3_step(pStmt);
+    sqlite3_finalize(pStmt);
+    pStmt = 0;
+    preserveRowid = rc==SQLITE_ROW;
+  }
+  if( preserveRowid ){
+    /* Only preserve the rowid if we can find a name to use for the
+    ** rowid */
+    static char *azRowid[] = { "rowid", "_rowid_", "oid" };
+    int i, j;
+    for(j=0; j<3; j++){
+      for(i=1; i<=nCol; i++){
+        if( sqlite3_stricmp(azRowid[j],azCol[i])==0 ) break;
+      }
+      if( i>nCol ){
+        /* At this point, we know that azRowid[j] is not the name of any
+        ** ordinary column in the table.  Verify that azRowid[j] is a valid
+        ** name for the rowid before adding it to azCol[0].  WITHOUT ROWID
+        ** tables will fail this last check */
+        int rc;
+        rc = sqlite3_table_column_metadata(p->db,0,zTab,azRowid[j],0,0,0,0,0);
+        if( rc==SQLITE_OK ) azCol[0] = azRowid[j];
+        break;
+      }
+    }
+  }
+  return azCol;
+
+col_oom:
+  sqlite3_finalize(pStmt);
+  freeColumnList(azCol);
+  p->nErr++;
+  p->rc = SQLITE_NOMEM;
+  return 0;
+}
+
+/*
+** Send mprintf-formatted content to the output callback.
+*/
+static void output_formatted(DState *p, const char *zFormat, ...){
+  va_list ap;
+  char *z;
+  va_start(ap, zFormat);
+  z = sqlite3_mprintf(zFormat, ap);
+  va_end(ap);
+  p->xCallback(z, p->pArg);
+  sqlite3_free(z);
+}
+
+/*
+** Output the given string as a quoted string using SQL quoting conventions.
+**
+** The "\n" and "\r" characters are converted to char(10) and char(13)
+** to prevent them from being transformed by end-of-line translators.
+*/
+static void output_quoted_string(DState *p, const unsigned char *z){
+  int i;
+  char c;
+  int inQuote = 0;
+  int bStarted = 0;
+
+  for(i=0; (c = z[i])!=0 && c!='\'' && c!='\n' && c!='\r'; i++){}
+  if( c==0 ){
+    output_formatted(p, "'%s'", z);
+    return;
+  }
+  while( *z ){
+    for(i=0; (c = z[i])!=0 && c!='\n' && c!='\r' && c!='\''; i++){}
+    if( c=='\'' ) i++;
+    if( i ){
+      if( !inQuote ){
+        if( bStarted ) p->xCallback("||", p->pArg);
+        p->xCallback("'", p->pArg);
+        inQuote = 1;
+      }
+      output_formatted(p, "%.*s", i, z);
+      z += i;
+      bStarted = 1;
+    }
+    if( c=='\'' ){
+      p->xCallback("'", p->pArg);
+      continue;
+    }
+    if( inQuote ){
+      p->xCallback("'", p->pArg);
+      inQuote = 0;
+    }
+    if( c==0 ){
+      break;
+    }
+    for(i=0; (c = z[i])=='\r' || c=='\n'; i++){
+      if( bStarted ) p->xCallback("||", p->pArg);
+      output_formatted(p, "char(%d)", c);
+      bStarted = 1;
+    }
+    z += i;
+  }
+  if( inQuote ) p->xCallback("'", p->pArg);
+}
+
+/*
+** This is an sqlite3_exec callback routine used for dumping the database.
+** Each row received by this callback consists of a table name,
+** the table type ("index" or "table") and SQL to create the table.
+** This routine should print text sufficient to recreate the table.
+*/
+static int dump_callback(void *pArg, int nArg, char **azArg, char **azCol){
+  int rc;
+  const char *zTable;
+  const char *zType;
+  const char *zSql;
+  DState *p = (DState*)pArg;
+  sqlite3_stmt *pStmt;
+
+  (void)azCol;
+  if( nArg!=3 ) return 1;
+  zTable = azArg[0];
+  zType = azArg[1];
+  zSql = azArg[2];
+
+  if( strcmp(zTable, "sqlite_sequence")==0 ){
+    p->xCallback("DELETE FROM sqlite_sequence;\n", p->pArg);
+  }else if( sqlite3_strglob("sqlite_stat?", zTable)==0 ){
+    p->xCallback("ANALYZE sqlite_master;\n", p->pArg);
+  }else if( strncmp(zTable, "sqlite_", 7)==0 ){
+    return 0;
+  }else if( strncmp(zSql, "CREATE VIRTUAL TABLE", 20)==0 ){
+    if( !p->writableSchema ){
+      p->xCallback("PRAGMA writable_schema=ON;\n", p->pArg);
+      p->writableSchema = 1;
+    }
+    output_formatted(p,
+       "INSERT INTO sqlite_master(type,name,tbl_name,rootpage,sql)"
+       "VALUES('table','%q','%q',0,'%q');",
+       zTable, zTable, zSql);
+    return 0;
+  }else{
+    if( sqlite3_strglob("CREATE TABLE ['\"]*", zSql)==0 ){
+      p->xCallback("CREATE TABLE IF NOT EXISTS ", p->pArg);
+      p->xCallback(zSql+13, p->pArg);
+    }else{
+      p->xCallback(zSql, p->pArg);
+    }
+    p->xCallback(";\n", p->pArg);
+  }
+
+  if( strcmp(zType, "table")==0 ){
+    DText sSelect;
+    DText sTable;
+    char **azCol;
+    int i;
+    int nCol;
+
+    azCol = tableColumnList(p, zTable);
+    if( azCol==0 ) return 0;
+
+    /* Always quote the table name, even if it appears to be pure ascii,
+    ** in case it is a keyword. Ex:  INSERT INTO "table" ... */
+    initText(&sTable);
+    appendText(&sTable, zTable, quoteChar(zTable));
+    /* If preserving the rowid, add a column list after the table name.
+    ** In other words:  "INSERT INTO tab(rowid,a,b,c,...) VALUES(...)"
+    ** instead of the usual "INSERT INTO tab VALUES(...)".
+    */
+    if( azCol[0] ){
+      appendText(&sTable, "(", 0);
+      appendText(&sTable, azCol[0], 0);
+      for(i=1; azCol[i]; i++){
+        appendText(&sTable, ",", 0);
+        appendText(&sTable, azCol[i], quoteChar(azCol[i]));
+      }
+      appendText(&sTable, ")", 0);
+    }
+    appendText(&sTable, " VALUES(", 0);
+
+    /* Build an appropriate SELECT statement */
+    initText(&sSelect);
+    appendText(&sSelect, "SELECT ", 0);
+    if( azCol[0] ){
+      appendText(&sSelect, azCol[0], 0);
+      appendText(&sSelect, ",", 0);
+    }
+    for(i=1; azCol[i]; i++){
+      appendText(&sSelect, azCol[i], quoteChar(azCol[i]));
+      if( azCol[i+1] ){
+        appendText(&sSelect, ",", 0);
+      }
+    }
+    nCol = i-1;
+    freeColumnList(azCol);
+    appendText(&sSelect, " FROM ", 0);
+    appendText(&sSelect, zTable, quoteChar(zTable));
+
+    rc = sqlite3_prepare_v2(p->db, sSelect.z, -1, &pStmt, 0);
+    if( rc!=SQLITE_OK ){
+      p->nErr++;
+      if( p->rc==SQLITE_OK ) p->rc = rc;
+    }else{
+      while( SQLITE_ROW==sqlite3_step(pStmt) ){
+        p->xCallback(sTable.z, p->pArg);
+        for(i=0; i<nCol; i++){
+          if( i ) p->xCallback(",", p->pArg);
+          switch( sqlite3_column_type(pStmt,i) ){
+            case SQLITE_INTEGER: {
+              output_formatted(p, "%lld", sqlite3_column_int64(pStmt,i));
+              break;
+            }
+            case SQLITE_FLOAT: {
+              double r = sqlite3_column_double(pStmt,i);
+              output_formatted(p, "%!.20g", r);
+              break;
+            }
+            case SQLITE_NULL: {
+              p->xCallback("NULL", p->pArg);
+              break;
+            }
+            case SQLITE_TEXT: {
+              output_quoted_string(p, sqlite3_column_text(pStmt,i));
+              break;
+            }
+            case SQLITE_BLOB: {
+              int nByte = sqlite3_column_bytes(pStmt,i);
+              unsigned char *a = (unsigned char*)sqlite3_column_blob(pStmt,i);
+              int j;
+              p->xCallback("x'", p->pArg);
+              for(j=0; j<nByte; j++){
+                char zWord[3];
+                zWord[0] = "0123456789abcdef"[(a[j]>>4)&15];
+                zWord[1] = "0123456789abcdef"[a[j]&15];
+                zWord[2] = 0;
+                p->xCallback(zWord, p->pArg);
+              }
+              p->xCallback("'", p->pArg);
+              break;
+            }
+          }
+        }
+        p->xCallback(");\n", p->pArg);
+      }
+    }
+    sqlite3_finalize(pStmt);
+    freeText(&sTable);
+    freeText(&sSelect);
+  }
+  return 0;
+}
+
+
+/*
+** Execute a query statement that will generate SQL output.  Print
+** the result columns, comma-separated, on a line and then add a
+** semicolon terminator to the end of that line.
+**
+** If the number of columns is 1 and that column contains text "--"
+** then write the semicolon on a separate line.  That way, if a
+** "--" comment occurs at the end of the statement, the comment
+** won't consume the semicolon terminator.
+*/
+static void output_sql_from_query(
+  DState *p,               /* Query context */
+  const char *zSelect,     /* SELECT statement to extract content */
+  ...
+){
+  sqlite3_stmt *pSelect;
+  int rc;
+  int nResult;
+  int i;
+  const char *z;
+  char *zSql;
+  va_list ap;
+  va_start(ap, zSelect);
+  zSql = sqlite3_mprintf(zSelect, ap);
+  va_end(ap);
+  if( zSql==0 ){
+    p->rc = SQLITE_NOMEM;
+    p->nErr++;
+    return;
+  }
+  rc = sqlite3_prepare_v2(p->db, zSql, -1, &pSelect, 0);
+  sqlite3_free(zSql);
+  if( rc!=SQLITE_OK || !pSelect ){
+    output_formatted(p, "/**** ERROR: (%d) %s *****/\n", rc,
+                sqlite3_errmsg(p->db));
+    p->nErr++;
+    return;
+  }
+  rc = sqlite3_step(pSelect);
+  nResult = sqlite3_column_count(pSelect);
+  while( rc==SQLITE_ROW ){
+    z = (const char*)sqlite3_column_text(pSelect, 0);
+    p->xCallback(z, p->pArg);
+    for(i=1; i<nResult; i++){
+      p->xCallback(",", p->pArg);
+      p->xCallback((const char*)sqlite3_column_text(pSelect,i), p->pArg);
+    }
+    if( z==0 ) z = "";
+    while( z[0] && (z[0]!='-' || z[1]!='-') ) z++;
+    if( z[0] ){
+      p->xCallback("\n;\n", p->pArg);
+    }else{
+      p->xCallback(";\n", p->pArg);
+    }
+    rc = sqlite3_step(pSelect);
+  }
+  rc = sqlite3_finalize(pSelect);
+  if( rc!=SQLITE_OK ){
+    output_formatted(p, "/**** ERROR: (%d) %s *****/\n", rc,
+                     sqlite3_errmsg(p->db));
+    if( (rc&0xff)!=SQLITE_CORRUPT ) p->nErr++;
+  }
+}
+
+/*
+** Run zQuery.  Use dump_callback() as the callback routine so that
+** the contents of the query are output as SQL statements.
+**
+** If we get a SQLITE_CORRUPT error, rerun the query after appending
+** "ORDER BY rowid DESC" to the end.
+*/
+static void run_schema_dump_query(
+  DState *p,
+  const char *zQuery,
+  ...
+){
+  char *zErr = 0;
+  char *z;
+  va_list ap;
+  va_start(ap, zQuery);
+  z = sqlite3_mprintf(zQuery, ap);
+  va_end(ap); 
+  sqlite3_exec(p->db, z, dump_callback, p, &zErr);
+  sqlite3_free(z);
+  if( zErr ){
+    output_formatted(p, "/****** %s ******/\n", zErr);
+    sqlite3_free(zErr);
+    p->nErr++;
+    zErr = 0;
+  }
+}
 
 /*
 ** Convert an SQLite database into SQL statements that will recreate that
@@ -57,7 +625,49 @@ int sqlite3_db_dump(
   int (*xCallback)(const char*,void*),   /* Output sent to this callback */
   void *pArg                             /* Second argument of the callback */
 ){
-  return SQLITE_OK;
+  DState x;
+  memset(&x, 0, sizeof(x));
+  x.rc = sqlite3_exec(db, "BEGIN", 0, 0, 0);
+  if( x.rc ) return x.rc;
+  x.db = db;
+  x.xCallback = xCallback;
+  x.pArg = pArg;
+  xCallback("PRAGMA foreign_keys=OFF;\nBEGIN TRANSACTION;\n", pArg);
+  if( zTable==0 ){
+    run_schema_dump_query(&x,
+      "SELECT name, type, sql FROM \"%w\".sqlite_master "
+      "WHERE sql NOT NULL AND type=='table' AND name!='sqlite_sequence'",
+      zSchema
+    );
+    run_schema_dump_query(&x,
+      "SELECT name, type, sql FROM \"%w\".sqlite_master "
+      "WHERE name=='sqlite_sequence'", zSchema
+    );
+    output_sql_from_query(&x,
+      "SELECT sql FROM sqlite_master "
+      "WHERE sql NOT NULL AND type IN ('index','trigger','view')", 0
+    );
+  }else{
+    run_schema_dump_query(&x,
+      "SELECT name, type, sql FROM \"%w\".sqlite_master "
+      "WHERE tbl_name=%Q COLLATE nocase AND type=='table'"
+      "  AND sql NOT NULL",
+      zSchema, zTable
+    );
+    output_sql_from_query(&x,
+      "SELECT sql FROM \"%w\".sqlite_master "
+      "WHERE sql NOT NULL"
+      "  AND type IN ('index','trigger','view')"
+      "  AND tbl_name=%Q COLLATE nocase",
+      zSchema, zTable
+    ); 
+  }
+  if( x.writableSchema ){
+    xCallback("PRAGMA writable_schema=OFF;\n", pArg);
+  }
+  xCallback(x.nErr ? "ROLLBACK; -- due to errors\n" : "COMMIT;\n", pArg);
+  sqlite3_exec(db, "COMMIT", 0, 0, 0);
+  return x.rc;
 }
 
 
index 4fc996e4ef735abe78b8208c81d81124637bce5f..dfbee1c83e5a046d57756ed42b4357c82c2e29f8 100644 (file)
--- a/manifest
+++ b/manifest
@@ -1,5 +1,5 @@
-C Infrastructure\sfor\san\sextension\sC-library\sto\simplement\ssqlite3_db_dump()\sand\na\scorresponding\s"dbdump"\scommand-line\sutility\s-\sboth\sof\swhich\sdo\sthe\ssame\nwork\sas\sthe\s".dump"\scommand\sof\sthe\sCLI.
-D 2017-03-13T19:26:34.140
+C First\sdraft\sof\sthe\scomplete\sdbdump.c\slibrary.
+D 2017-03-13T21:26:41.643
 F Makefile.in 9605f4c49eace601d5c12c85dd6e037cc613a6d823e857614ba26b42f1285db0
 F Makefile.linux-gcc 7bc79876b875010e8c8f9502eb935ca92aa3c434
 F Makefile.msc 9020fa41eb91f657ae0cc44145d0a2f3af520860
@@ -211,7 +211,7 @@ F ext/misc/carray.c 40c27641010a4dc67e3690bdb7c9d36ca58b3c2d
 F ext/misc/closure.c 0d2a038df8fbae7f19de42e7c7d71f2e4dc88704
 F ext/misc/compress.c 122faa92d25033d6c3f07c39231de074ab3d2e83
 F ext/misc/csv.c 531a46cbad789fca0aa9db69a0e6c8ac9e68767d
-F ext/misc/dbdump.c 5777b95fe1797a7fce841f4cb88bc30f66b8a3c929e988b1d3b3cf4aab044739
+F ext/misc/dbdump.c 819eb33f6ff788a466525557f7ea3742078853cc794b2eb0799a254dd6da81e2
 F ext/misc/eval.c f971962e92ebb8b0a4e6b62949463ee454d88fa2
 F ext/misc/fileio.c d4171c815d6543a9edef8308aab2951413cd8d0f
 F ext/misc/fuzzer.c 7c64b8197bb77b7d64eff7cac7848870235d4c25
@@ -1564,10 +1564,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 68f6dc7af1013f296a11db14c007cc13cc3fe56832848bfed835ed8f74dcc676
-R 26910a3d8e71b6c159f5bba8a9a5daac
-T *branch * dbdump
-T *sym-dbdump *
-T -sym-trunk *
+P 74c5ace498f72d7f5495203678bedd0bc540211131a4e4db7b62115d5322a288
+R dcbc2252d3d150af6e3faa70158994bd
 U drh
-Z 0266a8ae90d3841510ebcee8fa626828
+Z 9a73382dae2ea2eb6bda6a3d74fd54dd
index f9e622d35f53e1c06fa9b32f749786b3c07d98cd..0597c414ba895bc7ca5dfc24d3562ae097c562f9 100644 (file)
@@ -1 +1 @@
-74c5ace498f72d7f5495203678bedd0bc540211131a4e4db7b62115d5322a288
\ No newline at end of file
+84ea4fcc52d0af02648c52989c2e69f4961071e1620382555ec59a39161a7a7d
\ No newline at end of file