SQLite

Check-in [90b7b957f8]
Login

Many hyperlinks are disabled.
Use anonymous login to enable hyperlinks.

Overview
Comment:Change the way the fuzzer (test_fuzzer.c) works so that it loads its configuration from a database table.
Downloads: Tarball | ZIP archive
Timelines: family | ancestors | descendants | both | trunk
Files: files | file ages | folders
SHA1: 90b7b957f8933047fd2878048dfa3ec4891988b8
User & Date: dan 2012-02-20 20:03:48.835
Context
2012-02-20
22:44
Updates to the instructions in the header comment of the fuzzer implementation. New test cases for the fuzzer. (check-in: bf1dc7907c user: drh tags: trunk)
20:03
Change the way the fuzzer (test_fuzzer.c) works so that it loads its configuration from a database table. (check-in: 90b7b957f8 user: dan tags: trunk)
19:36
Fix a case in test_fuzzer.c causing transformations from the wrong ruleset to be applied in some cases. (check-in: cb5f5ebc56 user: dan tags: trunk)
Changes
Unified Diff Ignore Whitespace Patch
Changes to src/test_fuzzer.c.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23





24
25
26

27



28
29

30



31

32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47



48




49
50
51
52
53
54
55
56
/*
** 2011 March 24
**
** 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.
**
*************************************************************************
**
** Code for demonstartion virtual table that generates variations
** on an input word at increasing edit distances from the original.
**
** A fuzzer virtual table is created like this:
**
**     CREATE VIRTUAL TABLE temp.f USING fuzzer;
**
** The name of the new virtual table in the example above is "f".
** Note that all fuzzer virtual tables must be TEMP tables.  The
** "temp." prefix in front of the table name is required when the
** table is being created.  The "temp." prefix can be omitted when





** using the table as long as the name is unambiguous.
**
** Before being used, the fuzzer needs to be programmed by giving it

** character transformations and a cost associated with each transformation.



** Examples:
**

**    INSERT INTO f(cFrom,cTo,Cost) VALUES('','a',100);



**

** The above statement says that the cost of inserting a letter 'a' is
** 100.  (All costs are integers.  We recommend that costs be scaled so
** that the average cost is around 100.)
**
**    INSERT INTO f(cFrom,cTo,Cost) VALUES('b','',87);
**
** The above statement says that the cost of deleting a single letter
** 'b' is 87.
**
**    INSERT INTO f(cFrom,cTo,Cost) VALUES('o','oe',38);
**    INSERT INTO f(cFrom,cTo,Cost) VALUES('oe','o',40);
**
** This third example says that the cost of transforming the single
** letter "o" into the two-letter sequence "oe" is 38 and that the
** cost of transforming "oe" back into "o" is 40.
**



** After all the transformation costs have been set, the fuzzer table




** can be queried as follows:
**
**    SELECT word, distance FROM f
**     WHERE word MATCH 'abcdefg'
**       AND distance<200;
**
** This first query outputs the string "abcdefg" and all strings that
** can be derived from that string by appling the specified transformations.












|




|

|
|
|
|
>
>
>
>
>
|

|
>
|
>
>
>
|

>
|
>
>
>

>
|
|
|
<
<
<
|
<
<
<
<
<
|
|


>
>
>
|
>
>
>
>
|







1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48



49





50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
/*
** 2011 March 24
**
** 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.
**
*************************************************************************
**
** Code for a demonstration virtual table that generates variations
** on an input word at increasing edit distances from the original.
**
** A fuzzer virtual table is created like this:
**
**     CREATE VIRTUAL TABLE f USING fuzzer(<fuzzer-data-table>);
**
** When it is created, the new fuzzer table must be supplied with the
** name of a "fuzzer data table", which must reside in the same database
** file as the new fuzzer table. The fuzzer data table contains the various
** transformations and their costs that the fuzzer logic uses to generate
** variations.
**
** The fuzzer data table must contain exactly four columns (more precisely,
** the statement "SELECT * FROM <fuzzer_data_table>" must return records
** that consist of four columns). It does not matter what the columns are
** named. 
**
** Each row in the fuzzer table represents a single character transformation. 
** The left most column of the row (column 0) contains an integer value -
** the identifier of the ruleset to which the transformation rule belongs
** (see "MULTIPLE RULE SETS" below). The second column of the row (column 0)
** contains the input character or characters. The third column contains the
** output character or characters. And the fourth column contains the integer 
** cost of making the transformation. For example:
**
**    CREATE TABLE f_data(ruleset, cFrom, cTo, Cost);
**    INSERT INTO f_data(ruleset, cFrom, cTo, Cost) VALUES(0, '', 'a', 100);
**    INSERT INTO f_data(ruleset, cFrom, cTo, Cost) VALUES(0, 'b', '', 87);
**    INSERT INTO f_data(ruleset, cFrom, cTo, Cost) VALUES(0, 'o', 'oe', 38);
**    INSERT INTO f_data(ruleset, cFrom, cTo, Cost) VALUES(0, 'oe', 'o', 40);
**
** The first row inserted into the fuzzer data table by the SQL script
** above indicates that the cost of inserting a letter 'a' is 100.  (All 
** costs are integers.  We recommend that costs be scaled so that the 
** average cost is around 100.) The second INSERT statement creates a rule



** that the cost of that the cost of deleting a single letter 'b' is 87.





** The third and fourth INSERT statements mean that the cost of transforming 
** a single letter "o" into the two-letter sequence "oe" is 38 and that the
** cost of transforming "oe" back into "o" is 40.
**
** The contents of the fuzzer data table are loaded into main memory when
** a fuzzer table is first created, and may be internally reloaded by the
** system at any subsequent time. Therefore, the fuzzer data table should be 
** populated before the fuzzer table is created and not modified thereafter.
** If you do need to modify the contents of the fuzzer data table, it is
** recommended that the associated fuzzer table be dropped, the fuzzer data
** table edited, and the fuzzer table recreated within a single transaction.
**
** Once it has been created, the fuzzer table can be queried as follows:
**
**    SELECT word, distance FROM f
**     WHERE word MATCH 'abcdefg'
**       AND distance<200;
**
** This first query outputs the string "abcdefg" and all strings that
** can be derived from that string by appling the specified transformations.
92
93
94
95
96
97
98


99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120

121
122
123
124
125
126
127
**    LIMIT 50
**
** This last query will show up to 50 words out of the vocabulary that
** match or nearly match the $prefix.
**
** MULTIPLE RULE SETS
**


** An enhancement as of 2012-02-14 allows multiple rule sets to coexist in
** the same fuzzer.  This allows, for example, the fuzzer to operate in
** multiple languages.
**
** A new column "ruleset" is added to the table.  This column must have a
** value between 0 and 49.  The default value for the ruleset is 0.  But
** alternative values can be specified.  For example:
**
**    INSERT INTO f(ruleset,cFrom,cTo,Cost) VALUES(1,'qu','k',100);
**
** Only one ruleset will be used at a time.  When running a MATCH query,
** specify the desired ruleset using a "ruleset=N" term in the WHERE clause.
** For example:
**
**   SELECT vocabulary.w FROM f, vocabulary
**    WHERE f.word MATCH $word
**      AND f.distance<=200
**      AND f.word=vocabulary.w
**      AND f.ruleset=1  -- Specify the ruleset to use here
**    LIMIT 20
**
** If no ruleset is specified in the WHERE clause, ruleset 0 is used.

*/
#include "sqlite3.h"
#include <stdlib.h>
#include <string.h>
#include <assert.h>
#include <stdio.h>








