130f30133Sdrh /*
230f30133Sdrh ** 2018-03-21
330f30133Sdrh **
430f30133Sdrh ** The author disclaims copyright to this source code. In place of
530f30133Sdrh ** a legal notice, here is a blessing:
630f30133Sdrh **
730f30133Sdrh ** May you do good and not evil.
830f30133Sdrh ** May you find forgiveness for yourself and forgive others.
930f30133Sdrh ** May you share freely, never taking more than you give.
1030f30133Sdrh **
1130f30133Sdrh *************************************************************************
1230f30133Sdrh **
1330f30133Sdrh ** This program attempts to verify the correctness of the SQLite query
1430f30133Sdrh ** optimizer by fuzzing.
1530f30133Sdrh **
1630f30133Sdrh ** The input is an SQL script, presumably generated by a fuzzer. The
1730f30133Sdrh ** argument is the name of the input. If no files are named, standard
1830f30133Sdrh ** input is read.
1930f30133Sdrh **
2030f30133Sdrh ** The SQL script is run twice, once with optimization enabled, and again
2130f30133Sdrh ** with optimization disabled. If the output is not equivalent, an error
2230f30133Sdrh ** is printed and the program returns non-zero.
2330f30133Sdrh */
2430f30133Sdrh
2530f30133Sdrh /* Include the SQLite amalgamation, after making appropriate #defines.
2630f30133Sdrh */
2730f30133Sdrh #define SQLITE_THREADSAFE 0
2830f30133Sdrh #define SQLITE_OMIT_LOAD_EXTENSION 1
2930f30133Sdrh #include "sqlite3.c"
3030f30133Sdrh
3130f30133Sdrh /* Content of the read-only test database */
3230f30133Sdrh #include "optfuzz-db01.c"
3330f30133Sdrh
3430f30133Sdrh /*
3530f30133Sdrh ** Prepare a single SQL statement. Panic if anything goes wrong
3630f30133Sdrh */
prepare_sql(sqlite3 * db,const char * zFormat,...)3730f30133Sdrh static sqlite3_stmt *prepare_sql(sqlite3 *db, const char *zFormat, ...){
3830f30133Sdrh char *zSql;
3930f30133Sdrh int rc;
4030f30133Sdrh sqlite3_stmt *pStmt = 0;
4130f30133Sdrh va_list ap;
4230f30133Sdrh
4330f30133Sdrh va_start(ap, zFormat);
4430f30133Sdrh zSql = sqlite3_vmprintf(zFormat, ap);
4530f30133Sdrh va_end(ap);
4630f30133Sdrh rc = sqlite3_prepare_v2(db, zSql, -1, &pStmt, 0);
4730f30133Sdrh if( rc ){
4830f30133Sdrh printf("Error: %s\nSQL: %s\n",
4930f30133Sdrh sqlite3_errmsg(db), zSql);
5030f30133Sdrh exit(1);
5130f30133Sdrh }
5230f30133Sdrh sqlite3_free(zSql);
5330f30133Sdrh return pStmt;
5430f30133Sdrh }
5530f30133Sdrh
5630f30133Sdrh /*
5730f30133Sdrh ** Run SQL. Panic if anything goes wrong
5830f30133Sdrh */
run_sql(sqlite3 * db,const char * zFormat,...)5930f30133Sdrh static void run_sql(sqlite3 *db, const char *zFormat, ...){
6030f30133Sdrh char *zSql;
6130f30133Sdrh int rc;
6230f30133Sdrh char *zErr = 0;
6330f30133Sdrh va_list ap;
6430f30133Sdrh
6530f30133Sdrh va_start(ap, zFormat);
6630f30133Sdrh zSql = sqlite3_vmprintf(zFormat, ap);
6730f30133Sdrh va_end(ap);
6830f30133Sdrh rc = sqlite3_exec(db, zSql, 0, 0, &zErr);
6930f30133Sdrh if( rc || zErr ){
7030f30133Sdrh printf("Error: %s\nsqlite3_errmsg: %s\nSQL: %s\n",
7130f30133Sdrh zErr, sqlite3_errmsg(db), zSql);
7230f30133Sdrh exit(1);
7330f30133Sdrh }
7430f30133Sdrh sqlite3_free(zSql);
7530f30133Sdrh }
7630f30133Sdrh
7730f30133Sdrh /*
7830f30133Sdrh ** Run one or more SQL statements contained in zSql against database dbRun.
7930f30133Sdrh ** Store the input in database dbOut.
8030f30133Sdrh */
optfuzz_exec(sqlite3 * dbRun,const char * zSql,sqlite3 * dbOut,const char * zOutTab,int * pnStmt,int * pnRow,int bTrace)8130f30133Sdrh static int optfuzz_exec(
8230f30133Sdrh sqlite3 *dbRun, /* The database on which the SQL executes */
8330f30133Sdrh const char *zSql, /* The SQL to be executed */
8430f30133Sdrh sqlite3 *dbOut, /* Store results in this database */
8500f0375dSdrh const char *zOutTab, /* Store results in this table of dbOut */
8600f0375dSdrh int *pnStmt, /* Write the number of statements here */
8700f0375dSdrh int *pnRow, /* Write the number of rows here */
8800f0375dSdrh int bTrace /* Print query results if true */
8930f30133Sdrh ){
9030f30133Sdrh int rc = SQLITE_OK; /* Return code */
9130f30133Sdrh const char *zLeftover; /* Tail of unprocessed SQL */
9230f30133Sdrh sqlite3_stmt *pStmt = 0; /* The current SQL statement */
9330f30133Sdrh sqlite3_stmt *pIns = 0; /* Statement to insert into dbOut */
9430f30133Sdrh const char *zCol; /* Single column value */
9530f30133Sdrh int nCol; /* Number of output columns */
9630f30133Sdrh char zLine[4000]; /* Complete row value */
9730f30133Sdrh
9830f30133Sdrh run_sql(dbOut, "BEGIN");
9900f0375dSdrh run_sql(dbOut, "CREATE TABLE IF NOT EXISTS staging(x TEXT)");
10000f0375dSdrh run_sql(dbOut, "CREATE TABLE IF NOT EXISTS \"%w\"(x TEXT)", zOutTab);
10130f30133Sdrh pIns = prepare_sql(dbOut, "INSERT INTO staging(x) VALUES(?1)");
10200f0375dSdrh *pnRow = *pnStmt = 0;
10300f0375dSdrh while( rc==SQLITE_OK && zSql && zSql[0] ){
10400f0375dSdrh zLeftover = 0;
10530f30133Sdrh rc = sqlite3_prepare_v2(dbRun, zSql, -1, &pStmt, &zLeftover);
10600f0375dSdrh zSql = zLeftover;
10730f30133Sdrh assert( rc==SQLITE_OK || pStmt==0 );
10800f0375dSdrh if( rc!=SQLITE_OK ){
10900f0375dSdrh printf("Error with [%s]\n%s\n", zSql, sqlite3_errmsg(dbRun));
11000f0375dSdrh break;
11100f0375dSdrh }
11230f30133Sdrh if( !pStmt ) continue;
11300f0375dSdrh (*pnStmt)++;
11430f30133Sdrh nCol = sqlite3_column_count(pStmt);
11530f30133Sdrh run_sql(dbOut, "DELETE FROM staging;");
11600f0375dSdrh while( sqlite3_step(pStmt)==SQLITE_ROW ){
11730f30133Sdrh int i, j;
11830f30133Sdrh for(i=j=0; i<nCol && j<sizeof(zLine)-50; i++){
11930f30133Sdrh int eType = sqlite3_column_type(pStmt, i);
12030f30133Sdrh if( eType==SQLITE_NULL ){
12130f30133Sdrh zCol = "NULL";
12230f30133Sdrh }else{
12330f30133Sdrh zCol = (const char*)sqlite3_column_text(pStmt, i);
12430f30133Sdrh }
12530f30133Sdrh if( i ) zLine[j++] = ',';
12630f30133Sdrh if( eType==SQLITE_TEXT ){
12730f30133Sdrh sqlite3_snprintf(sizeof(zLine)-j, zLine+j, "'%q'", zCol);
12830f30133Sdrh }else{
12930f30133Sdrh sqlite3_snprintf(sizeof(zLine)-j, zLine+j, "%s", zCol);
13030f30133Sdrh }
13130f30133Sdrh j += (int)strlen(zLine+j);
13230f30133Sdrh }
13330f30133Sdrh /* Detect if any row is too large and throw an error, because we will
13430f30133Sdrh ** want to go back and look more closely at that case */
13530f30133Sdrh if( j>=sizeof(zLine)-100 ){
13630f30133Sdrh printf("Excessively long output line: %d bytes\n" ,j);
13730f30133Sdrh exit(1);
13830f30133Sdrh }
13900f0375dSdrh if( bTrace ){
14000f0375dSdrh printf("%s\n", zLine);
14100f0375dSdrh }
14200f0375dSdrh (*pnRow)++;
14330f30133Sdrh sqlite3_bind_text(pIns, 1, zLine, j, SQLITE_TRANSIENT);
14430f30133Sdrh rc = sqlite3_step(pIns);
14530f30133Sdrh assert( rc==SQLITE_DONE );
14600f0375dSdrh rc = sqlite3_reset(pIns);
14730f30133Sdrh }
14830f30133Sdrh run_sql(dbOut,
14930f30133Sdrh "INSERT INTO \"%w\"(x) VALUES('### %q ###')",
15000f0375dSdrh zOutTab, sqlite3_sql(pStmt)
15130f30133Sdrh );
15230f30133Sdrh run_sql(dbOut,
15330f30133Sdrh "INSERT INTO \"%w\"(x) SELECT group_concat(x,char(10))"
15400f0375dSdrh " FROM (SELECT x FROM staging ORDER BY x)",
15530f30133Sdrh zOutTab
15630f30133Sdrh );
15730f30133Sdrh run_sql(dbOut, "COMMIT");
15830f30133Sdrh sqlite3_finalize(pStmt);
15930f30133Sdrh pStmt = 0;
16030f30133Sdrh }
16130f30133Sdrh sqlite3_finalize(pStmt);
16230f30133Sdrh sqlite3_finalize(pIns);
16330f30133Sdrh return rc;
16430f30133Sdrh }
16530f30133Sdrh
16630f30133Sdrh /*
16730f30133Sdrh ** Read the content of file zName into memory obtained from sqlite3_malloc64()
16830f30133Sdrh ** and return a pointer to the buffer. The caller is responsible for freeing
16930f30133Sdrh ** the memory.
17030f30133Sdrh **
17130f30133Sdrh ** If parameter pnByte is not NULL, (*pnByte) is set to the number of bytes
17230f30133Sdrh ** read.
17330f30133Sdrh **
17430f30133Sdrh ** For convenience, a nul-terminator byte is always appended to the data read
17530f30133Sdrh ** from the file before the buffer is returned. This byte is not included in
17630f30133Sdrh ** the final value of (*pnByte), if applicable.
17730f30133Sdrh **
17830f30133Sdrh ** NULL is returned if any error is encountered. The final value of *pnByte
17930f30133Sdrh ** is undefined in this case.
18030f30133Sdrh */
readFile(const char * zName,int * pnByte)18130f30133Sdrh static char *readFile(const char *zName, int *pnByte){
18230f30133Sdrh FILE *in = fopen(zName, "rb");
18330f30133Sdrh long nIn;
18430f30133Sdrh size_t nRead;
18530f30133Sdrh char *pBuf;
18630f30133Sdrh if( in==0 ) return 0;
18730f30133Sdrh fseek(in, 0, SEEK_END);
18830f30133Sdrh nIn = ftell(in);
18930f30133Sdrh rewind(in);
19030f30133Sdrh pBuf = sqlite3_malloc64( nIn+1 );
19130f30133Sdrh if( pBuf==0 ) return 0;
19230f30133Sdrh nRead = fread(pBuf, nIn, 1, in);
19330f30133Sdrh fclose(in);
19430f30133Sdrh if( nRead!=1 ){
19530f30133Sdrh sqlite3_free(pBuf);
19630f30133Sdrh return 0;
19730f30133Sdrh }
19830f30133Sdrh pBuf[nIn] = 0;
19930f30133Sdrh if( pnByte ) *pnByte = nIn;
20030f30133Sdrh return pBuf;
20130f30133Sdrh }
20230f30133Sdrh
main(int argc,char ** argv)20330f30133Sdrh int main(int argc, char **argv){
20430f30133Sdrh int nIn = 0; /* Number of input files */
20530f30133Sdrh char **azIn = 0; /* Names of input files */
20630f30133Sdrh sqlite3 *dbOut = 0; /* Database to hold results */
20730f30133Sdrh sqlite3 *dbRun = 0; /* Database used for tests */
20800f0375dSdrh int bTrace = 0; /* Show query results */
209*66651c2bSdrh int bShowValid = 0; /* Just list inputs that are valid SQL */
21000f0375dSdrh int nRow, nStmt; /* Number of rows and statements */
21130f30133Sdrh int i, rc;
21230f30133Sdrh
21330f30133Sdrh for(i=1; i<argc; i++){
21430f30133Sdrh const char *z = argv[i];
21530f30133Sdrh if( z[0]=='-' && z[1]=='-' ) z++;
21630f30133Sdrh if( strcmp(z,"-help")==0 ){
21700f0375dSdrh printf("Usage: %s [OPTIONS] FILENAME ...\n", argv[0]);
21800f0375dSdrh printf("Options:\n");
21900f0375dSdrh printf(" --help Show his message\n");
22000f0375dSdrh printf(" --output-trace Show each line of SQL output\n");
221*66651c2bSdrh printf(" --valid-sql List FILEs that are valid SQL\n");
22230f30133Sdrh return 0;
22330f30133Sdrh }
22400f0375dSdrh else if( strcmp(z,"-output-trace")==0 ){
22500f0375dSdrh bTrace = 1;
22600f0375dSdrh }
227*66651c2bSdrh else if( strcmp(z,"-valid-sql")==0 ){
228*66651c2bSdrh bShowValid = 1;
229*66651c2bSdrh }
23030f30133Sdrh else if( z[0]=='-' ){
23130f30133Sdrh printf("unknown option \"%s\". Use --help for details\n", argv[i]);
23230f30133Sdrh return 1;
23330f30133Sdrh }
23430f30133Sdrh else {
23530f30133Sdrh nIn++;
23630f30133Sdrh azIn = realloc(azIn, sizeof(azIn[0])*nIn);
23730f30133Sdrh if( azIn==0 ){
23830f30133Sdrh printf("out of memory\n");
23930f30133Sdrh exit(1);
24030f30133Sdrh }
24130f30133Sdrh azIn[nIn-1] = argv[i];
24230f30133Sdrh }
24330f30133Sdrh }
24430f30133Sdrh
24530f30133Sdrh sqlite3_open(":memory:", &dbOut);
24630f30133Sdrh sqlite3_open(":memory:", &dbRun);
24730f30133Sdrh sqlite3_deserialize(dbRun, "main", data001, sizeof(data001),
24830f30133Sdrh sizeof(data001), SQLITE_DESERIALIZE_READONLY);
24930f30133Sdrh for(i=0; i<nIn; i++){
25030f30133Sdrh char *zSql = readFile(azIn[i], 0);
25130f30133Sdrh sqlite3_stmt *pCk;
252*66651c2bSdrh sqlite3_exec(dbRun, "ROLLBACK", 0, 0, 0);
253*66651c2bSdrh if( bShowValid ){
254*66651c2bSdrh rc = sqlite3_exec(dbRun, zSql, 0, 0, 0);
255*66651c2bSdrh if( rc==SQLITE_OK ) printf("%s\n", azIn[i]);
256*66651c2bSdrh sqlite3_free(zSql);
257*66651c2bSdrh continue;
258*66651c2bSdrh }
25930f30133Sdrh sqlite3_test_control(SQLITE_TESTCTRL_OPTIMIZATIONS, dbRun, 0);
26000f0375dSdrh if( bTrace ) printf("%s: Optimized\n", azIn[i]);
26100f0375dSdrh rc = optfuzz_exec(dbRun, zSql, dbOut, "opt", &nStmt, &nRow, bTrace);
26230f30133Sdrh if( rc ){
26300f0375dSdrh printf("%s: optimized run failed: %s\n",
26400f0375dSdrh azIn[i], sqlite3_errmsg(dbRun));
26500f0375dSdrh }else{
26600f0375dSdrh sqlite3_test_control(SQLITE_TESTCTRL_OPTIMIZATIONS, dbRun, 0xffff);
26700f0375dSdrh if( bTrace ) printf("%s: Non-optimized\n", azIn[i]);
26800f0375dSdrh rc = optfuzz_exec(dbRun, zSql, dbOut, "noopt", &nStmt, &nRow, bTrace);
26900f0375dSdrh if( rc ){
27000f0375dSdrh printf("%s: non-optimized run failed: %s\n",
27100f0375dSdrh azIn[i], sqlite3_errmsg(dbRun));
27230f30133Sdrh exit(1);
27330f30133Sdrh }
27430f30133Sdrh pCk = prepare_sql(dbOut,
27500f0375dSdrh "SELECT (SELECT group_concat(x,char(10)) FROM opt)=="
27600f0375dSdrh " (SELECT group_concat(x,char(10)) FROM noopt)");
27730f30133Sdrh rc = sqlite3_step(pCk);
27830f30133Sdrh if( rc!=SQLITE_ROW ){
27900f0375dSdrh printf("%s: comparison failed\n", sqlite3_errmsg(dbOut));
28030f30133Sdrh exit(1);
28130f30133Sdrh }
28230f30133Sdrh if( !sqlite3_column_int(pCk, 0) ){
28300f0375dSdrh printf("%s: opt/no-opt outputs differ\n", azIn[i]);
28400f0375dSdrh pCk = prepare_sql(dbOut,
28500f0375dSdrh "SELECT group_concat(x,char(10)) FROM opt "
28600f0375dSdrh "UNION ALL "
28700f0375dSdrh "SELECT group_concat(x,char(10)) FROM noopt");
28800f0375dSdrh sqlite3_step(pCk);
28900f0375dSdrh printf("opt:\n%s\n", sqlite3_column_text(pCk,0));
29000f0375dSdrh sqlite3_step(pCk);
29100f0375dSdrh printf("noopt:\n%s\n", sqlite3_column_text(pCk,0));
29230f30133Sdrh exit(1);
29300f0375dSdrh }else{
29400f0375dSdrh printf("%s: %d stmts %d rows ok\n", azIn[i], nStmt, nRow);
29530f30133Sdrh }
29630f30133Sdrh sqlite3_finalize(pCk);
29730f30133Sdrh }
29830f30133Sdrh sqlite3_free(zSql);
29930f30133Sdrh }
30030f30133Sdrh sqlite3_close(dbRun);
30130f30133Sdrh sqlite3_close(dbOut);
30230f30133Sdrh free(azIn);
30330f30133Sdrh if( sqlite3_memory_used() ){
30430f30133Sdrh printf("Memory leak of %lld bytes\n", sqlite3_memory_used());
30530f30133Sdrh exit(1);
30630f30133Sdrh }
30730f30133Sdrh return 0;
30830f30133Sdrh }
309