4 ** The author disclaims copyright to this source code. In place of
5 ** a legal notice, here is a blessing:
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.
11 *************************************************************************
13 ** Code for demonstartion virtual table that generates variations
14 ** on an input word at increasing edit distances from the original.
16 ** A fuzzer virtual table is created like this:
18 ** CREATE VIRTUAL TABLE temp.f USING fuzzer;
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.
26 ** Before being used, the fuzzer needs to be programmed by giving it
27 ** character transformations and a cost associated with each transformation.
30 ** INSERT INTO f(cFrom,cTo,Cost) VALUES('','a',100);
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.)
36 ** INSERT INTO f(cFrom,cTo,Cost) VALUES('b','',87);
38 ** The above statement says that the cost of deleting a single letter
41 ** INSERT INTO f(cFrom,cTo,Cost) VALUES('o','oe',38);
42 ** INSERT INTO f(cFrom,cTo,Cost) VALUES('oe','o',40);
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.
48 ** After all the transformation costs have been set, the fuzzer table
49 ** can be queried as follows:
51 ** SELECT word, distance FROM f
52 ** WHERE word MATCH 'abcdefg'
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.
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.
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.
75 ** SELECT vocabulary.w FROM f, vocabulary
76 ** WHERE f.word MATCH $word
77 ** AND f.distance<=200
78 ** AND f.word=vocabulary.w
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
85 ** A similar query can be used to find all words in the dictionary that
86 ** begin with some prefix $prefix:
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')
94 ** This last query will show up to 50 words out of the vocabulary that
95 ** match or nearly match the $prefix.
103 #ifndef SQLITE_OMIT_VIRTUALTABLE
106 ** Forward declaration of objects used by this implementation
108 typedef struct fuzzer_vtab fuzzer_vtab
;
109 typedef struct fuzzer_cursor fuzzer_cursor
;
110 typedef struct fuzzer_rule fuzzer_rule
;
111 typedef struct fuzzer_seen fuzzer_seen
;
112 typedef struct fuzzer_stem fuzzer_stem
;
115 ** Type of the "cost" of an edit operation. Might be changed to
116 ** "float" or "double" or "sqlite3_int64" in the future.
118 typedef int fuzzer_cost
;
122 ** Each transformation rule is stored as an instance of this object.
123 ** All rules are kept on a linked list sorted by rCost.
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) */
134 ** A stem object is used to generate variants. It is also used to record
135 ** previously generated outputs.
137 ** Every stem is added to a hash table as it is output. Generation of
138 ** duplicate stems is suppressed.
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
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 */
156 ** A fuzzer virtual-table object
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 */
166 #define FUZZER_HASH 4001 /* Hash table size */
167 #define FUZZER_NQUEUE 20 /* Number of slots on the stem queue */
169 /* A fuzzer cursor object */
170 struct 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 */
186 /* Methods for the fuzzer module */
187 static int fuzzerConnect(
190 int argc
, const char *const*argv
,
191 sqlite3_vtab
**ppVtab
,
196 if( strcmp(argv
[1],"temp")!=0 ){
197 *pzErr
= sqlite3_mprintf("%s virtual tables must be TEMP", argv
[0]);
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
;
210 /* Note that for this virtual table, the xCreate and xConnect
211 ** methods are identical. */
213 static int fuzzerDisconnect(sqlite3_vtab
*pVtab
){
214 fuzzer_vtab
*p
= (fuzzer_vtab
*)pVtab
;
215 assert( p
->nCursor
==0 );
218 fuzzer_rule
*pRule
= p
->pRule
;
219 p
->pRule
= pRule
->pNext
;
222 p
->pRule
= p
->pNewRule
;
228 /* The xDisconnect and xDestroy methods are also the same */
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.
235 static fuzzer_rule
*fuzzerMergeRules(fuzzer_rule
*pA
, fuzzer_rule
*pB
){
241 if( pA
->rCost
<=pB
->rCost
){
261 ** Open a new fuzzer cursor.
263 static int fuzzerOpen(sqlite3_vtab
*pVTab
, sqlite3_vtab_cursor
**ppCursor
){
264 fuzzer_vtab
*p
= (fuzzer_vtab
*)pVTab
;
266 pCur
= sqlite3_malloc( sizeof(*pCur
) );
267 if( pCur
==0 ) return SQLITE_NOMEM
;
268 memset(pCur
, 0, sizeof(*pCur
));
270 *ppCursor
= &pCur
->base
;
271 if( p
->nCursor
==0 && p
->pNewRule
){
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
;
279 for(i
=0; a
[i
] && i
<sizeof(a
)/sizeof(a
[0])-1; i
++){
280 pX
= fuzzerMergeRules(a
[i
], pX
);
283 a
[i
] = fuzzerMergeRules(a
[i
], pX
);
285 for(pX
=a
[0], i
=1; i
<sizeof(a
)/sizeof(a
[0]); i
++){
286 pX
= fuzzerMergeRules(a
[i
], pX
);
288 p
->pRule
= fuzzerMergeRules(p
->pRule
, pX
);
295 ** Free all stems in a list.
297 static void fuzzerClearStemList(fuzzer_stem
*pStem
){
299 fuzzer_stem
*pNext
= pStem
->pNext
;
306 ** Free up all the memory allocated by a cursor. Set it rLimit to 0
307 ** to indicate that it is at EOF.
309 static void fuzzerClearCursor(fuzzer_cursor
*pCur
, int clearHash
){
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
){
319 memset(pCur
->aQueue
, 0, sizeof(pCur
->aQueue
));
320 memset(pCur
->apHash
, 0, sizeof(pCur
->apHash
));
326 ** Close a fuzzer cursor.
328 static 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
--;
338 ** Compute the current output term for a fuzzer_stem.
340 static 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 */
345 const fuzzer_rule
*pRule
= pStem
->pRule
;
349 n
= pStem
->nBasis
+ pRule
->nTo
- pRule
->nFrom
;
351 (*pzBuf
) = sqlite3_realloc((*pzBuf
), n
+100);
352 if( (*pzBuf
)==0 ) return SQLITE_NOMEM
;
358 memcpy(z
, pStem
->zBasis
, pStem
->nBasis
+1);
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);
369 ** Compute a hash on zBasis.
371 static unsigned int fuzzerHash(const char *z
){
373 while( *z
){ h
= (h
<<3) ^ (h
>>29) ^ *(z
++); }
374 return h
% FUZZER_HASH
;
378 ** Current cost of a stem
380 static fuzzer_cost
fuzzerCost(fuzzer_stem
*pStem
){
381 return pStem
->rCostX
= pStem
->rBaseCost
+ pStem
->pRule
->rCost
;
386 ** Print a description of a fuzzer_stem on stderr.
388 static void fuzzerStemPrint(
394 fprintf(stderr
, "%s[%s](%d)-->self%s",
396 pStem
->zBasis
, pStem
->rBaseCost
,
402 if( fuzzerRender(pStem
, &zBuf
, &nBuf
)!=SQLITE_OK
) return;
403 fprintf(stderr
, "%s[%s](%d)-->{%s}(%d)%s",
405 pStem
->zBasis
, pStem
->rBaseCost
, zBuf
, pStem
->,
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
418 static int fuzzerSeen(fuzzer_cursor
*pCur
, fuzzer_stem
*pStem
){
420 fuzzer_stem
*pLookup
;
422 if( fuzzerRender(pStem
, &pCur
->zBuf
, &pCur
->nBuf
)==SQLITE_NOMEM
){
425 h
= fuzzerHash(pCur
->zBuf
);
426 pLookup
= pCur
->apHash
[h
];
427 while( pLookup
&& strcmp(pLookup
->zBasis
, pCur
->zBuf
)!=0 ){
428 pLookup
= pLookup
->pHash
;
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.
438 static 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
){
444 || memcmp(&pStem
->zBasis
[pStem
->n
], pRule
->zFrom
, pRule
->nFrom
)==0
446 /* Found a rewrite case. Make sure it is not a duplicate */
447 int rc
= fuzzerSeen(pCur
, pStem
);
448 if( rc
<0 ) return -1;
456 pStem
->pRule
= pRule
->pNext
;
457 if( pStem
->pRule
&& fuzzerCost(pStem
)>pCur
->rLimit
) pStem
->pRule
= 0;
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.
467 static fuzzer_stem
*fuzzerMergeStems(fuzzer_stem
*pA
, fuzzer_stem
*pB
){
473 if( pA
->rCostX
<=pB
->rCostX
){
492 ** Load pCur->pStem with the lowest-cost stem. Return a pointer
493 ** to the lowest-cost stem.
495 static fuzzer_stem
*fuzzerLowestCostStem(fuzzer_cursor
*pCur
){
496 fuzzer_stem
*pBest
, *pX
;
500 if( pCur
->pStem
==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
){
512 pCur
->aQueue
[iBest
] = pBest
->pNext
;
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.
526 static fuzzer_stem
*fuzzerInsert(fuzzer_cursor
*pCur
, fuzzer_stem
*pNew
){
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.
533 if( (pX
= pCur
->pStem
)!=0 && pX
->rCostX
>pNew
->rCostX
){
539 /* Insert the new value */
542 for(i
=0; i
<=pCur
->mxQueue
; i
++){
543 if( pCur
->aQueue
[i
] ){
544 pX
= fuzzerMergeStems(pX
, pCur
->aQueue
[i
]);
547 pCur
->aQueue
[i
] = pX
;
551 if( i
>pCur
->mxQueue
){
552 if( i
<FUZZER_NQUEUE
){
554 pCur
->aQueue
[i
] = pX
;
556 assert( pCur
->mxQueue
==FUZZER_NQUEUE
-1 );
557 pX
= fuzzerMergeStems(pX
, pCur
->aQueue
[FUZZER_NQUEUE
-1]);
558 pCur
->aQueue
[FUZZER_NQUEUE
-1] = pX
;
562 return fuzzerLowestCostStem(pCur
);
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.
569 static fuzzer_stem
*fuzzerNewStem(
572 fuzzer_cost rBaseCost
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
;
585 pNew
->rBaseCost
= pNew
->rCostX
= rBaseCost
;
586 h
= fuzzerHash(pNew
->zBasis
);
587 pNew
->pHash
= pCur
->apHash
[h
];
588 pCur
->apHash
[h
] = pNew
;
595 ** Advance a cursor to its next row of output
597 static int fuzzerNext(sqlite3_vtab_cursor
*cur
){
598 fuzzer_cursor
*pCur
= (fuzzer_cursor
*)cur
;
600 fuzzer_stem
*pStem
, *pNew
;
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.
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
);
613 if( fuzzerAdvance(pCur
, pNew
)==0 ){
614 pNew
->pNext
= pCur
->pDone
;
617 if( fuzzerInsert(pCur
, pNew
)==pNew
){
626 /* Adjust the priority queue so that the first element of the
627 ** stem list is the next lowest cost word.
629 while( (pStem
= pCur
->pStem
)!=0 ){
630 if( fuzzerAdvance(pCur
, pStem
) ){
632 pStem
= fuzzerInsert(pCur
, pStem
);
633 if( (rc
= fuzzerSeen(pCur
, pStem
))!=0 ){
634 if( rc
<0 ) return SQLITE_NOMEM
;
637 return SQLITE_OK
; /* New word found */
640 pStem
->pNext
= pCur
->pDone
;
642 if( fuzzerLowestCostStem(pCur
) ){
643 rc
= fuzzerSeen(pCur
, pCur
->pStem
);
644 if( rc
<0 ) return SQLITE_NOMEM
;
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;
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.
662 static int fuzzerFilter(
663 sqlite3_vtab_cursor
*pVtabCursor
,
664 int idxNum
, const char *idxStr
,
665 int argc
, sqlite3_value
**argv
667 fuzzer_cursor
*pCur
= (fuzzer_cursor
*)pVtabCursor
;
668 const char *zWord
= 0;
671 fuzzerClearCursor(pCur
, 1);
672 pCur
->rLimit
= 2147483647;
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]);
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
;
696 ** Only the word and distance columns have values. All other columns
699 static int fuzzerColumn(sqlite3_vtab_cursor
*cur
, sqlite3_context
*ctx
, int i
){
700 fuzzer_cursor
*pCur
= (fuzzer_cursor
*)cur
;
702 /* the "word" column */
703 if( fuzzerRender(pCur
->pStem
, &pCur
->zBuf
, &pCur
->nBuf
)==SQLITE_NOMEM
){
706 sqlite3_result_text(ctx
, pCur
->zBuf
, -1, SQLITE_TRANSIENT
);
708 /* the "distance" column */
709 sqlite3_result_int(ctx
, pCur
->pStem
->rCostX
);
711 /* All other columns are NULL */
712 sqlite3_result_null(ctx
);
720 static int fuzzerRowid(sqlite3_vtab_cursor
*cur
, sqlite_int64
*pRowid
){
721 fuzzer_cursor
*pCur
= (fuzzer_cursor
*)cur
;
722 *pRowid
= pCur
->iRowid
;
727 ** When the fuzzer_cursor.rLimit value is 0 or less, that is a signal
728 ** that the cursor has nothing more to output.
730 static int fuzzerEof(sqlite3_vtab_cursor
*cur
){
731 fuzzer_cursor
*pCur
= (fuzzer_cursor
*)cur
;
732 return pCur
->rLimit
<=(fuzzer_cost
)0;
736 ** Search for terms of these forms:
740 ** distance <= $value
742 ** The distance< and distance<= are both treated as distance<=.
743 ** The query plan number is as follows:
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].
751 static int fuzzerBestIndex(sqlite3_vtab
*tab
, sqlite3_index_info
*pIdxInfo
){
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;
760 && pConstraint
->iColumn
==0
761 && pConstraint
->op
==SQLITE_INDEX_CONSTRAINT_MATCH
764 pIdxInfo
->aConstraintUsage
[i
].argvIndex
= 1;
765 pIdxInfo
->aConstraintUsage
[i
].omit
= 1;
768 && pConstraint
->iColumn
==1
769 && (pConstraint
->op
==SQLITE_INDEX_CONSTRAINT_LT
770 || pConstraint
->op
==SQLITE_INDEX_CONSTRAINT_LE
)
777 pIdxInfo
->aConstraintUsage
[iDistTerm
].argvIndex
= 1;
778 }else if( iPlan
==3 ){
779 pIdxInfo
->aConstraintUsage
[iDistTerm
].argvIndex
= 2;
781 pIdxInfo
->idxNum
= iPlan
;
782 if( pIdxInfo
->nOrderBy
==1
783 && pIdxInfo
->aOrderBy
[0].iColumn
==1
784 && pIdxInfo
->aOrderBy
[0].desc
==0
786 pIdxInfo
->orderByConsumed
= 1;
788 pIdxInfo
->estimatedCost
= (double)10000;
794 ** Disallow all attempts to DELETE or UPDATE. Only INSERTs are allowed.
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.
801 static int fuzzerUpdate(
804 sqlite3_value
**argv
,
807 fuzzer_vtab
*p
= (fuzzer_vtab
*)pVTab
;
815 sqlite3_free(pVTab
->zErrMsg
);
816 pVTab
->zErrMsg
= sqlite3_mprintf("cannot delete from a %s virtual table",
818 return SQLITE_CONSTRAINT
;
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",
824 return SQLITE_CONSTRAINT
;
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 */
834 rCost
= sqlite3_value_int(argv
[6]);
836 sqlite3_free(pVTab
->zErrMsg
);
837 pVTab
->zErrMsg
= sqlite3_mprintf("cost must be positive");
838 return SQLITE_CONSTRAINT
;
840 nFrom
= strlen(zFrom
);
842 pRule
= sqlite3_malloc( sizeof(*pRule
) + nFrom
+ nTo
);
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);
851 pRule
->rCost
= rCost
;
852 pRule
->pNext
= p
->pNewRule
;
858 ** A virtual table module that provides read-only access to a
859 ** Tcl global variable namespace.
861 static sqlite3_module fuzzerModule
= {
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 */
884 #endif /* SQLITE_OMIT_VIRTUALTABLE */
888 ** Register the fuzzer virtual table
890 int fuzzer_register(sqlite3
*db
){
892 #ifndef SQLITE_OMIT_VIRTUALTABLE
893 rc
= sqlite3_create_module(db
, "fuzzer", &fuzzerModule
, 0);
901 ** Decode a pointer to an sqlite3 object.
903 extern int getDbPointer(Tcl_Interp
*interp
, const char *zA
, sqlite3
**ppDb
);
906 ** Register the echo virtual table module.
908 static 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 */
916 Tcl_WrongNumArgs(interp
, 1, objv
, "DB");
919 if( getDbPointer(interp
, Tcl_GetString(objv
[1]), &db
) ) return TCL_ERROR
;
926 ** Register commands with the TCL interpreter.
928 int Sqlitetestfuzzer_Init(Tcl_Interp
*interp
){
931 Tcl_ObjCmdProc
*xProc
;
934 { "register_fuzzer_module", register_fuzzer_module
, 0 },
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);
944 #endif /* SQLITE_TEST */