From: drh Date: Tue, 7 Feb 2017 20:51:38 +0000 (+0000) Subject: The dbselftest utility now generates hashes in the selftest table with --init. X-Git-Tag: version-3.17.0~13 X-Git-Url: http://git.ipfire.org/?a=commitdiff_plain;h=cbc65e5f4f3fc24a0cfe17eb723f4439cb7bc01e;p=thirdparty%2Fsqlite.git The dbselftest utility now generates hashes in the selftest table with --init. It also accepts multiple database files on the command-line. FossilOrigin-Name: e68829c9bbc69bf4a0dc057e0a6e977f2fac79be --- diff --git a/manifest b/manifest index 854ed84459..a37a9e38bf 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Omit\sfts5fault1.test\sfrom\sthe\sinmemory_journal\spermutation. -D 2017-02-07T19:36:14.794 +C The\sdbselftest\sutility\snow\sgenerates\shashes\sin\sthe\sselftest\stable\swith\s--init.\nIt\salso\saccepts\smultiple\sdatabase\sfiles\son\sthe\scommand-line. +D 2017-02-07T20:51:38.461 F Makefile.in edb6bcdd37748d2b1c3422ff727c748df7ffe918 F Makefile.linux-gcc 7bc79876b875010e8c8f9502eb935ca92aa3c434 F Makefile.msc ba953c8921fc7e18333f61898007206de7e23964 @@ -636,7 +636,7 @@ F test/cursorhint.test 7bc346788390475e77a345da2b92270d04d35856 F test/cursorhint2.test 8457e93d97f665f23f97cdbc8477d16e3480331b F test/date.test a6a5a48b90907bca9fbcc79a30be5a715c1ab2fc F test/dbfuzz.c 8cc2bdb818b4483a052f9f80f96be74cbd9a6e1d -F test/dbselftest.c eeb95d09932b4dc79e5886bd8cf5d812e7d28d94 +F test/dbselftest.c 4bf86fe04dc2d0d58dabc5e6b7a06fa4ef7827ea F test/dbstatus.test 73149851b3aff14fc6db478e58f9083a66422cf5 F test/dbstatus2.test e93ab03bfae6d62d4d935f20de928c19ca0ed0ab F test/default.test 0cb49b1c315a0d81c81d775e407f66906a2a604d @@ -1555,7 +1555,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 be82d5ae20ba62a165bdc28766a8dc8049abcac6 -R 4c6aa653d353c19152ed7b2aaba4bc83 -U dan -Z 450aa5587fc5eb1e148efd8100bf109e +P cb1e83f9583bf93ce7583d9f5e97272e2d43cfb8 +R 64e5750ab44994d5b187d93e5c4a1318 +U drh +Z f3a9d081d357071c2c4fc7a23224549e diff --git a/manifest.uuid b/manifest.uuid index 026b45bd03..6596630605 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -cb1e83f9583bf93ce7583d9f5e97272e2d43cfb8 \ No newline at end of file +e68829c9bbc69bf4a0dc057e0a6e977f2fac79be \ No newline at end of file diff --git a/test/dbselftest.c b/test/dbselftest.c index 1c6f8d8b9f..d8a9c331de 100644 --- a/test/dbselftest.c +++ b/test/dbselftest.c @@ -13,7 +13,7 @@ ** This program implements an SQLite database self-verification utility. ** Usage: ** -** dbselftest DATABASE +** dbselftest DATABASE ... ** ** This program reads the "selftest" table in DATABASE, in rowid order, ** and runs each of the tests described there, reporting results at the @@ -44,7 +44,7 @@ #include "sqlite3.h" static const char zHelp[] = - "Usage: dbselftest [OPTIONS] DBFILE\n" + "Usage: dbselftest [OPTIONS] DBFILE ...\n" "\n" " --init Create the selftest table\n" " -q Suppress most output. Errors only\n" @@ -294,6 +294,101 @@ static void sha1Func( sqlite3_result_text(context, zOut, 40, SQLITE_TRANSIENT); } +/* +** Run a prepared statement and compute the SHA1 hash on the +** result rows. +*/ +static void sha1RunStatement(SHA1Context *pCtx, sqlite3_stmt *pStmt){ + int nCol = sqlite3_column_count(pStmt); + const char *z = sqlite3_sql(pStmt); + int n = (int)strlen(z); + + hash_step_vformat(pCtx,"S%d:",n); + hash_step(pCtx,(unsigned char*)z,n); + + /* Compute a hash over the result of the query */ + while( SQLITE_ROW==sqlite3_step(pStmt) ){ + int i; + hash_step(pCtx,(const unsigned char*)"R",1); + for(i=0; i=1; j--){ + x[j] = u & 0xff; + u >>= 8; + } + x[0] = 'I'; + hash_step(pCtx, x, 9); + break; + } + case SQLITE_FLOAT: { + sqlite3_uint64 u; + int j; + unsigned char x[9]; + double r = sqlite3_column_double(pStmt,i); + memcpy(&u, &r, 8); + for(j=8; j>=1; j--){ + x[j] = u & 0xff; + u >>= 8; + } + x[0] = 'F'; + hash_step(pCtx,x,9); + break; + } + case SQLITE_TEXT: { + int n2 = sqlite3_column_bytes(pStmt, i); + const unsigned char *z2 = sqlite3_column_text(pStmt, i); + hash_step_vformat(pCtx,"T%d:",n2); + hash_step(pCtx, z2, n2); + break; + } + case SQLITE_BLOB: { + int n2 = sqlite3_column_bytes(pStmt, i); + const unsigned char *z2 = sqlite3_column_blob(pStmt, i); + hash_step_vformat(pCtx,"B%d:",n2); + hash_step(pCtx, z2, n2); + break; + } + } + } + } +} + +/* +** Run one or more statements of SQL. Compute a SHA1 hash of the output. +*/ +static int sha1Exec( + sqlite3 *db, /* Run against this database connection */ + const char *zSql, /* The SQL to be run */ + char *zOut /* Store the SHA1 hash as hexadecimal in this buffer */ +){ + sqlite3_stmt *pStmt = 0; /* A prepared statement */ + int rc; /* Result of an API call */ + SHA1Context cx; /* The SHA1 hash context */ + + hash_init(&cx); + while( zSql[0] ){ + rc = sqlite3_prepare_v2(db, zSql, -1, &pStmt, &zSql); + if( rc ){ + sqlite3_finalize(pStmt); + return rc; + } + sha1RunStatement(&cx, pStmt); + sqlite3_finalize(pStmt); + } + hash_finish(&cx, zOut); + return SQLITE_OK; +} + /* ** Implementation of the sha1_query(SQL) function. ** @@ -314,11 +409,7 @@ static void sha1QueryFunc( sqlite3 *db = sqlite3_context_db_handle(context); const char *zSql = (const char*)sqlite3_value_text(argv[0]); sqlite3_stmt *pStmt = 0; - int nCol; /* Number of columns in the result set */ - int i; /* Loop counter */ int rc; - int n; - const char *z; SHA1Context cx; char zOut[44]; @@ -342,66 +433,7 @@ static void sha1QueryFunc( sqlite3_free(zMsg); return; } - nCol = sqlite3_column_count(pStmt); - z = sqlite3_sql(pStmt); - n = (int)strlen(z); - hash_step_vformat(&cx,"S%d:",n); - hash_step(&cx,(unsigned char*)z,n); - - /* Compute a hash over the result of the query */ - while( SQLITE_ROW==sqlite3_step(pStmt) ){ - hash_step(&cx,(const unsigned char*)"R",1); - for(i=0; i=1; j--){ - x[j] = u & 0xff; - u >>= 8; - } - x[0] = 'I'; - hash_step(&cx, x, 9); - break; - } - case SQLITE_FLOAT: { - sqlite3_uint64 u; - int j; - unsigned char x[9]; - double r = sqlite3_column_double(pStmt,i); - memcpy(&u, &r, 8); - for(j=8; j>=1; j--){ - x[j] = u & 0xff; - u >>= 8; - } - x[0] = 'F'; - hash_step(&cx,x,9); - break; - } - case SQLITE_TEXT: { - int n2 = sqlite3_column_bytes(pStmt, i); - const unsigned char *z2 = sqlite3_column_text(pStmt, i); - hash_step_vformat(&cx,"T%d:",n2); - hash_step(&cx, z2, n2); - break; - } - case SQLITE_BLOB: { - int n2 = sqlite3_column_bytes(pStmt, i); - const unsigned char *z2 = sqlite3_column_blob(pStmt, i); - hash_step_vformat(&cx,"B%d:",n2); - hash_step(&cx, z2, n2); - break; - } - } - } - } + sha1RunStatement(&cx, pStmt); sqlite3_finalize(pStmt); } hash_finish(&cx, zOut); @@ -439,7 +471,7 @@ static void strAppend(Str *p, const char *z){ exit(1); } } - memcpy(p->z+n, z, n+1); + memcpy(p->z+p->n, z, n+1); p->n += n; } @@ -461,9 +493,138 @@ static int execCallback(void *pStr, int argc, char **argv, char **colv){ return 0; } +/* +** Run an SQL statement constructing using sqlite3_vmprintf(). +** Return the number of errors. +*/ +static int runSql(sqlite3 *db, const char *zFormat, ...){ + char *zSql; + char *zErr = 0; + int rc; + int nErr = 0; + va_list ap; + + va_start(ap, zFormat); + zSql = sqlite3_vmprintf(zFormat, ap); + va_end(ap); + if( zSql==0 ){ + printf("Out of memory\n"); + exit(1); + } + rc = sqlite3_exec(db, zSql, 0, 0, &zErr); + if( rc || zErr ){ + printf("SQL error in [%s]: code=%d: %s\n", zSql, rc, zErr); + nErr++; + } + sqlite3_free(zSql); + return nErr; +} + +/* +** Generate a prepared statement using a formatted string. +*/ +static sqlite3_stmt *prepareSql(sqlite3 *db, const char *zFormat, ...){ + char *zSql; + int rc; + sqlite3_stmt *pStmt = 0; + va_list ap; + + va_start(ap, zFormat); + zSql = sqlite3_vmprintf(zFormat, ap); + va_end(ap); + if( zSql==0 ){ + printf("Out of memory\n"); + exit(1); + } + rc = sqlite3_prepare_v2(db, zSql, -1, &pStmt, 0); + if( rc ){ + printf("SQL error in [%s]: code=%d: %s\n", zSql, rc, sqlite3_errmsg(db)); + sqlite3_finalize(pStmt); + pStmt = 0; + } + sqlite3_free(zSql); + return pStmt; +} + +/* +** Construct the standard selftest configuration for the database. +*/ +static int buildSelftestTable(sqlite3 *db){ + int rc; + sqlite3_stmt *pStmt; + int tno = 110; + char *zSql; + char zHash[50]; + + rc = runSql(db, + "CREATE TABLE IF NOT EXISTS selftest(\n" + " tno INTEGER PRIMARY KEY, -- test number\n" + " op TEXT, -- what kind of test\n" + " sql TEXT, -- SQL text for the test\n" + " ans TEXT -- expected answer\n" + ");" + "INSERT INTO selftest" + " VALUES(100,'memo','Hashes generated using --init',NULL);" + ); + if( rc ) return 1; + tno = 110; + zSql = "SELECT type,name,tbl_name,sql FROM sqlite_master"; + sha1Exec(db, zSql, zHash); + rc = runSql(db, + "INSERT INTO selftest(tno,op,sql,ans)" + " VALUES(%d,'sha1',%Q,%Q)", tno, zSql, zHash); + tno += 10; + pStmt = prepareSql(db, + "SELECT lower(name) FROM sqlite_master" + " WHERE type='table' AND sql NOT GLOB 'CREATE VIRTUAL*'" + " AND name<>'selftest'" + " ORDER BY 1"); + if( pStmt==0 ) return 1; + while( SQLITE_ROW==sqlite3_step(pStmt) ){ + zSql = sqlite3_mprintf("SELECT * FROM \"%w\" NOT INDEXED", + sqlite3_column_text(pStmt, 0)); + if( zSql==0 ){ + printf("Of of memory\n"); + exit(1); + } + sha1Exec(db, zSql, zHash); + rc = runSql(db, + "INSERT INTO selftest(tno,op,sql,ans)" + " VALUES(%d,'sha1',%Q,%Q)", tno, zSql, zHash); + tno += 10; + sqlite3_free(zSql); + if( rc ) break; + } + sqlite3_finalize(pStmt); + if( rc ) return 1; + rc = runSql(db, + "INSERT INTO selftest(tno,op,sql,ans)" + " VALUES(%d,'run','PRAGMA integrity_check','ok');", tno); + if( rc ) return 1; + return rc; +} + +/* +** Return true if the named table exists +*/ +static int tableExists(sqlite3 *db, const char *zTab){ + return sqlite3_table_column_metadata(db, "main", zTab, 0, 0, 0, 0, 0, 0) + == SQLITE_OK; +} + +/* +** Default selftest table content, for use when there is no selftest table +*/ +static char *azDefaultTest[] = { + 0, 0, 0, 0, + "0", "memo", "Missing SELFTEST table - default checks only", "", + "1", "run", "PRAGMA integrity_check", "ok" +}; + int main(int argc, char **argv){ int eVolume = VOLUME_LOW; /* How much output to display */ - const char *zDb = 0; /* Name of the database file */ + const char **azDb = 0; /* Name of the database file */ + int nDb = 0; /* Number of database files to check */ int doInit = 0; /* True if --init is present */ sqlite3 *db = 0; /* Open database connection */ int rc; /* Return code from API calls */ @@ -472,7 +633,9 @@ int main(int argc, char **argv){ int nRow = 0, nCol = 0; /* Rows and columns in azTest[] */ int i; /* Loop counter */ int nErr = 0; /* Number of errors */ + int iDb; /* Loop counter for databases */ Str str; /* Result accumulator */ + int nTest = 0; /* Number of tests run */ for(i=1; i=VOLUME_LOW ){ @@ -553,53 +678,109 @@ int main(int argc, char **argv){ } memset(&str, 0, sizeof(str)); strAppend(&str, "\n"); - for(i=1; i<=nRow; i++){ - int tno = atoi(azTest[i*nCol]); - const char *zOp = azTest[i*nCol+1]; - const char *zSql = azTest[i*nCol+2]; - const char *zAns = azTest[i*nCol+3]; - - if( eVolume>=VOLUME_ECHO ){ - char *zQuote = sqlite3_mprintf("%q", zSql); - printf("%d: %s %s\n", tno, zOp, zSql); - sqlite3_free(zQuote); + for(iDb=0; iDb=VOLUME_LOW ){ - printf("%s\n", zSql); + rc = sqlite3_create_function(db, "sha1", 1, SQLITE_UTF8, 0, + sha1Func, 0, 0); + if( rc==SQLITE_OK ){ + rc = sqlite3_create_function(db, "sha1_query", 1, SQLITE_UTF8, 0, + sha1QueryFunc, 0, 0); + } + if( rc ){ + printf("Initialization error: %s\n", sqlite3_errmsg(db)); + sqlite3_close(db); + return 1; + } + if( doInit && !tableExists(db, "selftest") ){ + buildSelftestTable(db); + } + if( !tableExists(db, "selftest") ){ + azTest = azDefaultTest; + nCol = 4; + nRow = 2; + }else{ + rc = sqlite3_get_table(db, + "SELECT tno,op,sql,ans FROM selftest ORDER BY tno", + &azTest, &nRow, &nCol, &zErrMsg); + if( rc || zErrMsg ){ + printf("Error querying selftest: %s\n", zErrMsg); + sqlite3_free_table(azTest); + continue; } - }else - if( strcmp(zOp,"run")==0 ){ - str.n = 0; - str.z[0] = 0; - zErrMsg = 0; - rc = sqlite3_exec(db, zSql, execCallback, &str, &zErrMsg); - if( eVolume>=VOLUME_VERBOSE ){ - printf("Result: %s\n", str.z); + } + for(i=1; i<=nRow; i++){ + int tno = atoi(azTest[i*nCol]); + const char *zOp = azTest[i*nCol+1]; + const char *zSql = azTest[i*nCol+2]; + const char *zAns = azTest[i*nCol+3]; + + if( eVolume>=VOLUME_ECHO ){ + char *zQuote = sqlite3_mprintf("%q", zSql); + printf("%d: %s %s\n", tno, zOp, zSql); + sqlite3_free(zQuote); } - if( rc || zErrMsg ){ - nErr++; - if( eVolume>=VOLUME_ERROR_ONLY ){ - printf("%d: error-code-%d: %s\n", tno, rc, zErrMsg); + if( strcmp(zOp,"memo")==0 ){ + if( eVolume>=VOLUME_LOW ){ + printf("%s: %s\n", azDb[iDb], zSql); + } + }else + if( strcmp(zOp,"sha1")==0 ){ + char zOut[44]; + rc = sha1Exec(db, zSql, zOut); + nTest++; + if( eVolume>=VOLUME_VERBOSE ){ + printf("Result: %s\n", zOut); } - sqlite3_free(zErrMsg); - }else if( strcmp(zAns,str.z)!=0 ){ - nErr++; - if( eVolume>=VOLUME_ERROR_ONLY ){ - printf("%d: Expected: [%s]\n", tno, zAns); - printf("%d: Got: [%s]\n", tno, str.z); + if( rc ){ + nErr++; + if( eVolume>=VOLUME_ERROR_ONLY ){ + printf("%d: error-code-%d: %s\n", tno, rc, sqlite3_errmsg(db)); + } + }else if( strcmp(zAns,zOut)!=0 ){ + nErr++; + if( eVolume>=VOLUME_ERROR_ONLY ){ + printf("%d: Expected: [%s]\n", tno, zAns); + printf("%d: Got: [%s]\n", tno, zOut); + } + } + }else + if( strcmp(zOp,"run")==0 ){ + str.n = 0; + str.z[0] = 0; + zErrMsg = 0; + rc = sqlite3_exec(db, zSql, execCallback, &str, &zErrMsg); + nTest++; + if( eVolume>=VOLUME_VERBOSE ){ + printf("Result: %s\n", str.z); } + if( rc || zErrMsg ){ + nErr++; + if( eVolume>=VOLUME_ERROR_ONLY ){ + printf("%d: error-code-%d: %s\n", tno, rc, zErrMsg); + } + sqlite3_free(zErrMsg); + }else if( strcmp(zAns,str.z)!=0 ){ + nErr++; + if( eVolume>=VOLUME_ERROR_ONLY ){ + printf("%d: Expected: [%s]\n", tno, zAns); + printf("%d: Got: [%s]\n", tno, str.z); + } + } + }else + { + printf("Unknown operation \"%s\" on selftest line %d\n", zOp, tno); + return 1; } - }else - { - printf("Unknown operation \"%s\" on selftest line %d\n", zOp, tno); - return 1; } + if( azTest!=azDefaultTest ) sqlite3_free_table(azTest); } - sqlite3_free_table(azTest); - sqlite3_close(db); if( eVolume>=VOLUME_LOW || (nErr>0 && eVolume>=VOLUME_ERROR_ONLY) ){ - printf("%d errors out of %d tests\n", nErr, nRow); + printf("%d errors out of %d tests on %d databases\n", nErr, nTest, nDb); } return nErr; }