1/*
2** 2011 March 24
3**
4** The author disclaims copyright to this source code.  In place of
5** a legal notice, here is a blessing:
6**
7**    May you do good and not evil.
8**    May you find forgiveness for yourself and forgive others.
9**    May you share freely, never taking more than you give.
10**
11*************************************************************************
12**
13** Code for demonstartion virtual table that generates variations
14** on an input word at increasing edit distances from the original.
15**
16** A fuzzer virtual table is created like this:
17**
18**     CREATE VIRTUAL TABLE temp.f USING fuzzer;
19**
20** The name of the new virtual table in the example above is "f".
21** Note that all fuzzer virtual tables must be TEMP tables.  The
22** "temp." prefix in front of the table name is required when the
23** table is being created.  The "temp." prefix can be omitted when
24** using the table as long as the name is unambiguous.
25**
26** Before being used, the fuzzer needs to be programmed by giving it
27** character transformations and a cost associated with each transformation.
28** Examples:
29**
30**    INSERT INTO f(cFrom,cTo,Cost) VALUES('','a',100);
31**
32** The above statement says that the cost of inserting a letter 'a' is
33** 100.  (All costs are integers.  We recommend that costs be scaled so
34** that the average cost is around 100.)
35**
36**    INSERT INTO f(cFrom,cTo,Cost) VALUES('b','',87);
37**
38** The above statement says that the cost of deleting a single letter
39** 'b' is 87.
40**
41**    INSERT INTO f(cFrom,cTo,Cost) VALUES('o','oe',38);
42**    INSERT INTO f(cFrom,cTo,Cost) VALUES('oe','o',40);
43**
44** This third example says that the cost of transforming the single
45** letter "o" into the two-letter sequence "oe" is 38 and that the
46** cost of transforming "oe" back into "o" is 40.
47**
48** After all the transformation costs have been set, the fuzzer table
49** can be queried as follows:
50**
51**    SELECT word, distance FROM f
52**     WHERE word MATCH 'abcdefg'
53**       AND distance<200;
54**
55** This first query outputs the string "abcdefg" and all strings that
56** can be derived from that string by appling the specified transformations.
57** The strings are output together with their total transformation cost
58** (called "distance") and appear in order of increasing cost.  No string
59** is output more than once.  If there are multiple ways to transform the
60** target string into the output string then the lowest cost transform is
61** the one that is returned.  In the example, the search is limited to
62** strings with a total distance of less than 200.
63**
64** It is important to put some kind of a limit on the fuzzer output.  This
65** can be either in the form of a LIMIT clause at the end of the query,
66** or better, a "distance<NNN" constraint where NNN is some number.  The
67** running time and memory requirement is exponential in the value of NNN
68** so you want to make sure that NNN is not too big.  A value of NNN that
69** is about twice the average transformation cost seems to give good results.
70**
71** The fuzzer table can be useful for tasks such as spelling correction.
72** Suppose there is a second table vocabulary(w) where the w column contains
73** all correctly spelled words.   Let $word be a word you want to look up.
74**
75**   SELECT vocabulary.w FROM f, vocabulary
76**    WHERE f.word MATCH $word
77**      AND f.distance<=200
78**      AND f.word=vocabulary.w
79**    LIMIT 20
80**
81** The query above gives the 20 closest words to the $word being tested.
82** (Note that for good performance, the vocubulary.w column should be
83** indexed.)
84**
85** A similar query can be used to find all words in the dictionary that
86** begin with some prefix $prefix:
87**
88**   SELECT vocabulary.w FROM f, vocabulary
89**    WHERE f.word MATCH $prefix
90**      AND f.distance<=200
91**      AND vocabulary.w BETWEEN f.word AND (f.word || x'F7BFBFBF')
92**    LIMIT 50
93**
94** This last query will show up to 50 words out of the vocabulary that
95** match or nearly match the $prefix.
96*/
97#include "sqlite3.h"
98#include <stdlib.h>
99#include <string.h>
100#include <assert.h>
101#include <stdio.h>
102
103#ifndef SQLITE_OMIT_VIRTUALTABLE
104
105/*
106** Forward declaration of objects used by this implementation
107*/
108typedef struct fuzzer_vtab fuzzer_vtab;
109typedef struct fuzzer_cursor fuzzer_cursor;
110typedef struct fuzzer_rule fuzzer_rule;
111typedef struct fuzzer_seen fuzzer_seen;
112typedef struct fuzzer_stem fuzzer_stem;
113
114/*
115** Type of the "cost" of an edit operation.  Might be changed to
116** "float" or "double" or "sqlite3_int64" in the future.
117*/
118typedef int fuzzer_cost;
119
120
121/*
122** Each transformation rule is stored as an instance of this object.
123** All rules are kept on a linked list sorted by rCost.
124*/
125struct fuzzer_rule {
126  fuzzer_rule *pNext;        /* Next rule in order of increasing rCost */
127  fuzzer_cost rCost;         /* Cost of this transformation */
128  int nFrom, nTo;            /* Length of the zFrom and zTo strings */
129  char *zFrom;               /* Transform from */
130  char zTo[4];               /* Transform to (extra space appended) */
131};
132
133/*
134** A stem object is used to generate variants.  It is also used to record
135** previously generated outputs.
136**
137** Every stem is added to a hash table as it is output.  Generation of
138** duplicate stems is suppressed.
139**
140** Active stems (those that might generate new outputs) are kepts on a linked
141** list sorted by increasing cost.  The cost is the sum of rBaseCost and
142** pRule->rCost.
143*/
144struct fuzzer_stem {
145  char *zBasis;              /* Word being fuzzed */
146  int nBasis;                /* Length of the zBasis string */
147  const fuzzer_rule *pRule;  /* Current rule to apply */
148  int n;                     /* Apply pRule at this character offset */
149  fuzzer_cost rBaseCost;     /* Base cost of getting to zBasis */
150  fuzzer_cost rCostX;        /* Precomputed rBaseCost + pRule->rCost */
151  fuzzer_stem *pNext;        /* Next stem in rCost order */
152  fuzzer_stem *pHash;        /* Next stem with same hash on zBasis */
153};
154
155/*
156** A fuzzer virtual-table object
157*/
158struct fuzzer_vtab {
159  sqlite3_vtab base;         /* Base class - must be first */
160  char *zClassName;          /* Name of this class.  Default: "fuzzer" */
161  fuzzer_rule *pRule;        /* All active rules in this fuzzer */
162  fuzzer_rule *pNewRule;     /* New rules to add when last cursor expires */
163  int nCursor;               /* Number of active cursors */
164};
165
166#define FUZZER_HASH  4001    /* Hash table size */
167#define FUZZER_NQUEUE  20    /* Number of slots on the stem queue */
168
169/* A fuzzer cursor object */
170struct fuzzer_cursor {
171  sqlite3_vtab_cursor base;  /* Base class - must be first */
172  sqlite3_int64 iRowid;      /* The rowid of the current word */
173  fuzzer_vtab *pVtab;        /* The virtual table this cursor belongs to */
174  fuzzer_cost rLimit;        /* Maximum cost of any term */
175  fuzzer_stem *pStem;        /* Stem with smallest rCostX */
176  fuzzer_stem *pDone;        /* Stems already processed to completion */
177  fuzzer_stem *aQueue[FUZZER_NQUEUE];  /* Queue of stems with higher rCostX */
178  int mxQueue;               /* Largest used index in aQueue[] */
179  char *zBuf;                /* Temporary use buffer */
180  int nBuf;                  /* Bytes allocated for zBuf */
181  int nStem;                 /* Number of stems allocated */
182  fuzzer_rule nullRule;      /* Null rule used first */
183  fuzzer_stem *apHash[FUZZER_HASH]; /* Hash of previously generated terms */
184};
185
186/* Methods for the fuzzer module */
187static int fuzzerConnect(
188  sqlite3 *db,
189  void *pAux,
190  int argc, const char *const*argv,
191  sqlite3_vtab **ppVtab,
192  char **pzErr
193){
194  fuzzer_vtab *pNew;
195  int n;
196  if( strcmp(argv[1],"temp")!=0 ){
197    *pzErr = sqlite3_mprintf("%s virtual tables must be TEMP", argv[0]);
198    return SQLITE_ERROR;
199  }
200  n = strlen(argv[0]) + 1;
201  pNew = sqlite3_malloc( sizeof(*pNew) + n );
202  if( pNew==0 ) return SQLITE_NOMEM;
203  pNew->zClassName = (char*)&pNew[1];
204  memcpy(pNew->zClassName, argv[0], n);
205  sqlite3_declare_vtab(db, "CREATE TABLE x(word,distance,cFrom,cTo,cost)");
206  memset(pNew, 0, sizeof(*pNew));
207  *ppVtab = &pNew->base;
208  return SQLITE_OK;
209}
210/* Note that for this virtual table, the xCreate and xConnect
211** methods are identical. */
212
213static int fuzzerDisconnect(sqlite3_vtab *pVtab){
214  fuzzer_vtab *p = (fuzzer_vtab*)pVtab;
215  assert( p->nCursor==0 );
216  do{
217    while( p->pRule ){
218      fuzzer_rule *pRule = p->pRule;
219      p->pRule = pRule->pNext;
220      sqlite3_free(pRule);
221    }
222    p->pRule = p->pNewRule;
223    p->pNewRule = 0;
224  }while( p->pRule );
225  sqlite3_free(p);
226  return SQLITE_OK;
227}
228/* The xDisconnect and xDestroy methods are also the same */
229
230/*
231** The two input rule lists are both sorted in order of increasing
232** cost.  Merge them together into a single list, sorted by cost, and
233** return a pointer to the head of that list.
234*/
235static fuzzer_rule *fuzzerMergeRules(fuzzer_rule *pA, fuzzer_rule *pB){
236  fuzzer_rule head;
237  fuzzer_rule *pTail;
238
239  pTail =  &head;
240  while( pA && pB ){
241    if( pA->rCost<=pB->rCost ){
242      pTail->pNext = pA;
243      pTail = pA;
244      pA = pA->pNext;
245    }else{
246      pTail->pNext = pB;
247      pTail = pB;
248      pB = pB->pNext;
249    }
250  }
251  if( pA==0 ){
252    pTail->pNext = pB;
253  }else{
254    pTail->pNext = pA;
255  }
256  return head.pNext;
257}
258
259
260/*
261** Open a new fuzzer cursor.
262*/
263static int fuzzerOpen(sqlite3_vtab *pVTab, sqlite3_vtab_cursor **ppCursor){
264  fuzzer_vtab *p = (fuzzer_vtab*)pVTab;
265  fuzzer_cursor *pCur;
266  pCur = sqlite3_malloc( sizeof(*pCur) );
267  if( pCur==0 ) return SQLITE_NOMEM;
268  memset(pCur, 0, sizeof(*pCur));
269  pCur->pVtab = p;
270  *ppCursor = &pCur->base;
271  if( p->nCursor==0 && p->pNewRule ){
272    unsigned int i;
273    fuzzer_rule *pX;
274    fuzzer_rule *a[15];
275    for(i=0; i<sizeof(a)/sizeof(a[0]); i++) a[i] = 0;
276    while( (pX = p->pNewRule)!=0 ){
277      p->pNewRule = pX->pNext;
278      pX->pNext = 0;
279      for(i=0; a[i] && i<sizeof(a)/sizeof(a[0])-1; i++){
280        pX = fuzzerMergeRules(a[i], pX);
281        a[i] = 0;
282      }
283      a[i] = fuzzerMergeRules(a[i], pX);
284    }
285    for(pX=a[0], i=1; i<sizeof(a)/sizeof(a[0]); i++){
286      pX = fuzzerMergeRules(a[i], pX);
287    }
288    p->pRule = fuzzerMergeRules(p->pRule, pX);
289  }
290  p->nCursor++;
291  return SQLITE_OK;
292}
293
294/*
295** Free all stems in a list.
296*/
297static void fuzzerClearStemList(fuzzer_stem *pStem){
298  while( pStem ){
299    fuzzer_stem *pNext = pStem->pNext;
300    sqlite3_free(pStem);
301    pStem = pNext;
302  }
303}
304
305/*
306** Free up all the memory allocated by a cursor.  Set it rLimit to 0
307** to indicate that it is at EOF.
308*/
309static void fuzzerClearCursor(fuzzer_cursor *pCur, int clearHash){
310  int i;
311  fuzzerClearStemList(pCur->pStem);
312  fuzzerClearStemList(pCur->pDone);
313  for(i=0; i<FUZZER_NQUEUE; i++) fuzzerClearStemList(pCur->aQueue[i]);
314  pCur->rLimit = (fuzzer_cost)0;
315  if( clearHash && pCur->nStem ){
316    pCur->mxQueue = 0;
317    pCur->pStem = 0;
318    pCur->pDone = 0;
319    memset(pCur->aQueue, 0, sizeof(pCur->aQueue));
320    memset(pCur->apHash, 0, sizeof(pCur->apHash));
321  }
322  pCur->nStem = 0;
323}
324
325/*
326** Close a fuzzer cursor.
327*/
328static int fuzzerClose(sqlite3_vtab_cursor *cur){
329  fuzzer_cursor *pCur = (fuzzer_cursor *)cur;
330  fuzzerClearCursor(pCur, 0);
331  sqlite3_free(pCur->zBuf);
332  pCur->pVtab->nCursor--;
333  sqlite3_free(pCur);
334  return SQLITE_OK;
335}
336
337/*
338** Compute the current output term for a fuzzer_stem.
339*/
340static int fuzzerRender(
341  fuzzer_stem *pStem,   /* The stem to be rendered */
342  char **pzBuf,         /* Write results into this buffer.  realloc if needed */
343  int *pnBuf            /* Size of the buffer */
344){
345  const fuzzer_rule *pRule = pStem->pRule;
346  int n;
347  char *z;
348
349  n = pStem->nBasis + pRule->nTo - pRule->nFrom;
350  if( (*pnBuf)<n+1 ){
351    (*pzBuf) = sqlite3_realloc((*pzBuf), n+100);
352    if( (*pzBuf)==0 ) return SQLITE_NOMEM;
353    (*pnBuf) = n+100;
354  }
355  n = pStem->n;
356  z = *pzBuf;
357  if( n<0 ){
358    memcpy(z, pStem->zBasis, pStem->nBasis+1);
359  }else{
360    memcpy(z, pStem->zBasis, n);
361    memcpy(&z[n], pRule->zTo, pRule->nTo);
362    memcpy(&z[n+pRule->nTo], &pStem->zBasis[n+pRule->nFrom],
363           pStem->nBasis-n-pRule->nFrom+1);
364  }
365  return SQLITE_OK;
366}
367
368/*
369** Compute a hash on zBasis.
370*/
371static unsigned int fuzzerHash(const char *z){
372  unsigned int h = 0;
373  while( *z ){ h = (h<<3) ^ (h>>29) ^ *(z++); }
374  return h % FUZZER_HASH;
375}
376
377/*
378** Current cost of a stem
379*/
380static fuzzer_cost fuzzerCost(fuzzer_stem *pStem){
381  return pStem->rCostX = pStem->rBaseCost + pStem->pRule->rCost;
382}
383
384#if 0
385/*
386** Print a description of a fuzzer_stem on stderr.
387*/
388static void fuzzerStemPrint(
389  const char *zPrefix,
390  fuzzer_stem *pStem,
391  const char *zSuffix
392){
393  if( pStem->n<0 ){
394    fprintf(stderr, "%s[%s](%d)-->self%s",
395       zPrefix,
396       pStem->zBasis, pStem->rBaseCost,
397       zSuffix
398    );
399  }else{
400    char *zBuf = 0;
401    int nBuf = 0;
402    if( fuzzerRender(pStem, &zBuf, &nBuf)!=SQLITE_OK ) return;
403    fprintf(stderr, "%s[%s](%d)-->{%s}(%d)%s",
404      zPrefix,
405      pStem->zBasis, pStem->rBaseCost, zBuf, pStem->,
406      zSuffix
407    );
408    sqlite3_free(zBuf);
409  }
410}
411#endif
412
413/*
414** Return 1 if the string to which the cursor is point has already
415** been emitted.  Return 0 if not.  Return -1 on a memory allocation
416** failures.
417*/
418static int fuzzerSeen(fuzzer_cursor *pCur, fuzzer_stem *pStem){
419  unsigned int h;
420  fuzzer_stem *pLookup;
421
422  if( fuzzerRender(pStem, &pCur->zBuf, &pCur->nBuf)==SQLITE_NOMEM ){
423    return -1;
424  }
425  h = fuzzerHash(pCur->zBuf);
426  pLookup = pCur->apHash[h];
427    while( pLookup && strcmp(pLookup->zBasis, pCur->zBuf)!=0 ){
428    pLookup = pLookup->pHash;
429  }
430  return pLookup!=0;
431}
432
433/*
434** Advance a fuzzer_stem to its next value.   Return 0 if there are
435** no more values that can be generated by this fuzzer_stem.  Return
436** -1 on a memory allocation failure.
437*/
438static int fuzzerAdvance(fuzzer_cursor *pCur, fuzzer_stem *pStem){
439  const fuzzer_rule *pRule;
440  while( (pRule = pStem->pRule)!=0 ){
441    while( pStem->n < pStem->nBasis - pRule->nFrom ){
442      pStem->n++;
443      if( pRule->nFrom==0
444       || memcmp(&pStem->zBasis[pStem->n], pRule->zFrom, pRule->nFrom)==0
445      ){
446        /* Found a rewrite case.  Make sure it is not a duplicate */
447        int rc = fuzzerSeen(pCur, pStem);
448        if( rc<0 ) return -1;
449        if( rc==0 ){
450          fuzzerCost(pStem);
451          return 1;
452        }
453      }
454    }
455    pStem->n = -1;
456    pStem->pRule = pRule->pNext;
457    if( pStem->pRule && fuzzerCost(pStem)>pCur->rLimit ) pStem->pRule = 0;
458  }
459  return 0;
460}
461
462/*
463** The two input stem lists are both sorted in order of increasing
464** rCostX.  Merge them together into a single list, sorted by rCostX, and
465** return a pointer to the head of that new list.
466*/
467static fuzzer_stem *fuzzerMergeStems(fuzzer_stem *pA, fuzzer_stem *pB){
468  fuzzer_stem head;
469  fuzzer_stem *pTail;
470
471  pTail =  &head;
472  while( pA && pB ){
473    if( pA->rCostX<=pB->rCostX ){
474      pTail->pNext = pA;
475      pTail = pA;
476      pA = pA->pNext;
477    }else{
478      pTail->pNext = pB;
479      pTail = pB;
480      pB = pB->pNext;
481    }
482  }
483  if( pA==0 ){
484    pTail->pNext = pB;
485  }else{
486    pTail->pNext = pA;
487  }
488  return head.pNext;
489}
490
491/*
492** Load pCur->pStem with the lowest-cost stem.  Return a pointer
493** to the lowest-cost stem.
494*/
495static fuzzer_stem *fuzzerLowestCostStem(fuzzer_cursor *pCur){
496  fuzzer_stem *pBest, *pX;
497  int iBest;
498  int i;
499
500  if( pCur->pStem==0 ){
501    iBest = -1;
502    pBest = 0;
503    for(i=0; i<=pCur->mxQueue; i++){
504      pX = pCur->aQueue[i];
505      if( pX==0 ) continue;
506      if( pBest==0 || pBest->rCostX>pX->rCostX ){
507        pBest = pX;
508        iBest = i;
509      }
510    }
511    if( pBest ){
512      pCur->aQueue[iBest] = pBest->pNext;
513      pBest->pNext = 0;
514      pCur->pStem = pBest;
515    }
516  }
517  return pCur->pStem;
518}
519
520/*
521** Insert pNew into queue of pending stems.  Then find the stem
522** with the lowest rCostX and move it into pCur->pStem.
523** list.  The insert is done such the pNew is in the correct order
524** according to fuzzer_stem.zBaseCost+fuzzer_stem.pRule->rCost.
525*/
526static fuzzer_stem *fuzzerInsert(fuzzer_cursor *pCur, fuzzer_stem *pNew){
527  fuzzer_stem *pX;
528  int i;
529
530  /* If pCur->pStem exists and is greater than pNew, then make pNew
531  ** the new pCur->pStem and insert the old pCur->pStem instead.
532  */
533  if( (pX = pCur->pStem)!=0 && pX->rCostX>pNew->rCostX ){
534    pNew->pNext = 0;
535    pCur->pStem = pNew;
536    pNew = pX;
537  }
538
539  /* Insert the new value */
540  pNew->pNext = 0;
541  pX = pNew;
542  for(i=0; i<=pCur->mxQueue; i++){
543    if( pCur->aQueue[i] ){
544      pX = fuzzerMergeStems(pX, pCur->aQueue[i]);
545      pCur->aQueue[i] = 0;
546    }else{
547      pCur->aQueue[i] = pX;
548      break;
549    }
550  }
551  if( i>pCur->mxQueue ){
552    if( i<FUZZER_NQUEUE ){
553      pCur->mxQueue = i;
554      pCur->aQueue[i] = pX;
555    }else{
556      assert( pCur->mxQueue==FUZZER_NQUEUE-1 );
557      pX = fuzzerMergeStems(pX, pCur->aQueue[FUZZER_NQUEUE-1]);
558      pCur->aQueue[FUZZER_NQUEUE-1] = pX;
559    }
560  }
561
562  return fuzzerLowestCostStem(pCur);
563}
564
565/*
566** Allocate a new fuzzer_stem.  Add it to the hash table but do not
567** link it into either the pCur->pStem or pCur->pDone lists.
568*/
569static fuzzer_stem *fuzzerNewStem(
570  fuzzer_cursor *pCur,
571  const char *zWord,
572  fuzzer_cost rBaseCost
573){
574  fuzzer_stem *pNew;
575  unsigned int h;
576
577  pNew = sqlite3_malloc( sizeof(*pNew) + strlen(zWord) + 1 );
578  if( pNew==0 ) return 0;
579  memset(pNew, 0, sizeof(*pNew));
580  pNew->zBasis = (char*)&pNew[1];
581  pNew->nBasis = strlen(zWord);
582  memcpy(pNew->zBasis, zWord, pNew->nBasis+1);
583  pNew->pRule = pCur->pVtab->pRule;
584  pNew->n = -1;
585  pNew->rBaseCost = pNew->rCostX = rBaseCost;
586  h = fuzzerHash(pNew->zBasis);
587  pNew->pHash = pCur->apHash[h];
588  pCur->apHash[h] = pNew;
589  pCur->nStem++;
590  return pNew;
591}
592
593
594/*
595** Advance a cursor to its next row of output
596*/
597static int fuzzerNext(sqlite3_vtab_cursor *cur){
598  fuzzer_cursor *pCur = (fuzzer_cursor*)cur;
599  int rc;
600  fuzzer_stem *pStem, *pNew;
601
602  pCur->iRowid++;
603
604  /* Use the element the cursor is currently point to to create
605  ** a new stem and insert the new stem into the priority queue.
606  */
607  pStem = pCur->pStem;
608  if( pStem->rCostX>0 ){
609    rc = fuzzerRender(pStem, &pCur->zBuf, &pCur->nBuf);
610    if( rc==SQLITE_NOMEM ) return SQLITE_NOMEM;
611    pNew = fuzzerNewStem(pCur, pCur->zBuf, pStem->rCostX);
612    if( pNew ){
613      if( fuzzerAdvance(pCur, pNew)==0 ){
614        pNew->pNext = pCur->pDone;
615        pCur->pDone = pNew;
616      }else{
617        if( fuzzerInsert(pCur, pNew)==pNew ){
618          return SQLITE_OK;
619        }
620      }
621    }else{
622      return SQLITE_NOMEM;
623    }
624  }
625
626  /* Adjust the priority queue so that the first element of the
627  ** stem list is the next lowest cost word.
628  */
629  while( (pStem = pCur->pStem)!=0 ){
630    if( fuzzerAdvance(pCur, pStem) ){
631      pCur->pStem = 0;
632      pStem = fuzzerInsert(pCur, pStem);
633      if( (rc = fuzzerSeen(pCur, pStem))!=0 ){
634        if( rc<0 ) return SQLITE_NOMEM;
635        continue;
636      }
637      return SQLITE_OK;  /* New word found */
638    }
639    pCur->pStem = 0;
640    pStem->pNext = pCur->pDone;
641    pCur->pDone = pStem;
642    if( fuzzerLowestCostStem(pCur) ){
643      rc = fuzzerSeen(pCur, pCur->pStem);
644      if( rc<0 ) return SQLITE_NOMEM;
645      if( rc==0 ){
646        return SQLITE_OK;
647      }
648    }
649  }
650
651  /* Reach this point only if queue has been exhausted and there is
652  ** nothing left to be output. */
653  pCur->rLimit = (fuzzer_cost)0;
654  return SQLITE_OK;
655}
656
657/*
658** Called to "rewind" a cursor back to the beginning so that
659** it starts its output over again.  Always called at least once
660** prior to any fuzzerColumn, fuzzerRowid, or fuzzerEof call.
661*/
662static int fuzzerFilter(
663  sqlite3_vtab_cursor *pVtabCursor,
664  int idxNum, const char *idxStr,
665  int argc, sqlite3_value **argv
666){
667  fuzzer_cursor *pCur = (fuzzer_cursor *)pVtabCursor;
668  const char *zWord = 0;
669  fuzzer_stem *pStem;
670
671  fuzzerClearCursor(pCur, 1);
672  pCur->rLimit = 2147483647;
673  if( idxNum==1 ){
674    zWord = (const char*)sqlite3_value_text(argv[0]);
675  }else if( idxNum==2 ){
676    pCur->rLimit = (fuzzer_cost)sqlite3_value_int(argv[0]);
677  }else if( idxNum==3 ){
678    zWord = (const char*)sqlite3_value_text(argv[0]);
679    pCur->rLimit = (fuzzer_cost)sqlite3_value_int(argv[1]);
680  }
681  if( zWord==0 ) zWord = "";
682  pCur->pStem = pStem = fuzzerNewStem(pCur, zWord, (fuzzer_cost)0);
683  if( pStem==0 ) return SQLITE_NOMEM;
684  pCur->nullRule.pNext = pCur->pVtab->pRule;
685  pCur->nullRule.rCost = 0;
686  pCur->nullRule.nFrom = 0;
687  pCur->nullRule.nTo = 0;
688  pCur->nullRule.zFrom = "";
689  pStem->pRule = &pCur->nullRule;
690  pStem->n = pStem->nBasis;
691  pCur->iRowid = 1;
692  return SQLITE_OK;
693}
694
695/*
696** Only the word and distance columns have values.  All other columns
697** return NULL
698*/
699static int fuzzerColumn(sqlite3_vtab_cursor *cur, sqlite3_context *ctx, int i){
700  fuzzer_cursor *pCur = (fuzzer_cursor*)cur;
701  if( i==0 ){
702    /* the "word" column */
703    if( fuzzerRender(pCur->pStem, &pCur->zBuf, &pCur->nBuf)==SQLITE_NOMEM ){
704      return SQLITE_NOMEM;
705    }
706    sqlite3_result_text(ctx, pCur->zBuf, -1, SQLITE_TRANSIENT);
707  }else if( i==1 ){
708    /* the "distance" column */
709    sqlite3_result_int(ctx, pCur->pStem->rCostX);
710  }else{
711    /* All other columns are NULL */
712    sqlite3_result_null(ctx);
713  }
714  return SQLITE_OK;
715}
716
717/*
718** The rowid.
719*/
720static int fuzzerRowid(sqlite3_vtab_cursor *cur, sqlite_int64 *pRowid){
721  fuzzer_cursor *pCur = (fuzzer_cursor*)cur;
722  *pRowid = pCur->iRowid;
723  return SQLITE_OK;
724}
725
726/*
727** When the fuzzer_cursor.rLimit value is 0 or less, that is a signal
728** that the cursor has nothing more to output.
729*/
730static int fuzzerEof(sqlite3_vtab_cursor *cur){
731  fuzzer_cursor *pCur = (fuzzer_cursor*)cur;
732  return pCur->rLimit<=(fuzzer_cost)0;
733}
734
735/*
736** Search for terms of these forms:
737**
738**       word MATCH $str
739**       distance < $value
740**       distance <= $value
741**
742** The distance< and distance<= are both treated as distance<=.
743** The query plan number is as follows:
744**
745**   0:    None of the terms above are found
746**   1:    There is a "word MATCH" term with $str in filter.argv[0].
747**   2:    There is a "distance<" term with $value in filter.argv[0].
748**   3:    Both "word MATCH" and "distance<" with $str in argv[0] and
749**         $value in argv[1].
750*/
751static int fuzzerBestIndex(sqlite3_vtab *tab, sqlite3_index_info *pIdxInfo){
752  int iPlan = 0;
753  int iDistTerm = -1;
754  int i;
755  const struct sqlite3_index_constraint *pConstraint;
756  pConstraint = pIdxInfo->aConstraint;
757  for(i=0; i<pIdxInfo->nConstraint; i++, pConstraint++){
758    if( pConstraint->usable==0 ) continue;
759    if( (iPlan & 1)==0
760     && pConstraint->iColumn==0
761     && pConstraint->op==SQLITE_INDEX_CONSTRAINT_MATCH
762    ){
763      iPlan |= 1;
764      pIdxInfo->aConstraintUsage[i].argvIndex = 1;
765      pIdxInfo->aConstraintUsage[i].omit = 1;
766    }
767    if( (iPlan & 2)==0
768     && pConstraint->iColumn==1
769     && (pConstraint->op==SQLITE_INDEX_CONSTRAINT_LT
770           || pConstraint->op==SQLITE_INDEX_CONSTRAINT_LE)
771    ){
772      iPlan |= 2;
773      iDistTerm = i;
774    }
775  }
776  if( iPlan==2 ){
777    pIdxInfo->aConstraintUsage[iDistTerm].argvIndex = 1;
778  }else if( iPlan==3 ){
779    pIdxInfo->aConstraintUsage[iDistTerm].argvIndex = 2;
780  }
781  pIdxInfo->idxNum = iPlan;
782  if( pIdxInfo->nOrderBy==1
783   && pIdxInfo->aOrderBy[0].iColumn==1
784   && pIdxInfo->aOrderBy[0].desc==0
785  ){
786    pIdxInfo->orderByConsumed = 1;
787  }
788  pIdxInfo->estimatedCost = (double)10000;
789
790  return SQLITE_OK;
791}
792
793/*
794** Disallow all attempts to DELETE or UPDATE.  Only INSERTs are allowed.
795**
796** On an insert, the cFrom, cTo, and cost columns are used to construct
797** a new rule.   All other columns are ignored.  The rule is ignored
798** if cFrom and cTo are identical.  A NULL value for cFrom or cTo is
799** interpreted as an empty string.  The cost must be positive.
800*/
801static int fuzzerUpdate(
802  sqlite3_vtab *pVTab,
803  int argc,
804  sqlite3_value **argv,
805  sqlite_int64 *pRowid
806){
807  fuzzer_vtab *p = (fuzzer_vtab*)pVTab;
808  fuzzer_rule *pRule;
809  const char *zFrom;
810  int nFrom;
811  const char *zTo;
812  int nTo;
813  fuzzer_cost rCost;
814  if( argc!=7 ){
815    sqlite3_free(pVTab->zErrMsg);
816    pVTab->zErrMsg = sqlite3_mprintf("cannot delete from a %s virtual table",
817                                     p->zClassName);
818    return SQLITE_CONSTRAINT;
819  }
820  if( sqlite3_value_type(argv[0])!=SQLITE_NULL ){
821    sqlite3_free(pVTab->zErrMsg);
822    pVTab->zErrMsg = sqlite3_mprintf("cannot update a %s virtual table",
823                                     p->zClassName);
824    return SQLITE_CONSTRAINT;
825  }
826  zFrom = (char*)sqlite3_value_text(argv[4]);
827  if( zFrom==0 ) zFrom = "";
828  zTo = (char*)sqlite3_value_text(argv[5]);
829  if( zTo==0 ) zTo = "";
830  if( strcmp(zFrom,zTo)==0 ){
831    /* Silently ignore null transformations */
832    return SQLITE_OK;
833  }
834  rCost = sqlite3_value_int(argv[6]);
835  if( rCost<=0 ){
836    sqlite3_free(pVTab->zErrMsg);
837    pVTab->zErrMsg = sqlite3_mprintf("cost must be positive");
838    return SQLITE_CONSTRAINT;
839  }
840  nFrom = strlen(zFrom);
841  nTo = strlen(zTo);
842  pRule = sqlite3_malloc( sizeof(*pRule) + nFrom + nTo );
843  if( pRule==0 ){
844    return SQLITE_NOMEM;
845  }
846  pRule->zFrom = &pRule->zTo[nTo+1];
847  pRule->nFrom = nFrom;
848  memcpy(pRule->zFrom, zFrom, nFrom+1);
849  memcpy(pRule->zTo, zTo, nTo+1);
850  pRule->nTo = nTo;
851  pRule->rCost = rCost;
852  pRule->pNext = p->pNewRule;
853  p->pNewRule = pRule;
854  return SQLITE_OK;
855}
856
857/*
858** A virtual table module that provides read-only access to a
859** Tcl global variable namespace.
860*/
861static sqlite3_module fuzzerModule = {
862  0,                           /* iVersion */
863  fuzzerConnect,
864  fuzzerConnect,
865  fuzzerBestIndex,
866  fuzzerDisconnect,
867  fuzzerDisconnect,
868  fuzzerOpen,                  /* xOpen - open a cursor */
869  fuzzerClose,                 /* xClose - close a cursor */
870  fuzzerFilter,                /* xFilter - configure scan constraints */
871  fuzzerNext,                  /* xNext - advance a cursor */
872  fuzzerEof,                   /* xEof - check for end of scan */
873  fuzzerColumn,                /* xColumn - read data */
874  fuzzerRowid,                 /* xRowid - read data */
875  fuzzerUpdate,                /* xUpdate - INSERT */
876  0,                           /* xBegin */
877  0,                           /* xSync */
878  0,                           /* xCommit */
879  0,                           /* xRollback */
880  0,                           /* xFindMethod */
881  0,                           /* xRename */
882};
883
884#endif /* SQLITE_OMIT_VIRTUALTABLE */
885
886
887/*
888** Register the fuzzer virtual table
889*/
890int fuzzer_register(sqlite3 *db){
891  int rc = SQLITE_OK;
892#ifndef SQLITE_OMIT_VIRTUALTABLE
893  rc = sqlite3_create_module(db, "fuzzer", &fuzzerModule, 0);
894#endif
895  return rc;
896}
897
898#ifdef SQLITE_TEST
899#include <tcl.h>
900/*
901** Decode a pointer to an sqlite3 object.
902*/
903extern int getDbPointer(Tcl_Interp *interp, const char *zA, sqlite3 **ppDb);
904
905/*
906** Register the echo virtual table module.
907*/
908static int register_fuzzer_module(
909  ClientData clientData, /* Pointer to sqlite3_enable_XXX function */
910  Tcl_Interp *interp,    /* The TCL interpreter that invoked this command */
911  int objc,              /* Number of arguments */
912  Tcl_Obj *CONST objv[]  /* Command arguments */
913){
914  sqlite3 *db;
915  if( objc!=2 ){
916    Tcl_WrongNumArgs(interp, 1, objv, "DB");
917    return TCL_ERROR;
918  }
919  if( getDbPointer(interp, Tcl_GetString(objv[1]), &db) ) return TCL_ERROR;
920  fuzzer_register(db);
921  return TCL_OK;
922}
923
924
925/*
926** Register commands with the TCL interpreter.
927*/
928int Sqlitetestfuzzer_Init(Tcl_Interp *interp){
929  static struct {
930     char *zName;
931     Tcl_ObjCmdProc *xProc;
932     void *clientData;
933  } aObjCmd[] = {
934     { "register_fuzzer_module",   register_fuzzer_module, 0 },
935  };
936  int i;
937  for(i=0; i<sizeof(aObjCmd)/sizeof(aObjCmd[0]); i++){
938    Tcl_CreateObjCommand(interp, aObjCmd[i].zName,
939        aObjCmd[i].xProc, aObjCmd[i].clientData, 0);
940  }
941  return TCL_OK;
942}
943
944#endif /* SQLITE_TEST */
945