>
>
|
|


<
|
|
<
<
<
<
|
|








|
>







105
106
107
108
109
110
111
112
113
114
115
116
117

118
119




120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
**    LIMIT 50
**
** This last query will show up to 50 words out of the vocabulary that
** match or nearly match the $prefix.
**
** MULTIPLE RULE SETS
**
** Normally, the "ruleset" value associated with all character transformations
** in the fuzzer data table is zero. However, if required, the fuzzer table
** allows multiple rulesets to be defined. Each query uses only a single
** ruleset. This allows, for example, a single fuzzer table to support 
** multiple languages.
**

** By default, only the rules from ruleset 0 are used. To specify an 
** alternative ruleset, a "ruleset = ?" expression must be added to the




** WHERE clause of a SELECT, where ? is the identifier of the desired 
** ruleset. For example:
**
**   SELECT vocabulary.w FROM f, vocabulary
**    WHERE f.word MATCH $word
**      AND f.distance<=200
**      AND f.word=vocabulary.w
**      AND f.ruleset=1  -- Specify the ruleset to use here
**    LIMIT 20
**
** If no "ruleset = ?" constraint is specified in the WHERE clause, ruleset 
** 0 is used.
*/
#include "sqlite3.h"
#include <stdlib.h>
#include <string.h>
#include <assert.h>
#include <stdio.h>

195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
/* 
** A fuzzer virtual-table object 
*/
struct fuzzer_vtab {
  sqlite3_vtab base;         /* Base class - must be first */
  char *zClassName;          /* Name of this class.  Default: "fuzzer" */
  fuzzer_rule *pRule;        /* All active rules in this fuzzer */
  fuzzer_rule *pNewRule;     /* New rules to add when last cursor expires */
  int nCursor;               /* Number of active cursors */
};

#define FUZZER_HASH  4001    /* Hash table size */
#define FUZZER_NQUEUE  20    /* Number of slots on the stem queue */

/* A fuzzer cursor object */







<







206
207
208
209
210
211
212

213
214
215
216
217
218
219
/* 
** A fuzzer virtual-table object 
*/
struct fuzzer_vtab {
  sqlite3_vtab base;         /* Base class - must be first */
  char *zClassName;          /* Name of this class.  Default: "fuzzer" */
  fuzzer_rule *pRule;        /* All active rules in this fuzzer */

  int nCursor;               /* Number of active cursors */
};

#define FUZZER_HASH  4001    /* Hash table size */
#define FUZZER_NQUEUE  20    /* Number of slots on the stem queue */

/* A fuzzer cursor object */
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
  int nBuf;                  /* Bytes allocated for zBuf */
  int nStem;                 /* Number of stems allocated */
  int iRuleset;              /* Only process rules from this ruleset */
  fuzzer_rule nullRule;      /* Null rule used first */
  fuzzer_stem *apHash[FUZZER_HASH]; /* Hash of previously generated terms */
};

/* Methods for the fuzzer module */
static int fuzzerConnect(
  sqlite3 *db,
  void *pAux,
  int argc, const char *const*argv,
  sqlite3_vtab **ppVtab,
  char **pzErr
){
  fuzzer_vtab *pNew;
  int n;
  if( strcmp(argv[1],"temp")!=0 ){
    *pzErr = sqlite3_mprintf("%s virtual tables must be TEMP", argv[0]);
    return SQLITE_ERROR;
  }
  n = strlen(argv[0]) + 1;
  pNew = sqlite3_malloc( sizeof(*pNew) + n );
  if( pNew==0 ) return SQLITE_NOMEM;
  pNew->zClassName = (char*)&pNew[1];
  memcpy(pNew->zClassName, argv[0], n);
  sqlite3_declare_vtab(db,
     "CREATE TABLE x(word,distance,ruleset,cFrom,cTo,cost)");
  memset(pNew, 0, sizeof(*pNew));
  *ppVtab = &pNew->base;
  return SQLITE_OK;
}
/* Note that for this virtual table, the xCreate and xConnect
** methods are identical. */

static int fuzzerDisconnect(sqlite3_vtab *pVtab){
  fuzzer_vtab *p = (fuzzer_vtab*)pVtab;
  assert( p->nCursor==0 );
  do{
    while( p->pRule ){
      fuzzer_rule *pRule = p->pRule;
      p->pRule = pRule->pNext;
      sqlite3_free(pRule);
    }
    p->pRule = p->pNewRule;
    p->pNewRule = 0;
  }while( p->pRule );
  sqlite3_free(p);
  return SQLITE_OK;
}
/* The xDisconnect and xDestroy methods are also the same */

/*
** The two input rule lists are both sorted in order of increasing
** cost.  Merge them together into a single list, sorted by cost, and
** return a pointer to the head of that list.
*/
static fuzzer_rule *fuzzerMergeRules(fuzzer_rule *pA, fuzzer_rule *pB){
  fuzzer_rule head;







<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<







230
231
232
233
234
235
236













































237
238
239
240
241
242
243
  int nBuf;                  /* Bytes allocated for zBuf */
  int nStem;                 /* Number of stems allocated */
  int iRuleset;              /* Only process rules from this ruleset */
  fuzzer_rule nullRule;      /* Null rule used first */
  fuzzer_stem *apHash[FUZZER_HASH]; /* Hash of previously generated terms */
};














































/*
** The two input rule lists are both sorted in order of increasing
** cost.  Merge them together into a single list, sorted by cost, and
** return a pointer to the head of that list.
*/
static fuzzer_rule *fuzzerMergeRules(fuzzer_rule *pA, fuzzer_rule *pB){
  fuzzer_rule head;
294
295
296
297
298
299
300




















































































































































































































301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
    pTail->pNext = pB;
  }else{
    pTail->pNext = pA;
  }
  return head.pNext;
}






















































































































































































































/*
** Open a new fuzzer cursor.
*/
static int fuzzerOpen(sqlite3_vtab *pVTab, sqlite3_vtab_cursor **ppCursor){
  fuzzer_vtab *p = (fuzzer_vtab*)pVTab;
  fuzzer_cursor *pCur;
  pCur = sqlite3_malloc( sizeof(*pCur) );
  if( pCur==0 ) return SQLITE_NOMEM;
  memset(pCur, 0, sizeof(*pCur));
  pCur->pVtab = p;
  *ppCursor = &pCur->base;
  if( p->nCursor==0 && p->pNewRule ){
    unsigned int i;
    fuzzer_rule *pX;
    fuzzer_rule *a[15];
    for(i=0; i<sizeof(a)/sizeof(a[0]); i++) a[i] = 0;
    while( (pX = p->pNewRule)!=0 ){
      p->pNewRule = pX->pNext;
      pX->pNext = 0;
      for(i=0; a[i] && i<sizeof(a)/sizeof(a[0])-1; i++){
        pX = fuzzerMergeRules(a[i], pX);
        a[i] = 0;
      }
      a[i] = fuzzerMergeRules(a[i], pX);
    }
    for(pX=a[0], i=1; i<sizeof(a)/sizeof(a[0]); i++){
      pX = fuzzerMergeRules(a[i], pX);
    }
    p->pRule = fuzzerMergeRules(p->pRule, pX);
  }
  p->nCursor++;
  return SQLITE_OK;
}

