1e50db1c5Sdrh /*
2e50db1c5Sdrh ** 2011 March 24
3e50db1c5Sdrh **
4e50db1c5Sdrh ** The author disclaims copyright to this source code. In place of
5e50db1c5Sdrh ** a legal notice, here is a blessing:
6e50db1c5Sdrh **
7e50db1c5Sdrh ** May you do good and not evil.
8e50db1c5Sdrh ** May you find forgiveness for yourself and forgive others.
9e50db1c5Sdrh ** May you share freely, never taking more than you give.
10e50db1c5Sdrh **
11e50db1c5Sdrh *************************************************************************
12e50db1c5Sdrh **
13e50db1c5Sdrh ** Code for a demonstration virtual table that generates variations
14e50db1c5Sdrh ** on an input word at increasing edit distances from the original.
15e50db1c5Sdrh **
16e50db1c5Sdrh ** A fuzzer virtual table is created like this:
17e50db1c5Sdrh **
18e50db1c5Sdrh ** CREATE VIRTUAL TABLE f USING fuzzer(<fuzzer-data-table>);
19e50db1c5Sdrh **
20e50db1c5Sdrh ** When it is created, the new fuzzer table must be supplied with the
21e50db1c5Sdrh ** name of a "fuzzer data table", which must reside in the same database
22e50db1c5Sdrh ** file as the new fuzzer table. The fuzzer data table contains the various
23e50db1c5Sdrh ** transformations and their costs that the fuzzer logic uses to generate
24e50db1c5Sdrh ** variations.
25e50db1c5Sdrh **
26e50db1c5Sdrh ** The fuzzer data table must contain exactly four columns (more precisely,
27e50db1c5Sdrh ** the statement "SELECT * FROM <fuzzer_data_table>" must return records
28e50db1c5Sdrh ** that consist of four columns). It does not matter what the columns are
29e50db1c5Sdrh ** named.
30e50db1c5Sdrh **
31e50db1c5Sdrh ** Each row in the fuzzer data table represents a single character
32e50db1c5Sdrh ** transformation. The left most column of the row (column 0) contains an
33e50db1c5Sdrh ** integer value - the identifier of the ruleset to which the transformation
34e50db1c5Sdrh ** rule belongs (see "MULTIPLE RULE SETS" below). The second column of the
35e50db1c5Sdrh ** row (column 0) contains the input character or characters. The third
36e50db1c5Sdrh ** column contains the output character or characters. And the fourth column
37e50db1c5Sdrh ** contains the integer cost of making the transformation. For example:
38e50db1c5Sdrh **
39e50db1c5Sdrh ** CREATE TABLE f_data(ruleset, cFrom, cTo, Cost);
40e50db1c5Sdrh ** INSERT INTO f_data(ruleset, cFrom, cTo, Cost) VALUES(0, '', 'a', 100);
41e50db1c5Sdrh ** INSERT INTO f_data(ruleset, cFrom, cTo, Cost) VALUES(0, 'b', '', 87);
42e50db1c5Sdrh ** INSERT INTO f_data(ruleset, cFrom, cTo, Cost) VALUES(0, 'o', 'oe', 38);
43e50db1c5Sdrh ** INSERT INTO f_data(ruleset, cFrom, cTo, Cost) VALUES(0, 'oe', 'o', 40);
44e50db1c5Sdrh **
45e50db1c5Sdrh ** The first row inserted into the fuzzer data table by the SQL script
46e50db1c5Sdrh ** above indicates that the cost of inserting a letter 'a' is 100. (All
47e50db1c5Sdrh ** costs are integers. We recommend that costs be scaled so that the
48e50db1c5Sdrh ** average cost is around 100.) The second INSERT statement creates a rule
49e50db1c5Sdrh ** saying that the cost of deleting a single letter 'b' is 87. The third
50e50db1c5Sdrh ** and fourth INSERT statements mean that the cost of transforming a
51e50db1c5Sdrh ** single letter "o" into the two-letter sequence "oe" is 38 and that the
52e50db1c5Sdrh ** cost of transforming "oe" back into "o" is 40.
53e50db1c5Sdrh **
54e50db1c5Sdrh ** The contents of the fuzzer data table are loaded into main memory when
55e50db1c5Sdrh ** a fuzzer table is first created, and may be internally reloaded by the
56e50db1c5Sdrh ** system at any subsequent time. Therefore, the fuzzer data table should be
57e50db1c5Sdrh ** populated before the fuzzer table is created and not modified thereafter.
58e50db1c5Sdrh ** If you do need to modify the contents of the fuzzer data table, it is
59e50db1c5Sdrh ** recommended that the associated fuzzer table be dropped, the fuzzer data
60e50db1c5Sdrh ** table edited, and the fuzzer table recreated within a single transaction.
61e50db1c5Sdrh ** Alternatively, the fuzzer data table can be edited then the database
62e50db1c5Sdrh ** connection can be closed and reopened.
63e50db1c5Sdrh **
64e50db1c5Sdrh ** Once it has been created, the fuzzer table can be queried as follows:
65e50db1c5Sdrh **
66e50db1c5Sdrh ** SELECT word, distance FROM f
67e50db1c5Sdrh ** WHERE word MATCH 'abcdefg'
68e50db1c5Sdrh ** AND distance<200;
69e50db1c5Sdrh **
70e50db1c5Sdrh ** This first query outputs the string "abcdefg" and all strings that
71e50db1c5Sdrh ** can be derived from that string by appling the specified transformations.
72e50db1c5Sdrh ** The strings are output together with their total transformation cost
73e50db1c5Sdrh ** (called "distance") and appear in order of increasing cost. No string
74e50db1c5Sdrh ** is output more than once. If there are multiple ways to transform the
75e50db1c5Sdrh ** target string into the output string then the lowest cost transform is
76e50db1c5Sdrh ** the one that is returned. In the example, the search is limited to
77e50db1c5Sdrh ** strings with a total distance of less than 200.
78e50db1c5Sdrh **
79e50db1c5Sdrh ** The fuzzer is a read-only table. Any attempt to DELETE, INSERT, or
80e50db1c5Sdrh ** UPDATE on a fuzzer table will throw an error.
81e50db1c5Sdrh **
82e50db1c5Sdrh ** It is important to put some kind of a limit on the fuzzer output. This
83e50db1c5Sdrh ** can be either in the form of a LIMIT clause at the end of the query,
84e50db1c5Sdrh ** or better, a "distance<NNN" constraint where NNN is some number. The
85e50db1c5Sdrh ** running time and memory requirement is exponential in the value of NNN
86e50db1c5Sdrh ** so you want to make sure that NNN is not too big. A value of NNN that
87e50db1c5Sdrh ** is about twice the average transformation cost seems to give good results.
88e50db1c5Sdrh **
89e50db1c5Sdrh ** The fuzzer table can be useful for tasks such as spelling correction.
90e50db1c5Sdrh ** Suppose there is a second table vocabulary(w) where the w column contains
91e50db1c5Sdrh ** all correctly spelled words. Let $word be a word you want to look up.
92e50db1c5Sdrh **
93e50db1c5Sdrh ** SELECT vocabulary.w FROM f, vocabulary
94e50db1c5Sdrh ** WHERE f.word MATCH $word
95e50db1c5Sdrh ** AND f.distance<=200
96e50db1c5Sdrh ** AND f.word=vocabulary.w
97e50db1c5Sdrh ** LIMIT 20
98e50db1c5Sdrh **
99e50db1c5Sdrh ** The query above gives the 20 closest words to the $word being tested.
100e50db1c5Sdrh ** (Note that for good performance, the vocubulary.w column should be
101e50db1c5Sdrh ** indexed.)
102e50db1c5Sdrh **
103e50db1c5Sdrh ** A similar query can be used to find all words in the dictionary that
104e50db1c5Sdrh ** begin with some prefix $prefix:
105e50db1c5Sdrh **
106e50db1c5Sdrh ** SELECT vocabulary.w FROM f, vocabulary
107e50db1c5Sdrh ** WHERE f.word MATCH $prefix
108e50db1c5Sdrh ** AND f.distance<=200
109e50db1c5Sdrh ** AND vocabulary.w BETWEEN f.word AND (f.word || x'F7BFBFBF')
110e50db1c5Sdrh ** LIMIT 50
111e50db1c5Sdrh **
112e50db1c5Sdrh ** This last query will show up to 50 words out of the vocabulary that
113e50db1c5Sdrh ** match or nearly match the $prefix.
114e50db1c5Sdrh **
115e50db1c5Sdrh ** MULTIPLE RULE SETS
116e50db1c5Sdrh **
117e50db1c5Sdrh ** Normally, the "ruleset" value associated with all character transformations
118e50db1c5Sdrh ** in the fuzzer data table is zero. However, if required, the fuzzer table
119e50db1c5Sdrh ** allows multiple rulesets to be defined. Each query uses only a single
120e50db1c5Sdrh ** ruleset. This allows, for example, a single fuzzer table to support
121e50db1c5Sdrh ** multiple languages.
122e50db1c5Sdrh **
123e50db1c5Sdrh ** By default, only the rules from ruleset 0 are used. To specify an
124e50db1c5Sdrh ** alternative ruleset, a "ruleset = ?" expression must be added to the
125e50db1c5Sdrh ** WHERE clause of a SELECT, where ? is the identifier of the desired
126e50db1c5Sdrh ** ruleset. For example:
127e50db1c5Sdrh **
128e50db1c5Sdrh ** SELECT vocabulary.w FROM f, vocabulary
129e50db1c5Sdrh ** WHERE f.word MATCH $word
130e50db1c5Sdrh ** AND f.distance<=200
131e50db1c5Sdrh ** AND f.word=vocabulary.w
132e50db1c5Sdrh ** AND f.ruleset=1 -- Specify the ruleset to use here
133e50db1c5Sdrh ** LIMIT 20
134e50db1c5Sdrh **
135e50db1c5Sdrh ** If no "ruleset = ?" constraint is specified in the WHERE clause, ruleset
136e50db1c5Sdrh ** 0 is used.
137e50db1c5Sdrh **
138e50db1c5Sdrh ** LIMITS
139e50db1c5Sdrh **
140e50db1c5Sdrh ** The maximum ruleset number is 2147483647. The maximum length of either
141e50db1c5Sdrh ** of the strings in the second or third column of the fuzzer data table
142e50db1c5Sdrh ** is 50 bytes. The maximum cost on a rule is 1000.
143e50db1c5Sdrh */
144e50db1c5Sdrh #include "sqlite3ext.h"
145e50db1c5Sdrh SQLITE_EXTENSION_INIT1
146e50db1c5Sdrh
147e50db1c5Sdrh /* If SQLITE_DEBUG is not defined, disable assert statements. */
148e50db1c5Sdrh #if !defined(NDEBUG) && !defined(SQLITE_DEBUG)
149e50db1c5Sdrh # define NDEBUG
150e50db1c5Sdrh #endif
151e50db1c5Sdrh
152e50db1c5Sdrh #include <stdlib.h>
153e50db1c5Sdrh #include <string.h>
154e50db1c5Sdrh #include <assert.h>
155e50db1c5Sdrh #include <stdio.h>
156e50db1c5Sdrh
157e50db1c5Sdrh #ifndef SQLITE_OMIT_VIRTUALTABLE
158e50db1c5Sdrh
159e50db1c5Sdrh /*
160e50db1c5Sdrh ** Forward declaration of objects used by this implementation
161e50db1c5Sdrh */
162e50db1c5Sdrh typedef struct fuzzer_vtab fuzzer_vtab;
163e50db1c5Sdrh typedef struct fuzzer_cursor fuzzer_cursor;
164e50db1c5Sdrh typedef struct fuzzer_rule fuzzer_rule;
165e50db1c5Sdrh typedef struct fuzzer_seen fuzzer_seen;
166e50db1c5Sdrh typedef struct fuzzer_stem fuzzer_stem;
167e50db1c5Sdrh
168e50db1c5Sdrh /*
169e50db1c5Sdrh ** Various types.
170e50db1c5Sdrh **
171e50db1c5Sdrh ** fuzzer_cost is the "cost" of an edit operation.
172e50db1c5Sdrh **
173e50db1c5Sdrh ** fuzzer_len is the length of a matching string.
174e50db1c5Sdrh **
175e50db1c5Sdrh ** fuzzer_ruleid is an ruleset identifier.
176e50db1c5Sdrh */
177e50db1c5Sdrh typedef int fuzzer_cost;
178e50db1c5Sdrh typedef signed char fuzzer_len;
179e50db1c5Sdrh typedef int fuzzer_ruleid;
180e50db1c5Sdrh
181e50db1c5Sdrh /*
182e50db1c5Sdrh ** Limits
183e50db1c5Sdrh */
184e50db1c5Sdrh #define FUZZER_MX_LENGTH 50 /* Maximum length of a rule string */
185e50db1c5Sdrh #define FUZZER_MX_RULEID 2147483647 /* Maximum rule ID */
186e50db1c5Sdrh #define FUZZER_MX_COST 1000 /* Maximum single-rule cost */
187e50db1c5Sdrh #define FUZZER_MX_OUTPUT_LENGTH 100 /* Maximum length of an output string */
188e50db1c5Sdrh
189e50db1c5Sdrh
190e50db1c5Sdrh /*
191e50db1c5Sdrh ** Each transformation rule is stored as an instance of this object.
192e50db1c5Sdrh ** All rules are kept on a linked list sorted by rCost.
193e50db1c5Sdrh */
194e50db1c5Sdrh struct fuzzer_rule {
195e50db1c5Sdrh fuzzer_rule *pNext; /* Next rule in order of increasing rCost */
196e50db1c5Sdrh char *zFrom; /* Transform from */
197e50db1c5Sdrh fuzzer_cost rCost; /* Cost of this transformation */
198e50db1c5Sdrh fuzzer_len nFrom, nTo; /* Length of the zFrom and zTo strings */
199e50db1c5Sdrh fuzzer_ruleid iRuleset; /* The rule set to which this rule belongs */
200e50db1c5Sdrh char zTo[4]; /* Transform to (extra space appended) */
201e50db1c5Sdrh };
202e50db1c5Sdrh
203e50db1c5Sdrh /*
204e50db1c5Sdrh ** A stem object is used to generate variants. It is also used to record
205e50db1c5Sdrh ** previously generated outputs.
206e50db1c5Sdrh **
207e50db1c5Sdrh ** Every stem is added to a hash table as it is output. Generation of
208e50db1c5Sdrh ** duplicate stems is suppressed.
209e50db1c5Sdrh **
210e50db1c5Sdrh ** Active stems (those that might generate new outputs) are kepts on a linked
211e50db1c5Sdrh ** list sorted by increasing cost. The cost is the sum of rBaseCost and
212e50db1c5Sdrh ** pRule->rCost.
213e50db1c5Sdrh */
214e50db1c5Sdrh struct fuzzer_stem {
215e50db1c5Sdrh char *zBasis; /* Word being fuzzed */
216e50db1c5Sdrh const fuzzer_rule *pRule; /* Current rule to apply */
217e50db1c5Sdrh fuzzer_stem *pNext; /* Next stem in rCost order */
218e50db1c5Sdrh fuzzer_stem *pHash; /* Next stem with same hash on zBasis */
219e50db1c5Sdrh fuzzer_cost rBaseCost; /* Base cost of getting to zBasis */
220e50db1c5Sdrh fuzzer_cost rCostX; /* Precomputed rBaseCost + pRule->rCost */
221e50db1c5Sdrh fuzzer_len nBasis; /* Length of the zBasis string */
222e50db1c5Sdrh fuzzer_len n; /* Apply pRule at this character offset */
223e50db1c5Sdrh };
224e50db1c5Sdrh
225e50db1c5Sdrh /*
226e50db1c5Sdrh ** A fuzzer virtual-table object
227e50db1c5Sdrh */
228e50db1c5Sdrh struct fuzzer_vtab {
229e50db1c5Sdrh sqlite3_vtab base; /* Base class - must be first */
230e50db1c5Sdrh char *zClassName; /* Name of this class. Default: "fuzzer" */
231e50db1c5Sdrh fuzzer_rule *pRule; /* All active rules in this fuzzer */
232e50db1c5Sdrh int nCursor; /* Number of active cursors */
233e50db1c5Sdrh };
234e50db1c5Sdrh
235e50db1c5Sdrh #define FUZZER_HASH 4001 /* Hash table size */
236e50db1c5Sdrh #define FUZZER_NQUEUE 20 /* Number of slots on the stem queue */
237e50db1c5Sdrh
238e50db1c5Sdrh /* A fuzzer cursor object */
239e50db1c5Sdrh struct fuzzer_cursor {
240e50db1c5Sdrh sqlite3_vtab_cursor base; /* Base class - must be first */
241e50db1c5Sdrh sqlite3_int64 iRowid; /* The rowid of the current word */
242e50db1c5Sdrh fuzzer_vtab *pVtab; /* The virtual table this cursor belongs to */
243e50db1c5Sdrh fuzzer_cost rLimit; /* Maximum cost of any term */
244e50db1c5Sdrh fuzzer_stem *pStem; /* Stem with smallest rCostX */
245e50db1c5Sdrh fuzzer_stem *pDone; /* Stems already processed to completion */
246e50db1c5Sdrh fuzzer_stem *aQueue[FUZZER_NQUEUE]; /* Queue of stems with higher rCostX */
247e50db1c5Sdrh int mxQueue; /* Largest used index in aQueue[] */
248e50db1c5Sdrh char *zBuf; /* Temporary use buffer */
249e50db1c5Sdrh int nBuf; /* Bytes allocated for zBuf */
250e50db1c5Sdrh int nStem; /* Number of stems allocated */
251e50db1c5Sdrh int iRuleset; /* Only process rules from this ruleset */
252e50db1c5Sdrh fuzzer_rule nullRule; /* Null rule used first */
253e50db1c5Sdrh fuzzer_stem *apHash[FUZZER_HASH]; /* Hash of previously generated terms */
254e50db1c5Sdrh };
255e50db1c5Sdrh
256e50db1c5Sdrh /*
257e50db1c5Sdrh ** The two input rule lists are both sorted in order of increasing
258e50db1c5Sdrh ** cost. Merge them together into a single list, sorted by cost, and
259e50db1c5Sdrh ** return a pointer to the head of that list.
260e50db1c5Sdrh */
fuzzerMergeRules(fuzzer_rule * pA,fuzzer_rule * pB)261e50db1c5Sdrh static fuzzer_rule *fuzzerMergeRules(fuzzer_rule *pA, fuzzer_rule *pB){
262e50db1c5Sdrh fuzzer_rule head;
263e50db1c5Sdrh fuzzer_rule *pTail;
264e50db1c5Sdrh
265e50db1c5Sdrh pTail = &head;
266e50db1c5Sdrh while( pA && pB ){
267e50db1c5Sdrh if( pA->rCost<=pB->rCost ){
268e50db1c5Sdrh pTail->pNext = pA;
269e50db1c5Sdrh pTail = pA;
270e50db1c5Sdrh pA = pA->pNext;
271e50db1c5Sdrh }else{
272e50db1c5Sdrh pTail->pNext = pB;
273e50db1c5Sdrh pTail = pB;
274e50db1c5Sdrh pB = pB->pNext;
275e50db1c5Sdrh }
276e50db1c5Sdrh }
277e50db1c5Sdrh if( pA==0 ){
278e50db1c5Sdrh pTail->pNext = pB;
279e50db1c5Sdrh }else{
280e50db1c5Sdrh pTail->pNext = pA;
281e50db1c5Sdrh }
282e50db1c5Sdrh return head.pNext;
283e50db1c5Sdrh }
284e50db1c5Sdrh
285e50db1c5Sdrh /*
286e50db1c5Sdrh ** Statement pStmt currently points to a row in the fuzzer data table. This
287e50db1c5Sdrh ** function allocates and populates a fuzzer_rule structure according to
288e50db1c5Sdrh ** the content of the row.
289e50db1c5Sdrh **
290e50db1c5Sdrh ** If successful, *ppRule is set to point to the new object and SQLITE_OK
291e50db1c5Sdrh ** is returned. Otherwise, *ppRule is zeroed, *pzErr may be set to point
292e50db1c5Sdrh ** to an error message and an SQLite error code returned.
293e50db1c5Sdrh */
fuzzerLoadOneRule(fuzzer_vtab * p,sqlite3_stmt * pStmt,fuzzer_rule ** ppRule,char ** pzErr)294e50db1c5Sdrh static int fuzzerLoadOneRule(
295e50db1c5Sdrh fuzzer_vtab *p, /* Fuzzer virtual table handle */
296e50db1c5Sdrh sqlite3_stmt *pStmt, /* Base rule on statements current row */
297e50db1c5Sdrh fuzzer_rule **ppRule, /* OUT: New rule object */
298e50db1c5Sdrh char **pzErr /* OUT: Error message */
299e50db1c5Sdrh ){
300e50db1c5Sdrh sqlite3_int64 iRuleset = sqlite3_column_int64(pStmt, 0);
301e50db1c5Sdrh const char *zFrom = (const char *)sqlite3_column_text(pStmt, 1);
302e50db1c5Sdrh const char *zTo = (const char *)sqlite3_column_text(pStmt, 2);
303e50db1c5Sdrh int nCost = sqlite3_column_int(pStmt, 3);
304e50db1c5Sdrh
305e50db1c5Sdrh int rc = SQLITE_OK; /* Return code */
306e50db1c5Sdrh int nFrom; /* Size of string zFrom, in bytes */
307e50db1c5Sdrh int nTo; /* Size of string zTo, in bytes */
308e50db1c5Sdrh fuzzer_rule *pRule = 0; /* New rule object to return */
309e50db1c5Sdrh
310e50db1c5Sdrh if( zFrom==0 ) zFrom = "";
311e50db1c5Sdrh if( zTo==0 ) zTo = "";
312e50db1c5Sdrh nFrom = (int)strlen(zFrom);
313e50db1c5Sdrh nTo = (int)strlen(zTo);
314e50db1c5Sdrh
315e50db1c5Sdrh /* Silently ignore null transformations */
316e50db1c5Sdrh if( strcmp(zFrom, zTo)==0 ){
317e50db1c5Sdrh *ppRule = 0;
318e50db1c5Sdrh return SQLITE_OK;
319e50db1c5Sdrh }
320e50db1c5Sdrh
321e50db1c5Sdrh if( nCost<=0 || nCost>FUZZER_MX_COST ){
322e50db1c5Sdrh *pzErr = sqlite3_mprintf("%s: cost must be between 1 and %d",
323e50db1c5Sdrh p->zClassName, FUZZER_MX_COST
324e50db1c5Sdrh );
325e50db1c5Sdrh rc = SQLITE_ERROR;
326e50db1c5Sdrh }else
327e50db1c5Sdrh if( nFrom>FUZZER_MX_LENGTH || nTo>FUZZER_MX_LENGTH ){
328e50db1c5Sdrh *pzErr = sqlite3_mprintf("%s: maximum string length is %d",
329e50db1c5Sdrh p->zClassName, FUZZER_MX_LENGTH
330e50db1c5Sdrh );
331e50db1c5Sdrh rc = SQLITE_ERROR;
332e50db1c5Sdrh }else
333e50db1c5Sdrh if( iRuleset<0 || iRuleset>FUZZER_MX_RULEID ){
334e50db1c5Sdrh *pzErr = sqlite3_mprintf("%s: ruleset must be between 0 and %d",
335e50db1c5Sdrh p->zClassName, FUZZER_MX_RULEID
336e50db1c5Sdrh );
337e50db1c5Sdrh rc = SQLITE_ERROR;
338e50db1c5Sdrh }else{
339e50db1c5Sdrh
3402d77d80aSdrh pRule = sqlite3_malloc64( sizeof(*pRule) + nFrom + nTo );
341e50db1c5Sdrh if( pRule==0 ){
342e50db1c5Sdrh rc = SQLITE_NOMEM;
343e50db1c5Sdrh }else{
344e50db1c5Sdrh memset(pRule, 0, sizeof(*pRule));
3454081d5daSdrh pRule->zFrom = pRule->zTo;
3464081d5daSdrh pRule->zFrom += nTo + 1;
34777fac879Smistachkin pRule->nFrom = (fuzzer_len)nFrom;
348e50db1c5Sdrh memcpy(pRule->zFrom, zFrom, nFrom+1);
349e50db1c5Sdrh memcpy(pRule->zTo, zTo, nTo+1);
35077fac879Smistachkin pRule->nTo = (fuzzer_len)nTo;
351e50db1c5Sdrh pRule->rCost = nCost;
352e50db1c5Sdrh pRule->iRuleset = (int)iRuleset;
353e50db1c5Sdrh }
354e50db1c5Sdrh }
355e50db1c5Sdrh
356e50db1c5Sdrh *ppRule = pRule;
357e50db1c5Sdrh return rc;
358e50db1c5Sdrh }
359e50db1c5Sdrh
360e50db1c5Sdrh /*
361e50db1c5Sdrh ** Load the content of the fuzzer data table into memory.
362e50db1c5Sdrh */
fuzzerLoadRules(sqlite3 * db,fuzzer_vtab * p,const char * zDb,const char * zData,char ** pzErr)363e50db1c5Sdrh static int fuzzerLoadRules(
364e50db1c5Sdrh sqlite3 *db, /* Database handle */
365e50db1c5Sdrh fuzzer_vtab *p, /* Virtual fuzzer table to configure */
366e50db1c5Sdrh const char *zDb, /* Database containing rules data */
367e50db1c5Sdrh const char *zData, /* Table containing rules data */
368e50db1c5Sdrh char **pzErr /* OUT: Error message */
369e50db1c5Sdrh ){
370e50db1c5Sdrh int rc = SQLITE_OK; /* Return code */
371e50db1c5Sdrh char *zSql; /* SELECT used to read from rules table */
372e50db1c5Sdrh fuzzer_rule *pHead = 0;
373e50db1c5Sdrh
374e50db1c5Sdrh zSql = sqlite3_mprintf("SELECT * FROM %Q.%Q", zDb, zData);
375e50db1c5Sdrh if( zSql==0 ){
376e50db1c5Sdrh rc = SQLITE_NOMEM;
377e50db1c5Sdrh }else{
378e50db1c5Sdrh int rc2; /* finalize() return code */
379e50db1c5Sdrh sqlite3_stmt *pStmt = 0;
380e50db1c5Sdrh rc = sqlite3_prepare_v2(db, zSql, -1, &pStmt, 0);
381e50db1c5Sdrh if( rc!=SQLITE_OK ){
382e50db1c5Sdrh *pzErr = sqlite3_mprintf("%s: %s", p->zClassName, sqlite3_errmsg(db));
383e50db1c5Sdrh }else if( sqlite3_column_count(pStmt)!=4 ){
384e50db1c5Sdrh *pzErr = sqlite3_mprintf("%s: %s has %d columns, expected 4",
385e50db1c5Sdrh p->zClassName, zData, sqlite3_column_count(pStmt)
386e50db1c5Sdrh );
387e50db1c5Sdrh rc = SQLITE_ERROR;
388e50db1c5Sdrh }else{
389e50db1c5Sdrh while( rc==SQLITE_OK && SQLITE_ROW==sqlite3_step(pStmt) ){
390e50db1c5Sdrh fuzzer_rule *pRule = 0;
391e50db1c5Sdrh rc = fuzzerLoadOneRule(p, pStmt, &pRule, pzErr);
392e50db1c5Sdrh if( pRule ){
393e50db1c5Sdrh pRule->pNext = pHead;
394e50db1c5Sdrh pHead = pRule;
395e50db1c5Sdrh }
396e50db1c5Sdrh }
397e50db1c5Sdrh }
398e50db1c5Sdrh rc2 = sqlite3_finalize(pStmt);
399e50db1c5Sdrh if( rc==SQLITE_OK ) rc = rc2;
400e50db1c5Sdrh }
401e50db1c5Sdrh sqlite3_free(zSql);
402e50db1c5Sdrh
403e50db1c5Sdrh /* All rules are now in a singly linked list starting at pHead. This
404e50db1c5Sdrh ** block sorts them by cost and then sets fuzzer_vtab.pRule to point to
405e50db1c5Sdrh ** point to the head of the sorted list.
406e50db1c5Sdrh */
407e50db1c5Sdrh if( rc==SQLITE_OK ){
408e50db1c5Sdrh unsigned int i;
409e50db1c5Sdrh fuzzer_rule *pX;
410e50db1c5Sdrh fuzzer_rule *a[15];
411e50db1c5Sdrh for(i=0; i<sizeof(a)/sizeof(a[0]); i++) a[i] = 0;
412e50db1c5Sdrh while( (pX = pHead)!=0 ){
413e50db1c5Sdrh pHead = pX->pNext;
414e50db1c5Sdrh pX->pNext = 0;
415e50db1c5Sdrh for(i=0; a[i] && i<sizeof(a)/sizeof(a[0])-1; i++){
416e50db1c5Sdrh pX = fuzzerMergeRules(a[i], pX);
417e50db1c5Sdrh a[i] = 0;
418e50db1c5Sdrh }
419e50db1c5Sdrh a[i] = fuzzerMergeRules(a[i], pX);
420e50db1c5Sdrh }
421e50db1c5Sdrh for(pX=a[0], i=1; i<sizeof(a)/sizeof(a[0]); i++){
422e50db1c5Sdrh pX = fuzzerMergeRules(a[i], pX);
423e50db1c5Sdrh }
424e50db1c5Sdrh p->pRule = fuzzerMergeRules(p->pRule, pX);
425e50db1c5Sdrh }else{
426e50db1c5Sdrh /* An error has occurred. Setting p->pRule to point to the head of the
427e50db1c5Sdrh ** allocated list ensures that the list will be cleaned up in this case.
428e50db1c5Sdrh */
429e50db1c5Sdrh assert( p->pRule==0 );
430e50db1c5Sdrh p->pRule = pHead;
431e50db1c5Sdrh }
432e50db1c5Sdrh
433e50db1c5Sdrh return rc;
434e50db1c5Sdrh }
435e50db1c5Sdrh
436e50db1c5Sdrh /*
437e50db1c5Sdrh ** This function converts an SQL quoted string into an unquoted string
438e50db1c5Sdrh ** and returns a pointer to a buffer allocated using sqlite3_malloc()
439e50db1c5Sdrh ** containing the result. The caller should eventually free this buffer
440e50db1c5Sdrh ** using sqlite3_free.
441e50db1c5Sdrh **
442e50db1c5Sdrh ** Examples:
443e50db1c5Sdrh **
444e50db1c5Sdrh ** "abc" becomes abc
445e50db1c5Sdrh ** 'xyz' becomes xyz
446e50db1c5Sdrh ** [pqr] becomes pqr
447e50db1c5Sdrh ** `mno` becomes mno
448e50db1c5Sdrh */
fuzzerDequote(const char * zIn)449e50db1c5Sdrh static char *fuzzerDequote(const char *zIn){
4502d77d80aSdrh sqlite3_int64 nIn; /* Size of input string, in bytes */
451e50db1c5Sdrh char *zOut; /* Output (dequoted) string */
452e50db1c5Sdrh
4532d77d80aSdrh nIn = strlen(zIn);
4542d77d80aSdrh zOut = sqlite3_malloc64(nIn+1);
455e50db1c5Sdrh if( zOut ){
456e50db1c5Sdrh char q = zIn[0]; /* Quote character (if any ) */
457e50db1c5Sdrh
458e50db1c5Sdrh if( q!='[' && q!= '\'' && q!='"' && q!='`' ){
459065f3bf4Smistachkin memcpy(zOut, zIn, (size_t)(nIn+1));
460e50db1c5Sdrh }else{
461e50db1c5Sdrh int iOut = 0; /* Index of next byte to write to output */
462e50db1c5Sdrh int iIn; /* Index of next byte to read from input */
463e50db1c5Sdrh
464e50db1c5Sdrh if( q=='[' ) q = ']';
465e50db1c5Sdrh for(iIn=1; iIn<nIn; iIn++){
466e50db1c5Sdrh if( zIn[iIn]==q ) iIn++;
467e50db1c5Sdrh zOut[iOut++] = zIn[iIn];
468e50db1c5Sdrh }
469e50db1c5Sdrh }
470e50db1c5Sdrh assert( (int)strlen(zOut)<=nIn );
471e50db1c5Sdrh }
472e50db1c5Sdrh return zOut;
473e50db1c5Sdrh }
474e50db1c5Sdrh
475e50db1c5Sdrh /*
476e50db1c5Sdrh ** xDisconnect/xDestroy method for the fuzzer module.
477e50db1c5Sdrh */
fuzzerDisconnect(sqlite3_vtab * pVtab)478e50db1c5Sdrh static int fuzzerDisconnect(sqlite3_vtab *pVtab){
479e50db1c5Sdrh fuzzer_vtab *p = (fuzzer_vtab*)pVtab;
480e50db1c5Sdrh assert( p->nCursor==0 );
481e50db1c5Sdrh while( p->pRule ){
482e50db1c5Sdrh fuzzer_rule *pRule = p->pRule;
483e50db1c5Sdrh p->pRule = pRule->pNext;
484e50db1c5Sdrh sqlite3_free(pRule);
485e50db1c5Sdrh }
486e50db1c5Sdrh sqlite3_free(p);
487e50db1c5Sdrh return SQLITE_OK;
488e50db1c5Sdrh }
489e50db1c5Sdrh
490e50db1c5Sdrh /*
491e50db1c5Sdrh ** xConnect/xCreate method for the fuzzer module. Arguments are:
492e50db1c5Sdrh **
493e50db1c5Sdrh ** argv[0] -> module name ("fuzzer")
494e50db1c5Sdrh ** argv[1] -> database name
495e50db1c5Sdrh ** argv[2] -> table name
496e50db1c5Sdrh ** argv[3] -> fuzzer rule table name
497e50db1c5Sdrh */
fuzzerConnect(sqlite3 * db,void * pAux,int argc,const char * const * argv,sqlite3_vtab ** ppVtab,char ** pzErr)498e50db1c5Sdrh static int fuzzerConnect(
499e50db1c5Sdrh sqlite3 *db,
500e50db1c5Sdrh void *pAux,
501e50db1c5Sdrh int argc, const char *const*argv,
502e50db1c5Sdrh sqlite3_vtab **ppVtab,
503e50db1c5Sdrh char **pzErr
504e50db1c5Sdrh ){
505e50db1c5Sdrh int rc = SQLITE_OK; /* Return code */
506e50db1c5Sdrh fuzzer_vtab *pNew = 0; /* New virtual table */
507e50db1c5Sdrh const char *zModule = argv[0];
508e50db1c5Sdrh const char *zDb = argv[1];
509e50db1c5Sdrh
510e50db1c5Sdrh if( argc!=4 ){
511e50db1c5Sdrh *pzErr = sqlite3_mprintf(
512e50db1c5Sdrh "%s: wrong number of CREATE VIRTUAL TABLE arguments", zModule
513e50db1c5Sdrh );
514e50db1c5Sdrh rc = SQLITE_ERROR;
515e50db1c5Sdrh }else{
5162d77d80aSdrh sqlite3_int64 nModule; /* Length of zModule, in bytes */
517e50db1c5Sdrh
5182d77d80aSdrh nModule = strlen(zModule);
5192d77d80aSdrh pNew = sqlite3_malloc64( sizeof(*pNew) + nModule + 1);
520e50db1c5Sdrh if( pNew==0 ){
521e50db1c5Sdrh rc = SQLITE_NOMEM;
522e50db1c5Sdrh }else{
523e50db1c5Sdrh char *zTab; /* Dequoted name of fuzzer data table */
524e50db1c5Sdrh
525e50db1c5Sdrh memset(pNew, 0, sizeof(*pNew));
526e50db1c5Sdrh pNew->zClassName = (char*)&pNew[1];
527065f3bf4Smistachkin memcpy(pNew->zClassName, zModule, (size_t)(nModule+1));
528e50db1c5Sdrh
529e50db1c5Sdrh zTab = fuzzerDequote(argv[3]);
530e50db1c5Sdrh if( zTab==0 ){
531e50db1c5Sdrh rc = SQLITE_NOMEM;
532e50db1c5Sdrh }else{
533e50db1c5Sdrh rc = fuzzerLoadRules(db, pNew, zDb, zTab, pzErr);
534e50db1c5Sdrh sqlite3_free(zTab);
535e50db1c5Sdrh }
536e50db1c5Sdrh
537e50db1c5Sdrh if( rc==SQLITE_OK ){
538e50db1c5Sdrh rc = sqlite3_declare_vtab(db, "CREATE TABLE x(word,distance,ruleset)");
539e50db1c5Sdrh }
540e50db1c5Sdrh if( rc!=SQLITE_OK ){
541e50db1c5Sdrh fuzzerDisconnect((sqlite3_vtab *)pNew);
542e50db1c5Sdrh pNew = 0;
543*2b1c2aadSdrh }else{
544*2b1c2aadSdrh sqlite3_vtab_config(db, SQLITE_VTAB_INNOCUOUS);
545e50db1c5Sdrh }
546e50db1c5Sdrh }
547e50db1c5Sdrh }
548e50db1c5Sdrh
549e50db1c5Sdrh *ppVtab = (sqlite3_vtab *)pNew;
550e50db1c5Sdrh return rc;
551e50db1c5Sdrh }
552e50db1c5Sdrh
553e50db1c5Sdrh /*
554e50db1c5Sdrh ** Open a new fuzzer cursor.
555e50db1c5Sdrh */
fuzzerOpen(sqlite3_vtab * pVTab,sqlite3_vtab_cursor ** ppCursor)556e50db1c5Sdrh static int fuzzerOpen(sqlite3_vtab *pVTab, sqlite3_vtab_cursor **ppCursor){
557e50db1c5Sdrh fuzzer_vtab *p = (fuzzer_vtab*)pVTab;
558e50db1c5Sdrh fuzzer_cursor *pCur;
559e50db1c5Sdrh pCur = sqlite3_malloc( sizeof(*pCur) );
560e50db1c5Sdrh if( pCur==0 ) return SQLITE_NOMEM;
561e50db1c5Sdrh memset(pCur, 0, sizeof(*pCur));
562e50db1c5Sdrh pCur->pVtab = p;
563e50db1c5Sdrh *ppCursor = &pCur->base;
564e50db1c5Sdrh p->nCursor++;
565e50db1c5Sdrh return SQLITE_OK;
566e50db1c5Sdrh }
567e50db1c5Sdrh
568e50db1c5Sdrh /*
569e50db1c5Sdrh ** Free all stems in a list.
570e50db1c5Sdrh */
fuzzerClearStemList(fuzzer_stem * pStem)571e50db1c5Sdrh static void fuzzerClearStemList(fuzzer_stem *pStem){
572e50db1c5Sdrh while( pStem ){
573e50db1c5Sdrh fuzzer_stem *pNext = pStem->pNext;
574e50db1c5Sdrh sqlite3_free(pStem);
575e50db1c5Sdrh pStem = pNext;
576e50db1c5Sdrh }
577e50db1c5Sdrh }
578e50db1c5Sdrh
579e50db1c5Sdrh /*
580e50db1c5Sdrh ** Free up all the memory allocated by a cursor. Set it rLimit to 0
581e50db1c5Sdrh ** to indicate that it is at EOF.
582e50db1c5Sdrh */
fuzzerClearCursor(fuzzer_cursor * pCur,int clearHash)583e50db1c5Sdrh static void fuzzerClearCursor(fuzzer_cursor *pCur, int clearHash){
584e50db1c5Sdrh int i;
585e50db1c5Sdrh fuzzerClearStemList(pCur->pStem);
586e50db1c5Sdrh fuzzerClearStemList(pCur->pDone);
587e50db1c5Sdrh for(i=0; i<FUZZER_NQUEUE; i++) fuzzerClearStemList(pCur->aQueue[i]);
588e50db1c5Sdrh pCur->rLimit = (fuzzer_cost)0;
589e50db1c5Sdrh if( clearHash && pCur->nStem ){
590e50db1c5Sdrh pCur->mxQueue = 0;
591e50db1c5Sdrh pCur->pStem = 0;
592e50db1c5Sdrh pCur->pDone = 0;
593e50db1c5Sdrh memset(pCur->aQueue, 0, sizeof(pCur->aQueue));
594e50db1c5Sdrh memset(pCur->apHash, 0, sizeof(pCur->apHash));
595e50db1c5Sdrh }
596e50db1c5Sdrh pCur->nStem = 0;
597e50db1c5Sdrh }
598e50db1c5Sdrh
599e50db1c5Sdrh /*
600e50db1c5Sdrh ** Close a fuzzer cursor.
601e50db1c5Sdrh */
fuzzerClose(sqlite3_vtab_cursor * cur)602e50db1c5Sdrh static int fuzzerClose(sqlite3_vtab_cursor *cur){
603e50db1c5Sdrh fuzzer_cursor *pCur = (fuzzer_cursor *)cur;
604e50db1c5Sdrh fuzzerClearCursor(pCur, 0);
605e50db1c5Sdrh sqlite3_free(pCur->zBuf);
606e50db1c5Sdrh pCur->pVtab->nCursor--;
607e50db1c5Sdrh sqlite3_free(pCur);
608e50db1c5Sdrh return SQLITE_OK;
609e50db1c5Sdrh }
610e50db1c5Sdrh
611e50db1c5Sdrh /*
612e50db1c5Sdrh ** Compute the current output term for a fuzzer_stem.
613e50db1c5Sdrh */
fuzzerRender(fuzzer_stem * pStem,char ** pzBuf,int * pnBuf)614e50db1c5Sdrh static int fuzzerRender(
615e50db1c5Sdrh fuzzer_stem *pStem, /* The stem to be rendered */
616e50db1c5Sdrh char **pzBuf, /* Write results into this buffer. realloc if needed */
617e50db1c5Sdrh int *pnBuf /* Size of the buffer */
618e50db1c5Sdrh ){
619e50db1c5Sdrh const fuzzer_rule *pRule = pStem->pRule;
620e50db1c5Sdrh int n; /* Size of output term without nul-term */
621e50db1c5Sdrh char *z; /* Buffer to assemble output term in */
622e50db1c5Sdrh
623e50db1c5Sdrh n = pStem->nBasis + pRule->nTo - pRule->nFrom;
624e50db1c5Sdrh if( (*pnBuf)<n+1 ){
625e50db1c5Sdrh (*pzBuf) = sqlite3_realloc((*pzBuf), n+100);
626e50db1c5Sdrh if( (*pzBuf)==0 ) return SQLITE_NOMEM;
627e50db1c5Sdrh (*pnBuf) = n+100;
628e50db1c5Sdrh }
629e50db1c5Sdrh n = pStem->n;
630e50db1c5Sdrh z = *pzBuf;
631e50db1c5Sdrh if( n<0 ){
632e50db1c5Sdrh memcpy(z, pStem->zBasis, pStem->nBasis+1);
633e50db1c5Sdrh }else{
634e50db1c5Sdrh memcpy(z, pStem->zBasis, n);
635e50db1c5Sdrh memcpy(&z[n], pRule->zTo, pRule->nTo);
636e50db1c5Sdrh memcpy(&z[n+pRule->nTo], &pStem->zBasis[n+pRule->nFrom],
637e50db1c5Sdrh pStem->nBasis-n-pRule->nFrom+1);
638e50db1c5Sdrh }
639e50db1c5Sdrh
640e50db1c5Sdrh assert( z[pStem->nBasis + pRule->nTo - pRule->nFrom]==0 );
641e50db1c5Sdrh return SQLITE_OK;
642e50db1c5Sdrh }
643e50db1c5Sdrh
644e50db1c5Sdrh /*
645e50db1c5Sdrh ** Compute a hash on zBasis.
646e50db1c5Sdrh */
fuzzerHash(const char * z)647e50db1c5Sdrh static unsigned int fuzzerHash(const char *z){
648e50db1c5Sdrh unsigned int h = 0;
649e50db1c5Sdrh while( *z ){ h = (h<<3) ^ (h>>29) ^ *(z++); }
650e50db1c5Sdrh return h % FUZZER_HASH;
651e50db1c5Sdrh }
652e50db1c5Sdrh
653e50db1c5Sdrh /*
654e50db1c5Sdrh ** Current cost of a stem
655e50db1c5Sdrh */
fuzzerCost(fuzzer_stem * pStem)656e50db1c5Sdrh static fuzzer_cost fuzzerCost(fuzzer_stem *pStem){
657e50db1c5Sdrh return pStem->rCostX = pStem->rBaseCost + pStem->pRule->rCost;
658e50db1c5Sdrh }
659e50db1c5Sdrh
660e50db1c5Sdrh #if 0
661e50db1c5Sdrh /*
662e50db1c5Sdrh ** Print a description of a fuzzer_stem on stderr.
663e50db1c5Sdrh */
664e50db1c5Sdrh static void fuzzerStemPrint(
665e50db1c5Sdrh const char *zPrefix,
666e50db1c5Sdrh fuzzer_stem *pStem,
667e50db1c5Sdrh const char *zSuffix
668e50db1c5Sdrh ){
669e50db1c5Sdrh if( pStem->n<0 ){
670e50db1c5Sdrh fprintf(stderr, "%s[%s](%d)-->self%s",
671e50db1c5Sdrh zPrefix,
672e50db1c5Sdrh pStem->zBasis, pStem->rBaseCost,
673e50db1c5Sdrh zSuffix
674e50db1c5Sdrh );
675e50db1c5Sdrh }else{
676e50db1c5Sdrh char *zBuf = 0;
677e50db1c5Sdrh int nBuf = 0;
678e50db1c5Sdrh if( fuzzerRender(pStem, &zBuf, &nBuf)!=SQLITE_OK ) return;
679e50db1c5Sdrh fprintf(stderr, "%s[%s](%d)-->{%s}(%d)%s",
680e50db1c5Sdrh zPrefix,
681e50db1c5Sdrh pStem->zBasis, pStem->rBaseCost, zBuf, pStem->,
682e50db1c5Sdrh zSuffix
683e50db1c5Sdrh );
684e50db1c5Sdrh sqlite3_free(zBuf);
685e50db1c5Sdrh }
686e50db1c5Sdrh }
687e50db1c5Sdrh #endif
688e50db1c5Sdrh
689e50db1c5Sdrh /*
690e50db1c5Sdrh ** Return 1 if the string to which the cursor is point has already
691e50db1c5Sdrh ** been emitted. Return 0 if not. Return -1 on a memory allocation
692e50db1c5Sdrh ** failures.
693e50db1c5Sdrh */
fuzzerSeen(fuzzer_cursor * pCur,fuzzer_stem * pStem)694e50db1c5Sdrh static int fuzzerSeen(fuzzer_cursor *pCur, fuzzer_stem *pStem){
695e50db1c5Sdrh unsigned int h;
696e50db1c5Sdrh fuzzer_stem *pLookup;
697e50db1c5Sdrh
698e50db1c5Sdrh if( fuzzerRender(pStem, &pCur->zBuf, &pCur->nBuf)==SQLITE_NOMEM ){
699e50db1c5Sdrh return -1;
700e50db1c5Sdrh }
701e50db1c5Sdrh h = fuzzerHash(pCur->zBuf);
702e50db1c5Sdrh pLookup = pCur->apHash[h];
703e50db1c5Sdrh while( pLookup && strcmp(pLookup->zBasis, pCur->zBuf)!=0 ){
704e50db1c5Sdrh pLookup = pLookup->pHash;
705e50db1c5Sdrh }
706e50db1c5Sdrh return pLookup!=0;
707e50db1c5Sdrh }
708e50db1c5Sdrh
709e50db1c5Sdrh /*
710e50db1c5Sdrh ** If argument pRule is NULL, this function returns false.
711e50db1c5Sdrh **
712e50db1c5Sdrh ** Otherwise, it returns true if rule pRule should be skipped. A rule
713e50db1c5Sdrh ** should be skipped if it does not belong to rule-set iRuleset, or if
714e50db1c5Sdrh ** applying it to stem pStem would create a string longer than
715e50db1c5Sdrh ** FUZZER_MX_OUTPUT_LENGTH bytes.
716e50db1c5Sdrh */
fuzzerSkipRule(const fuzzer_rule * pRule,fuzzer_stem * pStem,int iRuleset)717e50db1c5Sdrh static int fuzzerSkipRule(
718e50db1c5Sdrh const fuzzer_rule *pRule, /* Determine whether or not to skip this */
719e50db1c5Sdrh fuzzer_stem *pStem, /* Stem rule may be applied to */
720e50db1c5Sdrh int iRuleset /* Rule-set used by the current query */
721e50db1c5Sdrh ){
722e50db1c5Sdrh return pRule && (
723e50db1c5Sdrh (pRule->iRuleset!=iRuleset)
724e50db1c5Sdrh || (pStem->nBasis + pRule->nTo - pRule->nFrom)>FUZZER_MX_OUTPUT_LENGTH
725e50db1c5Sdrh );
726e50db1c5Sdrh }
727e50db1c5Sdrh
728e50db1c5Sdrh /*
729e50db1c5Sdrh ** Advance a fuzzer_stem to its next value. Return 0 if there are
730e50db1c5Sdrh ** no more values that can be generated by this fuzzer_stem. Return
731e50db1c5Sdrh ** -1 on a memory allocation failure.
732e50db1c5Sdrh */
fuzzerAdvance(fuzzer_cursor * pCur,fuzzer_stem * pStem)733e50db1c5Sdrh static int fuzzerAdvance(fuzzer_cursor *pCur, fuzzer_stem *pStem){
734e50db1c5Sdrh const fuzzer_rule *pRule;
735e50db1c5Sdrh while( (pRule = pStem->pRule)!=0 ){
736e50db1c5Sdrh assert( pRule==&pCur->nullRule || pRule->iRuleset==pCur->iRuleset );
737e50db1c5Sdrh while( pStem->n < pStem->nBasis - pRule->nFrom ){
738e50db1c5Sdrh pStem->n++;
739e50db1c5Sdrh if( pRule->nFrom==0
740e50db1c5Sdrh || memcmp(&pStem->zBasis[pStem->n], pRule->zFrom, pRule->nFrom)==0
741e50db1c5Sdrh ){
742e50db1c5Sdrh /* Found a rewrite case. Make sure it is not a duplicate */
743e50db1c5Sdrh int rc = fuzzerSeen(pCur, pStem);
744e50db1c5Sdrh if( rc<0 ) return -1;
745e50db1c5Sdrh if( rc==0 ){
746e50db1c5Sdrh fuzzerCost(pStem);
747e50db1c5Sdrh return 1;
748e50db1c5Sdrh }
749e50db1c5Sdrh }
750e50db1c5Sdrh }
751e50db1c5Sdrh pStem->n = -1;
752e50db1c5Sdrh do{
753e50db1c5Sdrh pRule = pRule->pNext;
754e50db1c5Sdrh }while( fuzzerSkipRule(pRule, pStem, pCur->iRuleset) );
755e50db1c5Sdrh pStem->pRule = pRule;
756e50db1c5Sdrh if( pRule && fuzzerCost(pStem)>pCur->rLimit ) pStem->pRule = 0;
757e50db1c5Sdrh }
758e50db1c5Sdrh return 0;
759e50db1c5Sdrh }
760e50db1c5Sdrh
761e50db1c5Sdrh /*
762e50db1c5Sdrh ** The two input stem lists are both sorted in order of increasing
763e50db1c5Sdrh ** rCostX. Merge them together into a single list, sorted by rCostX, and
764e50db1c5Sdrh ** return a pointer to the head of that new list.
765e50db1c5Sdrh */
fuzzerMergeStems(fuzzer_stem * pA,fuzzer_stem * pB)766e50db1c5Sdrh static fuzzer_stem *fuzzerMergeStems(fuzzer_stem *pA, fuzzer_stem *pB){
767e50db1c5Sdrh fuzzer_stem head;
768e50db1c5Sdrh fuzzer_stem *pTail;
769e50db1c5Sdrh
770e50db1c5Sdrh pTail = &head;
771e50db1c5Sdrh while( pA && pB ){
772e50db1c5Sdrh if( pA->rCostX<=pB->rCostX ){
773e50db1c5Sdrh pTail->pNext = pA;
774e50db1c5Sdrh pTail = pA;
775e50db1c5Sdrh pA = pA->pNext;
776e50db1c5Sdrh }else{
777e50db1c5Sdrh pTail->pNext = pB;
778e50db1c5Sdrh pTail = pB;
779e50db1c5Sdrh pB = pB->pNext;
780e50db1c5Sdrh }
781e50db1c5Sdrh }
782e50db1c5Sdrh if( pA==0 ){
783e50db1c5Sdrh pTail->pNext = pB;
784e50db1c5Sdrh }else{
785e50db1c5Sdrh pTail->pNext = pA;
786e50db1c5Sdrh }
787e50db1c5Sdrh return head.pNext;
788e50db1c5Sdrh }
789e50db1c5Sdrh
790e50db1c5Sdrh /*
791e50db1c5Sdrh ** Load pCur->pStem with the lowest-cost stem. Return a pointer
792e50db1c5Sdrh ** to the lowest-cost stem.
793e50db1c5Sdrh */
fuzzerLowestCostStem(fuzzer_cursor * pCur)794e50db1c5Sdrh static fuzzer_stem *fuzzerLowestCostStem(fuzzer_cursor *pCur){
795e50db1c5Sdrh fuzzer_stem *pBest, *pX;
796e50db1c5Sdrh int iBest;
797e50db1c5Sdrh int i;
798e50db1c5Sdrh
799e50db1c5Sdrh if( pCur->pStem==0 ){
800e50db1c5Sdrh iBest = -1;
801e50db1c5Sdrh pBest = 0;
802e50db1c5Sdrh for(i=0; i<=pCur->mxQueue; i++){
803e50db1c5Sdrh pX = pCur->aQueue[i];
804e50db1c5Sdrh if( pX==0 ) continue;
805e50db1c5Sdrh if( pBest==0 || pBest->rCostX>pX->rCostX ){
806e50db1c5Sdrh pBest = pX;
807e50db1c5Sdrh iBest = i;
808e50db1c5Sdrh }
809e50db1c5Sdrh }
810e50db1c5Sdrh if( pBest ){
811e50db1c5Sdrh pCur->aQueue[iBest] = pBest->pNext;
812e50db1c5Sdrh pBest->pNext = 0;
813e50db1c5Sdrh pCur->pStem = pBest;
814e50db1c5Sdrh }
815e50db1c5Sdrh }
816e50db1c5Sdrh return pCur->pStem;
817e50db1c5Sdrh }
818e50db1c5Sdrh
819e50db1c5Sdrh /*
820e50db1c5Sdrh ** Insert pNew into queue of pending stems. Then find the stem
821e50db1c5Sdrh ** with the lowest rCostX and move it into pCur->pStem.
822e50db1c5Sdrh ** list. The insert is done such the pNew is in the correct order
823e50db1c5Sdrh ** according to fuzzer_stem.zBaseCost+fuzzer_stem.pRule->rCost.
824e50db1c5Sdrh */
fuzzerInsert(fuzzer_cursor * pCur,fuzzer_stem * pNew)825e50db1c5Sdrh static fuzzer_stem *fuzzerInsert(fuzzer_cursor *pCur, fuzzer_stem *pNew){
826e50db1c5Sdrh fuzzer_stem *pX;
827e50db1c5Sdrh int i;
828e50db1c5Sdrh
829e50db1c5Sdrh /* If pCur->pStem exists and is greater than pNew, then make pNew
830e50db1c5Sdrh ** the new pCur->pStem and insert the old pCur->pStem instead.
831e50db1c5Sdrh */
832e50db1c5Sdrh if( (pX = pCur->pStem)!=0 && pX->rCostX>pNew->rCostX ){
833e50db1c5Sdrh pNew->pNext = 0;
834e50db1c5Sdrh pCur->pStem = pNew;
835e50db1c5Sdrh pNew = pX;
836e50db1c5Sdrh }
837e50db1c5Sdrh
838e50db1c5Sdrh /* Insert the new value */
839e50db1c5Sdrh pNew->pNext = 0;
840e50db1c5Sdrh pX = pNew;
841e50db1c5Sdrh for(i=0; i<=pCur->mxQueue; i++){
842e50db1c5Sdrh if( pCur->aQueue[i] ){
843e50db1c5Sdrh pX = fuzzerMergeStems(pX, pCur->aQueue[i]);
844e50db1c5Sdrh pCur->aQueue[i] = 0;
845e50db1c5Sdrh }else{
846e50db1c5Sdrh pCur->aQueue[i] = pX;
847e50db1c5Sdrh break;
848e50db1c5Sdrh }
849e50db1c5Sdrh }
850e50db1c5Sdrh if( i>pCur->mxQueue ){
851e50db1c5Sdrh if( i<FUZZER_NQUEUE ){
852e50db1c5Sdrh pCur->mxQueue = i;
853e50db1c5Sdrh pCur->aQueue[i] = pX;
854e50db1c5Sdrh }else{
855e50db1c5Sdrh assert( pCur->mxQueue==FUZZER_NQUEUE-1 );
856e50db1c5Sdrh pX = fuzzerMergeStems(pX, pCur->aQueue[FUZZER_NQUEUE-1]);
857e50db1c5Sdrh pCur->aQueue[FUZZER_NQUEUE-1] = pX;
858e50db1c5Sdrh }
859e50db1c5Sdrh }
860e50db1c5Sdrh
861e50db1c5Sdrh return fuzzerLowestCostStem(pCur);
862e50db1c5Sdrh }
863e50db1c5Sdrh
864e50db1c5Sdrh /*
865e50db1c5Sdrh ** Allocate a new fuzzer_stem. Add it to the hash table but do not
866e50db1c5Sdrh ** link it into either the pCur->pStem or pCur->pDone lists.
867e50db1c5Sdrh */
fuzzerNewStem(fuzzer_cursor * pCur,const char * zWord,fuzzer_cost rBaseCost)868e50db1c5Sdrh static fuzzer_stem *fuzzerNewStem(
869e50db1c5Sdrh fuzzer_cursor *pCur,
870e50db1c5Sdrh const char *zWord,
871e50db1c5Sdrh fuzzer_cost rBaseCost
872e50db1c5Sdrh ){
873e50db1c5Sdrh fuzzer_stem *pNew;
874e50db1c5Sdrh fuzzer_rule *pRule;
875e50db1c5Sdrh unsigned int h;
876e50db1c5Sdrh
8772d77d80aSdrh pNew = sqlite3_malloc64( sizeof(*pNew) + strlen(zWord) + 1 );
878e50db1c5Sdrh if( pNew==0 ) return 0;
879e50db1c5Sdrh memset(pNew, 0, sizeof(*pNew));
880e50db1c5Sdrh pNew->zBasis = (char*)&pNew[1];
8818e18922fSmistachkin pNew->nBasis = (fuzzer_len)strlen(zWord);
882e50db1c5Sdrh memcpy(pNew->zBasis, zWord, pNew->nBasis+1);
883e50db1c5Sdrh pRule = pCur->pVtab->pRule;
884e50db1c5Sdrh while( fuzzerSkipRule(pRule, pNew, pCur->iRuleset) ){
885e50db1c5Sdrh pRule = pRule->pNext;
886e50db1c5Sdrh }
887e50db1c5Sdrh pNew->pRule = pRule;
888e50db1c5Sdrh pNew->n = -1;
889e50db1c5Sdrh pNew->rBaseCost = pNew->rCostX = rBaseCost;
890e50db1c5Sdrh h = fuzzerHash(pNew->zBasis);
891e50db1c5Sdrh pNew->pHash = pCur->apHash[h];
892e50db1c5Sdrh pCur->apHash[h] = pNew;
893e50db1c5Sdrh pCur->nStem++;
894e50db1c5Sdrh return pNew;
895e50db1c5Sdrh }
896e50db1c5Sdrh
897e50db1c5Sdrh
898e50db1c5Sdrh /*
899e50db1c5Sdrh ** Advance a cursor to its next row of output
900e50db1c5Sdrh */
fuzzerNext(sqlite3_vtab_cursor * cur)901e50db1c5Sdrh static int fuzzerNext(sqlite3_vtab_cursor *cur){
902e50db1c5Sdrh fuzzer_cursor *pCur = (fuzzer_cursor*)cur;
903e50db1c5Sdrh int rc;
904e50db1c5Sdrh fuzzer_stem *pStem, *pNew;
905e50db1c5Sdrh
906e50db1c5Sdrh pCur->iRowid++;
907e50db1c5Sdrh
908e50db1c5Sdrh /* Use the element the cursor is currently point to to create
909e50db1c5Sdrh ** a new stem and insert the new stem into the priority queue.
910e50db1c5Sdrh */
911e50db1c5Sdrh pStem = pCur->pStem;
912e50db1c5Sdrh if( pStem->rCostX>0 ){
913e50db1c5Sdrh rc = fuzzerRender(pStem, &pCur->zBuf, &pCur->nBuf);
914e50db1c5Sdrh if( rc==SQLITE_NOMEM ) return SQLITE_NOMEM;
915e50db1c5Sdrh pNew = fuzzerNewStem(pCur, pCur->zBuf, pStem->rCostX);
916e50db1c5Sdrh if( pNew ){
917e50db1c5Sdrh if( fuzzerAdvance(pCur, pNew)==0 ){
918e50db1c5Sdrh pNew->pNext = pCur->pDone;
919e50db1c5Sdrh pCur->pDone = pNew;
920e50db1c5Sdrh }else{
921e50db1c5Sdrh if( fuzzerInsert(pCur, pNew)==pNew ){
922e50db1c5Sdrh return SQLITE_OK;
923e50db1c5Sdrh }
924e50db1c5Sdrh }
925e50db1c5Sdrh }else{
926e50db1c5Sdrh return SQLITE_NOMEM;
927e50db1c5Sdrh }
928e50db1c5Sdrh }
929e50db1c5Sdrh
930e50db1c5Sdrh /* Adjust the priority queue so that the first element of the
931e50db1c5Sdrh ** stem list is the next lowest cost word.
932e50db1c5Sdrh */
933e50db1c5Sdrh while( (pStem = pCur->pStem)!=0 ){
934e50db1c5Sdrh int res = fuzzerAdvance(pCur, pStem);
935e50db1c5Sdrh if( res<0 ){
936e50db1c5Sdrh return SQLITE_NOMEM;
937e50db1c5Sdrh }else if( res>0 ){
938e50db1c5Sdrh pCur->pStem = 0;
939e50db1c5Sdrh pStem = fuzzerInsert(pCur, pStem);
940e50db1c5Sdrh if( (rc = fuzzerSeen(pCur, pStem))!=0 ){
941e50db1c5Sdrh if( rc<0 ) return SQLITE_NOMEM;
942e50db1c5Sdrh continue;
943e50db1c5Sdrh }
944e50db1c5Sdrh return SQLITE_OK; /* New word found */
945e50db1c5Sdrh }
946e50db1c5Sdrh pCur->pStem = 0;
947e50db1c5Sdrh pStem->pNext = pCur->pDone;
948e50db1c5Sdrh pCur->pDone = pStem;
949e50db1c5Sdrh if( fuzzerLowestCostStem(pCur) ){
950e50db1c5Sdrh rc = fuzzerSeen(pCur, pCur->pStem);
951e50db1c5Sdrh if( rc<0 ) return SQLITE_NOMEM;
952e50db1c5Sdrh if( rc==0 ){
953e50db1c5Sdrh return SQLITE_OK;
954e50db1c5Sdrh }
955e50db1c5Sdrh }
956e50db1c5Sdrh }
957e50db1c5Sdrh
958e50db1c5Sdrh /* Reach this point only if queue has been exhausted and there is
959e50db1c5Sdrh ** nothing left to be output. */
960e50db1c5Sdrh pCur->rLimit = (fuzzer_cost)0;
961e50db1c5Sdrh return SQLITE_OK;
962e50db1c5Sdrh }
963e50db1c5Sdrh
964e50db1c5Sdrh /*
965e50db1c5Sdrh ** Called to "rewind" a cursor back to the beginning so that
966e50db1c5Sdrh ** it starts its output over again. Always called at least once
967e50db1c5Sdrh ** prior to any fuzzerColumn, fuzzerRowid, or fuzzerEof call.
968e50db1c5Sdrh */
fuzzerFilter(sqlite3_vtab_cursor * pVtabCursor,int idxNum,const char * idxStr,int argc,sqlite3_value ** argv)969e50db1c5Sdrh static int fuzzerFilter(
970e50db1c5Sdrh sqlite3_vtab_cursor *pVtabCursor,
971e50db1c5Sdrh int idxNum, const char *idxStr,
972e50db1c5Sdrh int argc, sqlite3_value **argv
973e50db1c5Sdrh ){
974e50db1c5Sdrh fuzzer_cursor *pCur = (fuzzer_cursor *)pVtabCursor;
975e50db1c5Sdrh const char *zWord = "";
976e50db1c5Sdrh fuzzer_stem *pStem;
977e50db1c5Sdrh int idx;
978e50db1c5Sdrh
979e50db1c5Sdrh fuzzerClearCursor(pCur, 1);
980e50db1c5Sdrh pCur->rLimit = 2147483647;
981e50db1c5Sdrh idx = 0;
982e50db1c5Sdrh if( idxNum & 1 ){
983e50db1c5Sdrh zWord = (const char*)sqlite3_value_text(argv[0]);
984e50db1c5Sdrh idx++;
985e50db1c5Sdrh }
986e50db1c5Sdrh if( idxNum & 2 ){
987e50db1c5Sdrh pCur->rLimit = (fuzzer_cost)sqlite3_value_int(argv[idx]);
988e50db1c5Sdrh idx++;
989e50db1c5Sdrh }
990e50db1c5Sdrh if( idxNum & 4 ){
991e50db1c5Sdrh pCur->iRuleset = (fuzzer_cost)sqlite3_value_int(argv[idx]);
992e50db1c5Sdrh idx++;
993e50db1c5Sdrh }
994e50db1c5Sdrh pCur->nullRule.pNext = pCur->pVtab->pRule;
995e50db1c5Sdrh pCur->nullRule.rCost = 0;
996e50db1c5Sdrh pCur->nullRule.nFrom = 0;
997e50db1c5Sdrh pCur->nullRule.nTo = 0;
998e50db1c5Sdrh pCur->nullRule.zFrom = "";
999e50db1c5Sdrh pCur->iRowid = 1;
1000e50db1c5Sdrh assert( pCur->pStem==0 );
1001e50db1c5Sdrh
1002e50db1c5Sdrh /* If the query term is longer than FUZZER_MX_OUTPUT_LENGTH bytes, this
1003e50db1c5Sdrh ** query will return zero rows. */
1004e50db1c5Sdrh if( (int)strlen(zWord)<FUZZER_MX_OUTPUT_LENGTH ){
1005e50db1c5Sdrh pCur->pStem = pStem = fuzzerNewStem(pCur, zWord, (fuzzer_cost)0);
1006e50db1c5Sdrh if( pStem==0 ) return SQLITE_NOMEM;
1007e50db1c5Sdrh pStem->pRule = &pCur->nullRule;
1008e50db1c5Sdrh pStem->n = pStem->nBasis;
1009e50db1c5Sdrh }else{
1010e50db1c5Sdrh pCur->rLimit = 0;
1011e50db1c5Sdrh }
1012e50db1c5Sdrh
1013e50db1c5Sdrh return SQLITE_OK;
1014e50db1c5Sdrh }
1015e50db1c5Sdrh
1016e50db1c5Sdrh /*
1017e50db1c5Sdrh ** Only the word and distance columns have values. All other columns
1018e50db1c5Sdrh ** return NULL
1019e50db1c5Sdrh */
fuzzerColumn(sqlite3_vtab_cursor * cur,sqlite3_context * ctx,int i)1020e50db1c5Sdrh static int fuzzerColumn(sqlite3_vtab_cursor *cur, sqlite3_context *ctx, int i){
1021e50db1c5Sdrh fuzzer_cursor *pCur = (fuzzer_cursor*)cur;
1022e50db1c5Sdrh if( i==0 ){
1023e50db1c5Sdrh /* the "word" column */
1024e50db1c5Sdrh if( fuzzerRender(pCur->pStem, &pCur->zBuf, &pCur->nBuf)==SQLITE_NOMEM ){
1025e50db1c5Sdrh return SQLITE_NOMEM;
1026e50db1c5Sdrh }
1027e50db1c5Sdrh sqlite3_result_text(ctx, pCur->zBuf, -1, SQLITE_TRANSIENT);
1028e50db1c5Sdrh }else if( i==1 ){
1029e50db1c5Sdrh /* the "distance" column */
1030e50db1c5Sdrh sqlite3_result_int(ctx, pCur->pStem->rCostX);
1031e50db1c5Sdrh }else{
1032e50db1c5Sdrh /* All other columns are NULL */
1033e50db1c5Sdrh sqlite3_result_null(ctx);
1034e50db1c5Sdrh }
1035e50db1c5Sdrh return SQLITE_OK;
1036e50db1c5Sdrh }
1037e50db1c5Sdrh
1038e50db1c5Sdrh /*
1039e50db1c5Sdrh ** The rowid.
1040e50db1c5Sdrh */
fuzzerRowid(sqlite3_vtab_cursor * cur,sqlite_int64 * pRowid)1041e50db1c5Sdrh static int fuzzerRowid(sqlite3_vtab_cursor *cur, sqlite_int64 *pRowid){
1042e50db1c5Sdrh fuzzer_cursor *pCur = (fuzzer_cursor*)cur;
1043e50db1c5Sdrh *pRowid = pCur->iRowid;
1044e50db1c5Sdrh return SQLITE_OK;
1045e50db1c5Sdrh }
1046e50db1c5Sdrh
1047e50db1c5Sdrh /*
1048e50db1c5Sdrh ** When the fuzzer_cursor.rLimit value is 0 or less, that is a signal
1049e50db1c5Sdrh ** that the cursor has nothing more to output.
1050e50db1c5Sdrh */
fuzzerEof(sqlite3_vtab_cursor * cur)1051e50db1c5Sdrh static int fuzzerEof(sqlite3_vtab_cursor *cur){
1052e50db1c5Sdrh fuzzer_cursor *pCur = (fuzzer_cursor*)cur;
1053e50db1c5Sdrh return pCur->rLimit<=(fuzzer_cost)0;
1054e50db1c5Sdrh }
1055e50db1c5Sdrh
1056e50db1c5Sdrh /*
1057e50db1c5Sdrh ** Search for terms of these forms:
1058e50db1c5Sdrh **
1059e50db1c5Sdrh ** (A) word MATCH $str
1060e50db1c5Sdrh ** (B1) distance < $value
1061e50db1c5Sdrh ** (B2) distance <= $value
1062e50db1c5Sdrh ** (C) ruleid == $ruleid
1063e50db1c5Sdrh **
1064e50db1c5Sdrh ** The distance< and distance<= are both treated as distance<=.
1065e50db1c5Sdrh ** The query plan number is a bit vector:
1066e50db1c5Sdrh **
1067e50db1c5Sdrh ** bit 1: Term of the form (A) found
1068e50db1c5Sdrh ** bit 2: Term like (B1) or (B2) found
1069e50db1c5Sdrh ** bit 3: Term like (C) found
1070e50db1c5Sdrh **
1071e50db1c5Sdrh ** If bit-1 is set, $str is always in filter.argv[0]. If bit-2 is set
1072e50db1c5Sdrh ** then $value is in filter.argv[0] if bit-1 is clear and is in
1073e50db1c5Sdrh ** filter.argv[1] if bit-1 is set. If bit-3 is set, then $ruleid is
1074e50db1c5Sdrh ** in filter.argv[0] if bit-1 and bit-2 are both zero, is in
1075e50db1c5Sdrh ** filter.argv[1] if exactly one of bit-1 and bit-2 are set, and is in
1076e50db1c5Sdrh ** filter.argv[2] if both bit-1 and bit-2 are set.
1077e50db1c5Sdrh */
fuzzerBestIndex(sqlite3_vtab * tab,sqlite3_index_info * pIdxInfo)1078e50db1c5Sdrh static int fuzzerBestIndex(sqlite3_vtab *tab, sqlite3_index_info *pIdxInfo){
1079e50db1c5Sdrh int iPlan = 0;
1080e50db1c5Sdrh int iDistTerm = -1;
1081e50db1c5Sdrh int iRulesetTerm = -1;
1082e50db1c5Sdrh int i;
1083a3855653Sdrh int seenMatch = 0;
1084e50db1c5Sdrh const struct sqlite3_index_constraint *pConstraint;
10854f402f26Sdrh double rCost = 1e12;
1086a3855653Sdrh
1087e50db1c5Sdrh pConstraint = pIdxInfo->aConstraint;
1088e50db1c5Sdrh for(i=0; i<pIdxInfo->nConstraint; i++, pConstraint++){
1089a3855653Sdrh if( pConstraint->iColumn==0
1090a3855653Sdrh && pConstraint->op==SQLITE_INDEX_CONSTRAINT_MATCH ){
1091a3855653Sdrh seenMatch = 1;
1092a3855653Sdrh }
1093e50db1c5Sdrh if( pConstraint->usable==0 ) continue;
1094e50db1c5Sdrh if( (iPlan & 1)==0
1095e50db1c5Sdrh && pConstraint->iColumn==0
1096e50db1c5Sdrh && pConstraint->op==SQLITE_INDEX_CONSTRAINT_MATCH
1097e50db1c5Sdrh ){
1098e50db1c5Sdrh iPlan |= 1;
1099e50db1c5Sdrh pIdxInfo->aConstraintUsage[i].argvIndex = 1;
1100e50db1c5Sdrh pIdxInfo->aConstraintUsage[i].omit = 1;
11014f402f26Sdrh rCost /= 1e6;
1102e50db1c5Sdrh }
1103e50db1c5Sdrh if( (iPlan & 2)==0
1104e50db1c5Sdrh && pConstraint->iColumn==1
1105e50db1c5Sdrh && (pConstraint->op==SQLITE_INDEX_CONSTRAINT_LT
1106e50db1c5Sdrh || pConstraint->op==SQLITE_INDEX_CONSTRAINT_LE)
1107e50db1c5Sdrh ){
1108e50db1c5Sdrh iPlan |= 2;
1109e50db1c5Sdrh iDistTerm = i;
1110a3855653Sdrh rCost /= 10.0;
1111e50db1c5Sdrh }
1112e50db1c5Sdrh if( (iPlan & 4)==0
1113e50db1c5Sdrh && pConstraint->iColumn==2
1114e50db1c5Sdrh && pConstraint->op==SQLITE_INDEX_CONSTRAINT_EQ
1115e50db1c5Sdrh ){
1116e50db1c5Sdrh iPlan |= 4;
1117e50db1c5Sdrh pIdxInfo->aConstraintUsage[i].omit = 1;
1118e50db1c5Sdrh iRulesetTerm = i;
1119a3855653Sdrh rCost /= 10.0;
1120e50db1c5Sdrh }
1121e50db1c5Sdrh }
1122e50db1c5Sdrh if( iPlan & 2 ){
1123e50db1c5Sdrh pIdxInfo->aConstraintUsage[iDistTerm].argvIndex = 1+((iPlan&1)!=0);
1124e50db1c5Sdrh }
1125e50db1c5Sdrh if( iPlan & 4 ){
1126e50db1c5Sdrh int idx = 1;
1127e50db1c5Sdrh if( iPlan & 1 ) idx++;
1128e50db1c5Sdrh if( iPlan & 2 ) idx++;
1129e50db1c5Sdrh pIdxInfo->aConstraintUsage[iRulesetTerm].argvIndex = idx;
1130e50db1c5Sdrh }
1131e50db1c5Sdrh pIdxInfo->idxNum = iPlan;
1132e50db1c5Sdrh if( pIdxInfo->nOrderBy==1
1133e50db1c5Sdrh && pIdxInfo->aOrderBy[0].iColumn==1
1134e50db1c5Sdrh && pIdxInfo->aOrderBy[0].desc==0
1135e50db1c5Sdrh ){
1136e50db1c5Sdrh pIdxInfo->orderByConsumed = 1;
1137e50db1c5Sdrh }
11384f402f26Sdrh if( seenMatch && (iPlan&1)==0 ) rCost = 1e99;
1139a3855653Sdrh pIdxInfo->estimatedCost = rCost;
1140e50db1c5Sdrh
1141e50db1c5Sdrh return SQLITE_OK;
1142e50db1c5Sdrh }
1143e50db1c5Sdrh
1144e50db1c5Sdrh /*
1145e50db1c5Sdrh ** A virtual table module that implements the "fuzzer".
1146e50db1c5Sdrh */
1147e50db1c5Sdrh static sqlite3_module fuzzerModule = {
1148e50db1c5Sdrh 0, /* iVersion */
1149e50db1c5Sdrh fuzzerConnect,
1150e50db1c5Sdrh fuzzerConnect,
1151e50db1c5Sdrh fuzzerBestIndex,
1152e50db1c5Sdrh fuzzerDisconnect,
1153e50db1c5Sdrh fuzzerDisconnect,
1154e50db1c5Sdrh fuzzerOpen, /* xOpen - open a cursor */
1155e50db1c5Sdrh fuzzerClose, /* xClose - close a cursor */
1156e50db1c5Sdrh fuzzerFilter, /* xFilter - configure scan constraints */
1157e50db1c5Sdrh fuzzerNext, /* xNext - advance a cursor */
1158e50db1c5Sdrh fuzzerEof, /* xEof - check for end of scan */
1159e50db1c5Sdrh fuzzerColumn, /* xColumn - read data */
1160e50db1c5Sdrh fuzzerRowid, /* xRowid - read data */
1161e50db1c5Sdrh 0, /* xUpdate */
1162e50db1c5Sdrh 0, /* xBegin */
1163e50db1c5Sdrh 0, /* xSync */
1164e50db1c5Sdrh 0, /* xCommit */
1165e50db1c5Sdrh 0, /* xRollback */
1166e50db1c5Sdrh 0, /* xFindMethod */
1167e50db1c5Sdrh 0, /* xRename */
1168e50db1c5Sdrh };
1169e50db1c5Sdrh
1170e50db1c5Sdrh #endif /* SQLITE_OMIT_VIRTUALTABLE */
1171e50db1c5Sdrh
1172e50db1c5Sdrh
1173e50db1c5Sdrh #ifdef _WIN32
1174e50db1c5Sdrh __declspec(dllexport)
1175e50db1c5Sdrh #endif
sqlite3_fuzzer_init(sqlite3 * db,char ** pzErrMsg,const sqlite3_api_routines * pApi)1176e50db1c5Sdrh int sqlite3_fuzzer_init(
1177e50db1c5Sdrh sqlite3 *db,
1178e50db1c5Sdrh char **pzErrMsg,
1179e50db1c5Sdrh const sqlite3_api_routines *pApi
1180e50db1c5Sdrh ){
1181e50db1c5Sdrh int rc = SQLITE_OK;
1182e50db1c5Sdrh SQLITE_EXTENSION_INIT2(pApi);
118311f71d6aSdan #ifndef SQLITE_OMIT_VIRTUALTABLE
1184e50db1c5Sdrh rc = sqlite3_create_module(db, "fuzzer", &fuzzerModule, 0);
118511f71d6aSdan #endif
1186e50db1c5Sdrh return rc;
1187e50db1c5Sdrh }
1188