/ Check-in [fb4c31ea]
Login

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

Overview
Comment:Added most of the logic. Simple test runs without segfaulting but does not give the correct answer.
Downloads: Tarball | ZIP archive | SQL archive
Timelines: family | ancestors | descendants | both | word-fuzzer
Files: files | file ages | folders
SHA1:fb4c31eac8a7290f61c50a3552245660e1271871
User & Date: drh 2011-03-26 19:04:47
Context
2011-03-29
14:08
Further improvements to the fuzzer. It still is not quite working. Pausing to work on other things.... check-in: 5f2f2fce user: drh tags: word-fuzzer
2011-03-26
19:04
Added most of the logic. Simple test runs without segfaulting but does not give the correct answer. check-in: fb4c31ea user: drh tags: word-fuzzer
15:05
Skeleton code for the word-fuzzer virtual table. check-in: ea3a4ee1 user: drh tags: word-fuzzer
Changes
Hide Diffs Unified Diffs Ignore Whitespace Patch

Changes to src/test_fuzzer.c.

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
...
169
170
171
172
173
174
175

176

177
178
179
180
181
182
183
...
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
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
...
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
...
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
*/
typedef struct fuzzer_vtab fuzzer_vtab;
typedef struct fuzzer_cursor fuzzer_cursor;
typedef struct fuzzer_rule fuzzer_rule;
typedef struct fuzzer_seen fuzzer_seen;
typedef struct fuzzer_stem fuzzer_stem;








/*
** Each transformation rule is stored as an instance of this object.
** All rules are kept on a linked list sorted by rCost.
*/
struct fuzzer_rule {
  fuzzer_rule *pNext;   /* Next rule in order of increasing rCost */
  float rCost;          /* Cost of this transformation */

  char *zFrom;          /* Transform from */
  char zTo[4];          /* Transform to (extra space appended) */
};

/*
** When generating fuzzed words, we have to remember all previously
** generated terms in order to suppress duplicates.  Each previously
** generated term is an instance of the following structure.
*/
struct fuzzer_seen {
  fuzzer_seen *pNext;    /* Next with the same hash */
  char zWord[4];         /* The generated term. */
};

/*
** A stem object is used to generate variants.  
*/
struct fuzzer_stem {
  char *zBasis;           /* Word being fuzzed */

  fuzzer_rule *pRule;     /* Next rule to apply */
  int n;                  /* Apply rule at this character offset */
  float rBaseCost;        /* Base cost of getting to zBasis */
  float rCost;            /* rBaseCost + cost of applying pRule at n */
  fuzzer_stem *pNext;     /* Next stem in rCost order */

};

/* 
** 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 */
};



/* A fuzzer cursor object */
struct fuzzer_cursor {
  sqlite3_vtab_cursor base;  /* Base class - must be first */

  float rMax;                /* Maximum cost of any term */
  fuzzer_stem *pStem;        /* Sorted list of stems for generating new terms */
  int nSeen;                 /* Number of terms already generated */
  int nHash;                 /* Number of slots in apHash */