/*
** Free all stems in a list.
*/







>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>












<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<







259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489



















490
491
492
493
494
495
496
    pTail->pNext = pB;
  }else{
    pTail->pNext = pA;
  }
  return head.pNext;
}

/*
** Statement pStmt currently points to a row in the fuzzer data table. This
** function allocates and populates a fuzzer_rule structure according to
** the content of the row.
**
** If successful, *ppRule is set to point to the new object and SQLITE_OK
** is returned. Otherwise, *ppRule is zeroed, *pzErr may be set to point
** to an error message and an SQLite error code returned.
*/
static int fuzzerLoadOneRule(
  fuzzer_vtab *p,                 /* Fuzzer virtual table handle */
  sqlite3_stmt *pStmt,            /* Base rule on statements current row */
  fuzzer_rule **ppRule,           /* OUT: New rule object */
  char **pzErr                    /* OUT: Error message */
){
  int iRuleset = sqlite3_column_int(pStmt, 0);
  const char *zFrom = (const char *)sqlite3_column_text(pStmt, 1);
  const char *zTo = (const char *)sqlite3_column_text(pStmt, 2);
  int nCost = sqlite3_column_int(pStmt, 3);

  int rc = SQLITE_OK;             /* Return code */
  int nFrom;                      /* Size of string zFrom, in bytes */
  int nTo;                        /* Size of string zTo, in bytes */
  fuzzer_rule *pRule = 0;         /* New rule object to return */

  if( zFrom==0 ) zFrom = "";
  if( zTo==0 ) zTo = "";
  nFrom = strlen(zFrom);
  nTo = strlen(zTo);

  /* Silently ignore null transformations */
  if( strcmp(zFrom, zTo)==0 ){
    *ppRule = 0;
    return SQLITE_OK;
  }

  if( nCost<=0 || nCost>FUZZER_MX_COST ){
    *pzErr = sqlite3_mprintf("cost must be between 1 and %d", FUZZER_MX_COST);
    rc = SQLITE_ERROR;
  }else
  if( nFrom>FUZZER_MX_LENGTH || nTo>FUZZER_MX_LENGTH ){
    *pzErr = sqlite3_mprintf("maximum string length is %d", FUZZER_MX_LENGTH);
    rc = SQLITE_ERROR;    
  }else
  if( iRuleset<0 || iRuleset>FUZZER_MX_RULEID ){
    *pzErr = sqlite3_mprintf(
        "ruleset must be between 0 and %d", FUZZER_MX_RULEID);
    rc = SQLITE_ERROR;    
  }else{

    pRule = sqlite3_malloc( sizeof(*pRule) + nFrom + nTo );
    if( pRule==0 ){
      rc = SQLITE_NOMEM;
    }else{
      memset(pRule, 0, sizeof(*pRule));
      pRule->zFrom = &pRule->zTo[nTo+1];
      pRule->nFrom = nFrom;
      memcpy(pRule->zFrom, zFrom, nFrom+1);
      memcpy(pRule->zTo, zTo, nTo+1);
      pRule->nTo = nTo;
      pRule->rCost = nCost;
      pRule->iRuleset = iRuleset;
    }
  }

  *ppRule = pRule;
  return rc;
}

/*
** Load the content of the fuzzer data table into memory.
*/
static int fuzzerLoadRules(
  sqlite3 *db,                    /* Database handle */
  fuzzer_vtab *p,                 /* Virtual fuzzer table to configure */
  const char *zDb,                /* Database containing rules data */
  const char *zData,              /* Table containing rules data */
  char **pzErr                    /* OUT: Error message */
){
  int rc = SQLITE_OK;             /* Return code */
  char *zSql;                     /* SELECT used to read from rules table */
  fuzzer_rule *pHead = 0;

  zSql = sqlite3_mprintf("SELECT * FROM %Q.%Q", zDb, zData);
  if( zSql==0 ){
    rc = SQLITE_NOMEM;
  }else{
    int rc2;                      /* finalize() return code */
    sqlite3_stmt *pStmt = 0;
    rc = sqlite3_prepare_v2(db, zSql, -1, &pStmt, 0);
    if( rc!=SQLITE_OK ){
      *pzErr = sqlite3_mprintf("%s: %s", p->zClassName, sqlite3_errmsg(db));
    }else if( sqlite3_column_count(pStmt)!=4 ){
      *pzErr = sqlite3_mprintf("%s: %s has %d columns, expected 4",
          p->zClassName, zData, sqlite3_column_count(pStmt)
      );
      rc = SQLITE_ERROR;
    }else{
      while( rc==SQLITE_OK && SQLITE_ROW==sqlite3_step(pStmt) ){
        fuzzer_rule *pRule = 0;
        rc = fuzzerLoadOneRule(p, pStmt, &pRule, pzErr);
        if( pRule ){
          pRule->pNext = pHead;
          pHead = pRule;
        }
      }
    }
    rc2 = sqlite3_finalize(pStmt);
    if( rc==SQLITE_OK ) rc = rc2;
  }
  sqlite3_free(zSql);

  /* All rules are now in a singly linked list starting at pHead. This
  ** block sorts them by cost and then sets fuzzer_vtab.pRule to point to 
  ** point to the head of the sorted list.
  */
  if( rc==SQLITE_OK ){
    unsigned int i;
    fuzzer_rule *pX;
    fuzzer_rule *a[15];
    for(i=0; i<sizeof(a)/sizeof(a[0]); i++) a[i] = 0;
    while( (pX = pHead)!=0 ){
      pHead = pX->pNext;
      pX->pNext = 0;
      for(i=0; a[i] && i<sizeof(a)/sizeof(a[0])-1; i++){
        pX = fuzzerMergeRules(a[i], pX);
        a[i] = 0;
      }
      a[i] = fuzzerMergeRules(a[i], pX);
    }
    for(pX=a[0], i=1; i<sizeof(a)/sizeof(a[0]); i++){
      pX = fuzzerMergeRules(a[i], pX);
    }
    p->pRule = fuzzerMergeRules(p->pRule, pX);
  }else{
    /* An error has occurred. Setting p->pRule to point to the head of the
    ** allocated list ensures that the list will be cleaned up in this case.
    */
    assert( p->pRule==0 );
    p->pRule = pHead;
  }

  return rc;
}


