xref: /sqlite-3.40.0/ext/session/changesetfuzz.c (revision 60ce5d31)
1 /*
2 ** 2018-11-01
3 **
4 ** The author disclaims copyright to this source code.  In place of
5 ** a legal notice, here is a blessing:
6 **
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.
10 **
11 *************************************************************************
12 ** This file contains code to implement the "changesetfuzz" command
13 ** line utility for fuzzing changeset blobs without corrupting them.
14 */
15 
16 
17 /************************************************************************
18 ** USAGE:
19 **
20 ** This program may be invoked in two ways:
21 **
22 **   changesetfuzz INPUT
23 **   changesetfuzz INPUT SEED N
24 **
25 ** Argument INPUT must be the name of a file containing a binary changeset.
26 ** In the first form above, this program outputs a human-readable version
27 ** of the same changeset. This is chiefly for debugging.
28 **
29 ** As well as changesets, this program can also dump and fuzz patchsets.
30 ** The term "changeset" is used for both patchsets and changesets from this
31 ** point on.
32 **
33 ** In the second form, arguments SEED and N must both be integers. In this
34 ** case, this program writes N binary changesets to disk. Each output
35 ** changeset is a slightly modified - "fuzzed" - version of the input.
36 ** The output changesets are written to files name "INPUT-$n", where $n is
37 ** an integer between 0 and N-1, inclusive. Output changesets are always
38 ** well-formed. Parameter SEED is used to seed the PRNG - any two
39 ** invocations of this program with the same SEED and input changeset create
40 ** the same N output changesets.
41 **
42 ** The ways in which an input changeset may be fuzzed are as follows:
43 **
44 **   1. Any two values within the changeset may be exchanged.
45 **
46 **   2. Any TEXT, BLOB, INTEGER or REAL value within the changeset
47 **      may have a single bit of its content flipped.
48 **
49 **   3. Any value within a changeset may be replaced by a pseudo-randomly
50 **      generated value.
51 **
52 ** The above operations never set a PRIMARY KEY column to NULL. Nor do they
53 ** set any value to "undefined", or replace any "undefined" value with
54 ** another. Any such operation risks producing a changeset that is not
55 ** well-formed.
56 **
57 **   4. A single change may be duplicated.
58 **
59 **   5. A single change may be removed, so long as this does not mean that
60 **      there are zero changes following a table-header within the changeset.
61 **
62 **   6. A single change may have its type (INSERT, DELETE, UPDATE) changed.
63 **      If an INSERT is changed to a DELETE (or vice versa), the type is
64 **      simply changed - no other modifications are required. If an INSERT
65 **      or DELETE is changed to an UPDATE, then the single record is duplicated
66 **      (as both the old.* and new.* records of the new UPDATE change). If an
67 **      UPDATE is changed to a DELETE or INSERT, the new.* record is discarded
68 **      and any "undefined" fields replaced with pseudo-randomly generated
69 **      values.
70 **
71 **   7. An UPDATE change that modifies N table columns may be modified so
72 **      that it updates N-1 columns, so long as (N>1).
73 **
74 **   8. The "indirect" flag may be toggled for any change.
75 **
76 ** Entire group of changes may also be operated on:
77 **
78 **   9. Duplicate an existing group.
79 **
80 **  10. Remove an existing group.
81 **
82 **  11. The positions of two groups may be exchanged.
83 **
84 ** There are also schema changes:
85 **
86 **  12. A non-PK column may be added to a table. In this case a NULL
87 **      value is appended to all records.
88 **
89 **  13. A PK column may be added to a table. In this case a non-NULL
90 **      value is appended to all INSERT, DELETE and UPDATE old.* records.
91 **      An "undefined" is appended to new.* UPDATE records.
92 **
93 **  14. A column may be removed from a table, provided that it is not the
94 **      only PRIMARY KEY column in the table. In this case the corresponding
95 **      field is removed from all records. In cases where this leaves an UPDATE
96 **      with no non-PK, non-undefined fields, the entire change is removed.
97 */
98 
99 #include "sqlite3.h"
100 #include <stdio.h>
101 #include <stdlib.h>
102 #include <string.h>
103 #include <assert.h>
104 #include <ctype.h>
105 
106 #define FUZZ_VALUE_SUB       1    /* Replace one value with a copy of another */
107 #define FUZZ_VALUE_MOD       2    /* Modify content by 1 bit */
108 #define FUZZ_VALUE_RND       3    /* Replace with pseudo-random value */
109 
110 #define FUZZ_CHANGE_DUP      4    /* Duplicate an existing change */
111 #define FUZZ_CHANGE_DEL      5    /* Completely remove one change */
112 #define FUZZ_CHANGE_TYPE     6    /* Change the type of one change */
113 #define FUZZ_CHANGE_FIELD    7    /* Change an UPDATE to modify fewer columns */
114 #define FUZZ_CHANGE_INDIRECT 8    /* Toggle the "indirect" flag of a change */
115 
116 #define FUZZ_GROUP_DUP       9    /* Duplicate a change group */
117 #define FUZZ_GROUP_DEL      10    /* Delete an entire change group */
118 #define FUZZ_GROUP_SWAP     11    /* Exchange the position of two groups */
119 
120 #define FUZZ_COLUMN_ADD     12     /* Add column to table definition */
121 #define FUZZ_COLUMN_ADDPK   13     /* Add PK column to table definition */
122 #define FUZZ_COLUMN_DEL     14     /* Remove column from table definition */
123 
124 
125 
126 typedef unsigned char u8;
127 typedef sqlite3_uint64 u64;
128 typedef sqlite3_int64 i64;
129 typedef unsigned int u32;
130 
131 /*
132 ** Show a usage message on stderr then quit.
133 */
134 static void usage(const char *argv0){
135   fprintf(stderr, "Usage: %s FILENAME ?SEED N?\n", argv0);
136   exit(1);
137 }
138 
139 /*
140 ** Read the content of a disk file into an in-memory buffer
141 */
142 static void fuzzReadFile(const char *zFilename, int *pSz, void **ppBuf){
143   FILE *f;
144   int sz;
145   void *pBuf;
146   f = fopen(zFilename, "rb");
147   if( f==0 ){
148     fprintf(stderr, "cannot open \"%s\" for reading\n", zFilename);
149     exit(1);
150   }
151   fseek(f, 0, SEEK_END);
152   sz = (int)ftell(f);
153   rewind(f);
154   pBuf = sqlite3_malloc( sz ? sz : 1 );
155   if( pBuf==0 ){
156     fprintf(stderr, "cannot allocate %d to hold content of \"%s\"\n",
157             sz, zFilename);
158     exit(1);
159   }
160   if( sz>0 ){
161     if( fread(pBuf, sz, 1, f)!=1 ){
162       fprintf(stderr, "cannot read all %d bytes of \"%s\"\n", sz, zFilename);
163       exit(1);
164     }
165     fclose(f);
166   }
167   *pSz = sz;
168   *ppBuf = pBuf;
169 }
170 
171 /*
172 ** Write the contents of buffer pBuf, size nBuf bytes, into file zFilename
173 ** on disk. zFilename, if it already exists, is clobbered.
174 */
175 static void fuzzWriteFile(const char *zFilename, void *pBuf, int nBuf){
176   FILE *f;
177   f = fopen(zFilename, "wb");
178   if( f==0 ){
179     fprintf(stderr, "cannot open \"%s\" for writing\n", zFilename);
180     exit(1);
181   }
182   if( fwrite(pBuf, nBuf, 1, f)!=1 ){
183     fprintf(stderr, "cannot write to \"%s\"\n", zFilename);
184     exit(1);
185   }
186   fclose(f);
187 }
188 
189 static int fuzzCorrupt(){
190   return SQLITE_CORRUPT;
191 }
192 
193 /*************************************************************************
194 ** The following block is a copy of the implementation of SQLite function
195 ** sqlite3_randomness. This version has two important differences:
196 **
197 **   1. It always uses the same seed. So the sequence of random data output
198 **      is the same for every run of the program.
199 **
200 **   2. It is not threadsafe.
201 */
202 static struct sqlite3PrngType {
203   unsigned char i, j;             /* State variables */
204   unsigned char s[256];           /* State variables */
205 } sqlite3Prng = {
206     0xAF, 0x28,
207   {
208     0x71, 0xF5, 0xB4, 0x6E, 0x80, 0xAB, 0x1D, 0xB8,
209     0xFB, 0xB7, 0x49, 0xBF, 0xFF, 0x72, 0x2D, 0x14,
210     0x79, 0x09, 0xE3, 0x78, 0x76, 0xB0, 0x2C, 0x0A,
211     0x8E, 0x23, 0xEE, 0xDF, 0xE0, 0x9A, 0x2F, 0x67,
212     0xE1, 0xBE, 0x0E, 0xA7, 0x08, 0x97, 0xEB, 0x77,
213     0x78, 0xBA, 0x9D, 0xCA, 0x49, 0x4C, 0x60, 0x9A,
214     0xF6, 0xBD, 0xDA, 0x7F, 0xBC, 0x48, 0x58, 0x52,
215     0xE5, 0xCD, 0x83, 0x72, 0x23, 0x52, 0xFF, 0x6D,
216     0xEF, 0x0F, 0x82, 0x29, 0xA0, 0x83, 0x3F, 0x7D,
217     0xA4, 0x88, 0x31, 0xE7, 0x88, 0x92, 0x3B, 0x9B,
218     0x3B, 0x2C, 0xC2, 0x4C, 0x71, 0xA2, 0xB0, 0xEA,
219     0x36, 0xD0, 0x00, 0xF1, 0xD3, 0x39, 0x17, 0x5D,
220     0x2A, 0x7A, 0xE4, 0xAD, 0xE1, 0x64, 0xCE, 0x0F,
221     0x9C, 0xD9, 0xF5, 0xED, 0xB0, 0x22, 0x5E, 0x62,
222     0x97, 0x02, 0xA3, 0x8C, 0x67, 0x80, 0xFC, 0x88,
223     0x14, 0x0B, 0x15, 0x10, 0x0F, 0xC7, 0x40, 0xD4,
224     0xF1, 0xF9, 0x0E, 0x1A, 0xCE, 0xB9, 0x1E, 0xA1,
225     0x72, 0x8E, 0xD7, 0x78, 0x39, 0xCD, 0xF4, 0x5D,
226     0x2A, 0x59, 0x26, 0x34, 0xF2, 0x73, 0x0B, 0xA0,
227     0x02, 0x51, 0x2C, 0x03, 0xA3, 0xA7, 0x43, 0x13,
228     0xE8, 0x98, 0x2B, 0xD2, 0x53, 0xF8, 0xEE, 0x91,
229     0x7D, 0xE7, 0xE3, 0xDA, 0xD5, 0xBB, 0xC0, 0x92,
230     0x9D, 0x98, 0x01, 0x2C, 0xF9, 0xB9, 0xA0, 0xEB,
231     0xCF, 0x32, 0xFA, 0x01, 0x49, 0xA5, 0x1D, 0x9A,
232     0x76, 0x86, 0x3F, 0x40, 0xD4, 0x89, 0x8F, 0x9C,
233     0xE2, 0xE3, 0x11, 0x31, 0x37, 0xB2, 0x49, 0x28,
234     0x35, 0xC0, 0x99, 0xB6, 0xD0, 0xBC, 0x66, 0x35,
235     0xF7, 0x83, 0x5B, 0xD7, 0x37, 0x1A, 0x2B, 0x18,
236     0xA6, 0xFF, 0x8D, 0x7C, 0x81, 0xA8, 0xFC, 0x9E,
237     0xC4, 0xEC, 0x80, 0xD0, 0x98, 0xA7, 0x76, 0xCC,
238     0x9C, 0x2F, 0x7B, 0xFF, 0x8E, 0x0E, 0xBB, 0x90,
239     0xAE, 0x13, 0x06, 0xF5, 0x1C, 0x4E, 0x52, 0xF7
240   }
241 };
242 
243 /*
244 ** Generate and return single random byte
245 */
246 static unsigned char fuzzRandomByte(void){
247   unsigned char t;
248   sqlite3Prng.i++;
249   t = sqlite3Prng.s[sqlite3Prng.i];
250   sqlite3Prng.j += t;
251   sqlite3Prng.s[sqlite3Prng.i] = sqlite3Prng.s[sqlite3Prng.j];
252   sqlite3Prng.s[sqlite3Prng.j] = t;
253   t += sqlite3Prng.s[sqlite3Prng.i];
254   return sqlite3Prng.s[t];
255 }
256 
257 /*
258 ** Return N random bytes.
259 */
260 static void fuzzRandomBlob(int nBuf, unsigned char *zBuf){
261   int i;
262   for(i=0; i<nBuf; i++){
263     zBuf[i] = fuzzRandomByte();
264   }
265 }
266 
267 /*
268 ** Return a random integer between 0 and nRange (not inclusive).
269 */
270 static unsigned int fuzzRandomInt(unsigned int nRange){
271   unsigned int ret;
272   assert( nRange>0 );
273   fuzzRandomBlob(sizeof(ret), (unsigned char*)&ret);
274   return (ret % nRange);
275 }
276 
277 static u64 fuzzRandomU64(){
278   u64 ret;
279   fuzzRandomBlob(sizeof(ret), (unsigned char*)&ret);
280   return ret;
281 }
282 
283 static void fuzzRandomSeed(unsigned int iSeed){
284   int i;
285   for(i=0; i<256; i+=4){
286     sqlite3Prng.s[i] ^= ((iSeed >> 24) & 0xFF);
287     sqlite3Prng.s[i+1] ^= ((iSeed >> 16) & 0xFF);
288     sqlite3Prng.s[i+2] ^= ((iSeed >>  8) & 0xFF);
289     sqlite3Prng.s[i+3] ^= ((iSeed >>  0) & 0xFF);
290   }
291 }
292 /*
293 ** End of code for generating pseudo-random values.
294 *************************************************************************/
295 
296 typedef struct FuzzChangeset FuzzChangeset;
297 typedef struct FuzzChangesetGroup FuzzChangesetGroup;
298 typedef struct FuzzChange FuzzChange;
299 
300 /*
301 ** Object containing partially parsed changeset.
302 */
303 struct FuzzChangeset {
304   int bPatchset;                  /* True for a patchset */
305   FuzzChangesetGroup **apGroup;   /* Array of groups in changeset */
306   int nGroup;                     /* Number of items in list pGroup */
307   u8 **apVal;                     /* Array of all values in changeset */
308   int nVal;                       /* Number of used slots in apVal[] */
309   int nChange;                    /* Number of changes in changeset */
310   int nUpdate;                    /* Number of UPDATE changes in changeset */
311 };
312 
313 /*
314 ** There is one object of this type for each change-group (table header)
315 ** in the input changeset.
316 */
317 struct FuzzChangesetGroup {
318   const char *zTab;               /* Name of table */
319   int nCol;                       /* Number of columns in table */
320   u8 *aPK;                        /* PK array for this table */
321   u8 *aChange;                    /* Buffer containing array of changes */
322   int szChange;                   /* Size of buffer aChange[] in bytes */
323   int nChange;                    /* Number of changes in buffer aChange[] */
324 };
325 
326 /*
327 ** Description of a fuzz change to be applied to a changeset.
328 */
329 struct FuzzChange {
330   int eType;                      /* One of the FUZZ_* constants above */
331   int iChange;                    /* Change or UPDATE to modify */
332   int iGroup;                     /* Group to modify */
333   int iDelete;                    /* Field to remove (FUZZ_COLUMN_DEL) */
334   u8 *pSub1;                      /* Replace this value with pSub2 */
335   u8 *pSub2;                      /* And this one with pSub1 */
336   u8 aSub[128];                   /* Buffer for substitute value */
337   int iCurrent;                   /* Current change number */
338 };
339 
340 /*
341 ** Allocate and return nByte bytes of zeroed memory.
342 */
343 static void *fuzzMalloc(int nByte){
344   void *pRet = sqlite3_malloc(nByte);
345   if( pRet ){
346     memset(pRet, 0, nByte);
347   }
348   return pRet;
349 }
350 
351 /*
352 ** Free the buffer indicated by the first argument. This function is used
353 ** to free buffers allocated by fuzzMalloc().
354 */
355 static void fuzzFree(void *p){
356   sqlite3_free(p);
357 }
358 
359 /*
360 ** Argument p points to a buffer containing an SQLite varint that, assuming the
361 ** input is not corrupt, may be between 0 and 0x7FFFFFFF, inclusive. Before
362 ** returning, this function sets (*pnVal) to the value of that varint, and
363 ** returns the number of bytes of space that it takes up.
364 */
365 static int fuzzGetVarint(u8 *p, int *pnVal){
366   int i;
367   sqlite3_uint64 nVal = 0;
368   for(i=0; i<9; i++){
369     nVal = (nVal<<7) + (p[i] & 0x7F);
370     if( (p[i] & 0x80)==0 ){
371       i++;
372       break;
373     }
374   }
375   *pnVal = (int)nVal;
376   return i;
377 }
378 
379 /*
380 ** Write value nVal into the buffer indicated by argument p as an SQLite
381 ** varint. nVal is guaranteed to be between 0 and (2^21-1), inclusive.
382 ** Return the number of bytes written to buffer p.
383 */
384 static int fuzzPutVarint(u8 *p, int nVal){
385   assert( nVal>0 && nVal<2097152 );
386   if( nVal<128 ){
387     p[0] = nVal;
388     return 1;
389   }
390   if( nVal<16384 ){
391     p[0] = ((nVal >> 7) & 0x7F) | 0x80;
392     p[1] = (nVal & 0x7F);
393     return 2;
394   }
395 
396   p[0] = ((nVal >> 14) & 0x7F) | 0x80;
397   p[1] = ((nVal >> 7) & 0x7F) | 0x80;
398   p[2] = (nVal & 0x7F);
399   return 3;
400 }
401 
402 /*
403 ** Read a 64-bit big-endian integer value from buffer aRec[]. Return
404 ** the value read.
405 */
406 static i64 fuzzGetI64(u8 *aRec){
407   return (i64)(
408       (((u64)aRec[0]) << 56)
409     + (((u64)aRec[1]) << 48)
410     + (((u64)aRec[2]) << 40)
411     + (((u64)aRec[3]) << 32)
412     + (((u64)aRec[4]) << 24)
413     + (((u64)aRec[5]) << 16)
414     + (((u64)aRec[6]) <<  8)
415     + (((u64)aRec[7]) <<  0)
416   );
417 }
418 
419 /*
420 ** Write value iVal to buffer aRec[] as an unsigned 64-bit big-endian integer.
421 */
422 static void fuzzPutU64(u8 *aRec, u64 iVal){
423   aRec[0] = (iVal>>56) & 0xFF;
424   aRec[1] = (iVal>>48) & 0xFF;
425   aRec[2] = (iVal>>40) & 0xFF;
426   aRec[3] = (iVal>>32) & 0xFF;
427   aRec[4] = (iVal>>24) & 0xFF;
428   aRec[5] = (iVal>>16) & 0xFF;
429   aRec[6] = (iVal>> 8) & 0xFF;
430   aRec[7] = (iVal)     & 0xFF;
431 }
432 
433 /*
434 ** Parse a single table-header from the input. Allocate a new change-group
435 ** object with the results. Return SQLITE_OK if successful, or an error code
436 ** otherwise.
437 */
438 static int fuzzParseHeader(
439   FuzzChangeset *pParse,          /* Changeset parse object */
440   u8 **ppHdr,                     /* IN/OUT: Iterator */
441   u8 *pEnd,                       /* 1 byte past EOF */
442   FuzzChangesetGroup **ppGrp      /* OUT: New change-group object */
443 ){
444   int rc = SQLITE_OK;
445   FuzzChangesetGroup *pGrp;
446   u8 cHdr = (pParse->bPatchset ? 'P' : 'T');
447 
448   assert( pEnd>(*ppHdr) );
449   pGrp = (FuzzChangesetGroup*)fuzzMalloc(sizeof(FuzzChangesetGroup));
450   if( !pGrp ){
451     rc = SQLITE_NOMEM;
452   }else{
453     u8 *p = *ppHdr;
454     if( p[0]!=cHdr ){
455       rc = fuzzCorrupt();
456     }else{
457       p++;
458       p += fuzzGetVarint(p, &pGrp->nCol);
459       pGrp->aPK = p;
460       p += pGrp->nCol;
461       pGrp->zTab = (const char*)p;
462       p = &p[strlen(p)+1];
463 
464       if( p>=pEnd ){
465         rc = fuzzCorrupt();
466       }
467     }
468     *ppHdr = p;
469   }
470 
471   if( rc!=SQLITE_OK ){
472     fuzzFree(pGrp);
473     pGrp = 0;
474   }
475 
476   *ppGrp = pGrp;
477   return rc;
478 }
479 
480 /*
481 ** Argument p points to a buffer containing a single changeset-record value.
482 ** This function attempts to determine the size of the value in bytes. If
483 ** successful, it sets (*pSz) to the size and returns SQLITE_OK. Or, if the
484 ** buffer does not contain a valid value, SQLITE_CORRUPT is returned and
485 ** the final value of (*pSz) is undefined.
486 */
487 static int fuzzChangeSize(u8 *p, int *pSz){
488   u8 eType = p[0];
489   switch( eType ){
490     case 0x00:                    /* undefined */
491     case 0x05:                    /* null */
492       *pSz = 1;
493       break;
494 
495     case 0x01:                    /* integer */
496     case 0x02:                    /* real */
497       *pSz = 9;
498       break;
499 
500     case 0x03:                    /* text */
501     case 0x04: {                  /* blob */
502       int nTxt;
503       int sz;
504       sz = fuzzGetVarint(&p[1], &nTxt);
505       *pSz = 1 + sz + nTxt;
506       break;
507     }
508 
509     default:
510       return fuzzCorrupt();
511   }
512   return SQLITE_OK;
513 }
514 
515 /*
516 ** When this function is called, (*ppRec) points to the start of a
517 ** record in a changeset being parsed. This function adds entries
518 ** to the pParse->apVal[] array for all values and advances (*ppRec)
519 ** to one byte past the end of the record. Argument pEnd points to
520 ** one byte past the end of the input changeset.
521 **
522 ** Argument bPkOnly is true if the record being parsed is part of
523 ** a DELETE record in a patchset. In this case, all non-primary-key
524 ** fields have been omitted from the record.
525 **
526 ** SQLITE_OK is returned if successful, or an SQLite error code otherwise.
527 */
528 static int fuzzParseRecord(
529   u8 **ppRec,                     /* IN/OUT: Iterator */
530   u8 *pEnd,                       /* One byte after end of input data */
531   FuzzChangeset *pParse,          /* Changeset parse context */
532   int bPkOnly                     /* True if non-PK fields omitted */
533 ){
534   int rc = SQLITE_OK;
535   FuzzChangesetGroup *pGrp = pParse->apGroup[pParse->nGroup-1];
536   int i;
537   u8 *p = *ppRec;
538 
539   for(i=0; rc==SQLITE_OK && i<pGrp->nCol; i++){
540     if( bPkOnly==0 || pGrp->aPK[i] ){
541       int sz;
542       if( p>=pEnd ) break;
543       if( (pParse->nVal & (pParse->nVal-1))==0 ){
544         int nNew = pParse->nVal ? pParse->nVal*2 : 4;
545         u8 **apNew = (u8**)sqlite3_realloc(pParse->apVal, nNew*sizeof(u8*));
546         if( apNew==0 ) return SQLITE_NOMEM;
547         pParse->apVal = apNew;
548       }
549       pParse->apVal[pParse->nVal++] = p;
550       rc = fuzzChangeSize(p, &sz);
551       p += sz;
552     }
553   }
554 
555   if( rc==SQLITE_OK && i<pGrp->nCol ){
556     rc = fuzzCorrupt();
557   }
558 
559   *ppRec = p;
560   return rc;
561 }
562 
563 /*
564 ** Parse the array of changes starting at (*ppData) and add entries for
565 ** all values to the pParse->apVal[] array. Argument pEnd points to one byte
566 ** past the end of the input changeset. If successful, set (*ppData) to point
567 ** to one byte past the end of the change array and return SQLITE_OK.
568 ** Otherwise, return an SQLite error code. The final value of (*ppData) is
569 ** undefined in this case.
570 */
571 static int fuzzParseChanges(u8 **ppData, u8 *pEnd, FuzzChangeset *pParse){
572   u8 cHdr = (pParse->bPatchset ? 'P' : 'T');
573   FuzzChangesetGroup *pGrp = pParse->apGroup[pParse->nGroup-1];
574   int rc = SQLITE_OK;
575   u8 *p = *ppData;
576 
577   pGrp->aChange = p;
578   while( rc==SQLITE_OK && p<pEnd && p[0]!=cHdr ){
579     u8 eOp = p[0];
580     u8 bIndirect = p[1];
581 
582     p += 2;
583     if( eOp==SQLITE_UPDATE ){
584       pParse->nUpdate++;
585       if( pParse->bPatchset==0 ){
586         rc = fuzzParseRecord(&p, pEnd, pParse, 0);
587       }
588     }else if( eOp!=SQLITE_INSERT && eOp!=SQLITE_DELETE ){
589       rc = fuzzCorrupt();
590     }
591     if( rc==SQLITE_OK ){
592       int bPkOnly = (eOp==SQLITE_DELETE && pParse->bPatchset);
593       rc = fuzzParseRecord(&p, pEnd, pParse, bPkOnly);
594     }
595     pGrp->nChange++;
596     pParse->nChange++;
597   }
598   pGrp->szChange = p - pGrp->aChange;
599 
600   *ppData = p;
601   return rc;
602 }
603 
604 /*
605 ** Parse the changeset stored in buffer pChangeset (nChangeset bytes in
606 ** size). If successful, write the results into (*pParse) and return
607 ** SQLITE_OK. Or, if an error occurs, return an SQLite error code. The
608 ** final state of (*pParse) is undefined in this case.
609 */
610 static int fuzzParseChangeset(
611   u8 *pChangeset,                 /* Buffer containing changeset */
612   int nChangeset,                 /* Size of buffer in bytes */
613   FuzzChangeset *pParse           /* OUT: Results of parse */
614 ){
615   u8 *pEnd = &pChangeset[nChangeset];
616   u8 *p = pChangeset;
617   int rc = SQLITE_OK;
618 
619   memset(pParse, 0, sizeof(FuzzChangeset));
620   if( nChangeset>0 ){
621     pParse->bPatchset = (pChangeset[0]=='P');
622   }
623 
624   while( rc==SQLITE_OK && p<pEnd ){
625     FuzzChangesetGroup *pGrp = 0;
626 
627     /* Read a table-header from the changeset */
628     rc = fuzzParseHeader(pParse, &p, pEnd, &pGrp);
629     assert( (rc==SQLITE_OK)==(pGrp!=0) );
630 
631     /* If the table-header was successfully parsed, add the new change-group
632     ** to the array and parse the associated changes. */
633     if( rc==SQLITE_OK ){
634       FuzzChangesetGroup **apNew = (FuzzChangesetGroup**)sqlite3_realloc(
635           pParse->apGroup, sizeof(FuzzChangesetGroup*)*(pParse->nGroup+1)
636       );
637       if( apNew==0 ){
638         rc = SQLITE_NOMEM;
639       }else{
640         apNew[pParse->nGroup] = pGrp;
641         pParse->apGroup = apNew;
642         pParse->nGroup++;
643       }
644       rc = fuzzParseChanges(&p, pEnd, pParse);
645     }
646   }
647 
648   return rc;
649 }
650 
651 /*
652 ** When this function is called, (*ppRec) points to the first byte of
653 ** a record that is part of change-group pGrp. This function attempts
654 ** to output a human-readable version of the record to stdout and advance
655 ** (*ppRec) to point to the first byte past the end of the record before
656 ** returning. If successful, SQLITE_OK is returned. Otherwise, an SQLite
657 ** error code.
658 **
659 ** If parameter bPkOnly is non-zero, then all non-primary-key fields have
660 ** been omitted from the record. This occurs for records that are part
661 ** of DELETE changes in patchsets.
662 */
663 static int fuzzPrintRecord(FuzzChangesetGroup *pGrp, u8 **ppRec, int bPKOnly){
664   int rc = SQLITE_OK;
665   u8 *p = *ppRec;
666   int i;
667   const char *zPre = " (";
668 
669   for(i=0; i<pGrp->nCol; i++){
670     if( bPKOnly==0 || pGrp->aPK[i] ){
671       u8 eType = p++[0];
672       switch( eType ){
673         case 0x00:                    /* undefined */
674           printf("%sn/a", zPre);
675           break;
676 
677         case 0x01: {                  /* integer */
678           sqlite3_int64 iVal = 0;
679           iVal = fuzzGetI64(p);
680           printf("%s%lld", zPre, iVal);
681           p += 8;
682           break;
683         }
684 
685         case 0x02: {                  /* real */
686           sqlite3_int64 iVal = 0;
687           double fVal = 0.0;
688           iVal = fuzzGetI64(p);
689           memcpy(&fVal, &iVal, 8);
690           printf("%s%f", zPre, fVal);
691           p += 8;
692           break;
693         }
694 
695         case 0x03:                    /* text */
696         case 0x04: {                  /* blob */
697           int nTxt;
698           int sz;
699           int i;
700           p += fuzzGetVarint(p, &nTxt);
701           printf("%s%s", zPre, eType==0x03 ? "'" : "X'");
702           for(i=0; i<nTxt; i++){
703             if( eType==0x03 ){
704               printf("%c", p[i]);
705             }else{
706               char aHex[16] = {'0', '1', '2', '3', '4', '5', '6', '7',
707                                '8', '9', 'A', 'B', 'C', 'D', 'E', 'F'
708               };
709               printf("%c", aHex[ p[i]>>4 ]);
710               printf("%c", aHex[ p[i] & 0x0F ]);
711             }
712           }
713           printf("'");
714           p += nTxt;
715           break;
716         }
717 
718         case 0x05:                    /* null */
719           printf("%sNULL", zPre);
720           break;
721       }
722       zPre = ", ";
723     }
724   }
725   printf(")");
726 
727   *ppRec = p;
728   return rc;
729 }
730 
731 /*
732 ** Print a human-readable version of the table-header and all changes in the
733 ** change-group passed as the second argument.
734 */
735 static void fuzzPrintGroup(FuzzChangeset *pParse, FuzzChangesetGroup *pGrp){
736   int i;
737   u8 *p;
738 
739   /* The table header */
740   printf("TABLE:  %s nCol=%d aPK=", pGrp->zTab, pGrp->nCol);
741   for(i=0; i<pGrp->nCol; i++){
742     printf("%d", (int)pGrp->aPK[i]);
743   }
744   printf("\n");
745 
746   /* The array of changes */
747   p = pGrp->aChange;
748   for(i=0; i<pGrp->nChange; i++){
749     u8 eType = p[0];
750     u8 bIndirect = p[1];
751     printf("%s (ind=%d):",
752         (eType==SQLITE_INSERT) ? "INSERT" :
753         (eType==SQLITE_DELETE ? "DELETE" : "UPDATE"),
754         bIndirect
755     );
756     p += 2;
757 
758     if( pParse->bPatchset==0 && eType==SQLITE_UPDATE ){
759       fuzzPrintRecord(pGrp, &p, 0);
760     }
761     fuzzPrintRecord(pGrp, &p, eType==SQLITE_DELETE && pParse->bPatchset);
762     printf("\n");
763   }
764 }
765 
766 /*
767 ** Initialize the object passed as the second parameter with details
768 ** of the change that will be attempted (type of change, to which part of the
769 ** changeset it applies etc.). If successful, return SQLITE_OK. Or, if an
770 ** error occurs, return an SQLite error code.
771 **
772 ** If a negative value is returned, then the selected change would have
773 ** produced a non-well-formed changeset. In this case the caller should
774 ** call this function again.
775 */
776 static int fuzzSelectChange(FuzzChangeset *pParse, FuzzChange *pChange){
777   int iSub;
778 
779   memset(pChange, 0, sizeof(FuzzChange));
780   pChange->eType = fuzzRandomInt(FUZZ_COLUMN_DEL) + 1;
781 
782   assert( pChange->eType==FUZZ_VALUE_SUB
783        || pChange->eType==FUZZ_VALUE_MOD
784        || pChange->eType==FUZZ_VALUE_RND
785        || pChange->eType==FUZZ_CHANGE_DUP
786        || pChange->eType==FUZZ_CHANGE_DEL
787        || pChange->eType==FUZZ_CHANGE_TYPE
788        || pChange->eType==FUZZ_CHANGE_FIELD
789        || pChange->eType==FUZZ_CHANGE_INDIRECT
790        || pChange->eType==FUZZ_GROUP_DUP
791        || pChange->eType==FUZZ_GROUP_DEL
792        || pChange->eType==FUZZ_GROUP_SWAP
793        || pChange->eType==FUZZ_COLUMN_ADD
794        || pChange->eType==FUZZ_COLUMN_ADDPK
795        || pChange->eType==FUZZ_COLUMN_DEL
796   );
797 
798   pChange->iGroup = fuzzRandomInt(pParse->nGroup);
799   pChange->iChange = fuzzRandomInt(pParse->nChange);
800   if( pChange->eType==FUZZ_CHANGE_FIELD ){
801     if( pParse->nUpdate==0 ) return -1;
802     pChange->iChange = fuzzRandomInt(pParse->nUpdate);
803   }
804 
805   pChange->iDelete = -1;
806   if( pChange->eType==FUZZ_COLUMN_DEL ){
807     FuzzChangesetGroup *pGrp = pParse->apGroup[pChange->iGroup];
808     int i;
809     pChange->iDelete = fuzzRandomInt(pGrp->nCol);
810     for(i=pGrp->nCol-1; i>=0; i--){
811       if( pGrp->aPK[i] && pChange->iDelete!=i ) break;
812     }
813     if( i<0 ) return -1;
814   }
815 
816   if( pChange->eType==FUZZ_GROUP_SWAP ){
817     FuzzChangesetGroup *pGrp;
818     int iGrp = pChange->iGroup;
819     if( pParse->nGroup==1 ) return -1;
820     while( iGrp==pChange->iGroup ){
821       iGrp = fuzzRandomInt(pParse->nGroup);
822     }
823     pGrp = pParse->apGroup[pChange->iGroup];
824     pParse->apGroup[pChange->iGroup] = pParse->apGroup[iGrp];
825     pParse->apGroup[iGrp] = pGrp;
826   }
827 
828   if( pChange->eType==FUZZ_VALUE_SUB
829    || pChange->eType==FUZZ_VALUE_MOD
830    || pChange->eType==FUZZ_VALUE_RND
831   ){
832     iSub = fuzzRandomInt(pParse->nVal);
833     pChange->pSub1 = pParse->apVal[iSub];
834     if( pChange->eType==FUZZ_VALUE_SUB ){
835       iSub = fuzzRandomInt(pParse->nVal);
836       pChange->pSub2 = pParse->apVal[iSub];
837     }else{
838       pChange->pSub2 = pChange->aSub;
839     }
840 
841     if( pChange->eType==FUZZ_VALUE_RND ){
842       pChange->aSub[0] = (u8)(fuzzRandomInt(5) + 1);
843       switch( pChange->aSub[0] ){
844         case 0x01: {                  /* integer */
845           u64 iVal = fuzzRandomU64();
846           fuzzPutU64(&pChange->aSub[1], iVal);
847           break;
848         }
849 
850         case 0x02: {                  /* real */
851           u64 iVal1 = fuzzRandomU64();
852           u64 iVal2 = fuzzRandomU64();
853           double d = (double)iVal1 / (double)iVal2;
854           memcpy(&iVal1, &d, sizeof(iVal1));
855           fuzzPutU64(&pChange->aSub[1], iVal1);
856           break;
857         }
858 
859         case 0x03:                    /* text */
860         case 0x04: {                  /* blob */
861           int nByte = fuzzRandomInt(48);
862           pChange->aSub[1] = nByte;
863           fuzzRandomBlob(nByte, &pChange->aSub[2]);
864           if( pChange->aSub[0]==0x03 ){
865             int i;
866             for(i=0; i<nByte; i++){
867               pChange->aSub[2+i] &= 0x7F;
868             }
869           }
870           break;
871         }
872       }
873     }
874     if( pChange->eType==FUZZ_VALUE_MOD ){
875       int sz;
876       int iMod = -1;
877       fuzzChangeSize(pChange->pSub1, &sz);
878       memcpy(pChange->aSub, pChange->pSub1, sz);
879       switch( pChange->aSub[0] ){
880         case 0x01:
881         case 0x02:
882           iMod = fuzzRandomInt(8) + 1;
883           break;
884 
885         case 0x03:                    /* text */
886         case 0x04: {                  /* blob */
887           int nByte;
888           int iFirst = 1 + fuzzGetVarint(&pChange->aSub[1], &nByte);
889           if( nByte>0 ){
890             iMod = fuzzRandomInt(nByte) + iFirst;
891           }
892           break;
893         }
894       }
895 
896       if( iMod>=0 ){
897         u8 mask = (1 << fuzzRandomInt(8 - (pChange->aSub[0]==0x03)));
898         pChange->aSub[iMod] ^= mask;
899       }
900     }
901   }
902 
903   return SQLITE_OK;
904 }
905 
906 /*
907 ** Copy a single change from the input to the output changeset, making
908 ** any modifications specified by (*pFuzz).
909 */
910 static int fuzzCopyChange(
911   FuzzChangeset *pParse,
912   int iGrp,
913   FuzzChange *pFuzz,
914   u8 **pp, u8 **ppOut             /* IN/OUT: Input and output pointers */
915 ){
916   int bPS = pParse->bPatchset;
917   FuzzChangesetGroup *pGrp = pParse->apGroup[iGrp];
918   u8 *p = *pp;
919   u8 *pOut = *ppOut;
920   u8 eType = p++[0];
921   int iRec;
922   int nRec = ((eType==SQLITE_UPDATE && !bPS) ? 2 : 1);
923   int iUndef = -1;
924   int nUpdate = 0;
925 
926   u8 eNew = eType;
927   if( pFuzz->iCurrent==pFuzz->iChange && pFuzz->eType==FUZZ_CHANGE_TYPE ){
928     switch( eType ){
929       case SQLITE_INSERT:
930         eNew = SQLITE_DELETE;
931         break;
932       case SQLITE_DELETE:
933         eNew = SQLITE_UPDATE;
934         break;
935       case SQLITE_UPDATE:
936         eNew = SQLITE_INSERT;
937         break;
938     }
939   }
940 
941   if( pFuzz->iCurrent==pFuzz->iChange
942    && pFuzz->eType==FUZZ_CHANGE_FIELD && eType==SQLITE_UPDATE
943   ){
944     int sz;
945     int i;
946     int nDef = 0;
947     u8 *pCsr = p+1;
948     for(i=0; i<pGrp->nCol; i++){
949       if( pCsr[0] && pGrp->aPK[i]==0 ) nDef++;
950       fuzzChangeSize(pCsr, &sz);
951       pCsr += sz;
952     }
953     if( nDef<=1 ) return -1;
954     nDef = fuzzRandomInt(nDef);
955     pCsr = p+1;
956     for(i=0; i<pGrp->nCol; i++){
957       if( pCsr[0] && pGrp->aPK[i]==0 ){
958         if( nDef==0 ) iUndef = i;
959         nDef--;
960       }
961       fuzzChangeSize(pCsr, &sz);
962       pCsr += sz;
963     }
964   }
965 
966   /* Copy the change type and indirect flag. If the fuzz mode is
967   ** FUZZ_CHANGE_INDIRECT, and the current change is the one selected for
968   ** fuzzing, invert the indirect flag.  */
969   *(pOut++) = eNew;
970   if( pFuzz->eType==FUZZ_CHANGE_INDIRECT && pFuzz->iCurrent==pFuzz->iChange ){
971     *(pOut++) = !(*(p++));
972   }else{
973     *(pOut++) = *(p++);
974   }
975 
976   for(iRec=0; iRec<nRec; iRec++){
977     int i;
978 
979     /* Copy the next record from the output to the input.
980     */
981     for(i=0; i<pGrp->nCol; i++){
982       int sz;
983       u8 *pCopy = p;
984 
985       /* If this is a patchset, and the input is a DELETE, then the only
986       ** fields present are the PK fields. So, if this is not a PK, skip to
987       ** the next column. If the current fuzz is FUZZ_CHANGE_TYPE, then
988       ** write a randomly selected value to the output.  */
989       if( bPS && eType==SQLITE_DELETE && pGrp->aPK[i]==0 ){
990         if( eType!=eNew ){
991           assert( eNew==SQLITE_UPDATE );
992           do {
993             pCopy = pParse->apVal[fuzzRandomInt(pParse->nVal)];
994           }while( pCopy[0]==0x00 );
995           fuzzChangeSize(pCopy, &sz);
996           memcpy(pOut, pCopy, sz);
997           pOut += sz;
998         }
999         continue;
1000       }
1001 
1002       if( p==pFuzz->pSub1 ){
1003         pCopy = pFuzz->pSub2;
1004       }else if( p==pFuzz->pSub2 ){
1005         pCopy = pFuzz->pSub1;
1006       }else if( i==iUndef ){
1007         pCopy = "\0";
1008       }
1009 
1010       if( pCopy[0]==0x00 && eNew!=eType && eType==SQLITE_UPDATE && iRec==0 ){
1011         while( pCopy[0]==0x00 ){
1012           pCopy = pParse->apVal[fuzzRandomInt(pParse->nVal)];
1013         }
1014       }else if( p[0]==0x00 && pCopy[0]!=0x00 ){
1015         return -1;
1016       }else{
1017         if( pGrp->aPK[i]>0 && pCopy[0]==0x05 ) return -1;
1018       }
1019 
1020       if( (pFuzz->iGroup!=iGrp || i!=pFuzz->iDelete)
1021        && (eNew==eType || eType!=SQLITE_UPDATE || iRec==0)
1022        && (eNew==eType || eNew!=SQLITE_DELETE || !bPS || pGrp->aPK[i])
1023       ){
1024         fuzzChangeSize(pCopy, &sz);
1025         memcpy(pOut, pCopy, sz);
1026         pOut += sz;
1027         nUpdate += (pGrp->aPK[i]==0 && pCopy[0]!=0x00);
1028       }
1029 
1030       fuzzChangeSize(p, &sz);
1031       p += sz;
1032     }
1033 
1034     if( iGrp==pFuzz->iGroup ){
1035       if( pFuzz->eType==FUZZ_COLUMN_ADD ){
1036         if( !bPS || eType!=SQLITE_DELETE ) *(pOut++) = 0x05;
1037       }else if( pFuzz->eType==FUZZ_COLUMN_ADDPK ){
1038         if( iRec==1 ){
1039           *(pOut++) = 0x00;
1040         }else{
1041           u8 *pNew;
1042           int szNew;
1043           do {
1044             pNew = pParse->apVal[fuzzRandomInt(pParse->nVal)];
1045           }while( pNew[0]==0x00 || pNew[0]==0x05 );
1046           fuzzChangeSize(pNew, &szNew);
1047           memcpy(pOut, pNew, szNew);
1048           pOut += szNew;
1049         }
1050       }
1051     }
1052   }
1053 
1054   if( pFuzz->iCurrent==pFuzz->iChange ){
1055     if( pFuzz->eType==FUZZ_CHANGE_DUP ){
1056       int nByte = pOut - (*ppOut);
1057       memcpy(pOut, *ppOut, nByte);
1058       pOut += nByte;
1059     }
1060 
1061     if( pFuzz->eType==FUZZ_CHANGE_DEL ){
1062       pOut = *ppOut;
1063     }
1064     if( eNew!=eType && eNew==SQLITE_UPDATE && !bPS ){
1065       int i;
1066       u8 *pCsr = (*ppOut) + 2;
1067       for(i=0; i<pGrp->nCol; i++){
1068         int sz;
1069         u8 *pCopy = pCsr;
1070         if( pGrp->aPK[i] ) pCopy = "\0";
1071         fuzzChangeSize(pCopy, &sz);
1072         memcpy(pOut, pCopy, sz);
1073         pOut += sz;
1074         fuzzChangeSize(pCsr, &sz);
1075         pCsr += sz;
1076       }
1077     }
1078   }
1079 
1080   /* If a column is being deleted from this group, and this change was an
1081   ** UPDATE, and there are now no non-PK, non-undefined columns in the
1082   ** change, remove it altogether. */
1083   if( pFuzz->eType==FUZZ_COLUMN_DEL && pFuzz->iGroup==iGrp
1084    && eType==SQLITE_UPDATE && nUpdate==0
1085   ){
1086     pOut = *ppOut;
1087   }
1088 
1089   *pp = p;
1090   *ppOut = pOut;
1091   pFuzz->iCurrent += (eType==SQLITE_UPDATE || pFuzz->eType!=FUZZ_CHANGE_FIELD);
1092   return SQLITE_OK;
1093 }
1094 
1095 /*
1096 ** Fuzz the changeset parsed into object pParse and write the results
1097 ** to file zOut on disk. Argument pBuf points to a buffer that is guaranteed
1098 ** to be large enough to hold the fuzzed changeset.
1099 **
1100 ** Return SQLITE_OK if successful, or an SQLite error code if an error occurs.
1101 */
1102 static int fuzzDoOneFuzz(
1103   const char *zOut,               /* Filename to write modified changeset to */
1104   u8 *pBuf,                       /* Buffer to use for modified changeset */
1105   FuzzChangeset *pParse           /* Parse of input changeset */
1106 ){
1107   FuzzChange change;
1108   int iGrp;
1109   int rc = -1;
1110 
1111   while( rc<0 ){
1112     u8 *pOut = pBuf;
1113     rc = fuzzSelectChange(pParse, &change);
1114     for(iGrp=0; rc==SQLITE_OK && iGrp<pParse->nGroup; iGrp++){
1115       FuzzChangesetGroup *pGrp = pParse->apGroup[iGrp];
1116       int nTab = strlen(pGrp->zTab) + 1;
1117       int j;
1118       int nRep = 1;
1119 
1120       /* If this is the group to delete for a FUZZ_GROUP_DEL change, jump to
1121       ** the next group. Unless this is the only group in the changeset - in
1122       ** that case this change cannot be applied.
1123       **
1124       ** Or, if this is a FUZZ_GROUP_DUP, set nRep to 2 to output two
1125       ** copies of the group. */
1126       if( change.iGroup==iGrp ){
1127         if( change.eType==FUZZ_GROUP_DEL ){
1128           if( pParse->nGroup==1 ) rc = -1;
1129           continue;
1130         }
1131         else if( change.eType==FUZZ_GROUP_DUP ){
1132           nRep = 2;
1133         }
1134       }
1135 
1136       for(j=0; j<nRep; j++){
1137         int i;
1138         u8 *pSaved;
1139         u8 *p = pGrp->aChange;
1140         int nCol = pGrp->nCol;
1141         int iPKDel = 0;
1142         if( iGrp==change.iGroup ){
1143           if( change.eType==FUZZ_COLUMN_ADD
1144            || change.eType==FUZZ_COLUMN_ADDPK
1145           ){
1146             nCol++;
1147           }else if( change.eType==FUZZ_COLUMN_DEL ){
1148             nCol--;
1149             iPKDel = pGrp->aPK[change.iDelete];
1150           }
1151         }
1152 
1153         /* Output a table header */
1154         pOut++[0] = pParse->bPatchset ? 'P' : 'T';
1155         pOut += fuzzPutVarint(pOut, nCol);
1156 
1157         for(i=0; i<pGrp->nCol; i++){
1158           if( iGrp!=change.iGroup || i!=change.iDelete ){
1159             u8 v = pGrp->aPK[i];
1160             if( iPKDel && v>iPKDel ) v--;
1161             *(pOut++) = v;
1162           }
1163         }
1164         if( nCol>pGrp->nCol ){
1165           if( change.eType==FUZZ_COLUMN_ADD ){
1166             *(pOut++) = 0x00;
1167           }else{
1168             u8 max = 0;
1169             for(i=0; i<pGrp->nCol; i++){
1170               if( pGrp->aPK[i]>max ) max = pGrp->aPK[i];
1171             }
1172             *(pOut++) = max+1;
1173           }
1174         }
1175         memcpy(pOut, pGrp->zTab, nTab);
1176         pOut += nTab;
1177 
1178         /* Output the change array. */
1179         pSaved = pOut;
1180         for(i=0; rc==SQLITE_OK && i<pGrp->nChange; i++){
1181           rc = fuzzCopyChange(pParse, iGrp, &change, &p, &pOut);
1182         }
1183         if( pOut==pSaved ) rc = -1;
1184       }
1185     }
1186     if( rc==SQLITE_OK ){
1187       fuzzWriteFile(zOut, pBuf, pOut-pBuf);
1188     }
1189   }
1190 
1191   return rc;
1192 }
1193 
1194 int main(int argc, char **argv){
1195   int nRepeat = 0;                /* Number of output files */
1196   int iSeed = 0;                  /* Value of PRNG seed */
1197   const char *zInput;             /* Name of input file */
1198   void *pChangeset = 0;           /* Input changeset */
1199   int nChangeset = 0;             /* Size of input changeset in bytes */
1200   int i;                          /* Current output file */
1201   FuzzChangeset changeset;        /* Partially parsed changeset */
1202   int rc;
1203   u8 *pBuf = 0;
1204 
1205   if( argc!=4 && argc!=2 ) usage(argv[0]);
1206   zInput = argv[1];
1207 
1208   fuzzReadFile(zInput, &nChangeset, &pChangeset);
1209   rc = fuzzParseChangeset(pChangeset, nChangeset, &changeset);
1210 
1211   if( rc==SQLITE_OK ){
1212     if( argc==2 ){
1213       for(i=0; i<changeset.nGroup; i++){
1214         fuzzPrintGroup(&changeset, changeset.apGroup[i]);
1215       }
1216     }else{
1217       pBuf = (u8*)fuzzMalloc(nChangeset*2 + 1024);
1218       if( pBuf==0 ){
1219         rc = SQLITE_NOMEM;
1220       }else{
1221         iSeed = atoi(argv[2]);
1222         nRepeat = atoi(argv[3]);
1223         fuzzRandomSeed((unsigned int)iSeed);
1224         for(i=0; rc==SQLITE_OK && i<nRepeat; i++){
1225           char *zOut = sqlite3_mprintf("%s-%d", zInput, i);
1226           rc = fuzzDoOneFuzz(zOut, pBuf, &changeset);
1227           sqlite3_free(zOut);
1228         }
1229         fuzzFree(pBuf);
1230       }
1231     }
1232   }
1233 
1234   if( rc!=SQLITE_OK ){
1235     fprintf(stderr, "error while processing changeset: %d\n", rc);
1236   }
1237 
1238   return rc;
1239 }
1240 
1241