  fuzzer_seen **apHash;      /* Hash table 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;
  char *zSql;
  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);
  zSql = sqlite3_mprintf(
     "CREATE TABLE x(word, distance, cFrom, cTo, cost, \"%w\" HIDDEN)",
     argv[2]
  );
  sqlite3_declare_vtab(db, zSql);
  sqlite3_free(zSql);
  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 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));

  *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;
................................................................................
      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);
  }
   
  return SQLITE_OK;
}

/*



















** Close a fuzzer cursor.
*/
static int fuzzerClose(sqlite3_vtab_cursor *cur){
  fuzzer_cursor *pCur = (fuzzer_cursor *)cur;
  int i;
  for(i=0; i<pCur->nHash; i++){
    fuzzer_seen *pSeen = pCur->apHash[i];
    while( pSeen ){
      fuzzer_seen *pNext = pSeen->pNext;

      sqlite3_free(pSeen);
      pSeen = pNext;


    }
  }
  sqlite3_free(pCur->apHash);
  while( pCur->pStem ){





    fuzzer_stem *pStem = pCur->pStem;











    pCur->pStem = pStem->pNext;
    sqlite3_free(pStem);







  }
  sqlite3_free(pCur);
  return SQLITE_OK;
}









































































































static int fuzzerNext(sqlite3_vtab_cursor *cur){

































  return 0;
}






static int fuzzerFilter(
  sqlite3_vtab_cursor *pVtabCursor, 
  int idxNum, const char *idxStr,
  int argc, sqlite3_value **argv
){
  fuzzer_cursor *pCur = (fuzzer_cursor *)pVtabCursor;
  return fuzzerNext(pVtabCursor);


}



















static int fuzzerColumn(sqlite3_vtab_cursor *cur, sqlite3_context *ctx, int i){
  fuzzer_cursor *pCur = (fuzzer_cursor*)cur;













  return SQLITE_OK;
}




static int fuzzerRowid(sqlite3_vtab_cursor *cur, sqlite_int64 *pRowid){
  *pRowid = 0;
  return SQLITE_OK;
}





static int fuzzerEof(sqlite3_vtab_cursor *cur){
  fuzzer_cursor *pCur = (fuzzer_cursor*)cur;
  return 1;

}

















static int fuzzerBestIndex(sqlite3_vtab *tab, sqlite3_index_info *pIdxInfo){






































  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
................................................................................
){
  fuzzer_vtab *p = (fuzzer_vtab*)pVTab;
  fuzzer_rule *pRule;
  const char *zFrom;
  int nFrom;
  const char *zTo;
  int nTo;
  float rCost;
  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);
................................................................................
  if( zFrom==0 ) zFrom = "";
  zTo = (char*)sqlite3_value_text(argv[5]);
  if( zTo==0 ) zTo = "";
  if( strcmp(zFrom,zTo)==0 ){
    /* Silently ignore null transformations */
    return SQLITE_OK;
  }
  rCost = (float)sqlite3_value_double(argv[6]);
  if( rCost<=0 ){
    sqlite3_free(pVTab->zErrMsg);
    pVTab->zErrMsg = sqlite3_mprintf("cost must be positive");
    return SQLITE_CONSTRAINT;    
  }
  nFrom = strlen(zFrom)+1;
  nTo = strlen(zTo)+1;
  if( nTo<4 ) nTo = 4;
  pRule = sqlite3_malloc( sizeof(*pRule) + nFrom + nTo - 4 );
  if( pRule==0 ){
    return SQLITE_NOMEM;
  }
  pRule->zFrom = &pRule->zTo[nTo];

  memcpy(pRule->zFrom, zFrom, nFrom);
  memcpy(pRule->zTo, zTo, nTo);

  pRule->rCost = rCost;
  pRule->pNext = p->pNewRule;
  p->pNewRule = pRule;
  return SQLITE_OK;
}

/*







>
>
>
>
>
>






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






|
>
|
|
|
<
|
>













>
>



>
|

<
|
>
>
|











<










<
<
<
<
|
<







 







>

>







 







<




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




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

<



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

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


>
>
>
>
>






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


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



>
>
>

|



>
>
>
>


<
>


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

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







 







|
|







 







|













>


>







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
...
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
...
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
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
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
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
...
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
...
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
*/
typedef struct fuzzer_vtab fuzzer_vtab;
typedef struct fuzzer_cursor fuzzer_cursor;
typedef struct fuzzer_rule fuzzer_rule;
typedef struct fuzzer_seen fuzzer_seen;
typedef struct fuzzer_stem fuzzer_stem;

/*
** Type of the "cost" of an edit operation.  Might be changed to
** "float" or "double" or "sqlite3_int64" in the future.
*/
typedef int fuzzer_cost;