/*
** xConnect/xCreate method for the fuzzer module. Arguments are:
**
**   argv[0]   -> module name  ("fuzzer")
**   argv[1]   -> database name
**   argv[2]   -> table name
**   argv[3]   -> fuzzer rule table name
*/
static int fuzzerConnect(
  sqlite3 *db,
  void *pAux,
  int argc, const char *const*argv,
  sqlite3_vtab **ppVtab,
  char **pzErr
){
  int rc = SQLITE_OK;             /* Return code */
  fuzzer_vtab *pNew = 0;          /* New virtual table */
  const char *zModule = argv[0];
  const char *zDb = argv[1];

  if( argc!=4 ){
    *pzErr = sqlite3_mprintf(
        "%s: wrong number of CREATE VIRTUAL TABLE arguments", zModule
    );
    rc = SQLITE_ERROR;
  }else{
    int nModule;                  /* Length of zModule, in bytes */

    nModule = strlen(zModule);
    pNew = sqlite3_malloc( sizeof(*pNew) + nModule + 1);
    if( pNew==0 ){
      rc = SQLITE_NOMEM;
    }else{
      memset(pNew, 0, sizeof(*pNew));
      pNew->zClassName = (char*)&pNew[1];
      memcpy(pNew->zClassName, zModule, nModule+1);

      rc = fuzzerLoadRules(db, pNew, zDb, argv[3], pzErr);
      if( rc==SQLITE_OK ){
        sqlite3_declare_vtab(db, "CREATE TABLE x(word, distance,ruleset)");
      }else{
        sqlite3_free(pNew);
        pNew = 0;
      }
    }
  }

  *ppVtab = (sqlite3_vtab *)pNew;
  return rc;
}
/* Note that for this virtual table, the xCreate and xConnect
** methods are identical. */

static int fuzzerDisconnect(sqlite3_vtab *pVtab){
  fuzzer_vtab *p = (fuzzer_vtab*)pVtab;
  assert( p->nCursor==0 );
  while( p->pRule ){
    fuzzer_rule *pRule = p->pRule;
    p->pRule = pRule->pNext;
    sqlite3_free(pRule);
  }
  sqlite3_free(p);
  return SQLITE_OK;
}
/* The xDisconnect and xDestroy methods are also the same */


/*
** Open a new fuzzer cursor.
*/
static int fuzzerOpen(sqlite3_vtab *pVTab, sqlite3_vtab_cursor **ppCursor){
  fuzzer_vtab *p = (fuzzer_vtab*)pVTab;
  fuzzer_cursor *pCur;
  pCur = sqlite3_malloc( sizeof(*pCur) );
  if( pCur==0 ) return SQLITE_NOMEM;
  memset(pCur, 0, sizeof(*pCur));
  pCur->pVtab = p;
  *ppCursor = &pCur->base;



















  p->nCursor++;
  return SQLITE_OK;
}

/*
** Free all stems in a list.
*/
475
476
477
478
479
480
481

482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
/*
** Advance a fuzzer_stem to its next value.   Return 0 if there are
** no more values that can be generated by this fuzzer_stem.  Return
** -1 on a memory allocation failure.
*/
static int fuzzerAdvance(fuzzer_cursor *pCur, fuzzer_stem *pStem){
  const fuzzer_rule *pRule;

  while( (pRule = pStem->pRule)!=0 ){
    assert( pRule==&pCur->nullRule || pRule->iRuleset==pCur->iRuleset );
    while( pStem->n < pStem->nBasis - pRule->nFrom ){
      pStem->n++;
      if( pRule->nFrom==0
       || memcmp(&pStem->zBasis[pStem->n], pRule->zFrom, pRule->nFrom)==0
      ){
        /* Found a rewrite case.  Make sure it is not a duplicate */
        int rc = fuzzerSeen(pCur, pStem);
        if( rc<0 ) return -1;
        if( rc==0 ){
          fuzzerCost(pStem);
          return 1;
        }
      }
    }
    pStem->n = -1;
    do{
      pRule = pRule->pNext;
    }while( pRule && pRule->iRuleset!=pCur->iRuleset );
    pStem->pRule = pRule;
    if( pRule && fuzzerCost(pStem)>pCur->rLimit ) pStem->pRule = 0;
  }
  return 0;
}

/*







>



















|







633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
/*
** Advance a fuzzer_stem to its next value.   Return 0 if there are
** no more values that can be generated by this fuzzer_stem.  Return
** -1 on a memory allocation failure.
*/
static int fuzzerAdvance(fuzzer_cursor *pCur, fuzzer_stem *pStem){
  const fuzzer_rule *pRule;
  const int iSet = pCur->iRuleset;
  while( (pRule = pStem->pRule)!=0 ){
    assert( pRule==&pCur->nullRule || pRule->iRuleset==pCur->iRuleset );
    while( pStem->n < pStem->nBasis - pRule->nFrom ){
      pStem->n++;
      if( pRule->nFrom==0
       || memcmp(&pStem->zBasis[pStem->n], pRule->zFrom, pRule->nFrom)==0
      ){
        /* Found a rewrite case.  Make sure it is not a duplicate */
        int rc = fuzzerSeen(pCur, pStem);
        if( rc<0 ) return -1;
        if( rc==0 ){
          fuzzerCost(pStem);
          return 1;
        }
      }
    }
    pStem->n = -1;
    do{
      pRule = pRule->pNext;
    }while( pRule && pRule->iRuleset!=iSet );
    pStem->pRule = pRule;
    if( pRule && fuzzerCost(pStem)>pCur->rLimit ) pStem->pRule = 0;
  }
  return 0;
}

