| Index: third_party/sqlite/src/tool/sqldiff.c
|
| diff --git a/third_party/sqlite/src/tool/sqldiff.c b/third_party/sqlite/src/tool/sqldiff.c
|
| index 56ff53ee25766d6e4941c60a1f85c5a73a89c73a..67f3197bbfeb75a04a75fa43a84a9b59ae9642d5 100644
|
| --- a/third_party/sqlite/src/tool/sqldiff.c
|
| +++ b/third_party/sqlite/src/tool/sqldiff.c
|
| @@ -33,6 +33,7 @@ struct GlobalVars {
|
| const char *zArgv0; /* Name of program */
|
| int bSchemaOnly; /* Only show schema differences */
|
| int bSchemaPK; /* Use the schema-defined PK, not the true PK */
|
| + int bHandleVtab; /* Handle fts3, fts4, fts5 and rtree vtabs */
|
| unsigned fDebug; /* Debug flags */
|
| sqlite3 *db; /* The database connection */
|
| } g;
|
| @@ -402,7 +403,8 @@ static void printQuoted(FILE *out, sqlite3_value *X){
|
| }
|
| fprintf(out, "'");
|
| }else{
|
| - fprintf(out, "NULL");
|
| + /* Could be an OOM, could be a zero-byte blob */
|
| + fprintf(out, "X''");
|
| }
|
| break;
|
| }
|
| @@ -683,7 +685,7 @@ static void diff_one_table(const char *zTab, FILE *out){
|
|
|
| /* Run the query and output differences */
|
| if( !g.bSchemaOnly ){
|
| - pStmt = db_prepare(sql.z);
|
| + pStmt = db_prepare("%s", sql.z);
|
| while( SQLITE_ROW==sqlite3_step(pStmt) ){
|
| int iType = sqlite3_column_int(pStmt, nPk);
|
| if( iType==1 || iType==2 ){
|
| @@ -994,7 +996,7 @@ static int rbuDeltaCreate(
|
| zDelta += lenOut;
|
| putInt(checksum(zOut, lenOut), &zDelta);
|
| *(zDelta++) = ';';
|
| - return zDelta - zOrigDelta;
|
| + return (int)(zDelta - zOrigDelta);
|
| }
|
|
|
| /* Compute the hash table used to locate matching sections in the
|
| @@ -1141,7 +1143,7 @@ static int rbuDeltaCreate(
|
| putInt(checksum(zOut, lenOut), &zDelta);
|
| *(zDelta++) = ';';
|
| sqlite3_free(collide);
|
| - return zDelta - zOrigDelta;
|
| + return (int)(zDelta - zOrigDelta);
|
| }
|
|
|
| /*
|
| @@ -1178,8 +1180,9 @@ static void getRbudiffQuery(
|
| strPrintf(pSql, " FROM aux.%Q AS n WHERE NOT EXISTS (\n", zTab);
|
| strPrintf(pSql, " SELECT 1 FROM ", zTab);
|
| strPrintf(pSql, " main.%Q AS o WHERE ", zTab);
|
| - strPrintfArray(pSql, " AND ", "(n.%Q IS o.%Q)", azCol, nPK);
|
| - strPrintf(pSql, "\n)");
|
| + strPrintfArray(pSql, " AND ", "(n.%Q = o.%Q)", azCol, nPK);
|
| + strPrintf(pSql, "\n) AND ");
|
| + strPrintfArray(pSql, " AND ", "(n.%Q IS NOT NULL)", azCol, nPK);
|
|
|
| /* Deleted rows: */
|
| strPrintf(pSql, "\nUNION ALL\nSELECT ");
|
| @@ -1193,8 +1196,9 @@ static void getRbudiffQuery(
|
| strPrintf(pSql, " FROM main.%Q AS n WHERE NOT EXISTS (\n", zTab);
|
| strPrintf(pSql, " SELECT 1 FROM ", zTab);
|
| strPrintf(pSql, " aux.%Q AS o WHERE ", zTab);
|
| - strPrintfArray(pSql, " AND ", "(n.%Q IS o.%Q)", azCol, nPK);
|
| - strPrintf(pSql, "\n) ");
|
| + strPrintfArray(pSql, " AND ", "(n.%Q = o.%Q)", azCol, nPK);
|
| + strPrintf(pSql, "\n) AND ");
|
| + strPrintfArray(pSql, " AND ", "(n.%Q IS NOT NULL)", azCol, nPK);
|
|
|
| /* Updated rows. If all table columns are part of the primary key, there
|
| ** can be no updates. In this case this part of the compound SELECT can
|
| @@ -1225,7 +1229,7 @@ static void getRbudiffQuery(
|
| );
|
|
|
| strPrintf(pSql, "\nFROM main.%Q AS o, aux.%Q AS n\nWHERE ", zTab, zTab);
|
| - strPrintfArray(pSql, " AND ", "(n.%Q IS o.%Q)", azCol, nPK);
|
| + strPrintfArray(pSql, " AND ", "(n.%Q = o.%Q)", azCol, nPK);
|
| strPrintf(pSql, " AND ota_control LIKE '%%x%%'");
|
| }
|
|
|
| @@ -1244,6 +1248,7 @@ static void rbudiff_one_table(const char *zTab, FILE *out){
|
| Str sql = {0, 0, 0}; /* Query to find differences */
|
| Str insert = {0, 0, 0}; /* First part of output INSERT statement */
|
| sqlite3_stmt *pStmt = 0;
|
| + int nRow = 0; /* Total rows in data_xxx table */
|
|
|
| /* --rbu mode must use real primary keys. */
|
| g.bSchemaPK = 1;
|
| @@ -1289,6 +1294,7 @@ static void rbudiff_one_table(const char *zTab, FILE *out){
|
|
|
| /* Output the first part of the INSERT statement */
|
| fprintf(out, "%s", insert.z);
|
| + nRow++;
|
|
|
| if( sqlite3_column_type(pStmt, nCol)==SQLITE_INTEGER ){
|
| for(i=0; i<=nCol; i++){
|
| @@ -1299,7 +1305,7 @@ static void rbudiff_one_table(const char *zTab, FILE *out){
|
| char *zOtaControl;
|
| int nOtaControl = sqlite3_column_bytes(pStmt, nCol);
|
|
|
| - zOtaControl = (char*)sqlite3_malloc(nOtaControl);
|
| + zOtaControl = (char*)sqlite3_malloc(nOtaControl+1);
|
| memcpy(zOtaControl, sqlite3_column_text(pStmt, nCol), nOtaControl+1);
|
|
|
| for(i=0; i<nCol; i++){
|
| @@ -1342,6 +1348,12 @@ static void rbudiff_one_table(const char *zTab, FILE *out){
|
| }
|
|
|
| sqlite3_finalize(pStmt);
|
| + if( nRow>0 ){
|
| + Str cnt = {0, 0, 0};
|
| + strPrintf(&cnt, "INSERT INTO rbu_count VALUES('data_%q', %d);", zTab, nRow);
|
| + fprintf(out, "%s\n", cnt.z);
|
| + strFree(&cnt);
|
| + }
|
|
|
| strFree(&ct);
|
| strFree(&sql);
|
| @@ -1452,7 +1464,7 @@ static void summarize_one_table(const char *zTab, FILE *out){
|
| }
|
|
|
| /* Run the query and output difference summary */
|
| - pStmt = db_prepare(sql.z);
|
| + pStmt = db_prepare("%s", sql.z);
|
| nUpdate = 0;
|
| nInsert = 0;
|
| nDelete = 0;
|
| @@ -1728,6 +1740,144 @@ end_changeset_one_table:
|
| }
|
|
|
| /*
|
| +** Extract the next SQL keyword or quoted string from buffer zIn and copy it
|
| +** (or a prefix of it if it will not fit) into buffer zBuf, size nBuf bytes.
|
| +** Return a pointer to the character within zIn immediately following
|
| +** the token or quoted string just extracted.
|
| +*/
|
| +const char *gobble_token(const char *zIn, char *zBuf, int nBuf){
|
| + const char *p = zIn;
|
| + char *pOut = zBuf;
|
| + char *pEnd = &pOut[nBuf-1];
|
| + char q = 0; /* quote character, if any */
|
| +
|
| + if( p==0 ) return 0;
|
| + while( *p==' ' ) p++;
|
| + switch( *p ){
|
| + case '"': q = '"'; break;
|
| + case '\'': q = '\''; break;
|
| + case '`': q = '`'; break;
|
| + case '[': q = ']'; break;
|
| + }
|
| +
|
| + if( q ){
|
| + p++;
|
| + while( *p && pOut<pEnd ){
|
| + if( *p==q ){
|
| + p++;
|
| + if( *p!=q ) break;
|
| + }
|
| + if( pOut<pEnd ) *pOut++ = *p;
|
| + p++;
|
| + }
|
| + }else{
|
| + while( *p && *p!=' ' && *p!='(' ){
|
| + if( pOut<pEnd ) *pOut++ = *p;
|
| + p++;
|
| + }
|
| + }
|
| +
|
| + *pOut = '\0';
|
| + return p;
|
| +}
|
| +
|
| +/*
|
| +** This function is the implementation of SQL scalar function "module_name":
|
| +**
|
| +** module_name(SQL)
|
| +**
|
| +** The only argument should be an SQL statement of the type that may appear
|
| +** in the sqlite_master table. If the statement is a "CREATE VIRTUAL TABLE"
|
| +** statement, then the value returned is the name of the module that it
|
| +** uses. Otherwise, if the statement is not a CVT, NULL is returned.
|
| +*/
|
| +static void module_name_func(
|
| + sqlite3_context *pCtx,
|
| + int nVal, sqlite3_value **apVal
|
| +){
|
| + const char *zSql;
|
| + char zToken[32];
|
| +
|
| + assert( nVal==1 );
|
| + zSql = (const char*)sqlite3_value_text(apVal[0]);
|
| +
|
| + zSql = gobble_token(zSql, zToken, sizeof(zToken));
|
| + if( zSql==0 || sqlite3_stricmp(zToken, "create") ) return;
|
| + zSql = gobble_token(zSql, zToken, sizeof(zToken));
|
| + if( zSql==0 || sqlite3_stricmp(zToken, "virtual") ) return;
|
| + zSql = gobble_token(zSql, zToken, sizeof(zToken));
|
| + if( zSql==0 || sqlite3_stricmp(zToken, "table") ) return;
|
| + zSql = gobble_token(zSql, zToken, sizeof(zToken));
|
| + if( zSql==0 ) return;
|
| + zSql = gobble_token(zSql, zToken, sizeof(zToken));
|
| + if( zSql==0 || sqlite3_stricmp(zToken, "using") ) return;
|
| + zSql = gobble_token(zSql, zToken, sizeof(zToken));
|
| +
|
| + sqlite3_result_text(pCtx, zToken, -1, SQLITE_TRANSIENT);
|
| +}
|
| +
|
| +/*
|
| +** Return the text of an SQL statement that itself returns the list of
|
| +** tables to process within the database.
|
| +*/
|
| +const char *all_tables_sql(){
|
| + if( g.bHandleVtab ){
|
| + int rc;
|
| +
|
| + rc = sqlite3_exec(g.db,
|
| + "CREATE TEMP TABLE tblmap(module COLLATE nocase, postfix);"
|
| + "INSERT INTO temp.tblmap VALUES"
|
| + "('fts3', '_content'), ('fts3', '_segments'), ('fts3', '_segdir'),"
|
| +
|
| + "('fts4', '_content'), ('fts4', '_segments'), ('fts4', '_segdir'),"
|
| + "('fts4', '_docsize'), ('fts4', '_stat'),"
|
| +
|
| + "('fts5', '_data'), ('fts5', '_idx'), ('fts5', '_content'),"
|
| + "('fts5', '_docsize'), ('fts5', '_config'),"
|
| +
|
| + "('rtree', '_node'), ('rtree', '_rowid'), ('rtree', '_parent');"
|
| + , 0, 0, 0
|
| + );
|
| + assert( rc==SQLITE_OK );
|
| +
|
| + rc = sqlite3_create_function(
|
| + g.db, "module_name", 1, SQLITE_UTF8, 0, module_name_func, 0, 0
|
| + );
|
| + assert( rc==SQLITE_OK );
|
| +
|
| + return
|
| + "SELECT name FROM main.sqlite_master\n"
|
| + " WHERE type='table' AND (\n"
|
| + " module_name(sql) IS NULL OR \n"
|
| + " module_name(sql) IN (SELECT module FROM temp.tblmap)\n"
|
| + " ) AND name NOT IN (\n"
|
| + " SELECT a.name || b.postfix \n"
|
| + "FROM main.sqlite_master AS a, temp.tblmap AS b \n"
|
| + "WHERE module_name(a.sql) = b.module\n"
|
| + " )\n"
|
| + "UNION \n"
|
| + "SELECT name FROM aux.sqlite_master\n"
|
| + " WHERE type='table' AND (\n"
|
| + " module_name(sql) IS NULL OR \n"
|
| + " module_name(sql) IN (SELECT module FROM temp.tblmap)\n"
|
| + " ) AND name NOT IN (\n"
|
| + " SELECT a.name || b.postfix \n"
|
| + "FROM aux.sqlite_master AS a, temp.tblmap AS b \n"
|
| + "WHERE module_name(a.sql) = b.module\n"
|
| + " )\n"
|
| + " ORDER BY name";
|
| + }else{
|
| + return
|
| + "SELECT name FROM main.sqlite_master\n"
|
| + " WHERE type='table' AND sql NOT LIKE 'CREATE VIRTUAL%%'\n"
|
| + " UNION\n"
|
| + "SELECT name FROM aux.sqlite_master\n"
|
| + " WHERE type='table' AND sql NOT LIKE 'CREATE VIRTUAL%%'\n"
|
| + " ORDER BY name";
|
| + }
|
| +}
|
| +
|
| +/*
|
| ** Print sketchy documentation for this utility program
|
| */
|
| static void showHelp(void){
|
| @@ -1743,6 +1893,7 @@ static void showHelp(void){
|
| " --summary Show only a summary of the differences\n"
|
| " --table TAB Show only differences in table TAB\n"
|
| " --transaction Show SQL output inside a transaction\n"
|
| +" --vtab Handle fts3, fts4, fts5 and rtree tables\n"
|
| );
|
| }
|
|
|
| @@ -1757,8 +1908,10 @@ int main(int argc, char **argv){
|
| char *zTab = 0;
|
| FILE *out = stdout;
|
| void (*xDiff)(const char*,FILE*) = diff_one_table;
|
| +#ifndef SQLITE_OMIT_LOAD_EXTENSION
|
| int nExt = 0;
|
| char **azExt = 0;
|
| +#endif
|
| int useTransaction = 0;
|
| int neverUseTransaction = 0;
|
|
|
| @@ -1811,6 +1964,9 @@ int main(int argc, char **argv){
|
| if( strcmp(z,"transaction")==0 ){
|
| useTransaction = 1;
|
| }else
|
| + if( strcmp(z,"vtab")==0 ){
|
| + g.bHandleVtab = 1;
|
| + }else
|
| {
|
| cmdlineError("unknown option: %s", argv[i]);
|
| }
|
| @@ -1841,8 +1997,8 @@ int main(int argc, char **argv){
|
| cmdlineError("error loading %s: %s", azExt[i], zErrMsg);
|
| }
|
| }
|
| -#endif
|
| free(azExt);
|
| +#endif
|
| zSql = sqlite3_mprintf("ATTACH %Q as aux;", zDb2);
|
| rc = sqlite3_exec(g.db, zSql, 0, 0, &zErrMsg);
|
| if( rc || zErrMsg ){
|
| @@ -1854,19 +2010,18 @@ int main(int argc, char **argv){
|
| }
|
|
|
| if( neverUseTransaction ) useTransaction = 0;
|
| - if( useTransaction ) printf("BEGIN TRANSACTION;\n");
|
| + if( useTransaction ) fprintf(out, "BEGIN TRANSACTION;\n");
|
| + if( xDiff==rbudiff_one_table ){
|
| + fprintf(out, "CREATE TABLE IF NOT EXISTS rbu_count"
|
| + "(tbl TEXT PRIMARY KEY COLLATE NOCASE, cnt INTEGER) "
|
| + "WITHOUT ROWID;\n"
|
| + );
|
| + }
|
| if( zTab ){
|
| xDiff(zTab, out);
|
| }else{
|
| /* Handle tables one by one */
|
| - pStmt = db_prepare(
|
| - "SELECT name FROM main.sqlite_master\n"
|
| - " WHERE type='table' AND sql NOT LIKE 'CREATE VIRTUAL%%'\n"
|
| - " UNION\n"
|
| - "SELECT name FROM aux.sqlite_master\n"
|
| - " WHERE type='table' AND sql NOT LIKE 'CREATE VIRTUAL%%'\n"
|
| - " ORDER BY name"
|
| - );
|
| + pStmt = db_prepare("%s", all_tables_sql() );
|
| while( SQLITE_ROW==sqlite3_step(pStmt) ){
|
| xDiff((const char*)sqlite3_column_text(pStmt,0), out);
|
| }
|
|
|