/*
** Each transformation rule is stored as an instance of this object.
** All rules are kept on a linked list sorted by rCost.
*/
struct fuzzer_rule {
  fuzzer_rule *pNext;        /* Next rule in order of increasing rCost */
  fuzzer_cost rCost;         /* Cost of this transformation */
  int nFrom, nTo;            /* Length of the zFrom and zTo strings */
  char *zFrom;               /* Transform from */
  char zTo[4];               /* Transform to (extra space appended) */










};

/*
** A stem object is used to generate variants.  
*/
struct fuzzer_stem {
  char *zBasis;              /* Word being fuzzed */
  int nBasis;                /* Length of the zBasis string */
  const fuzzer_rule *pRule;  /* Current rule to apply */
  int n;                     /* Apply pRule at this character offset */
  fuzzer_cost rBaseCost;     /* Base cost of getting to zBasis */

  fuzzer_stem *pNext;        /* Next stem in rCost order */
  fuzzer_stem *pHash;        /* Next stem with same hash on zBasis */
};

/* 
** 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 */

/* A fuzzer cursor object */
struct fuzzer_cursor {
  sqlite3_vtab_cursor base;  /* Base class - must be first */
  fuzzer_vtab *pVtab;        /* The virtual table this cursor belongs to */
  fuzzer_cost rLimit;        /* Maximum cost of any term */
  fuzzer_stem *pStem;        /* Sorted list of stems for generating new terms */

  fuzzer_stem *pDone;        /* Stems already processed to completion */
  char *zBuf;                /* Temporary use buffer */
  int nBuf;                  /* Bytes allocated for zBuf */
  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,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 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++;
  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;
................................................................................
      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);
  }

  return SQLITE_OK;
}

/*
** Free up all the memory allocated by a cursor.  Set it rLimit to 0
** to indicate that it is at EOF.
*/
static void fuzzerClearCursor(fuzzer_cursor *pCur, int clearHash){
  if( pCur->pStem==0 && pCur->pDone==0 ) clearHash = 0;
  do{
    while( pCur->pStem ){
      fuzzer_stem *pStem = pCur->pStem;
      pCur->pStem = pStem->pNext;
      sqlite3_free(pStem);
    }
    pCur->pStem = pCur->pDone;
    pCur->pDone = 0;
  }while( pCur->pStem );
  pCur->rLimit = (fuzzer_cost)0;
  if( clearHash ) memset(pCur->apHash, 0, sizeof(pCur->apHash));
}

/*
** Close a fuzzer cursor.
*/
static int fuzzerClose(sqlite3_vtab_cursor *cur){
  fuzzer_cursor *pCur = (fuzzer_cursor *)cur;





  fuzzerClearCursor(pCur, 0);
  sqlite3_free(pCur->zBuf);

  pCur->pVtab->nCursor--;
  return SQLITE_OK;
}



/*
** Compute the current output term for a fuzzer_stem.
*/
static int fuzzerComputeWord(
  fuzzer_cursor *pCur,
  fuzzer_stem *pStem
){
  const fuzzer_rule *pRule = pStem->pRule;
  int n;

  n = pStem->nBasis;
  if( pStem->n>=0 ) n += pRule->nTo - pRule->nFrom;
  if( pCur->nBuf<n+1 ){
    pCur->zBuf = sqlite3_realloc(pCur->zBuf, n+100);
    if( pCur->zBuf==0 ) return SQLITE_NOMEM;
    pCur->nBuf = n+100;
  }
  n = pStem->n;

  if( n<0 ){
    memcpy(pCur->zBuf, pStem->zBasis, pStem->nBasis+1);
  }else{
    memcpy(pCur->zBuf, pStem->zBasis, n);
    memcpy(&pCur->zBuf[n], pRule->zTo, pRule->nTo);
    memcpy(&pCur->zBuf[n+pRule->nTo], &pStem->zBasis[n+pRule->nFrom], 
           pStem->nBasis-n-pRule->nFrom+1);
  }

  return SQLITE_OK;
}


