SQLite

Check-in [5f2f2fce40]
Login

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

Overview
Comment:Further improvements to the fuzzer. It still is not quite working. Pausing to work on other things....
Downloads: Tarball | ZIP archive
Timelines: family | ancestors | descendants | both | word-fuzzer
Files: files | file ages | folders
SHA1: 5f2f2fce40f43debeb0492c9b460b85c7dad2bde
User & Date: drh 2011-03-29 14:08:09.188
Context
2011-03-29
18:21
The first simple test-case appears to be working now. (check-in: dd41155bc7 user: drh tags: word-fuzzer)
14:08
Further improvements to the fuzzer. It still is not quite working. Pausing to work on other things.... (check-in: 5f2f2fce40 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: fb4c31eac8 user: drh tags: word-fuzzer)
Changes
Unified Diff Ignore Whitespace Patch
install-sh became executable.
Changes to src/test_fuzzer.c.
13
14
15
16
17
18
19

20
21
22
23
24
25
26
** Code for demonstartion virtual table that generates variations
** on an input word at increasing edit distances from the original.
*/
#include "sqlite3.h"
#include <stdlib.h>
#include <string.h>
#include <assert.h>


#ifndef SQLITE_OMIT_VIRTUALTABLE

/*
** Forward declaration of objects used by this implementation
*/
typedef struct fuzzer_vtab fuzzer_vtab;







>







13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
** Code for demonstartion virtual table that generates variations
** on an input word at increasing edit distances from the original.
*/
#include "sqlite3.h"
#include <stdlib.h>
#include <string.h>
#include <assert.h>
#include <stdio.h>

#ifndef SQLITE_OMIT_VIRTUALTABLE

/*
** Forward declaration of objects used by this implementation
*/
typedef struct fuzzer_vtab fuzzer_vtab;
45
46
47
48
49
50
51
52








53
54
55
56
57
58
59
  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 */







|
>
>
>
>
>
>
>
>







46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
  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.  It is also used to record
** previously generated outputs.
**
** Every stem is added to a hash table as it is output.  Generation of
** duplicate stems is suppressed.
**
** Active stems (those that might generate new outputs) are kepts on a linked
** list sorted by increasing cost.  The cost is the sum of rBaseCost and
** pRule->rCost.
*/
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 */
79
80
81
82
83
84
85

86
87
88
89
90
91
92
  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,







>







88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
  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_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,
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
  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;
      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);
  }

  return SQLITE_OK;
}

/*
** Free up all the memory allocated by a cursor.  Set it rLimit to 0
** to indicate that it is at EOF.
*/







<



















>







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
  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 up all the memory allocated by a cursor.  Set it rLimit to 0
** to indicate that it is at EOF.
*/
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
** 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;







>






|
<
|
>
>



>

|
<
|
|
|
|


<
|
<
|
|
|
|
<










|








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



|
>










<
|
|
<
<
<
<
<
<
|




|












>
>
>
>







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
** 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--;
  sqlite3_free(pCur);
  return SQLITE_OK;
}