/*
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
    pIdxInfo->orderByConsumed = 1;
  }
  pIdxInfo->estimatedCost = (double)10000;
   
  return SQLITE_OK;
}

/*
** Disallow all attempts to DELETE or UPDATE.  Only INSERTs are allowed.
**
** On an insert, the cFrom, cTo, and cost columns are used to construct
** a new rule.   All other columns are ignored.  The rule is ignored
** if cFrom and cTo are identical.  A NULL value for cFrom or cTo is
** interpreted as an empty string.  The cost must be positive.
*/
static int fuzzerUpdate(
  sqlite3_vtab *pVTab,
  int argc,
  sqlite3_value **argv,
  sqlite_int64 *pRowid
){
  fuzzer_vtab *p = (fuzzer_vtab*)pVTab;
  fuzzer_rule *pRule;
  const char *zFrom;
  int nFrom;
  const char *zTo;
  int nTo;
  fuzzer_cost rCost;
  int rulesetId;
  if( argc!=8 ){
    sqlite3_free(pVTab->zErrMsg);
    pVTab->zErrMsg = sqlite3_mprintf("cannot delete from a %s virtual table",
                                     p->zClassName);
    return SQLITE_CONSTRAINT;
  }
  if( sqlite3_value_type(argv[0])!=SQLITE_NULL ){
    sqlite3_free(pVTab->zErrMsg);
    pVTab->zErrMsg = sqlite3_mprintf("cannot update a %s virtual table",
                                     p->zClassName);
    return SQLITE_CONSTRAINT;
  }
  zFrom = (char*)sqlite3_value_text(argv[5]);
  if( zFrom==0 ) zFrom = "";
  zTo = (char*)sqlite3_value_text(argv[6]);
  if( zTo==0 ) zTo = "";
  if( strcmp(zFrom,zTo)==0 ){
    /* Silently ignore null transformations */
    return SQLITE_OK;
  }
  rCost = sqlite3_value_int(argv[7]);
  if( rCost<=0 || rCost>FUZZER_MX_COST ){
    sqlite3_free(pVTab->zErrMsg);
    pVTab->zErrMsg = sqlite3_mprintf("cost must be between 1 and %d",
                                     FUZZER_MX_COST);
    return SQLITE_CONSTRAINT;    
  }
  nFrom = strlen(zFrom);
  nTo = strlen(zTo);
  if( nFrom>FUZZER_MX_LENGTH || nTo>FUZZER_MX_LENGTH ){
    sqlite3_free(pVTab->zErrMsg);
    pVTab->zErrMsg = sqlite3_mprintf("maximum string length is %d",
                                     FUZZER_MX_LENGTH);
    return SQLITE_CONSTRAINT;    
  }
  rulesetId = sqlite3_value_int(argv[4]);
  if( rulesetId<0 || rulesetId>FUZZER_MX_RULEID ){
    sqlite3_free(pVTab->zErrMsg);
    pVTab->zErrMsg = sqlite3_mprintf("rulesetid must be between 0 and %d",
                                     FUZZER_MX_RULEID);
    return SQLITE_CONSTRAINT;    
  }
  pRule = sqlite3_malloc( sizeof(*pRule) + nFrom + nTo );
  if( pRule==0 ){
    return SQLITE_NOMEM;
  }
  pRule->zFrom = &pRule->zTo[nTo+1];
  pRule->nFrom = nFrom;
  memcpy(pRule->zFrom, zFrom, nFrom+1);
  memcpy(pRule->zTo, zTo, nTo+1);
  pRule->nTo = nTo;
  pRule->rCost = rCost;
  pRule->pNext = p->pNewRule;
  pRule->iRuleset = rulesetId;
  p->pNewRule = pRule;
  return SQLITE_OK;
}

/*
** A virtual table module that provides read-only access to a
** Tcl global variable namespace.
*/
static sqlite3_module fuzzerModule = {
  0,                           /* iVersion */
  fuzzerConnect,
  fuzzerConnect,
  fuzzerBestIndex,
  fuzzerDisconnect, 
  fuzzerDisconnect,
  fuzzerOpen,                  /* xOpen - open a cursor */
  fuzzerClose,                 /* xClose - close a cursor */
  fuzzerFilter,                /* xFilter - configure scan constraints */
  fuzzerNext,                  /* xNext - advance a cursor */
  fuzzerEof,                   /* xEof - check for end of scan */
  fuzzerColumn,                /* xColumn - read data */
  fuzzerRowid,                 /* xRowid - read data */
  fuzzerUpdate,                /* xUpdate - INSERT */
  0,                           /* xBegin */
  0,                           /* xSync */
  0,                           /* xCommit */
  0,                           /* xRollback */
  0,                           /* xFindMethod */
  0,                           /* xRename */
};







<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<


















|







1019
1020
1021
1022
1023
1024
1025
















































































1026
1027
1028
1029
1030
1031
1032
1033
1034
1035
1036
1037
1038
1039
1040
1041
1042
1043
1044
1045
1046
1047
1048
1049
1050
1051
    pIdxInfo->orderByConsumed = 1;
  }
  pIdxInfo->estimatedCost = (double)10000;
   
  return SQLITE_OK;
}

















































































/*
** A virtual table module that provides read-only access to a
** Tcl global variable namespace.
*/
static sqlite3_module fuzzerModule = {
  0,                           /* iVersion */
  fuzzerConnect,
  fuzzerConnect,
  fuzzerBestIndex,
  fuzzerDisconnect, 
  fuzzerDisconnect,
  fuzzerOpen,                  /* xOpen - open a cursor */
  fuzzerClose,                 /* xClose - close a cursor */
  fuzzerFilter,                /* xFilter - configure scan constraints */
  fuzzerNext,                  /* xNext - advance a cursor */
  fuzzerEof,                   /* xEof - check for end of scan */
  fuzzerColumn,                /* xColumn - read data */
  fuzzerRowid,                 /* xRowid - read data */
  0,                           /* xUpdate */
  0,                           /* xBegin */
  0,                           /* xSync */
  0,                           /* xCommit */
  0,                           /* xRollback */
  0,                           /* xFindMethod */
  0,                           /* xRename */
};
Changes to test/fuzzer1.test.
19
20
21
22
23
24
25





26
27

28
29















30

31
32




33









34
35
36
37

38
39
40
41
42




43
44
45
46
47
48
49
50
51


52

53
54
55




56
57
58
59
60




61
62
63
64
65



66
67
68
69
70
71
72
73
74

ifcapable !vtab {
  finish_test
  return
}

register_fuzzer_module db





do_test fuzzer1-1.0 {
  catchsql {CREATE VIRTUAL TABLE fault1 USING fuzzer;}

} {1 {fuzzer virtual tables must be TEMP}}
do_test fuzzer1-1.1 {















  db eval {CREATE VIRTUAL TABLE temp.f1 USING fuzzer;}

} {}
do_test fuzzer1-1.2 {




  db eval {









    INSERT INTO f1(cfrom, cto, cost) VALUES('e','a',1);
    INSERT INTO f1(cfrom, cto, cost) VALUES('a','e',10);
    INSERT INTO f1(cfrom, cto, cost) VALUES('e','o',100);
  }

} {}

do_test fuzzer1-1.3 {
  db eval {
    SELECT word, distance FROM f1 WHERE word MATCH 'abcde'




  }
} {abcde 0 abcda 1 ebcde 10 ebcda 11 abcdo 100 ebcdo 110 obcde 110 obcda 111 obcdo 210}

do_test fuzzer1-1.4 {
  db eval {
    INSERT INTO f1(ruleset, cfrom, cto, cost) VALUES(1,'b','x',1);
    INSERT INTO f1(ruleset, cfrom, cto, cost) VALUES(1,'d','y',10);
    INSERT INTO f1(ruleset, cfrom, cto, cost) VALUES(1,'y','z',100);
  }


} {}