/*
** Compute a hash on zBasis.
*/
static unsigned int fuzzerHash(const char *z){
  unsigned int h = 0;
  while( *z ){ h = (h<<3) ^ (h>>29) ^ *(z++); }
  return h%10007;
}

/*
** Current cost of a stem
*/
static fuzzer_cost fuzzerCost(fuzzer_stem *pStem){
  return pStem->rBaseCost + pStem->pRule->rCost;
}

/*
** Advance a fuzzer_stem to its next value.   Return 0 if there are
** no more values that can be generated by this fuzzer_stem.
*/
static int fuzzerAdvance(fuzzer_cursor *pCur, fuzzer_stem *pStem){
  const fuzzer_rule *pRule;
  while( (pRule = pStem->pRule)!=0 ){
    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 */
        unsigned int h;
        fuzzer_stem *pLookup;

        fuzzerComputeWord(pCur, pStem);
        h = fuzzerHash(pCur->zBuf);
        pLookup = pCur->apHash[h];
        while( pLookup && strcmp(pLookup->zBasis, pCur->zBuf)!=0 ){
          pLookup = pLookup->pHash;
        }
        if( pLookup==0 ) return 1;  /* A new output is found. */
      }
    }
    pStem->n = -1;
    pStem->pRule = pRule->pNext;
    if( fuzzerCost(pStem)>pCur->rLimit ) pStem->pRule = 0;
  }
  return 0;
}

/*
** Insert pNew into the list at pList.  Return a pointer to the new
** list.  The insert is done such the pNew is in the correct order
** according to fuzzer_stem.zBaseCost+fuzzer_stem.pRule->rCost.
*/
static fuzzer_stem *fuzzerInsert(fuzzer_stem *pList, fuzzer_stem *pNew){
  fuzzer_cost c1;

  c1 = fuzzerCost(pNew);
  if( c1 <= fuzzerCost(pList) ){
    pNew->pNext = pList;
    return pNew;
  }else{
    fuzzer_stem *pPrev;
    pPrev = pList;
    while( pPrev->pNext && fuzzerCost(pPrev->pNext)<c1 ){
      pPrev = pPrev->pNext;
    }
    pNew->pNext = pPrev->pNext;
    pPrev->pNext = pNew;
    return pList;
  }
}

/*
** Allocate a new fuzzer_stem.  Add it to the hash table but do not
** link it into either the pCur->pStem or pCur->pDone lists.
*/
static fuzzer_stem *fuzzerNewStem(
  fuzzer_cursor *pCur,
  const char *zWord,
  fuzzer_cost rBaseCost
){
  fuzzer_stem *pNew;
  unsigned int h;

  pNew = sqlite3_malloc( sizeof(*pNew) + strlen(zWord) + 1 );
  if( pNew==0 ) return 0;
  memset(pNew, 0, sizeof(*pNew));
  pNew->zBasis = (char*)&pNew[1];
  pNew->nBasis = strlen(zWord);
  memcpy(pNew->zBasis, zWord, pNew->nBasis+1);
  pNew->pRule = pCur->pVtab->pRule;
  pNew->n = -1;
  pNew->rBaseCost = rBaseCost;
  h = fuzzerHash(pNew->zBasis);
  pNew->pHash = pCur->apHash[h];
  pCur->apHash[h] = pNew;
  return pNew;
}


