/* ** 2018-03-21 ** ** The author disclaims copyright to this source code. In place of ** a legal notice, here is a blessing: ** ** May you do good and not evil. ** May you find forgiveness for yourself and forgive others. ** May you share freely, never taking more than you give. ** ************************************************************************* ** ** This program attempts to verify the correctness of the SQLite query ** optimizer by fuzzing. ** ** The input is an SQL script, presumably generated by a fuzzer. The ** argument is the name of the input. If no files are named, standard ** input is read. ** ** The SQL script is run twice, once with optimization enabled, and again ** with optimization disabled. If the output is not equivalent, an error ** is printed and the program returns non-zero. */ /* Include the SQLite amalgamation, after making appropriate #defines. */ #define SQLITE_THREADSAFE 0 #define SQLITE_OMIT_LOAD_EXTENSION 1 #define SQLITE_ENABLE_DESERIALIZE 1 #include "sqlite3.c" /* Content of the read-only test database */ #include "optfuzz-db01.c" /* ** Prepare a single SQL statement. Panic if anything goes wrong */ static sqlite3_stmt *prepare_sql(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); rc = sqlite3_prepare_v2(db, zSql, -1, &pStmt, 0); if( rc ){ printf("Error: %s\nSQL: %s\n", sqlite3_errmsg(db), zSql); exit(1); } sqlite3_free(zSql); return pStmt; } /* ** Run SQL. Panic if anything goes wrong */ static void run_sql(sqlite3 *db, const char *zFormat, ...){ char *zSql; int rc; char *zErr = 0; va_list ap; va_start(ap, zFormat); zSql = sqlite3_vmprintf(zFormat, ap); va_end(ap); rc = sqlite3_exec(db, zSql, 0, 0, &zErr); if( rc || zErr ){ printf("Error: %s\nsqlite3_errmsg: %s\nSQL: %s\n", zErr, sqlite3_errmsg(db), zSql); exit(1); } sqlite3_free(zSql); } /* ** Run one or more SQL statements contained in zSql against database dbRun. ** Store the input in database dbOut. */ static int optfuzz_exec( sqlite3 *dbRun, /* The database on which the SQL executes */ const char *zSql, /* The SQL to be executed */ sqlite3 *dbOut, /* Store results in this database */ const char *zOutTab, /* Store results in this table of dbOut */ int *pnStmt, /* Write the number of statements here */ int *pnRow, /* Write the number of rows here */ int bTrace /* Print query results if true */ ){ int rc = SQLITE_OK; /* Return code */ const char *zLeftover; /* Tail of unprocessed SQL */ sqlite3_stmt *pStmt = 0; /* The current SQL statement */ sqlite3_stmt *pIns = 0; /* Statement to insert into dbOut */ const char *zCol; /* Single column value */ int nCol; /* Number of output columns */ char zLine[4000]; /* Complete row value */ run_sql(dbOut, "BEGIN"); run_sql(dbOut, "CREATE TABLE IF NOT EXISTS staging(x TEXT)"); run_sql(dbOut, "CREATE TABLE IF NOT EXISTS \"%w\"(x TEXT)", zOutTab); pIns = prepare_sql(dbOut, "INSERT INTO staging(x) VALUES(?1)"); *pnRow = *pnStmt = 0; while( rc==SQLITE_OK && zSql && zSql[0] ){ zLeftover = 0; rc = sqlite3_prepare_v2(dbRun, zSql, -1, &pStmt, &zLeftover); zSql = zLeftover; assert( rc==SQLITE_OK || pStmt==0 ); if( rc!=SQLITE_OK ){ printf("Error with [%s]\n%s\n", zSql, sqlite3_errmsg(dbRun)); break; } if( !pStmt ) continue; (*pnStmt)++; nCol = sqlite3_column_count(pStmt); run_sql(dbOut, "DELETE FROM staging;"); while( sqlite3_step(pStmt)==SQLITE_ROW ){ int i, j; for(i=j=0; i=sizeof(zLine)-100 ){ printf("Excessively long output line: %d bytes\n" ,j); exit(1); } if( bTrace ){ printf("%s\n", zLine); } (*pnRow)++; sqlite3_bind_text(pIns, 1, zLine, j, SQLITE_TRANSIENT); rc = sqlite3_step(pIns); assert( rc==SQLITE_DONE ); rc = sqlite3_reset(pIns); } run_sql(dbOut, "INSERT INTO \"%w\"(x) VALUES('### %q ###')", zOutTab, sqlite3_sql(pStmt) ); run_sql(dbOut, "INSERT INTO \"%w\"(x) SELECT group_concat(x,char(10))" " FROM (SELECT x FROM staging ORDER BY x)", zOutTab ); run_sql(dbOut, "COMMIT"); sqlite3_finalize(pStmt); pStmt = 0; } sqlite3_finalize(pStmt); sqlite3_finalize(pIns); return rc; } /* ** Read the content of file zName into memory obtained from sqlite3_malloc64() ** and return a pointer to the buffer. The caller is responsible for freeing ** the memory. ** ** If parameter pnByte is not NULL, (*pnByte) is set to the number of bytes ** read. ** ** For convenience, a nul-terminator byte is always appended to the data read ** from the file before the buffer is returned. This byte is not included in ** the final value of (*pnByte), if applicable. ** ** NULL is returned if any error is encountered. The final value of *pnByte ** is undefined in this case. */ static char *readFile(const char *zName, int *pnByte){ FILE *in = fopen(zName, "rb"); long nIn; size_t nRead; char *pBuf; if( in==0 ) return 0; fseek(in, 0, SEEK_END); nIn = ftell(in); rewind(in); pBuf = sqlite3_malloc64( nIn+1 ); if( pBuf==0 ) return 0; nRead = fread(pBuf, nIn, 1, in); fclose(in); if( nRead!=1 ){ sqlite3_free(pBuf); return 0; } pBuf[nIn] = 0; if( pnByte ) *pnByte = nIn; return pBuf; } int main(int argc, char **argv){ int nIn = 0; /* Number of input files */ char **azIn = 0; /* Names of input files */ sqlite3 *dbOut = 0; /* Database to hold results */ sqlite3 *dbRun = 0; /* Database used for tests */ int bTrace = 0; /* Show query results */ int bShowValid = 0; /* Just list inputs that are valid SQL */ int nRow, nStmt; /* Number of rows and statements */ int i, rc; for(i=1; i