4 ** The author disclaims copyright to this source code. In place of
5 ** a legal notice, here is a blessing:
7 ** May you do good and not evil.
8 ** May you find forgiveness for yourself and forgive others.
9 ** May you share freely, never taking more than you give.
11 *************************************************************************
13 ** This program attempts to verify the correctness of the SQLite query
14 ** optimizer by fuzzing.
16 ** The input is an SQL script, presumably generated by a fuzzer. The
17 ** argument is the name of the input. If no files are named, standard
20 ** The SQL script is run twice, once with optimization enabled, and again
21 ** with optimization disabled. If the output is not equivalent, an error
22 ** is printed and the program returns non-zero.
25 /* Include the SQLite amalgamation, after making appropriate #defines.
27 #define SQLITE_THREADSAFE 0
28 #define SQLITE_OMIT_LOAD_EXTENSION 1
31 /* Content of the read-only test database */
32 #include "optfuzz-db01.c"
35 ** Prepare a single SQL statement. Panic if anything goes wrong
37 static sqlite3_stmt
*prepare_sql(sqlite3
*db
, const char *zFormat
, ...){
40 sqlite3_stmt
*pStmt
= 0;
43 va_start(ap
, zFormat
);
44 zSql
= sqlite3_vmprintf(zFormat
, ap
);
46 rc
= sqlite3_prepare_v2(db
, zSql
, -1, &pStmt
, 0);
48 printf("Error: %s\nSQL: %s\n",
49 sqlite3_errmsg(db
), zSql
);
57 ** Run SQL. Panic if anything goes wrong
59 static void run_sql(sqlite3
*db
, const char *zFormat
, ...){
65 va_start(ap
, zFormat
);
66 zSql
= sqlite3_vmprintf(zFormat
, ap
);
68 rc
= sqlite3_exec(db
, zSql
, 0, 0, &zErr
);
70 printf("Error: %s\nsqlite3_errmsg: %s\nSQL: %s\n",
71 zErr
, sqlite3_errmsg(db
), zSql
);
78 ** Run one or more SQL statements contained in zSql against database dbRun.
79 ** Store the input in database dbOut.
81 static int optfuzz_exec(
82 sqlite3
*dbRun
, /* The database on which the SQL executes */
83 const char *zSql
, /* The SQL to be executed */
84 sqlite3
*dbOut
, /* Store results in this database */
85 const char *zOutTab
, /* Store results in this table of dbOut */
86 int *pnStmt
, /* Write the number of statements here */
87 int *pnRow
, /* Write the number of rows here */
88 int bTrace
/* Print query results if true */
90 int rc
= SQLITE_OK
; /* Return code */
91 const char *zLeftover
; /* Tail of unprocessed SQL */
92 sqlite3_stmt
*pStmt
= 0; /* The current SQL statement */
93 sqlite3_stmt
*pIns
= 0; /* Statement to insert into dbOut */
94 const char *zCol
; /* Single column value */
95 int nCol
; /* Number of output columns */
96 char zLine
[4000]; /* Complete row value */
98 run_sql(dbOut
, "BEGIN");
99 run_sql(dbOut
, "CREATE TABLE IF NOT EXISTS staging(x TEXT)");
100 run_sql(dbOut
, "CREATE TABLE IF NOT EXISTS \"%w\"(x TEXT)", zOutTab
);
101 pIns
= prepare_sql(dbOut
, "INSERT INTO staging(x) VALUES(?1)");
102 *pnRow
= *pnStmt
= 0;
103 while( rc
==SQLITE_OK
&& zSql
&& zSql
[0] ){
105 rc
= sqlite3_prepare_v2(dbRun
, zSql
, -1, &pStmt
, &zLeftover
);
107 assert( rc
==SQLITE_OK
|| pStmt
==0 );
109 printf("Error with [%s]\n%s\n", zSql
, sqlite3_errmsg(dbRun
));
112 if( !pStmt
) continue;
114 nCol
= sqlite3_column_count(pStmt
);
115 run_sql(dbOut
, "DELETE FROM staging;");
116 while( sqlite3_step(pStmt
)==SQLITE_ROW
){
118 for(i
=j
=0; i
<nCol
&& j
<sizeof(zLine
)-50; i
++){
119 int eType
= sqlite3_column_type(pStmt
, i
);
120 if( eType
==SQLITE_NULL
){
123 zCol
= (const char*)sqlite3_column_text(pStmt
, i
);
125 if( i
) zLine
[j
++] = ',';
126 if( eType
==SQLITE_TEXT
){
127 sqlite3_snprintf(sizeof(zLine
)-j
, zLine
+j
, "'%q'", zCol
);
129 sqlite3_snprintf(sizeof(zLine
)-j
, zLine
+j
, "%s", zCol
);
131 j
+= (int)strlen(zLine
+j
);
133 /* Detect if any row is too large and throw an error, because we will
134 ** want to go back and look more closely at that case */
135 if( j
>=sizeof(zLine
)-100 ){
136 printf("Excessively long output line: %d bytes\n" ,j
);
140 printf("%s\n", zLine
);
143 sqlite3_bind_text(pIns
, 1, zLine
, j
, SQLITE_TRANSIENT
);
144 rc
= sqlite3_step(pIns
);
145 assert( rc
==SQLITE_DONE
);
146 rc
= sqlite3_reset(pIns
);
149 "INSERT INTO \"%w\"(x) VALUES('### %q ###')",
150 zOutTab
, sqlite3_sql(pStmt
)
153 "INSERT INTO \"%w\"(x) SELECT group_concat(x,char(10))"
154 " FROM (SELECT x FROM staging ORDER BY x)",
157 run_sql(dbOut
, "COMMIT");
158 sqlite3_finalize(pStmt
);
161 sqlite3_finalize(pStmt
);
162 sqlite3_finalize(pIns
);
167 ** Read the content of file zName into memory obtained from sqlite3_malloc64()
168 ** and return a pointer to the buffer. The caller is responsible for freeing
171 ** If parameter pnByte is not NULL, (*pnByte) is set to the number of bytes
174 ** For convenience, a nul-terminator byte is always appended to the data read
175 ** from the file before the buffer is returned. This byte is not included in
176 ** the final value of (*pnByte), if applicable.
178 ** NULL is returned if any error is encountered. The final value of *pnByte
179 ** is undefined in this case.
181 static char *readFile(const char *zName
, int *pnByte
){
182 FILE *in
= fopen(zName
, "rb");
186 if( in
==0 ) return 0;
187 fseek(in
, 0, SEEK_END
);
190 pBuf
= sqlite3_malloc64( nIn
+1 );
191 if( pBuf
==0 ) return 0;
192 nRead
= fread(pBuf
, nIn
, 1, in
);
199 if( pnByte
) *pnByte
= nIn
;
203 int main(int argc
, char **argv
){
204 int nIn
= 0; /* Number of input files */
205 char **azIn
= 0; /* Names of input files */
206 sqlite3
*dbOut
= 0; /* Database to hold results */
207 sqlite3
*dbRun
= 0; /* Database used for tests */
208 int bTrace
= 0; /* Show query results */
209 int bShowValid
= 0; /* Just list inputs that are valid SQL */
210 int nRow
, nStmt
; /* Number of rows and statements */
213 for(i
=1; i
<argc
; i
++){
214 const char *z
= argv
[i
];
215 if( z
[0]=='-' && z
[1]=='-' ) z
++;
216 if( strcmp(z
,"-help")==0 ){
217 printf("Usage: %s [OPTIONS] FILENAME ...\n", argv
[0]);
218 printf("Options:\n");
219 printf(" --help Show his message\n");
220 printf(" --output-trace Show each line of SQL output\n");
221 printf(" --valid-sql List FILEs that are valid SQL\n");
224 else if( strcmp(z
,"-output-trace")==0 ){
227 else if( strcmp(z
,"-valid-sql")==0 ){
230 else if( z
[0]=='-' ){
231 printf("unknown option \"%s\". Use --help for details\n", argv
[i
]);
236 azIn
= realloc(azIn
, sizeof(azIn
[0])*nIn
);
238 printf("out of memory\n");
241 azIn
[nIn
-1] = argv
[i
];
245 sqlite3_open(":memory:", &dbOut
);
246 sqlite3_open(":memory:", &dbRun
);
247 sqlite3_deserialize(dbRun
, "main", data001
, sizeof(data001
),
248 sizeof(data001
), SQLITE_DESERIALIZE_READONLY
);
249 for(i
=0; i
<nIn
; i
++){
250 char *zSql
= readFile(azIn
[i
], 0);
252 sqlite3_exec(dbRun
, "ROLLBACK", 0, 0, 0);
254 rc
= sqlite3_exec(dbRun
, zSql
, 0, 0, 0);
255 if( rc
==SQLITE_OK
) printf("%s\n", azIn
[i
]);
259 sqlite3_test_control(SQLITE_TESTCTRL_OPTIMIZATIONS
, dbRun
, 0);
260 if( bTrace
) printf("%s: Optimized\n", azIn
[i
]);
261 rc
= optfuzz_exec(dbRun
, zSql
, dbOut
, "opt", &nStmt
, &nRow
, bTrace
);
263 printf("%s: optimized run failed: %s\n",
264 azIn
[i
], sqlite3_errmsg(dbRun
));
266 sqlite3_test_control(SQLITE_TESTCTRL_OPTIMIZATIONS
, dbRun
, 0xffff);
267 if( bTrace
) printf("%s: Non-optimized\n", azIn
[i
]);
268 rc
= optfuzz_exec(dbRun
, zSql
, dbOut
, "noopt", &nStmt
, &nRow
, bTrace
);
270 printf("%s: non-optimized run failed: %s\n",
271 azIn
[i
], sqlite3_errmsg(dbRun
));
274 pCk
= prepare_sql(dbOut
,
275 "SELECT (SELECT group_concat(x,char(10)) FROM opt)=="
276 " (SELECT group_concat(x,char(10)) FROM noopt)");
277 rc
= sqlite3_step(pCk
);
278 if( rc
!=SQLITE_ROW
){
279 printf("%s: comparison failed\n", sqlite3_errmsg(dbOut
));
282 if( !sqlite3_column_int(pCk
, 0) ){
283 printf("%s: opt/no-opt outputs differ\n", azIn
[i
]);
284 pCk
= prepare_sql(dbOut
,
285 "SELECT group_concat(x,char(10)) FROM opt "
287 "SELECT group_concat(x,char(10)) FROM noopt");
289 printf("opt:\n%s\n", sqlite3_column_text(pCk
,0));
291 printf("noopt:\n%s\n", sqlite3_column_text(pCk
,0));
294 printf("%s: %d stmts %d rows ok\n", azIn
[i
], nStmt
, nRow
);
296 sqlite3_finalize(pCk
);
300 sqlite3_close(dbRun
);
301 sqlite3_close(dbOut
);
303 if( sqlite3_memory_used() ){
304 printf("Memory leak of %lld bytes\n", sqlite3_memory_used());