/*
** Advance a cursor to its next row of output
*/
static int fuzzerNext(sqlite3_vtab_cursor *cur){
  fuzzer_cursor *pCur = (fuzzer_cursor*)pCur;
  fuzzer_stem *pStem, *pNew;

  /* Use the element the cursor is currently point to to create
  ** a new stem and insert the new stem into the priority queue.
  */
  fuzzerComputeWord(pCur, pCur->pStem);
  pNew = fuzzerNewStem(pCur, pCur->zBuf, fuzzerCost(pCur->pStem));
  if( pNew ){
    if( fuzzerAdvance(pCur, pNew)==0 ){
      pNew->pNext = pCur->pDone;
      pCur->pDone = pNew;
    }else{
      pCur->pStem = fuzzerInsert(pCur->pStem, pNew);
    }
  }

  /* Adjust the priority queue so that the first element of the
  ** stem list is the next lowest cost word.
  */
  while( (pStem = pCur->pStem)!=0 ){
    if( fuzzerAdvance(pCur, pStem) ){
      pCur->pStem = fuzzerInsert(pStem->pNext, pStem);
      return SQLITE_OK;  /* New word found */
    }
    pCur->pStem = pStem->pNext;
    pStem->pNext = pCur->pDone;
    pCur->pDone = pStem;
  }

  /* Reach this point only if queue has been exhausted and there is
  ** nothing left to be output. */
  pCur->rLimit = (fuzzer_cost)0;
  return SQLITE_OK;
}

/*
** Called to "rewind" a cursor back to the beginning so that
** it starts its output over again.  Always called at least once
** prior to any fuzzerColumn, fuzzerRowid, or fuzzerEof call.
*/
static int fuzzerFilter(
  sqlite3_vtab_cursor *pVtabCursor, 
  int idxNum, const char *idxStr,
  int argc, sqlite3_value **argv
){
  fuzzer_cursor *pCur = (fuzzer_cursor *)pVtabCursor;

  const char *zWord = 0;
  pCur->rLimit = 2147483647;

  fuzzerClearCursor(pCur, 1);
  if( idxNum==1 ){
    zWord = (const char*)sqlite3_value_text(argv[0]);
  }else if( idxNum==2 ){
    pCur->rLimit = (fuzzer_cost)sqlite3_value_int(argv[0]);
  }else if( idxNum==3 ){
    zWord = (const char*)sqlite3_value_text(argv[0]);
    pCur->rLimit = (fuzzer_cost)sqlite3_value_int(argv[1]);
  }
  if( zWord==0 ) zWord = "";
  pCur->pStem = fuzzerNewStem(pCur, zWord, (fuzzer_cost)0);
  if( pCur->pStem==0 ) return SQLITE_NOMEM;
  return SQLITE_OK;
}

/*
** Only the word and distance columns have values.  All other columns
** return NULL
*/
static int fuzzerColumn(sqlite3_vtab_cursor *cur, sqlite3_context *ctx, int i){
  fuzzer_cursor *pCur = (fuzzer_cursor*)cur;
  if( i==0 ){
    /* the "word" column */
    if( fuzzerComputeWord(pCur, pCur->pStem)==SQLITE_NOMEM ){
      return SQLITE_NOMEM;
    }
    sqlite3_result_text(ctx, pCur->zBuf, -1, SQLITE_TRANSIENT);
  }else if( i==1 ){
    /* the "distance" column */
    sqlite3_result_int(ctx, fuzzerCost(pCur->pStem));
  }else{
    /* All other columns are NULL */
    sqlite3_result_null(ctx);
  }
  return SQLITE_OK;
}

/*
** The rowid is always 0
*/
static int fuzzerRowid(sqlite3_vtab_cursor *cur, sqlite_int64 *pRowid){
  *pRowid = 0;  /* The rowid is always 0 */
  return SQLITE_OK;
}

/*
** When the fuzzer_cursor.rLimit value is 0 or less, that is a signal
** that the cursor has nothing more to output.
*/
static int fuzzerEof(sqlite3_vtab_cursor *cur){
  fuzzer_cursor *pCur = (fuzzer_cursor*)cur;

  return pCur->rLimit<=(fuzzer_cost)0;
}