/*
** Compute the current output term for a fuzzer_stem.
*/
static int fuzzerRender(

  fuzzer_stem *pStem,   /* The stem to be rendered */
  char **pzBuf,         /* Write results into this buffer.  realloc if needed */
  int *pnBuf            /* Size of the buffer */
){
  const fuzzer_rule *pRule = pStem->pRule;
  int n;
  char *z;

  n = pStem->nBasis + pRule->nTo - pRule->nFrom;

  if( (*pnBuf)<n+1 ){
    (*pzBuf) = sqlite3_realloc((*pzBuf), n+100);
    if( (*pzBuf)==0 ) return SQLITE_NOMEM;
    (*pnBuf) = n+100;
  }
  n = pStem->n;

  z = *pzBuf;

  memcpy(z, pStem->zBasis, n);
  memcpy(&z[n], pRule->zTo, pRule->nTo);
  memcpy(&z[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 % FUZZER_HASH;
}

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

/*
** Return 1 if the string to which the cursor is point has already
** been emitted.  Return 0 if not.  Return -1 on a memory allocation
** failures.
*/
static int fuzzerSeen(fuzzer_cursor *pCur, fuzzer_stem *pStem){
  unsigned int h;
  fuzzer_stem *pLookup;

  if( fuzzerRender(pStem, &pCur->zBuf, &pCur->nBuf)==SQLITE_NOMEM ){
    return -1;
  }
  h = fuzzerHash(pCur->zBuf);
  pLookup = pCur->apHash[h];
    while( pLookup && strcmp(pLookup->zBasis, pCur->zBuf)!=0 ){
    pLookup = pLookup->pHash;
  }
  return pLookup!=0;
}

/*
** 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 ){
    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 ) return 1;  
      }
    }
    pStem->n = -1;
    pStem->pRule = pRule->pNext;
    if( pStem->pRule && 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;

  if( pList==0 ){
    pNew->pNext = 0;
    return pNew;
  }
  c1 = fuzzerCost(pNew);
  if( c1 <= fuzzerCost(pList) ){
    pNew->pNext = pList;
    return pNew;
  }else{
    fuzzer_stem *pPrev;
    pPrev = pList;
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
}


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







|
>





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









>
>
>
>
|
>







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
}


/*
** Advance a cursor to its next row of output
*/
static int fuzzerNext(sqlite3_vtab_cursor *cur){
  fuzzer_cursor *pCur = (fuzzer_cursor*)cur;
  int rc;
  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.
  */
  pStem = pCur->pStem;
  if( fuzzerCost(pStem)>0 ){
    rc = fuzzerRender(pStem, &pCur->zBuf, &pCur->nBuf);
    if( rc==SQLITE_NOMEM ) return SQLITE_NOMEM;
    pNew = fuzzerNewStem(pCur, pCur->zBuf, fuzzerCost(pStem));
    if( pNew ){
      if( fuzzerAdvance(pCur, pNew)==0 ){
        pNew->pNext = pCur->pDone;
        pCur->pDone = pNew;
      }else{
        pCur->pStem = fuzzerInsert(pStem, pNew);
        if( pCur->pStem==pNew ){
          return SQLITE_OK;
        }
      }
    }else{
      return SQLITE_NOMEM;
    }
  }

  /* 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);
      if( pCur->pStem!=pStem && (rc = fuzzerSeen(pCur, pStem))!=0 ){
        if( rc<0 ) return SQLITE_NOMEM;
        continue;
      }else{
        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
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
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{







|


>









|
|
>
>
>
>
>
>
>











|







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
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;
  fuzzer_stem *pStem;

  fuzzerClearCursor(pCur, 1);
  pCur->rLimit = 2147483647;
  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 = pStem = fuzzerNewStem(pCur, zWord, (fuzzer_cost)0);
  if( pStem==0 ) return SQLITE_NOMEM;
  pCur->nullRule.pNext = pCur->pVtab->pRule;
  pCur->nullRule.rCost = 0;
  pCur->nullRule.nFrom = 0;
  pCur->nullRule.nTo = 0;
  pCur->nullRule.zFrom = "";
  pStem->pRule = &pCur->nullRule;
  pStem->n = pStem->nBasis;
  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( fuzzerRender(pCur->pStem, &pCur->zBuf, &pCur->nBuf)==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{
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
  }
  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;
}








|
|
<
|



|

|
|







613
614
615
616
617
618
619
620
621

622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
  }
  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);
  nTo = strlen(zTo);

  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;
  p->pNewRule = pRule;
  return SQLITE_OK;
}

Changes to test/fuzzer1.test.
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
} {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'
  }







|
|







28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
} {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'
  }
test/progress.test became a regular file.
tool/mkopts.tcl became a regular file.