do_test fuzzer1-1.5 {
  db eval {
    SELECT word, distance FROM f1 WHERE word MATCH 'abcde'




  }
} {abcde 0 abcda 1 ebcde 10 ebcda 11 abcdo 100 ebcdo 110 obcde 110 obcda 111 obcdo 210}
do_test fuzzer1-1.6 {
  db eval {
    SELECT word, distance FROM f1 WHERE word MATCH 'abcde' AND ruleset=0




  }
} {abcde 0 abcda 1 ebcde 10 ebcda 11 abcdo 100 ebcdo 110 obcde 110 obcda 111 obcdo 210}
do_test fuzzer1-1.7 {
  db eval {
    SELECT word, distance FROM f1 WHERE word MATCH 'abcde' AND ruleset=1



  }
} {abcde 0 axcde 1 abcye 10 axcye 11 abcze 110 axcze 111}
do_test fuzzer1-1.8 {
  db eval {
    SELECT word, distance FROM f1 WHERE word MATCH 'abcde' AND distance<100
  }
} {abcde 0 abcda 1 ebcde 10 ebcda 11}
do_test fuzzer1-1.9 {
  db eval {







>
>
>
>
>
|
|
>
|
|
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
|
>
|
|
>
>
>
>
|
>
>
>
>
>
>
>
>
>
|
|
|
|
>


|
<

>
>
>
>
|
<

|
<
|
|
|
|
>
>

>
|
<
|
>
>
>
>
|
|
|
<
|
>
>
>
>
|
|
|
<
|
>
>
>
|
|







19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76

77
78
79
80
81
82

83
84

85
86
87
88
89
90
91
92
93

94
95
96
97
98
99
100
101

102
103
104
105
106
107
108
109

110
111
112
113
114
115
116
117
118
119
120
121
122

ifcapable !vtab {
  finish_test
  return
}

register_fuzzer_module db



# Check configuration errors.
#
do_catchsql_test fuzzer1-1.1 {
  CREATE VIRTUAL TABLE f USING fuzzer;
} {1 {fuzzer: wrong number of CREATE VIRTUAL TABLE arguments}}

do_catchsql_test fuzzer1-1.2 {
  CREATE VIRTUAL TABLE f USING fuzzer(one, two);
} {1 {fuzzer: wrong number of CREATE VIRTUAL TABLE arguments}}

do_catchsql_test fuzzer1-1.3 {
  CREATE VIRTUAL TABLE f USING fuzzer(nosuchtable);
} {1 {fuzzer: no such table: main.nosuchtable}}

do_catchsql_test fuzzer1-1.4 {
  CREATE TEMP TABLE nosuchtable(a, b, c, d);
  CREATE VIRTUAL TABLE f USING fuzzer(nosuchtable);
} {1 {fuzzer: no such table: main.nosuchtable}}

do_catchsql_test fuzzer1-1.5 {
  DROP TABLE temp.nosuchtable;
  CREATE TABLE nosuchtable(a, b, c, d);
  CREATE VIRTUAL TABLE temp.f USING fuzzer(nosuchtable);
} {1 {fuzzer: no such table: temp.nosuchtable}}

do_catchsql_test fuzzer1-1.6 {
  DROP TABLE IF EXISTS f_rules;
  CREATE TABLE f_rules(a, b, c);
  CREATE VIRTUAL TABLE f USING fuzzer(f_rules);
} {1 {fuzzer: f_rules has 3 columns, expected 4}}

do_catchsql_test fuzzer1-1.7 {
  DROP TABLE IF EXISTS f_rules;
  CREATE TABLE f_rules(a, b, c, d, e);
  CREATE VIRTUAL TABLE f USING fuzzer(f_rules);
} {1 {fuzzer: f_rules has 5 columns, expected 4}}


do_execsql_test fuzzer1-2.1 {
  CREATE TABLE f1_rules(ruleset DEFAULT 0, cfrom, cto, cost);
  INSERT INTO f1_rules(cfrom, cto, cost) VALUES('e','a',1);
  INSERT INTO f1_rules(cfrom, cto, cost) VALUES('a','e',10);
  INSERT INTO f1_rules(cfrom, cto, cost) VALUES('e','o',100);

  CREATE VIRTUAL TABLE f1 USING fuzzer(f1_rules);
} {}

do_execsql_test fuzzer1-2.1 {

    SELECT word, distance FROM f1 WHERE word MATCH 'abcde'
} {
  abcde 0   abcda 1   ebcde 10 
  ebcda 11  abcdo 100 ebcdo 110 
  obcde 110 obcda 111 obcdo 210
}


do_execsql_test fuzzer1-2.4 {

  INSERT INTO f1_rules(ruleset, cfrom, cto, cost) VALUES(1,'b','x',1);
  INSERT INTO f1_rules(ruleset, cfrom, cto, cost) VALUES(1,'d','y',10);
  INSERT INTO f1_rules(ruleset, cfrom, cto, cost) VALUES(1,'y','z',100);

  DROP TABLE f1;
  CREATE VIRTUAL TABLE f1 USING fuzzer(f1_rules);
} {}

do_execsql_test fuzzer1-2.5 {

  SELECT word, distance FROM f1 WHERE word MATCH 'abcde'
} {
  abcde 0   abcda 1   ebcde 10 
  ebcda 11  abcdo 100 ebcdo 110 
  obcde 110 obcda 111 obcdo 210
}

do_execsql_test fuzzer1-2.6 {

  SELECT word, distance FROM f1 WHERE word MATCH 'abcde' AND ruleset=0
} {
  abcde 0   abcda 1   ebcde 10 
  ebcda 11  abcdo 100 ebcdo 110 
  obcde 110 obcda 111 obcdo 210
}

do_execsql_test fuzzer1-2.7 {

  SELECT word, distance FROM f1 WHERE word MATCH 'abcde' AND ruleset=1
} {
  abcde 0 axcde 1 abcye 10 
  axcye 11 abcze 110 axcze 111
}

do_test fuzzer1-1.8 {
  db eval {
    SELECT word, distance FROM f1 WHERE word MATCH 'abcde' AND distance<100
  }
} {abcde 0 abcda 1 ebcde 10 ebcda 11}
do_test fuzzer1-1.9 {
  db eval {
98
99
100
101
102
103
104
105
106

107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175


176
177
178
179
180
181
182
    SELECT word, distance FROM f1
    WHERE word MATCH 'abcde' AND distance<=11 AND ruleset=1
  }
} {abcde 0 axcde 1 abcye 10 axcye 11}

do_test fuzzer1-2.0 {
  execsql {
    CREATE VIRTUAL TABLE temp.f2 USING fuzzer;
    -- costs based on English letter frequencies

    INSERT INTO f2(cFrom,cTo,cost) VALUES('a','e',24);
    INSERT INTO f2(cFrom,cTo,cost) VALUES('a','o',47);
    INSERT INTO f2(cFrom,cTo,cost) VALUES('a','u',50);
    INSERT INTO f2(cFrom,cTo,cost) VALUES('e','a',23);
    INSERT INTO f2(cFrom,cTo,cost) VALUES('e','i',33);
    INSERT INTO f2(cFrom,cTo,cost) VALUES('e','o',37);
    INSERT INTO f2(cFrom,cTo,cost) VALUES('i','e',33);
    INSERT INTO f2(cFrom,cTo,cost) VALUES('i','y',33);
    INSERT INTO f2(cFrom,cTo,cost) VALUES('o','a',41);
    INSERT INTO f2(cFrom,cTo,cost) VALUES('o','e',46);
    INSERT INTO f2(cFrom,cTo,cost) VALUES('o','u',57);
    INSERT INTO f2(cFrom,cTo,cost) VALUES('u','o',58);
    INSERT INTO f2(cFrom,cTo,cost) VALUES('y','i',33);

    INSERT INTO f2(cFrom,cTo,cost) VALUES('t','th',70);
    INSERT INTO f2(cFrom,cTo,cost) VALUES('th','t',66);

 
    INSERT INTO f2(cFrom,cTo,cost) VALUES('a','',84);
    INSERT INTO f2(cFrom,cTo,cost) VALUES('','b',106);
    INSERT INTO f2(cFrom,cTo,cost) VALUES('b','',106);
    INSERT INTO f2(cFrom,cTo,cost) VALUES('','c',94);
    INSERT INTO f2(cFrom,cTo,cost) VALUES('c','',94);
    INSERT INTO f2(cFrom,cTo,cost) VALUES('','d',89);
    INSERT INTO f2(cFrom,cTo,cost) VALUES('d','',89);
    INSERT INTO f2(cFrom,cTo,cost) VALUES('','e',83);
    INSERT INTO f2(cFrom,cTo,cost) VALUES('e','',83);
    INSERT INTO f2(cFrom,cTo,cost) VALUES('','f',97);
    INSERT INTO f2(cFrom,cTo,cost) VALUES('f','',97);
    INSERT INTO f2(cFrom,cTo,cost) VALUES('','g',99);
    INSERT INTO f2(cFrom,cTo,cost) VALUES('g','',99);
    INSERT INTO f2(cFrom,cTo,cost) VALUES('','h',86);
    INSERT INTO f2(cFrom,cTo,cost) VALUES('h','',86);
    INSERT INTO f2(cFrom,cTo,cost) VALUES('','i',85);
    INSERT INTO f2(cFrom,cTo,cost) VALUES('i','',85);
    INSERT INTO f2(cFrom,cTo,cost) VALUES('','j',120);
    INSERT INTO f2(cFrom,cTo,cost) VALUES('j','',120);
    INSERT INTO f2(cFrom,cTo,cost) VALUES('','k',120);
    INSERT INTO f2(cFrom,cTo,cost) VALUES('k','',120);
    INSERT INTO f2(cFrom,cTo,cost) VALUES('','l',89);
    INSERT INTO f2(cFrom,cTo,cost) VALUES('l','',89);
    INSERT INTO f2(cFrom,cTo,cost) VALUES('','m',96);
    INSERT INTO f2(cFrom,cTo,cost) VALUES('m','',96);
    INSERT INTO f2(cFrom,cTo,cost) VALUES('','n',85);
    INSERT INTO f2(cFrom,cTo,cost) VALUES('n','',85);
    INSERT INTO f2(cFrom,cTo,cost) VALUES('','o',85);
    INSERT INTO f2(cFrom,cTo,cost) VALUES('o','',85);
    INSERT INTO f2(cFrom,cTo,cost) VALUES('','p',100);
    INSERT INTO f2(cFrom,cTo,cost) VALUES('p','',100);
    INSERT INTO f2(cFrom,cTo,cost) VALUES('','q',120);
    INSERT INTO f2(cFrom,cTo,cost) VALUES('q','',120);
    INSERT INTO f2(cFrom,cTo,cost) VALUES('','r',86);
    INSERT INTO f2(cFrom,cTo,cost) VALUES('r','',86);
    INSERT INTO f2(cFrom,cTo,cost) VALUES('','s',86);
    INSERT INTO f2(cFrom,cTo,cost) VALUES('s','',86);
    INSERT INTO f2(cFrom,cTo,cost) VALUES('','t',84);
    INSERT INTO f2(cFrom,cTo,cost) VALUES('t','',84);
    INSERT INTO f2(cFrom,cTo,cost) VALUES('','u',94);
    INSERT INTO f2(cFrom,cTo,cost) VALUES('u','',94);
    INSERT INTO f2(cFrom,cTo,cost) VALUES('','v',120);
    INSERT INTO f2(cFrom,cTo,cost) VALUES('v','',120);
    INSERT INTO f2(cFrom,cTo,cost) VALUES('','w',96);
    INSERT INTO f2(cFrom,cTo,cost) VALUES('w','',96);
    INSERT INTO f2(cFrom,cTo,cost) VALUES('','x',120);
    INSERT INTO f2(cFrom,cTo,cost) VALUES('x','',120);
    INSERT INTO f2(cFrom,cTo,cost) VALUES('','y',100);
    INSERT INTO f2(cFrom,cTo,cost) VALUES('y','',100);
    INSERT INTO f2(cFrom,cTo,cost) VALUES('','z',120);
    INSERT INTO f2(cFrom,cTo,cost) VALUES('z','',120);



    -- Street names for the 28269 ZIPCODE.
    --
    CREATE TEMP TABLE streetname(n TEXT UNIQUE);
    INSERT INTO streetname VALUES('abbotsinch');
    INSERT INTO streetname VALUES('abbottsgate');
    INSERT INTO streetname VALUES('abbywood');







<

>
|
|
|
|
|
|
|
|
|
|
|
|
|

|
|
|
<
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
>
>







146
147
148
149
150
151
152

153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171

172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
    SELECT word, distance FROM f1
    WHERE word MATCH 'abcde' AND distance<=11 AND ruleset=1
  }
} {abcde 0 axcde 1 abcye 10 axcye 11}

do_test fuzzer1-2.0 {
  execsql {

    -- costs based on English letter frequencies
    CREATE TEMP TABLE f2_rules(ruleset, cFrom, cTo, cost);
    INSERT INTO f2_rules(cFrom,cTo,cost) VALUES('a','e',24);
    INSERT INTO f2_rules(cFrom,cTo,cost) VALUES('a','o',47);
    INSERT INTO f2_rules(cFrom,cTo,cost) VALUES('a','u',50);
    INSERT INTO f2_rules(cFrom,cTo,cost) VALUES('e','a',23);
    INSERT INTO f2_rules(cFrom,cTo,cost) VALUES('e','i',33);
    INSERT INTO f2_rules(cFrom,cTo,cost) VALUES('e','o',37);
    INSERT INTO f2_rules(cFrom,cTo,cost) VALUES('i','e',33);
    INSERT INTO f2_rules(cFrom,cTo,cost) VALUES('i','y',33);
    INSERT INTO f2_rules(cFrom,cTo,cost) VALUES('o','a',41);
    INSERT INTO f2_rules(cFrom,cTo,cost) VALUES('o','e',46);
    INSERT INTO f2_rules(cFrom,cTo,cost) VALUES('o','u',57);
    INSERT INTO f2_rules(cFrom,cTo,cost) VALUES('u','o',58);
    INSERT INTO f2_rules(cFrom,cTo,cost) VALUES('y','i',33);

    INSERT INTO f2_rules(cFrom,cTo,cost) VALUES('t','th',70);
    INSERT INTO f2_rules(cFrom,cTo,cost) VALUES('th','t',66);
 

    INSERT INTO f2_rules(cFrom,cTo,cost) VALUES('a','',84);
    INSERT INTO f2_rules(cFrom,cTo,cost) VALUES('','b',106);
    INSERT INTO f2_rules(cFrom,cTo,cost) VALUES('b','',106);
    INSERT INTO f2_rules(cFrom,cTo,cost) VALUES('','c',94);
    INSERT INTO f2_rules(cFrom,cTo,cost) VALUES('c','',94);
    INSERT INTO f2_rules(cFrom,cTo,cost) VALUES('','d',89);
    INSERT INTO f2_rules(cFrom,cTo,cost) VALUES('d','',89);
    INSERT INTO f2_rules(cFrom,cTo,cost) VALUES('','e',83);
    INSERT INTO f2_rules(cFrom,cTo,cost) VALUES('e','',83);
    INSERT INTO f2_rules(cFrom,cTo,cost) VALUES('','f',97);
    INSERT INTO f2_rules(cFrom,cTo,cost) VALUES('f','',97);
    INSERT INTO f2_rules(cFrom,cTo,cost) VALUES('','g',99);
    INSERT INTO f2_rules(cFrom,cTo,cost) VALUES('g','',99);
    INSERT INTO f2_rules(cFrom,cTo,cost) VALUES('','h',86);
    INSERT INTO f2_rules(cFrom,cTo,cost) VALUES('h','',86);
    INSERT INTO f2_rules(cFrom,cTo,cost) VALUES('','i',85);
    INSERT INTO f2_rules(cFrom,cTo,cost) VALUES('i','',85);
    INSERT INTO f2_rules(cFrom,cTo,cost) VALUES('','j',120);
    INSERT INTO f2_rules(cFrom,cTo,cost) VALUES('j','',120);
    INSERT INTO f2_rules(cFrom,cTo,cost) VALUES('','k',120);
    INSERT INTO f2_rules(cFrom,cTo,cost) VALUES('k','',120);
    INSERT INTO f2_rules(cFrom,cTo,cost) VALUES('','l',89);
    INSERT INTO f2_rules(cFrom,cTo,cost) VALUES('l','',89);
    INSERT INTO f2_rules(cFrom,cTo,cost) VALUES('','m',96);
    INSERT INTO f2_rules(cFrom,cTo,cost) VALUES('m','',96);
    INSERT INTO f2_rules(cFrom,cTo,cost) VALUES('','n',85);
    INSERT INTO f2_rules(cFrom,cTo,cost) VALUES('n','',85);
    INSERT INTO f2_rules(cFrom,cTo,cost) VALUES('','o',85);
    INSERT INTO f2_rules(cFrom,cTo,cost) VALUES('o','',85);
    INSERT INTO f2_rules(cFrom,cTo,cost) VALUES('','p',100);
    INSERT INTO f2_rules(cFrom,cTo,cost) VALUES('p','',100);
    INSERT INTO f2_rules(cFrom,cTo,cost) VALUES('','q',120);
    INSERT INTO f2_rules(cFrom,cTo,cost) VALUES('q','',120);
    INSERT INTO f2_rules(cFrom,cTo,cost) VALUES('','r',86);
    INSERT INTO f2_rules(cFrom,cTo,cost) VALUES('r','',86);
    INSERT INTO f2_rules(cFrom,cTo,cost) VALUES('','s',86);
    INSERT INTO f2_rules(cFrom,cTo,cost) VALUES('s','',86);
    INSERT INTO f2_rules(cFrom,cTo,cost) VALUES('','t',84);
    INSERT INTO f2_rules(cFrom,cTo,cost) VALUES('t','',84);
    INSERT INTO f2_rules(cFrom,cTo,cost) VALUES('','u',94);
    INSERT INTO f2_rules(cFrom,cTo,cost) VALUES('u','',94);
    INSERT INTO f2_rules(cFrom,cTo,cost) VALUES('','v',120);
    INSERT INTO f2_rules(cFrom,cTo,cost) VALUES('v','',120);
    INSERT INTO f2_rules(cFrom,cTo,cost) VALUES('','w',96);
    INSERT INTO f2_rules(cFrom,cTo,cost) VALUES('w','',96);
    INSERT INTO f2_rules(cFrom,cTo,cost) VALUES('','x',120);
    INSERT INTO f2_rules(cFrom,cTo,cost) VALUES('x','',120);
    INSERT INTO f2_rules(cFrom,cTo,cost) VALUES('','y',100);
    INSERT INTO f2_rules(cFrom,cTo,cost) VALUES('y','',100);
    INSERT INTO f2_rules(cFrom,cTo,cost) VALUES('','z',120);
    INSERT INTO f2_rules(cFrom,cTo,cost) VALUES('z','',120);

    CREATE VIRTUAL TABLE temp.f2 USING fuzzer(f2_rules);

    -- Street names for the 28269 ZIPCODE.
    --
    CREATE TEMP TABLE streetname(n TEXT UNIQUE);
    INSERT INTO streetname VALUES('abbotsinch');
    INSERT INTO streetname VALUES('abbottsgate');
    INSERT INTO streetname VALUES('abbywood');
1431
1432
1433
1434
1435
1436
1437
1438
1439
1440
1441
1442
1443

1444
1445
1446
1447
    SELECT DISTINCT streetname.n FROM f2, streetname
     WHERE f2.word MATCH 'tayle'
       AND f2.distance<=200
       AND streetname.n>=f2.word AND streetname.n<=(f2.word || x'F7BFBFBF')
  }
} {{tyler finley} trailer taymouth steelewood tallia tallu talwyn thelema}


do_execsql_test fuzzer1-3.1 {
  CREATE VIRTUAL TABLE temp.f3 USING fuzzer;
  CREATE TABLE f3(ruleset, cfrom, cto, cost);
  INSERT INTO f3(ruleset, cfrom, cto, cost) VALUES(0, 'x','y', 10);
  INSERT INTO f3(ruleset, cfrom, cto, cost) VALUES(1, 'a','b', 10);

  SELECT word FROM f3 WHERE word MATCH 'ax'
} {ax ay}

finish_test







|
|
|
|
|
|
>




1480
1481
1482
1483
1484
1485
1486
1487
1488
1489
1490
1491
1492
1493
1494
1495
1496
1497
    SELECT DISTINCT streetname.n FROM f2, streetname
     WHERE f2.word MATCH 'tayle'
       AND f2.distance<=200
       AND streetname.n>=f2.word AND streetname.n<=(f2.word || x'F7BFBFBF')
  }
} {{tyler finley} trailer taymouth steelewood tallia tallu talwyn thelema}

forcedelete test.db2
do_execsql_test fuzzer1-4.1 {
  ATTACH 'test.db2' AS aux;
  CREATE TABLE aux.f3_rules(ruleset, cfrom, cto, cost);
  INSERT INTO f3_rules(ruleset, cfrom, cto, cost) VALUES(0, 'x','y', 10);
  INSERT INTO f3_rules(ruleset, cfrom, cto, cost) VALUES(1, 'a','b', 10);
  CREATE VIRTUAL TABLE aux.f3 USING fuzzer(f3_rules);
  SELECT word FROM f3 WHERE word MATCH 'ax'
} {ax ay}

finish_test