/*
** Search for terms of these forms:
**
**       word MATCH $str
**       distance < $value
**       distance <= $value
**
** The distance< and distance<= are both treated as distance<=.
** The query plan number is as follows:
**
**   0:    None of the terms above are found
**   1:    There is a "word MATCH" term with $str in filter.argv[0].
**   2:    There is a "distance<" term with $value in filter.argv[0].
**   3:    Both "word MATCH" and "distance<" with $str in argv[0] and
**         $value in argv[1].
*/
static int fuzzerBestIndex(sqlite3_vtab *tab, sqlite3_index_info *pIdxInfo){
  int iPlan = 0;
  int iDistTerm = -1;
  int i;
  const struct sqlite3_index_constraint *pConstraint;
  pConstraint = pIdxInfo->aConstraint;
  for(i=0; i<pIdxInfo->nConstraint; i++, pConstraint++){
    if( pConstraint->usable==0 ) continue;
    if( (iPlan & 1)==0 
     && pConstraint->iColumn==0
     && pConstraint->op==SQLITE_INDEX_CONSTRAINT_MATCH
    ){
      iPlan |= 1;
      pIdxInfo->aConstraintUsage[i].argvIndex = 1;
      pIdxInfo->aConstraintUsage[i].omit = 1;
    }
    if( (iPlan & 2)==0
     && pConstraint->iColumn==1
     && (pConstraint->op==SQLITE_INDEX_CONSTRAINT_LT
           || pConstraint->op==SQLITE_INDEX_CONSTRAINT_LE)
    ){
      iPlan |= 2;
      iDistTerm = i;
    }
  }
  if( iPlan==2 ){
    pIdxInfo->aConstraintUsage[iDistTerm].argvIndex = 1;
  }else if( iPlan==3 ){
    pIdxInfo->aConstraintUsage[iDistTerm].argvIndex = 2;
  }
  pIdxInfo->idxNum = iPlan;
  if( pIdxInfo->nOrderBy==1
   && pIdxInfo->aOrderBy[0].iColumn==1
   && pIdxInfo->aOrderBy[0].desc==0
  ){
    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
................................................................................
){
  fuzzer_vtab *p = (fuzzer_vtab*)pVTab;
  fuzzer_rule *pRule;
  const char *zFrom;
  int nFrom;
  const char *zTo;
  int nTo;
  fuzzer_cost rCost;
  if( argc!=7 ){
    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);
................................................................................
  if( zFrom==0 ) zFrom = "";
  zTo = (char*)sqlite3_value_text(argv[5]);
  if( zTo==0 ) zTo = "";
  if( strcmp(zFrom,zTo)==0 ){
    /* Silently ignore null transformations */
    return SQLITE_OK;
  }
  rCost = sqlite3_value_int(argv[6]);
  if( rCost<=0 ){
    sqlite3_free(pVTab->zErrMsg);
    pVTab->zErrMsg = sqlite3_mprintf("cost must be positive");
    return SQLITE_CONSTRAINT;    
  }
  nFrom = strlen(zFrom)+1;
  nTo = strlen(zTo)+1;
  if( nTo<4 ) nTo = 4;
  pRule = sqlite3_malloc( sizeof(*pRule) + nFrom + nTo - 4 );
  if( pRule==0 ){
    return SQLITE_NOMEM;
  }
  pRule->zFrom = &pRule->zTo[nTo];
  pRule->nFrom = nFrom;
  memcpy(pRule->zFrom, zFrom, nFrom);
  memcpy(pRule->zTo, zTo, nTo);
  pRule->nTo = nTo;
  pRule->rCost = rCost;
  pRule->pNext = p->pNewRule;
  p->pNewRule = pRule;
  return SQLITE_OK;
}

/*

Changes to test/fuzzer1.test.

27
28
29
30
31
32
33
34
35
36






37
38
39
40
41
  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',0.1);
    INSERT INTO f1(cfrom, cto, cost) VALUES('a','e',0.1);
    INSERT INTO f1(cfrom, cto, cost) VALUES('e','o',0.1);






  }
} {}


finish_test







|
|
|
>
>
>
>
>
>





27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
  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',1);
    INSERT INTO f1(cfrom, cto, cost) VALUES('e','o',2);
  }
} {}

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


finish_test