Fossil SCM
Add the .clone command to "fossil sqlite3". Taken from SQLite trunk.
Commit
90bd20308b5705caa687acb2aab1f62a6c77a4bb
Parent
e707906a8a1e7e3…
1 file changed
+218
+218
| --- src/shell.c | ||
| +++ src/shell.c | ||
| @@ -1541,10 +1541,11 @@ | ||
| 1541 | 1541 | ** Text of a help message |
| 1542 | 1542 | */ |
| 1543 | 1543 | static char zHelp[] = |
| 1544 | 1544 | ".backup ?DB? FILE Backup DB (default \"main\") to FILE\n" |
| 1545 | 1545 | ".bail ON|OFF Stop after hitting an error. Default OFF\n" |
| 1546 | + ".clone NEWDB Clone data into NEWDB from the existing database\n" | |
| 1546 | 1547 | ".databases List names and files of attached databases\n" |
| 1547 | 1548 | ".dump ?TABLE? ... Dump the database in an SQL text format\n" |
| 1548 | 1549 | " If TABLE specified, only dump tables matching\n" |
| 1549 | 1550 | " LIKE pattern TABLE.\n" |
| 1550 | 1551 | ".echo ON|OFF Turn command echo on or off\n" |
| @@ -1893,10 +1894,223 @@ | ||
| 1893 | 1894 | p->cTerm = c; |
| 1894 | 1895 | } |
| 1895 | 1896 | if( p->z ) p->z[p->n] = 0; |
| 1896 | 1897 | return p->z; |
| 1897 | 1898 | } |
| 1899 | + | |
| 1900 | +/* | |
| 1901 | +** Try to transfer data for table zTable. If an error is seen while | |
| 1902 | +** moving forward, try to go backwards. The backwards movement won't | |
| 1903 | +** work for WITHOUT ROWID tables. | |
| 1904 | +*/ | |
| 1905 | +static void tryToCloneData( | |
| 1906 | + struct callback_data *p, | |
| 1907 | + sqlite3 *newDb, | |
| 1908 | + const char *zTable | |
| 1909 | +){ | |
| 1910 | + sqlite3_stmt *pQuery = 0; | |
| 1911 | + sqlite3_stmt *pInsert = 0; | |
| 1912 | + char *zQuery = 0; | |
| 1913 | + char *zInsert = 0; | |
| 1914 | + int rc; | |
| 1915 | + int i, j, n; | |
| 1916 | + int nTable = (int)strlen(zTable); | |
| 1917 | + int k = 0; | |
| 1918 | + int cnt = 0; | |
| 1919 | + const int spinRate = 10000; | |
| 1920 | + | |
| 1921 | + zQuery = sqlite3_mprintf("SELECT * FROM \"%w\"", zTable); | |
| 1922 | + rc = sqlite3_prepare_v2(p->db, zQuery, -1, &pQuery, 0); | |
| 1923 | + if( rc ){ | |
| 1924 | + fprintf(stderr, "Error %d: %s on [%s]\n", | |
| 1925 | + sqlite3_extended_errcode(p->db), sqlite3_errmsg(p->db), | |
| 1926 | + zQuery); | |
| 1927 | + goto end_data_xfer; | |
| 1928 | + } | |
| 1929 | + n = sqlite3_column_count(pQuery); | |
| 1930 | + zInsert = sqlite3_malloc(200 + nTable + n*3); | |
| 1931 | + if( zInsert==0 ){ | |
| 1932 | + fprintf(stderr, "out of memory\n"); | |
| 1933 | + goto end_data_xfer; | |
| 1934 | + } | |
| 1935 | + sqlite3_snprintf(200+nTable,zInsert, | |
| 1936 | + "INSERT OR IGNORE INTO \"%s\" VALUES(?", zTable); | |
| 1937 | + i = (int)strlen(zInsert); | |
| 1938 | + for(j=1; j<n; j++){ | |
| 1939 | + memcpy(zInsert+i, ",?", 2); | |
| 1940 | + i += 2; | |
| 1941 | + } | |
| 1942 | + memcpy(zInsert+i, ");", 3); | |
| 1943 | + rc = sqlite3_prepare_v2(newDb, zInsert, -1, &pInsert, 0); | |
| 1944 | + if( rc ){ | |
| 1945 | + fprintf(stderr, "Error %d: %s on [%s]\n", | |
| 1946 | + sqlite3_extended_errcode(newDb), sqlite3_errmsg(newDb), | |
| 1947 | + zQuery); | |
| 1948 | + goto end_data_xfer; | |
| 1949 | + } | |
| 1950 | + for(k=0; k<2; k++){ | |
| 1951 | + while( (rc = sqlite3_step(pQuery))==SQLITE_ROW ){ | |
| 1952 | + for(i=0; i<n; i++){ | |
| 1953 | + switch( sqlite3_column_type(pQuery, i) ){ | |
| 1954 | + case SQLITE_NULL: { | |
| 1955 | + sqlite3_bind_null(pInsert, i+1); | |
| 1956 | + break; | |
| 1957 | + } | |
| 1958 | + case SQLITE_INTEGER: { | |
| 1959 | + sqlite3_bind_int64(pInsert, i+1, sqlite3_column_int64(pQuery,i)); | |
| 1960 | + break; | |
| 1961 | + } | |
| 1962 | + case SQLITE_FLOAT: { | |
| 1963 | + sqlite3_bind_double(pInsert, i+1, sqlite3_column_double(pQuery,i)); | |
| 1964 | + break; | |
| 1965 | + } | |
| 1966 | + case SQLITE_TEXT: { | |
| 1967 | + sqlite3_bind_text(pInsert, i+1, | |
| 1968 | + (const char*)sqlite3_column_text(pQuery,i), | |
| 1969 | + -1, SQLITE_STATIC); | |
| 1970 | + break; | |
| 1971 | + } | |
| 1972 | + case SQLITE_BLOB: { | |
| 1973 | + sqlite3_bind_blob(pInsert, i+1, sqlite3_column_blob(pQuery,i), | |
| 1974 | + sqlite3_column_bytes(pQuery,i), | |
| 1975 | + SQLITE_STATIC); | |
| 1976 | + break; | |
| 1977 | + } | |
| 1978 | + } | |
| 1979 | + } /* End for */ | |
| 1980 | + rc = sqlite3_step(pInsert); | |
| 1981 | + if( rc!=SQLITE_OK && rc!=SQLITE_ROW && rc!=SQLITE_DONE ){ | |
| 1982 | + fprintf(stderr, "Error %d: %s\n", sqlite3_extended_errcode(newDb), | |
| 1983 | + sqlite3_errmsg(newDb)); | |
| 1984 | + } | |
| 1985 | + sqlite3_reset(pInsert); | |
| 1986 | + cnt++; | |
| 1987 | + if( (cnt%spinRate)==0 ){ | |
| 1988 | + printf("%c\b", "|/-\\"[(cnt/spinRate)%4]); | |
| 1989 | + fflush(stdout); | |
| 1990 | + } | |
| 1991 | + } /* End while */ | |
| 1992 | + if( rc==SQLITE_DONE ) break; | |
| 1993 | + sqlite3_finalize(pQuery); | |
| 1994 | + sqlite3_free(zQuery); | |
| 1995 | + zQuery = sqlite3_mprintf("SELECT * FROM \"%w\" ORDER BY rowid DESC;", | |
| 1996 | + zTable); | |
| 1997 | + rc = sqlite3_prepare_v2(p->db, zQuery, -1, &pQuery, 0); | |
| 1998 | + if( rc ){ | |
| 1999 | + fprintf(stderr, "Warning: cannot step \"%s\" backwards", zTable); | |
| 2000 | + break; | |
| 2001 | + } | |
| 2002 | + } /* End for(k=0...) */ | |
| 2003 | + | |
| 2004 | +end_data_xfer: | |
| 2005 | + sqlite3_finalize(pQuery); | |
| 2006 | + sqlite3_finalize(pInsert); | |
| 2007 | + sqlite3_free(zQuery); | |
| 2008 | + sqlite3_free(zInsert); | |
| 2009 | +} | |
| 2010 | + | |
| 2011 | + | |
| 2012 | +/* | |
| 2013 | +** Try to transfer all rows of the schema that match zWhere. For | |
| 2014 | +** each row, invoke xForEach() on the object defined by that row. | |
| 2015 | +** If an error is encountered while moving forward through the | |
| 2016 | +** sqlite_master table, try again moving backwards. | |
| 2017 | +*/ | |
| 2018 | +static void tryToCloneSchema( | |
| 2019 | + struct callback_data *p, | |
| 2020 | + sqlite3 *newDb, | |
| 2021 | + const char *zWhere, | |
| 2022 | + void (*xForEach)(struct callback_data*,sqlite3*,const char*) | |
| 2023 | +){ | |
| 2024 | + sqlite3_stmt *pQuery = 0; | |
| 2025 | + char *zQuery = 0; | |
| 2026 | + int rc; | |
| 2027 | + const unsigned char *zName; | |
| 2028 | + const unsigned char *zSql; | |
| 2029 | + char *zErrMsg = 0; | |
| 2030 | + | |
| 2031 | + zQuery = sqlite3_mprintf("SELECT name, sql FROM sqlite_master" | |
| 2032 | + " WHERE %s", zWhere); | |
| 2033 | + rc = sqlite3_prepare_v2(p->db, zQuery, -1, &pQuery, 0); | |
| 2034 | + if( rc ){ | |
| 2035 | + fprintf(stderr, "Error: (%d) %s on [%s]\n", | |
| 2036 | + sqlite3_extended_errcode(p->db), sqlite3_errmsg(p->db), | |
| 2037 | + zQuery); | |
| 2038 | + goto end_schema_xfer; | |
| 2039 | + } | |
| 2040 | + while( (rc = sqlite3_step(pQuery))==SQLITE_ROW ){ | |
| 2041 | + zName = sqlite3_column_text(pQuery, 0); | |
| 2042 | + zSql = sqlite3_column_text(pQuery, 1); | |
| 2043 | + printf("%s... ", zName); fflush(stdout); | |
| 2044 | + sqlite3_exec(newDb, (const char*)zSql, 0, 0, &zErrMsg); | |
| 2045 | + if( zErrMsg ){ | |
| 2046 | + fprintf(stderr, "Error: %s\nSQL: [%s]\n", zErrMsg, zSql); | |
| 2047 | + sqlite3_free(zErrMsg); | |
| 2048 | + zErrMsg = 0; | |
| 2049 | + } | |
| 2050 | + if( xForEach ){ | |
| 2051 | + xForEach(p, newDb, (const char*)zName); | |
| 2052 | + } | |
| 2053 | + printf("done\n"); | |
| 2054 | + } | |
| 2055 | + if( rc!=SQLITE_DONE ){ | |
| 2056 | + sqlite3_finalize(pQuery); | |
| 2057 | + sqlite3_free(zQuery); | |
| 2058 | + zQuery = sqlite3_mprintf("SELECT name, sql FROM sqlite_master" | |
| 2059 | + " WHERE %s ORDER BY rowid DESC", zWhere); | |
| 2060 | + rc = sqlite3_prepare_v2(p->db, zQuery, -1, &pQuery, 0); | |
| 2061 | + if( rc ){ | |
| 2062 | + fprintf(stderr, "Error: (%d) %s on [%s]\n", | |
| 2063 | + sqlite3_extended_errcode(p->db), sqlite3_errmsg(p->db), | |
| 2064 | + zQuery); | |
| 2065 | + goto end_schema_xfer; | |
| 2066 | + } | |
| 2067 | + while( (rc = sqlite3_step(pQuery))==SQLITE_ROW ){ | |
| 2068 | + zName = sqlite3_column_text(pQuery, 0); | |
| 2069 | + zSql = sqlite3_column_text(pQuery, 1); | |
| 2070 | + printf("%s... ", zName); fflush(stdout); | |
| 2071 | + sqlite3_exec(newDb, (const char*)zSql, 0, 0, &zErrMsg); | |
| 2072 | + if( zErrMsg ){ | |
| 2073 | + fprintf(stderr, "Error: %s\nSQL: [%s]\n", zErrMsg, zSql); | |
| 2074 | + sqlite3_free(zErrMsg); | |
| 2075 | + zErrMsg = 0; | |
| 2076 | + } | |
| 2077 | + if( xForEach ){ | |
| 2078 | + xForEach(p, newDb, (const char*)zName); | |
| 2079 | + } | |
| 2080 | + printf("done\n"); | |
| 2081 | + } | |
| 2082 | + } | |
| 2083 | +end_schema_xfer: | |
| 2084 | + sqlite3_finalize(pQuery); | |
| 2085 | + sqlite3_free(zQuery); | |
| 2086 | +} | |
| 2087 | + | |
| 2088 | +/* | |
| 2089 | +** Open a new database file named "zNewDb". Try to recover as much information | |
| 2090 | +** as possible out of the main database (which might be corrupt) and write it | |
| 2091 | +** into zNewDb. | |
| 2092 | +*/ | |
| 2093 | +static void tryToClone(struct callback_data *p, const char *zNewDb){ | |
| 2094 | + int rc; | |
| 2095 | + sqlite3 *newDb = 0; | |
| 2096 | + if( access(zNewDb,0)==0 ){ | |
| 2097 | + fprintf(stderr, "File \"%s\" already exists.\n", zNewDb); | |
| 2098 | + return; | |
| 2099 | + } | |
| 2100 | + rc = sqlite3_open(zNewDb, &newDb); | |
| 2101 | + if( rc ){ | |
| 2102 | + fprintf(stderr, "Cannot create output database: %s\n", | |
| 2103 | + sqlite3_errmsg(newDb)); | |
| 2104 | + }else{ | |
| 2105 | + sqlite3_exec(newDb, "BEGIN EXCLUSIVE;", 0, 0, 0); | |
| 2106 | + tryToCloneSchema(p, newDb, "type='table'", tryToCloneData); | |
| 2107 | + tryToCloneSchema(p, newDb, "type!='table'", 0); | |
| 2108 | + sqlite3_exec(newDb, "COMMIT;", 0, 0, 0); | |
| 2109 | + } | |
| 2110 | + sqlite3_close(newDb); | |
| 2111 | +} | |
| 1898 | 2112 | |
| 1899 | 2113 | /* |
| 1900 | 2114 | ** If an input line begins with "." then invoke this routine to |
| 1901 | 2115 | ** process that line. |
| 1902 | 2116 | ** |
| @@ -2000,10 +2214,14 @@ | ||
| 2000 | 2214 | ** routine named test_breakpoint(). |
| 2001 | 2215 | */ |
| 2002 | 2216 | if( c=='b' && n>=3 && strncmp(azArg[0], "breakpoint", n)==0 ){ |
| 2003 | 2217 | test_breakpoint(); |
| 2004 | 2218 | }else |
| 2219 | + | |
| 2220 | + if( c=='c' && strncmp(azArg[0], "clone", n)==0 && nArg>1 && nArg<3 ){ | |
| 2221 | + tryToClone(p, azArg[1]); | |
| 2222 | + }else | |
| 2005 | 2223 | |
| 2006 | 2224 | if( c=='d' && n>1 && strncmp(azArg[0], "databases", n)==0 && nArg==1 ){ |
| 2007 | 2225 | struct callback_data data; |
| 2008 | 2226 | char *zErrMsg = 0; |
| 2009 | 2227 | open_db(p, 0); |
| 2010 | 2228 |
| --- src/shell.c | |
| +++ src/shell.c | |
| @@ -1541,10 +1541,11 @@ | |
| 1541 | ** Text of a help message |
| 1542 | */ |
| 1543 | static char zHelp[] = |
| 1544 | ".backup ?DB? FILE Backup DB (default \"main\") to FILE\n" |
| 1545 | ".bail ON|OFF Stop after hitting an error. Default OFF\n" |
| 1546 | ".databases List names and files of attached databases\n" |
| 1547 | ".dump ?TABLE? ... Dump the database in an SQL text format\n" |
| 1548 | " If TABLE specified, only dump tables matching\n" |
| 1549 | " LIKE pattern TABLE.\n" |
| 1550 | ".echo ON|OFF Turn command echo on or off\n" |
| @@ -1893,10 +1894,223 @@ | |
| 1893 | p->cTerm = c; |
| 1894 | } |
| 1895 | if( p->z ) p->z[p->n] = 0; |
| 1896 | return p->z; |
| 1897 | } |
| 1898 | |
| 1899 | /* |
| 1900 | ** If an input line begins with "." then invoke this routine to |
| 1901 | ** process that line. |
| 1902 | ** |
| @@ -2000,10 +2214,14 @@ | |
| 2000 | ** routine named test_breakpoint(). |
| 2001 | */ |
| 2002 | if( c=='b' && n>=3 && strncmp(azArg[0], "breakpoint", n)==0 ){ |
| 2003 | test_breakpoint(); |
| 2004 | }else |
| 2005 | |
| 2006 | if( c=='d' && n>1 && strncmp(azArg[0], "databases", n)==0 && nArg==1 ){ |
| 2007 | struct callback_data data; |
| 2008 | char *zErrMsg = 0; |
| 2009 | open_db(p, 0); |
| 2010 |
| --- src/shell.c | |
| +++ src/shell.c | |
| @@ -1541,10 +1541,11 @@ | |
| 1541 | ** Text of a help message |
| 1542 | */ |
| 1543 | static char zHelp[] = |
| 1544 | ".backup ?DB? FILE Backup DB (default \"main\") to FILE\n" |
| 1545 | ".bail ON|OFF Stop after hitting an error. Default OFF\n" |
| 1546 | ".clone NEWDB Clone data into NEWDB from the existing database\n" |
| 1547 | ".databases List names and files of attached databases\n" |
| 1548 | ".dump ?TABLE? ... Dump the database in an SQL text format\n" |
| 1549 | " If TABLE specified, only dump tables matching\n" |
| 1550 | " LIKE pattern TABLE.\n" |
| 1551 | ".echo ON|OFF Turn command echo on or off\n" |
| @@ -1893,10 +1894,223 @@ | |
| 1894 | p->cTerm = c; |
| 1895 | } |
| 1896 | if( p->z ) p->z[p->n] = 0; |
| 1897 | return p->z; |
| 1898 | } |
| 1899 | |
| 1900 | /* |
| 1901 | ** Try to transfer data for table zTable. If an error is seen while |
| 1902 | ** moving forward, try to go backwards. The backwards movement won't |
| 1903 | ** work for WITHOUT ROWID tables. |
| 1904 | */ |
| 1905 | static void tryToCloneData( |
| 1906 | struct callback_data *p, |
| 1907 | sqlite3 *newDb, |
| 1908 | const char *zTable |
| 1909 | ){ |
| 1910 | sqlite3_stmt *pQuery = 0; |
| 1911 | sqlite3_stmt *pInsert = 0; |
| 1912 | char *zQuery = 0; |
| 1913 | char *zInsert = 0; |
| 1914 | int rc; |
| 1915 | int i, j, n; |
| 1916 | int nTable = (int)strlen(zTable); |
| 1917 | int k = 0; |
| 1918 | int cnt = 0; |
| 1919 | const int spinRate = 10000; |
| 1920 | |
| 1921 | zQuery = sqlite3_mprintf("SELECT * FROM \"%w\"", zTable); |
| 1922 | rc = sqlite3_prepare_v2(p->db, zQuery, -1, &pQuery, 0); |
| 1923 | if( rc ){ |
| 1924 | fprintf(stderr, "Error %d: %s on [%s]\n", |
| 1925 | sqlite3_extended_errcode(p->db), sqlite3_errmsg(p->db), |
| 1926 | zQuery); |
| 1927 | goto end_data_xfer; |
| 1928 | } |
| 1929 | n = sqlite3_column_count(pQuery); |
| 1930 | zInsert = sqlite3_malloc(200 + nTable + n*3); |
| 1931 | if( zInsert==0 ){ |
| 1932 | fprintf(stderr, "out of memory\n"); |
| 1933 | goto end_data_xfer; |
| 1934 | } |
| 1935 | sqlite3_snprintf(200+nTable,zInsert, |
| 1936 | "INSERT OR IGNORE INTO \"%s\" VALUES(?", zTable); |
| 1937 | i = (int)strlen(zInsert); |
| 1938 | for(j=1; j<n; j++){ |
| 1939 | memcpy(zInsert+i, ",?", 2); |
| 1940 | i += 2; |
| 1941 | } |
| 1942 | memcpy(zInsert+i, ");", 3); |
| 1943 | rc = sqlite3_prepare_v2(newDb, zInsert, -1, &pInsert, 0); |
| 1944 | if( rc ){ |
| 1945 | fprintf(stderr, "Error %d: %s on [%s]\n", |
| 1946 | sqlite3_extended_errcode(newDb), sqlite3_errmsg(newDb), |
| 1947 | zQuery); |
| 1948 | goto end_data_xfer; |
| 1949 | } |
| 1950 | for(k=0; k<2; k++){ |
| 1951 | while( (rc = sqlite3_step(pQuery))==SQLITE_ROW ){ |
| 1952 | for(i=0; i<n; i++){ |
| 1953 | switch( sqlite3_column_type(pQuery, i) ){ |
| 1954 | case SQLITE_NULL: { |
| 1955 | sqlite3_bind_null(pInsert, i+1); |
| 1956 | break; |
| 1957 | } |
| 1958 | case SQLITE_INTEGER: { |
| 1959 | sqlite3_bind_int64(pInsert, i+1, sqlite3_column_int64(pQuery,i)); |
| 1960 | break; |
| 1961 | } |
| 1962 | case SQLITE_FLOAT: { |
| 1963 | sqlite3_bind_double(pInsert, i+1, sqlite3_column_double(pQuery,i)); |
| 1964 | break; |
| 1965 | } |
| 1966 | case SQLITE_TEXT: { |
| 1967 | sqlite3_bind_text(pInsert, i+1, |
| 1968 | (const char*)sqlite3_column_text(pQuery,i), |
| 1969 | -1, SQLITE_STATIC); |
| 1970 | break; |
| 1971 | } |
| 1972 | case SQLITE_BLOB: { |
| 1973 | sqlite3_bind_blob(pInsert, i+1, sqlite3_column_blob(pQuery,i), |
| 1974 | sqlite3_column_bytes(pQuery,i), |
| 1975 | SQLITE_STATIC); |
| 1976 | break; |
| 1977 | } |
| 1978 | } |
| 1979 | } /* End for */ |
| 1980 | rc = sqlite3_step(pInsert); |
| 1981 | if( rc!=SQLITE_OK && rc!=SQLITE_ROW && rc!=SQLITE_DONE ){ |
| 1982 | fprintf(stderr, "Error %d: %s\n", sqlite3_extended_errcode(newDb), |
| 1983 | sqlite3_errmsg(newDb)); |
| 1984 | } |
| 1985 | sqlite3_reset(pInsert); |
| 1986 | cnt++; |
| 1987 | if( (cnt%spinRate)==0 ){ |
| 1988 | printf("%c\b", "|/-\\"[(cnt/spinRate)%4]); |
| 1989 | fflush(stdout); |
| 1990 | } |
| 1991 | } /* End while */ |
| 1992 | if( rc==SQLITE_DONE ) break; |
| 1993 | sqlite3_finalize(pQuery); |
| 1994 | sqlite3_free(zQuery); |
| 1995 | zQuery = sqlite3_mprintf("SELECT * FROM \"%w\" ORDER BY rowid DESC;", |
| 1996 | zTable); |
| 1997 | rc = sqlite3_prepare_v2(p->db, zQuery, -1, &pQuery, 0); |
| 1998 | if( rc ){ |
| 1999 | fprintf(stderr, "Warning: cannot step \"%s\" backwards", zTable); |
| 2000 | break; |
| 2001 | } |
| 2002 | } /* End for(k=0...) */ |
| 2003 | |
| 2004 | end_data_xfer: |
| 2005 | sqlite3_finalize(pQuery); |
| 2006 | sqlite3_finalize(pInsert); |
| 2007 | sqlite3_free(zQuery); |
| 2008 | sqlite3_free(zInsert); |
| 2009 | } |
| 2010 | |
| 2011 | |
| 2012 | /* |
| 2013 | ** Try to transfer all rows of the schema that match zWhere. For |
| 2014 | ** each row, invoke xForEach() on the object defined by that row. |
| 2015 | ** If an error is encountered while moving forward through the |
| 2016 | ** sqlite_master table, try again moving backwards. |
| 2017 | */ |
| 2018 | static void tryToCloneSchema( |
| 2019 | struct callback_data *p, |
| 2020 | sqlite3 *newDb, |
| 2021 | const char *zWhere, |
| 2022 | void (*xForEach)(struct callback_data*,sqlite3*,const char*) |
| 2023 | ){ |
| 2024 | sqlite3_stmt *pQuery = 0; |
| 2025 | char *zQuery = 0; |
| 2026 | int rc; |
| 2027 | const unsigned char *zName; |
| 2028 | const unsigned char *zSql; |
| 2029 | char *zErrMsg = 0; |
| 2030 | |
| 2031 | zQuery = sqlite3_mprintf("SELECT name, sql FROM sqlite_master" |
| 2032 | " WHERE %s", zWhere); |
| 2033 | rc = sqlite3_prepare_v2(p->db, zQuery, -1, &pQuery, 0); |
| 2034 | if( rc ){ |
| 2035 | fprintf(stderr, "Error: (%d) %s on [%s]\n", |
| 2036 | sqlite3_extended_errcode(p->db), sqlite3_errmsg(p->db), |
| 2037 | zQuery); |
| 2038 | goto end_schema_xfer; |
| 2039 | } |
| 2040 | while( (rc = sqlite3_step(pQuery))==SQLITE_ROW ){ |
| 2041 | zName = sqlite3_column_text(pQuery, 0); |
| 2042 | zSql = sqlite3_column_text(pQuery, 1); |
| 2043 | printf("%s... ", zName); fflush(stdout); |
| 2044 | sqlite3_exec(newDb, (const char*)zSql, 0, 0, &zErrMsg); |
| 2045 | if( zErrMsg ){ |
| 2046 | fprintf(stderr, "Error: %s\nSQL: [%s]\n", zErrMsg, zSql); |
| 2047 | sqlite3_free(zErrMsg); |
| 2048 | zErrMsg = 0; |
| 2049 | } |
| 2050 | if( xForEach ){ |
| 2051 | xForEach(p, newDb, (const char*)zName); |
| 2052 | } |
| 2053 | printf("done\n"); |
| 2054 | } |
| 2055 | if( rc!=SQLITE_DONE ){ |
| 2056 | sqlite3_finalize(pQuery); |
| 2057 | sqlite3_free(zQuery); |
| 2058 | zQuery = sqlite3_mprintf("SELECT name, sql FROM sqlite_master" |
| 2059 | " WHERE %s ORDER BY rowid DESC", zWhere); |
| 2060 | rc = sqlite3_prepare_v2(p->db, zQuery, -1, &pQuery, 0); |
| 2061 | if( rc ){ |
| 2062 | fprintf(stderr, "Error: (%d) %s on [%s]\n", |
| 2063 | sqlite3_extended_errcode(p->db), sqlite3_errmsg(p->db), |
| 2064 | zQuery); |
| 2065 | goto end_schema_xfer; |
| 2066 | } |
| 2067 | while( (rc = sqlite3_step(pQuery))==SQLITE_ROW ){ |
| 2068 | zName = sqlite3_column_text(pQuery, 0); |
| 2069 | zSql = sqlite3_column_text(pQuery, 1); |
| 2070 | printf("%s... ", zName); fflush(stdout); |
| 2071 | sqlite3_exec(newDb, (const char*)zSql, 0, 0, &zErrMsg); |
| 2072 | if( zErrMsg ){ |
| 2073 | fprintf(stderr, "Error: %s\nSQL: [%s]\n", zErrMsg, zSql); |
| 2074 | sqlite3_free(zErrMsg); |
| 2075 | zErrMsg = 0; |
| 2076 | } |
| 2077 | if( xForEach ){ |
| 2078 | xForEach(p, newDb, (const char*)zName); |
| 2079 | } |
| 2080 | printf("done\n"); |
| 2081 | } |
| 2082 | } |
| 2083 | end_schema_xfer: |
| 2084 | sqlite3_finalize(pQuery); |
| 2085 | sqlite3_free(zQuery); |
| 2086 | } |
| 2087 | |
| 2088 | /* |
| 2089 | ** Open a new database file named "zNewDb". Try to recover as much information |
| 2090 | ** as possible out of the main database (which might be corrupt) and write it |
| 2091 | ** into zNewDb. |
| 2092 | */ |
| 2093 | static void tryToClone(struct callback_data *p, const char *zNewDb){ |
| 2094 | int rc; |
| 2095 | sqlite3 *newDb = 0; |
| 2096 | if( access(zNewDb,0)==0 ){ |
| 2097 | fprintf(stderr, "File \"%s\" already exists.\n", zNewDb); |
| 2098 | return; |
| 2099 | } |
| 2100 | rc = sqlite3_open(zNewDb, &newDb); |
| 2101 | if( rc ){ |
| 2102 | fprintf(stderr, "Cannot create output database: %s\n", |
| 2103 | sqlite3_errmsg(newDb)); |
| 2104 | }else{ |
| 2105 | sqlite3_exec(newDb, "BEGIN EXCLUSIVE;", 0, 0, 0); |
| 2106 | tryToCloneSchema(p, newDb, "type='table'", tryToCloneData); |
| 2107 | tryToCloneSchema(p, newDb, "type!='table'", 0); |
| 2108 | sqlite3_exec(newDb, "COMMIT;", 0, 0, 0); |
| 2109 | } |
| 2110 | sqlite3_close(newDb); |
| 2111 | } |
| 2112 | |
| 2113 | /* |
| 2114 | ** If an input line begins with "." then invoke this routine to |
| 2115 | ** process that line. |
| 2116 | ** |
| @@ -2000,10 +2214,14 @@ | |
| 2214 | ** routine named test_breakpoint(). |
| 2215 | */ |
| 2216 | if( c=='b' && n>=3 && strncmp(azArg[0], "breakpoint", n)==0 ){ |
| 2217 | test_breakpoint(); |
| 2218 | }else |
| 2219 | |
| 2220 | if( c=='c' && strncmp(azArg[0], "clone", n)==0 && nArg>1 && nArg<3 ){ |
| 2221 | tryToClone(p, azArg[1]); |
| 2222 | }else |
| 2223 | |
| 2224 | if( c=='d' && n>1 && strncmp(azArg[0], "databases", n)==0 && nArg==1 ){ |
| 2225 | struct callback_data data; |
| 2226 | char *zErrMsg = 0; |
| 2227 | open_db(p, 0); |
| 2228 |