1 /*
2 ** 2013-03-14
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 ** This file contains code for a demonstration virtual table that finds
14 ** "approximate matches" - strings from a finite set that are nearly the
15 ** same as a single input string.  The virtual table is called "amatch".
16 **
17 ** A amatch virtual table is created like this:
18 **
19 **     CREATE VIRTUAL TABLE f USING approximate_match(
20 **        vocabulary_table=<tablename>,      -- V
21 **        vocabulary_word=<columnname>,      -- W
22 **        vocabulary_language=<columnname>,  -- L
23 **        edit_distances=<edit-cost-table>
24 **     );
25 **
26 ** When it is created, the new amatch table must be supplied with the
27 ** the name of a table V and columns V.W and V.L such that
28 **
29 **     SELECT W FROM V WHERE L=$language
30 **
31 ** returns the allowed vocabulary for the match.  If the "vocabulary_language"
32 ** or L columnname is left unspecified or is an empty string, then no
33 ** filtering of the vocabulary by language is performed.
34 **
35 ** For efficiency, it is essential that the vocabulary table be indexed:
36 **
37 **     CREATE vocab_index ON V(W)
38 **
39 ** A separate edit-cost-table provides scoring information that defines
40 ** what it means for one string to be "close" to another.
41 **
42 ** The edit-cost-table must contain exactly four columns (more precisely,
43 ** the statement "SELECT * FROM <edit-cost-table>" must return records
44 ** that consist of four columns). It does not matter what the columns are
45 ** named.
46 **
47 ** Each row in the edit-cost-table represents a single character
48 ** transformation going from user input to the vocabulary. The leftmost
49 ** column of the row (column 0) contains an integer identifier of the
50 ** language to which the transformation rule belongs (see "MULTIPLE LANGUAGES"
51 ** below). The second column of the row (column 1) contains the input
52 ** character or characters - the characters of user input. The third
53 ** column contains characters as they appear in the vocabulary table.
54 ** And the fourth column contains the integer cost of making the
55 ** transformation. For example:
56 **
57 **    CREATE TABLE f_data(iLang, cFrom, cTo, Cost);
58 **    INSERT INTO f_data(iLang, cFrom, cTo, Cost) VALUES(0, '', 'a', 100);
59 **    INSERT INTO f_data(iLang, cFrom, cTo, Cost) VALUES(0, 'b', '', 87);
60 **    INSERT INTO f_data(iLang, cFrom, cTo, Cost) VALUES(0, 'o', 'oe', 38);
61 **    INSERT INTO f_data(iLang, cFrom, cTo, Cost) VALUES(0, 'oe', 'o', 40);
62 **
63 ** The first row inserted into the edit-cost-table by the SQL script
64 ** above indicates that the cost of having an extra 'a' in the vocabulary
65 ** table that is missing in the user input 100.  (All costs are integers.
66 ** Overall cost must not exceed 16777216.)  The second INSERT statement
67 ** creates a rule saying that the cost of having a single letter 'b' in
68 ** user input which is missing in the vocabulary table is 87.  The third
69 ** INSERT statement mean that the cost of matching an 'o' in user input
70 ** against an 'oe' in the vocabulary table is 38.  And so forth.
71 **
72 ** The following rules are special:
73 **
74 **    INSERT INTO f_data(iLang, cFrom, cTo, Cost) VALUES(0, '?', '', 97);
75 **    INSERT INTO f_data(iLang, cFrom, cTo, Cost) VALUES(0, '', '?', 98);
76 **    INSERT INTO f_data(iLang, cFrom, cTo, Cost) VALUES(0, '?', '?', 99);
77 **
78 ** The '?' to '' rule is the cost of having any single character in the input
79 ** that is not found in the vocabular.  The '' to '?' rule is the cost of
80 ** having a character in the vocabulary table that is missing from input.
81 ** And the '?' to '?' rule is the cost of doing an arbitrary character
82 ** substitution.  These three generic rules apply across all languages.
83 ** In other words, the iLang field is ignored for the generic substitution
84 ** rules.  If more than one cost is given for a generic substitution rule,
85 ** then the lowest cost is used.
86 **
87 ** Once it has been created, the amatch virtual table can be queried
88 ** as follows:
89 **
90 **    SELECT word, distance FROM f
91 **     WHERE word MATCH 'abcdefg'
92 **       AND distance<200;
93 **
94 ** This query outputs the strings contained in the T(F) field that
95 ** are close to "abcdefg" and in order of increasing distance.  No string
96 ** is output more than once.  If there are multiple ways to transform the
97 ** target string ("abcdefg") into a string in the vocabulary table then
98 ** the lowest cost transform is the one that is returned.  In this example,
99 ** the search is limited to strings with a total distance of less than 200.
100 **
101 ** For efficiency, it is important to put tight bounds on the distance.
102 ** The time and memory space needed to perform this query is exponential
103 ** in the maximum distance.  A good rule of thumb is to limit the distance
104 ** to no more than 1.5 or 2 times the maximum cost of any rule in the
105 ** edit-cost-table.
106 **
107 ** The amatch is a read-only table.  Any attempt to DELETE, INSERT, or
108 ** UPDATE on a amatch table will throw an error.
109 **
110 ** It is important to put some kind of a limit on the amatch output.  This
111 ** can be either in the form of a LIMIT clause at the end of the query,
112 ** or better, a "distance<NNN" constraint where NNN is some number.  The
113 ** running time and memory requirement is exponential in the value of NNN
114 ** so you want to make sure that NNN is not too big.  A value of NNN that
115 ** is about twice the average transformation cost seems to give good results.
116 **
117 ** The amatch table can be useful for tasks such as spelling correction.
118 ** Suppose all allowed words are in table vocabulary(w).  Then one would create
119 ** an amatch virtual table like this:
120 **
121 **   CREATE VIRTUAL TABLE ex1 USING amatch(
122 **       vocabtable=vocabulary,
123 **       vocabcolumn=w,
124 **       edit_distances=ec1
125 **   );
126 **
127 ** Then given an input word $word, look up close spellings this way:
128 **
129 **   SELECT word, distance FROM ex1
130 **    WHERE word MATCH $word AND distance<200;
131 **
132 ** MULTIPLE LANGUAGES
133 **
134 ** Normally, the "iLang" value associated with all character transformations
135 ** in the edit-cost-table is zero. However, if required, the amatch
136 ** virtual table allows multiple languages to be defined. Each query uses
137 ** only a single iLang value.   This allows, for example, a single
138 ** amatch table to support multiple languages.
139 **
140 ** By default, only the rules with iLang=0 are used. To specify an
141 ** alternative language, a "language = ?" expression must be added to the
142 ** WHERE clause of a SELECT, where ? is the integer identifier of the desired
143 ** language. For example:
144 **
145 **   SELECT word, distance FROM ex1
146 **    WHERE word MATCH $word
147 **      AND distance<=200
148 **      AND language=1 -- Specify use language 1 instead of 0
149 **
150 ** If no "language = ?" constraint is specified in the WHERE clause, language
151 ** 0 is used.
152 **
153 ** LIMITS
154 **
155 ** The maximum language number is 2147483647.  The maximum length of either
156 ** of the strings in the second or third column of the amatch data table
157 ** is 50 bytes.  The maximum cost on a rule is 1000.
158 */
159 #include "sqlite3ext.h"
160 SQLITE_EXTENSION_INIT1
161 #include <stdlib.h>
162 #include <string.h>
163 #include <assert.h>
164 #include <stdio.h>
165 #include <ctype.h>
166 
167 #ifndef SQLITE_OMIT_VIRTUALTABLE
168 
169 /*
170 ** Forward declaration of objects used by this implementation
171 */
172 typedef struct amatch_vtab amatch_vtab;
173 typedef struct amatch_cursor amatch_cursor;
174 typedef struct amatch_rule amatch_rule;
175 typedef struct amatch_word amatch_word;
176 typedef struct amatch_avl amatch_avl;
177 
178 
179 /*****************************************************************************
180 ** AVL Tree implementation
181 */
182 /*
183 ** Objects that want to be members of the AVL tree should embedded an
184 ** instance of this structure.
185 */
186 struct amatch_avl {
187   amatch_word *pWord;   /* Points to the object being stored in the tree */
188   char *zKey;           /* Key.  zero-terminated string.  Must be unique */
189   amatch_avl *pBefore;  /* Other elements less than zKey */
190   amatch_avl *pAfter;   /* Other elements greater than zKey */
191   amatch_avl *pUp;      /* Parent element */
192   short int height;     /* Height of this node.  Leaf==1 */
193   short int imbalance;  /* Height difference between pBefore and pAfter */
194 };
195 
196 /* Recompute the amatch_avl.height and amatch_avl.imbalance fields for p.
197 ** Assume that the children of p have correct heights.
198 */
amatchAvlRecomputeHeight(amatch_avl * p)199 static void amatchAvlRecomputeHeight(amatch_avl *p){
200   short int hBefore = p->pBefore ? p->pBefore->height : 0;
201   short int hAfter = p->pAfter ? p->pAfter->height : 0;
202   p->imbalance = hBefore - hAfter;  /* -: pAfter higher.  +: pBefore higher */
203   p->height = (hBefore>hAfter ? hBefore : hAfter)+1;
204 }
205 
206 /*
207 **     P                B
208 **    / \              / \
209 **   B   Z    ==>     X   P
210 **  / \                  / \
211 ** X   Y                Y   Z
212 **
213 */
amatchAvlRotateBefore(amatch_avl * pP)214 static amatch_avl *amatchAvlRotateBefore(amatch_avl *pP){
215   amatch_avl *pB = pP->pBefore;
216   amatch_avl *pY = pB->pAfter;
217   pB->pUp = pP->pUp;
218   pB->pAfter = pP;
219   pP->pUp = pB;
220   pP->pBefore = pY;
221   if( pY ) pY->pUp = pP;
222   amatchAvlRecomputeHeight(pP);
223   amatchAvlRecomputeHeight(pB);
224   return pB;
225 }
226 
227 /*
228 **     P                A
229 **    / \              / \
230 **   X   A    ==>     P   Z
231 **      / \          / \
232 **     Y   Z        X   Y
233 **
234 */
amatchAvlRotateAfter(amatch_avl * pP)235 static amatch_avl *amatchAvlRotateAfter(amatch_avl *pP){
236   amatch_avl *pA = pP->pAfter;
237   amatch_avl *pY = pA->pBefore;
238   pA->pUp = pP->pUp;
239   pA->pBefore = pP;
240   pP->pUp = pA;
241   pP->pAfter = pY;
242   if( pY ) pY->pUp = pP;
243   amatchAvlRecomputeHeight(pP);
244   amatchAvlRecomputeHeight(pA);
245   return pA;
246 }
247 
248 /*
249 ** Return a pointer to the pBefore or pAfter pointer in the parent
250 ** of p that points to p.  Or if p is the root node, return pp.
251 */
amatchAvlFromPtr(amatch_avl * p,amatch_avl ** pp)252 static amatch_avl **amatchAvlFromPtr(amatch_avl *p, amatch_avl **pp){
253   amatch_avl *pUp = p->pUp;
254   if( pUp==0 ) return pp;
255   if( pUp->pAfter==p ) return &pUp->pAfter;
256   return &pUp->pBefore;
257 }
258 
259 /*
260 ** Rebalance all nodes starting with p and working up to the root.
261 ** Return the new root.
262 */
amatchAvlBalance(amatch_avl * p)263 static amatch_avl *amatchAvlBalance(amatch_avl *p){
264   amatch_avl *pTop = p;
265   amatch_avl **pp;
266   while( p ){
267     amatchAvlRecomputeHeight(p);
268     if( p->imbalance>=2 ){
269       amatch_avl *pB = p->pBefore;
270       if( pB->imbalance<0 ) p->pBefore = amatchAvlRotateAfter(pB);
271       pp = amatchAvlFromPtr(p,&p);
272       p = *pp = amatchAvlRotateBefore(p);
273     }else if( p->imbalance<=(-2) ){
274       amatch_avl *pA = p->pAfter;
275       if( pA->imbalance>0 ) p->pAfter = amatchAvlRotateBefore(pA);
276       pp = amatchAvlFromPtr(p,&p);
277       p = *pp = amatchAvlRotateAfter(p);
278     }
279     pTop = p;
280     p = p->pUp;
281   }
282   return pTop;
283 }
284 
285 /* Search the tree rooted at p for an entry with zKey.  Return a pointer
286 ** to the entry or return NULL.
287 */
amatchAvlSearch(amatch_avl * p,const char * zKey)288 static amatch_avl *amatchAvlSearch(amatch_avl *p, const char *zKey){
289   int c;
290   while( p && (c = strcmp(zKey, p->zKey))!=0 ){
291     p = (c<0) ? p->pBefore : p->pAfter;
292   }
293   return p;
294 }
295 
296 /* Find the first node (the one with the smallest key).
297 */
amatchAvlFirst(amatch_avl * p)298 static amatch_avl *amatchAvlFirst(amatch_avl *p){
299   if( p ) while( p->pBefore ) p = p->pBefore;
300   return p;
301 }
302 
303 #if 0 /* NOT USED */
304 /* Return the node with the next larger key after p.
305 */
306 static amatch_avl *amatchAvlNext(amatch_avl *p){
307   amatch_avl *pPrev = 0;
308   while( p && p->pAfter==pPrev ){
309     pPrev = p;
310     p = p->pUp;
311   }
312   if( p && pPrev==0 ){
313     p = amatchAvlFirst(p->pAfter);
314   }
315   return p;
316 }
317 #endif
318 
319 #if 0 /* NOT USED */
320 /* Verify AVL tree integrity
321 */
322 static int amatchAvlIntegrity(amatch_avl *pHead){
323   amatch_avl *p;
324   if( pHead==0 ) return 1;
325   if( (p = pHead->pBefore)!=0 ){
326     assert( p->pUp==pHead );
327     assert( amatchAvlIntegrity(p) );
328     assert( strcmp(p->zKey, pHead->zKey)<0 );
329     while( p->pAfter ) p = p->pAfter;
330     assert( strcmp(p->zKey, pHead->zKey)<0 );
331   }
332   if( (p = pHead->pAfter)!=0 ){
333     assert( p->pUp==pHead );
334     assert( amatchAvlIntegrity(p) );
335     assert( strcmp(p->zKey, pHead->zKey)>0 );
336     p = amatchAvlFirst(p);
337     assert( strcmp(p->zKey, pHead->zKey)>0 );
338   }
339   return 1;
340 }
341 static int amatchAvlIntegrity2(amatch_avl *pHead){
342   amatch_avl *p, *pNext;
343   for(p=amatchAvlFirst(pHead); p; p=pNext){
344     pNext = amatchAvlNext(p);
345     if( pNext==0 ) break;
346     assert( strcmp(p->zKey, pNext->zKey)<0 );
347   }
348   return 1;
349 }
350 #endif
351 
352 /* Insert a new node pNew.  Return NULL on success.  If the key is not
353 ** unique, then do not perform the insert but instead leave pNew unchanged
354 ** and return a pointer to an existing node with the same key.
355 */
amatchAvlInsert(amatch_avl ** ppHead,amatch_avl * pNew)356 static amatch_avl *amatchAvlInsert(amatch_avl **ppHead, amatch_avl *pNew){
357   int c;
358   amatch_avl *p = *ppHead;
359   if( p==0 ){
360     p = pNew;
361     pNew->pUp = 0;
362   }else{
363     while( p ){
364       c = strcmp(pNew->zKey, p->zKey);
365       if( c<0 ){
366         if( p->pBefore ){
367           p = p->pBefore;
368         }else{
369           p->pBefore = pNew;
370           pNew->pUp = p;
371           break;
372         }
373       }else if( c>0 ){
374         if( p->pAfter ){
375           p = p->pAfter;
376         }else{
377           p->pAfter = pNew;
378           pNew->pUp = p;
379           break;
380         }
381       }else{
382         return p;
383       }
384     }
385   }
386   pNew->pBefore = 0;
387   pNew->pAfter = 0;
388   pNew->height = 1;
389   pNew->imbalance = 0;
390   *ppHead = amatchAvlBalance(p);
391   /* assert( amatchAvlIntegrity(*ppHead) ); */
392   /* assert( amatchAvlIntegrity2(*ppHead) ); */
393   return 0;
394 }
395 
396 /* Remove node pOld from the tree.  pOld must be an element of the tree or
397 ** the AVL tree will become corrupt.
398 */
amatchAvlRemove(amatch_avl ** ppHead,amatch_avl * pOld)399 static void amatchAvlRemove(amatch_avl **ppHead, amatch_avl *pOld){
400   amatch_avl **ppParent;
401   amatch_avl *pBalance = 0;
402   /* assert( amatchAvlSearch(*ppHead, pOld->zKey)==pOld ); */
403   ppParent = amatchAvlFromPtr(pOld, ppHead);
404   if( pOld->pBefore==0 && pOld->pAfter==0 ){
405     *ppParent = 0;
406     pBalance = pOld->pUp;
407   }else if( pOld->pBefore && pOld->pAfter ){
408     amatch_avl *pX, *pY;
409     pX = amatchAvlFirst(pOld->pAfter);
410     *amatchAvlFromPtr(pX, 0) = pX->pAfter;
411     if( pX->pAfter ) pX->pAfter->pUp = pX->pUp;
412     pBalance = pX->pUp;
413     pX->pAfter = pOld->pAfter;
414     if( pX->pAfter ){
415       pX->pAfter->pUp = pX;
416     }else{
417       assert( pBalance==pOld );
418       pBalance = pX;
419     }
420     pX->pBefore = pY = pOld->pBefore;
421     if( pY ) pY->pUp = pX;
422     pX->pUp = pOld->pUp;
423     *ppParent = pX;
424   }else if( pOld->pBefore==0 ){
425     *ppParent = pBalance = pOld->pAfter;
426     pBalance->pUp = pOld->pUp;
427   }else if( pOld->pAfter==0 ){
428     *ppParent = pBalance = pOld->pBefore;
429     pBalance->pUp = pOld->pUp;
430   }
431   *ppHead = amatchAvlBalance(pBalance);
432   pOld->pUp = 0;
433   pOld->pBefore = 0;
434   pOld->pAfter = 0;
435   /* assert( amatchAvlIntegrity(*ppHead) ); */
436   /* assert( amatchAvlIntegrity2(*ppHead) ); */
437 }
438 /*
439 ** End of the AVL Tree implementation
440 ******************************************************************************/
441 
442 
443 /*
444 ** Various types.
445 **
446 ** amatch_cost is the "cost" of an edit operation.
447 **
448 ** amatch_len is the length of a matching string.
449 **
450 ** amatch_langid is an ruleset identifier.
451 */
452 typedef int amatch_cost;
453 typedef signed char amatch_len;
454 typedef int amatch_langid;
455 
456 /*
457 ** Limits
458 */
459 #define AMATCH_MX_LENGTH          50  /* Maximum length of a rule string */
460 #define AMATCH_MX_LANGID  2147483647  /* Maximum rule ID */
461 #define AMATCH_MX_COST          1000  /* Maximum single-rule cost */
462 
463 /*
464 ** A match or partial match
465 */
466 struct amatch_word {
467   amatch_word *pNext;   /* Next on a list of all amatch_words */
468   amatch_avl sCost;     /* Linkage of this node into the cost tree */
469   amatch_avl sWord;     /* Linkage of this node into the word tree */
470   amatch_cost rCost;    /* Cost of the match so far */
471   int iSeq;             /* Sequence number */
472   char zCost[10];       /* Cost key (text rendering of rCost) */
473   short int nMatch;     /* Input characters matched */
474   char zWord[4];        /* Text of the word.  Extra space appended as needed */
475 };
476 
477 /*
478 ** Each transformation rule is stored as an instance of this object.
479 ** All rules are kept on a linked list sorted by rCost.
480 */
481 struct amatch_rule {
482   amatch_rule *pNext;      /* Next rule in order of increasing rCost */
483   char *zFrom;             /* Transform from (a string from user input) */
484   amatch_cost rCost;       /* Cost of this transformation */
485   amatch_langid iLang;     /* The langauge to which this rule belongs */
486   amatch_len nFrom, nTo;   /* Length of the zFrom and zTo strings */
487   char zTo[4];             /* Tranform to V.W value (extra space appended) */
488 };
489 
490 /*
491 ** A amatch virtual-table object
492 */
493 struct amatch_vtab {
494   sqlite3_vtab base;         /* Base class - must be first */
495   char *zClassName;          /* Name of this class.  Default: "amatch" */
496   char *zDb;                 /* Name of database.  (ex: "main") */
497   char *zSelf;               /* Name of this virtual table */
498   char *zCostTab;            /* Name of edit-cost-table */
499   char *zVocabTab;           /* Name of vocabulary table */
500   char *zVocabWord;          /* Name of vocabulary table word column */
501   char *zVocabLang;          /* Name of vocabulary table language column */
502   amatch_rule *pRule;        /* All active rules in this amatch */
503   amatch_cost rIns;          /* Generic insertion cost  '' -> ? */
504   amatch_cost rDel;          /* Generic deletion cost  ? -> '' */
505   amatch_cost rSub;          /* Generic substitution cost ? -> ? */
506   sqlite3 *db;               /* The database connection */
507   sqlite3_stmt *pVCheck;     /* Query to check zVocabTab */
508   int nCursor;               /* Number of active cursors */
509 };
510 
511 /* A amatch cursor object */
512 struct amatch_cursor {
513   sqlite3_vtab_cursor base;  /* Base class - must be first */
514   sqlite3_int64 iRowid;      /* The rowid of the current word */
515   amatch_langid iLang;       /* Use this language ID */
516   amatch_cost rLimit;        /* Maximum cost of any term */
517   int nBuf;                  /* Space allocated for zBuf */
518   int oomErr;                /* True following an OOM error */
519   int nWord;                 /* Number of amatch_word objects */
520   char *zBuf;                /* Temp-use buffer space */
521   char *zInput;              /* Input word to match against */
522   amatch_vtab *pVtab;        /* The virtual table this cursor belongs to */
523   amatch_word *pAllWords;    /* List of all amatch_word objects */
524   amatch_word *pCurrent;     /* Most recent solution */
525   amatch_avl *pCost;         /* amatch_word objects keyed by iCost */
526   amatch_avl *pWord;         /* amatch_word objects keyed by zWord */
527 };
528 
529 /*
530 ** The two input rule lists are both sorted in order of increasing
531 ** cost.  Merge them together into a single list, sorted by cost, and
532 ** return a pointer to the head of that list.
533 */
amatchMergeRules(amatch_rule * pA,amatch_rule * pB)534 static amatch_rule *amatchMergeRules(amatch_rule *pA, amatch_rule *pB){
535   amatch_rule head;
536   amatch_rule *pTail;
537 
538   pTail =  &head;
539   while( pA && pB ){
540     if( pA->rCost<=pB->rCost ){
541       pTail->pNext = pA;
542       pTail = pA;
543       pA = pA->pNext;
544     }else{
545       pTail->pNext = pB;
546       pTail = pB;
547       pB = pB->pNext;
548     }
549   }
550   if( pA==0 ){
551     pTail->pNext = pB;
552   }else{
553     pTail->pNext = pA;
554   }
555   return head.pNext;
556 }
557 
558 /*
559 ** Statement pStmt currently points to a row in the amatch data table. This
560 ** function allocates and populates a amatch_rule structure according to
561 ** the content of the row.
562 **
563 ** If successful, *ppRule is set to point to the new object and SQLITE_OK
564 ** is returned. Otherwise, *ppRule is zeroed, *pzErr may be set to point
565 ** to an error message and an SQLite error code returned.
566 */
amatchLoadOneRule(amatch_vtab * p,sqlite3_stmt * pStmt,amatch_rule ** ppRule,char ** pzErr)567 static int amatchLoadOneRule(
568   amatch_vtab *p,                 /* Fuzzer virtual table handle */
569   sqlite3_stmt *pStmt,            /* Base rule on statements current row */
570   amatch_rule **ppRule,           /* OUT: New rule object */
571   char **pzErr                    /* OUT: Error message */
572 ){
573   sqlite3_int64 iLang = sqlite3_column_int64(pStmt, 0);
574   const char *zFrom = (const char *)sqlite3_column_text(pStmt, 1);
575   const char *zTo = (const char *)sqlite3_column_text(pStmt, 2);
576   amatch_cost rCost = sqlite3_column_int(pStmt, 3);
577 
578   int rc = SQLITE_OK;             /* Return code */
579   int nFrom;                      /* Size of string zFrom, in bytes */
580   int nTo;                        /* Size of string zTo, in bytes */
581   amatch_rule *pRule = 0;         /* New rule object to return */
582 
583   if( zFrom==0 ) zFrom = "";
584   if( zTo==0 ) zTo = "";
585   nFrom = (int)strlen(zFrom);
586   nTo = (int)strlen(zTo);
587 
588   /* Silently ignore null transformations */
589   if( strcmp(zFrom, zTo)==0 ){
590     if( zFrom[0]=='?' && zFrom[1]==0 ){
591       if( p->rSub==0 || p->rSub>rCost ) p->rSub = rCost;
592     }
593     *ppRule = 0;
594     return SQLITE_OK;
595   }
596 
597   if( rCost<=0 || rCost>AMATCH_MX_COST ){
598     *pzErr = sqlite3_mprintf("%s: cost must be between 1 and %d",
599         p->zClassName, AMATCH_MX_COST
600     );
601     rc = SQLITE_ERROR;
602   }else
603   if( nFrom>AMATCH_MX_LENGTH || nTo>AMATCH_MX_LENGTH ){
604     *pzErr = sqlite3_mprintf("%s: maximum string length is %d",
605         p->zClassName, AMATCH_MX_LENGTH
606     );
607     rc = SQLITE_ERROR;
608   }else
609   if( iLang<0 || iLang>AMATCH_MX_LANGID ){
610     *pzErr = sqlite3_mprintf("%s: iLang must be between 0 and %d",
611         p->zClassName, AMATCH_MX_LANGID
612     );
613     rc = SQLITE_ERROR;
614   }else
615   if( strcmp(zFrom,"")==0 && strcmp(zTo,"?")==0 ){
616     if( p->rIns==0 || p->rIns>rCost ) p->rIns = rCost;
617   }else
618   if( strcmp(zFrom,"?")==0 && strcmp(zTo,"")==0 ){
619     if( p->rDel==0 || p->rDel>rCost ) p->rDel = rCost;
620   }else
621   {
622     pRule = sqlite3_malloc64( sizeof(*pRule) + nFrom + nTo );
623     if( pRule==0 ){
624       rc = SQLITE_NOMEM;
625     }else{
626       memset(pRule, 0, sizeof(*pRule));
627       pRule->zFrom = &pRule->zTo[nTo+1];
628       pRule->nFrom = (amatch_len)nFrom;
629       memcpy(pRule->zFrom, zFrom, nFrom+1);
630       memcpy(pRule->zTo, zTo, nTo+1);
631       pRule->nTo = (amatch_len)nTo;
632       pRule->rCost = rCost;
633       pRule->iLang = (int)iLang;
634     }
635   }
636 
637   *ppRule = pRule;
638   return rc;
639 }
640 
641 /*
642 ** Free all the content in the edit-cost-table
643 */
amatchFreeRules(amatch_vtab * p)644 static void amatchFreeRules(amatch_vtab *p){
645   while( p->pRule ){
646     amatch_rule *pRule = p->pRule;
647     p->pRule = pRule->pNext;
648     sqlite3_free(pRule);
649   }
650   p->pRule = 0;
651 }
652 
653 /*
654 ** Load the content of the amatch data table into memory.
655 */
amatchLoadRules(sqlite3 * db,amatch_vtab * p,char ** pzErr)656 static int amatchLoadRules(
657   sqlite3 *db,                    /* Database handle */
658   amatch_vtab *p,                 /* Virtual amatch table to configure */
659   char **pzErr                    /* OUT: Error message */
660 ){
661   int rc = SQLITE_OK;             /* Return code */
662   char *zSql;                     /* SELECT used to read from rules table */
663   amatch_rule *pHead = 0;
664 
665   zSql = sqlite3_mprintf("SELECT * FROM %Q.%Q", p->zDb, p->zCostTab);
666   if( zSql==0 ){
667     rc = SQLITE_NOMEM;
668   }else{
669     int rc2;                      /* finalize() return code */
670     sqlite3_stmt *pStmt = 0;
671     rc = sqlite3_prepare_v2(db, zSql, -1, &pStmt, 0);
672     if( rc!=SQLITE_OK ){
673       *pzErr = sqlite3_mprintf("%s: %s", p->zClassName, sqlite3_errmsg(db));
674     }else if( sqlite3_column_count(pStmt)!=4 ){
675       *pzErr = sqlite3_mprintf("%s: %s has %d columns, expected 4",
676           p->zClassName, p->zCostTab, sqlite3_column_count(pStmt)
677       );
678       rc = SQLITE_ERROR;
679     }else{
680       while( rc==SQLITE_OK && SQLITE_ROW==sqlite3_step(pStmt) ){
681         amatch_rule *pRule = 0;
682         rc = amatchLoadOneRule(p, pStmt, &pRule, pzErr);
683         if( pRule ){
684           pRule->pNext = pHead;
685           pHead = pRule;
686         }
687       }
688     }
689     rc2 = sqlite3_finalize(pStmt);
690     if( rc==SQLITE_OK ) rc = rc2;
691   }
692   sqlite3_free(zSql);
693 
694   /* All rules are now in a singly linked list starting at pHead. This
695   ** block sorts them by cost and then sets amatch_vtab.pRule to point to
696   ** point to the head of the sorted list.
697   */
698   if( rc==SQLITE_OK ){
699     unsigned int i;
700     amatch_rule *pX;
701     amatch_rule *a[15];
702     for(i=0; i<sizeof(a)/sizeof(a[0]); i++) a[i] = 0;
703     while( (pX = pHead)!=0 ){
704       pHead = pX->pNext;
705       pX->pNext = 0;
706       for(i=0; a[i] && i<sizeof(a)/sizeof(a[0])-1; i++){
707         pX = amatchMergeRules(a[i], pX);
708         a[i] = 0;
709       }
710       a[i] = amatchMergeRules(a[i], pX);
711     }
712     for(pX=a[0], i=1; i<sizeof(a)/sizeof(a[0]); i++){
713       pX = amatchMergeRules(a[i], pX);
714     }
715     p->pRule = amatchMergeRules(p->pRule, pX);
716   }else{
717     /* An error has occurred. Setting p->pRule to point to the head of the
718     ** allocated list ensures that the list will be cleaned up in this case.
719     */
720     assert( p->pRule==0 );
721     p->pRule = pHead;
722   }
723 
724   return rc;
725 }
726 
727 /*
728 ** This function converts an SQL quoted string into an unquoted string
729 ** and returns a pointer to a buffer allocated using sqlite3_malloc()
730 ** containing the result. The caller should eventually free this buffer
731 ** using sqlite3_free.
732 **
733 ** Examples:
734 **
735 **     "abc"   becomes   abc
736 **     'xyz'   becomes   xyz
737 **     [pqr]   becomes   pqr
738 **     `mno`   becomes   mno
739 */
amatchDequote(const char * zIn)740 static char *amatchDequote(const char *zIn){
741   sqlite3_int64 nIn;              /* Size of input string, in bytes */
742   char *zOut;                     /* Output (dequoted) string */
743 
744   nIn = strlen(zIn);
745   zOut = sqlite3_malloc64(nIn+1);
746   if( zOut ){
747     char q = zIn[0];              /* Quote character (if any ) */
748 
749     if( q!='[' && q!= '\'' && q!='"' && q!='`' ){
750       memcpy(zOut, zIn, (size_t)(nIn+1));
751     }else{
752       int iOut = 0;               /* Index of next byte to write to output */
753       int iIn;                    /* Index of next byte to read from input */
754 
755       if( q=='[' ) q = ']';
756       for(iIn=1; iIn<nIn; iIn++){
757         if( zIn[iIn]==q ) iIn++;
758         zOut[iOut++] = zIn[iIn];
759       }
760     }
761     assert( (int)strlen(zOut)<=nIn );
762   }
763   return zOut;
764 }
765 
766 /*
767 ** Deallocate the pVCheck prepared statement.
768 */
amatchVCheckClear(amatch_vtab * p)769 static void amatchVCheckClear(amatch_vtab *p){
770   if( p->pVCheck ){
771     sqlite3_finalize(p->pVCheck);
772     p->pVCheck = 0;
773   }
774 }
775 
776 /*
777 ** Deallocate an amatch_vtab object
778 */
amatchFree(amatch_vtab * p)779 static void amatchFree(amatch_vtab *p){
780   if( p ){
781     amatchFreeRules(p);
782     amatchVCheckClear(p);
783     sqlite3_free(p->zClassName);
784     sqlite3_free(p->zDb);
785     sqlite3_free(p->zCostTab);
786     sqlite3_free(p->zVocabTab);
787     sqlite3_free(p->zVocabWord);
788     sqlite3_free(p->zVocabLang);
789     sqlite3_free(p->zSelf);
790     memset(p, 0, sizeof(*p));
791     sqlite3_free(p);
792   }
793 }
794 
795 /*
796 ** xDisconnect/xDestroy method for the amatch module.
797 */
amatchDisconnect(sqlite3_vtab * pVtab)798 static int amatchDisconnect(sqlite3_vtab *pVtab){
799   amatch_vtab *p = (amatch_vtab*)pVtab;
800   assert( p->nCursor==0 );
801   amatchFree(p);
802   return SQLITE_OK;
803 }
804 
805 /*
806 ** Check to see if the argument is of the form:
807 **
808 **       KEY = VALUE
809 **
810 ** If it is, return a pointer to the first character of VALUE.
811 ** If not, return NULL.  Spaces around the = are ignored.
812 */
amatchValueOfKey(const char * zKey,const char * zStr)813 static const char *amatchValueOfKey(const char *zKey, const char *zStr){
814   int nKey = (int)strlen(zKey);
815   int nStr = (int)strlen(zStr);
816   int i;
817   if( nStr<nKey+1 ) return 0;
818   if( memcmp(zStr, zKey, nKey)!=0 ) return 0;
819   for(i=nKey; isspace((unsigned char)zStr[i]); i++){}
820   if( zStr[i]!='=' ) return 0;
821   i++;
822   while( isspace((unsigned char)zStr[i]) ){ i++; }
823   return zStr+i;
824 }
825 
826 /*
827 ** xConnect/xCreate method for the amatch module. Arguments are:
828 **
829 **   argv[0]    -> module name  ("approximate_match")
830 **   argv[1]    -> database name
831 **   argv[2]    -> table name
832 **   argv[3...] -> arguments
833 */
amatchConnect(sqlite3 * db,void * pAux,int argc,const char * const * argv,sqlite3_vtab ** ppVtab,char ** pzErr)834 static int amatchConnect(
835   sqlite3 *db,
836   void *pAux,
837   int argc, const char *const*argv,
838   sqlite3_vtab **ppVtab,
839   char **pzErr
840 ){
841   int rc = SQLITE_OK;             /* Return code */
842   amatch_vtab *pNew = 0;          /* New virtual table */
843   const char *zModule = argv[0];
844   const char *zDb = argv[1];
845   const char *zVal;
846   int i;
847 
848   (void)pAux;
849   *ppVtab = 0;
850   pNew = sqlite3_malloc( sizeof(*pNew) );
851   if( pNew==0 ) return SQLITE_NOMEM;
852   rc = SQLITE_NOMEM;
853   memset(pNew, 0, sizeof(*pNew));
854   pNew->db = db;
855   pNew->zClassName = sqlite3_mprintf("%s", zModule);
856   if( pNew->zClassName==0 ) goto amatchConnectError;
857   pNew->zDb = sqlite3_mprintf("%s", zDb);
858   if( pNew->zDb==0 ) goto amatchConnectError;
859   pNew->zSelf = sqlite3_mprintf("%s", argv[2]);
860   if( pNew->zSelf==0 ) goto amatchConnectError;
861   for(i=3; i<argc; i++){
862     zVal = amatchValueOfKey("vocabulary_table", argv[i]);
863     if( zVal ){
864       sqlite3_free(pNew->zVocabTab);
865       pNew->zVocabTab = amatchDequote(zVal);
866       if( pNew->zVocabTab==0 ) goto amatchConnectError;
867       continue;
868     }
869     zVal = amatchValueOfKey("vocabulary_word", argv[i]);
870     if( zVal ){
871       sqlite3_free(pNew->zVocabWord);
872       pNew->zVocabWord = amatchDequote(zVal);
873       if( pNew->zVocabWord==0 ) goto amatchConnectError;
874       continue;
875     }
876     zVal = amatchValueOfKey("vocabulary_language", argv[i]);
877     if( zVal ){
878       sqlite3_free(pNew->zVocabLang);
879       pNew->zVocabLang = amatchDequote(zVal);
880       if( pNew->zVocabLang==0 ) goto amatchConnectError;
881       continue;
882     }
883     zVal = amatchValueOfKey("edit_distances", argv[i]);
884     if( zVal ){
885       sqlite3_free(pNew->zCostTab);
886       pNew->zCostTab = amatchDequote(zVal);
887       if( pNew->zCostTab==0 ) goto amatchConnectError;
888       continue;
889     }
890     *pzErr = sqlite3_mprintf("unrecognized argument: [%s]\n", argv[i]);
891     amatchFree(pNew);
892     *ppVtab = 0;
893     return SQLITE_ERROR;
894   }
895   rc = SQLITE_OK;
896   if( pNew->zCostTab==0 ){
897     *pzErr = sqlite3_mprintf("no edit_distances table specified");
898     rc = SQLITE_ERROR;
899   }else{
900     rc = amatchLoadRules(db, pNew, pzErr);
901   }
902   if( rc==SQLITE_OK ){
903     sqlite3_vtab_config(db, SQLITE_VTAB_INNOCUOUS);
904     rc = sqlite3_declare_vtab(db,
905            "CREATE TABLE x(word,distance,language,"
906            "command HIDDEN,nword HIDDEN)"
907          );
908 #define AMATCH_COL_WORD       0
909 #define AMATCH_COL_DISTANCE   1
910 #define AMATCH_COL_LANGUAGE   2
911 #define AMATCH_COL_COMMAND    3
912 #define AMATCH_COL_NWORD      4
913   }
914   if( rc!=SQLITE_OK ){
915     amatchFree(pNew);
916   }
917   *ppVtab = &pNew->base;
918   return rc;
919 
920 amatchConnectError:
921   amatchFree(pNew);
922   return rc;
923 }
924 
925 /*
926 ** Open a new amatch cursor.
927 */
amatchOpen(sqlite3_vtab * pVTab,sqlite3_vtab_cursor ** ppCursor)928 static int amatchOpen(sqlite3_vtab *pVTab, sqlite3_vtab_cursor **ppCursor){
929   amatch_vtab *p = (amatch_vtab*)pVTab;
930   amatch_cursor *pCur;
931   pCur = sqlite3_malloc( sizeof(*pCur) );
932   if( pCur==0 ) return SQLITE_NOMEM;
933   memset(pCur, 0, sizeof(*pCur));
934   pCur->pVtab = p;
935   *ppCursor = &pCur->base;
936   p->nCursor++;
937   return SQLITE_OK;
938 }
939 
940 /*
941 ** Free up all the memory allocated by a cursor.  Set it rLimit to 0
942 ** to indicate that it is at EOF.
943 */
amatchClearCursor(amatch_cursor * pCur)944 static void amatchClearCursor(amatch_cursor *pCur){
945   amatch_word *pWord, *pNextWord;
946   for(pWord=pCur->pAllWords; pWord; pWord=pNextWord){
947     pNextWord = pWord->pNext;
948     sqlite3_free(pWord);
949   }
950   pCur->pAllWords = 0;
951   sqlite3_free(pCur->zInput);
952   pCur->zInput = 0;
953   sqlite3_free(pCur->zBuf);
954   pCur->zBuf = 0;
955   pCur->nBuf = 0;
956   pCur->pCost = 0;
957   pCur->pWord = 0;
958   pCur->pCurrent = 0;
959   pCur->rLimit = 1000000;
960   pCur->iLang = 0;
961   pCur->nWord = 0;
962 }
963 
964 /*
965 ** Close a amatch cursor.
966 */
amatchClose(sqlite3_vtab_cursor * cur)967 static int amatchClose(sqlite3_vtab_cursor *cur){
968   amatch_cursor *pCur = (amatch_cursor *)cur;
969   amatchClearCursor(pCur);
970   pCur->pVtab->nCursor--;
971   sqlite3_free(pCur);
972   return SQLITE_OK;
973 }
974 
975 /*
976 ** Render a 24-bit unsigned integer as a 4-byte base-64 number.
977 */
amatchEncodeInt(int x,char * z)978 static void amatchEncodeInt(int x, char *z){
979   static const char a[] =
980     "0123456789"
981     "ABCDEFGHIJ"
982     "KLMNOPQRST"
983     "UVWXYZ^abc"
984     "defghijklm"
985     "nopqrstuvw"
986     "xyz~";
987   z[0] = a[(x>>18)&0x3f];
988   z[1] = a[(x>>12)&0x3f];
989   z[2] = a[(x>>6)&0x3f];
990   z[3] = a[x&0x3f];
991 }
992 
993 /*
994 ** Write the zCost[] field for a amatch_word object
995 */
amatchWriteCost(amatch_word * pWord)996 static void amatchWriteCost(amatch_word *pWord){
997   amatchEncodeInt(pWord->rCost, pWord->zCost);
998   amatchEncodeInt(pWord->iSeq, pWord->zCost+4);
999   pWord->zCost[8] = 0;
1000 }
1001 
1002 /* Circumvent compiler warnings about the use of strcpy() by supplying
1003 ** our own implementation.
1004 */
amatchStrcpy(char * dest,const char * src)1005 static void amatchStrcpy(char *dest, const char *src){
1006   while( (*(dest++) = *(src++))!=0 ){}
1007 }
amatchStrcat(char * dest,const char * src)1008 static void amatchStrcat(char *dest, const char *src){
1009   while( *dest ) dest++;
1010   amatchStrcpy(dest, src);
1011 }
1012 
1013 /*
1014 ** Add a new amatch_word object to the queue.
1015 **
1016 ** If a prior amatch_word object with the same zWord, and nMatch
1017 ** already exists, update its rCost (if the new rCost is less) but
1018 ** otherwise leave it unchanged.  Do not add a duplicate.
1019 **
1020 ** Do nothing if the cost exceeds threshold.
1021 */
amatchAddWord(amatch_cursor * pCur,amatch_cost rCost,int nMatch,const char * zWordBase,const char * zWordTail)1022 static void amatchAddWord(
1023   amatch_cursor *pCur,
1024   amatch_cost rCost,
1025   int nMatch,
1026   const char *zWordBase,
1027   const char *zWordTail
1028 ){
1029   amatch_word *pWord;
1030   amatch_avl *pNode;
1031   amatch_avl *pOther;
1032   int nBase, nTail;
1033   char zBuf[4];
1034 
1035   if( rCost>pCur->rLimit ){
1036     return;
1037   }
1038   nBase = (int)strlen(zWordBase);
1039   nTail = (int)strlen(zWordTail);
1040   if( nBase+nTail+3>pCur->nBuf ){
1041     pCur->nBuf = nBase+nTail+100;
1042     pCur->zBuf = sqlite3_realloc(pCur->zBuf, pCur->nBuf);
1043     if( pCur->zBuf==0 ){
1044       pCur->nBuf = 0;
1045       return;
1046     }
1047   }
1048   amatchEncodeInt(nMatch, zBuf);
1049   memcpy(pCur->zBuf, zBuf+2, 2);
1050   memcpy(pCur->zBuf+2, zWordBase, nBase);
1051   memcpy(pCur->zBuf+2+nBase, zWordTail, nTail+1);
1052   pNode = amatchAvlSearch(pCur->pWord, pCur->zBuf);
1053   if( pNode ){
1054     pWord = pNode->pWord;
1055     if( pWord->rCost>rCost ){
1056 #ifdef AMATCH_TRACE_1
1057       printf("UPDATE [%s][%.*s^%s] %d (\"%s\" \"%s\")\n",
1058              pWord->zWord+2, pWord->nMatch, pCur->zInput, pCur->zInput,
1059              pWord->rCost, pWord->zWord, pWord->zCost);
1060 #endif
1061       amatchAvlRemove(&pCur->pCost, &pWord->sCost);
1062       pWord->rCost = rCost;
1063       amatchWriteCost(pWord);
1064 #ifdef AMATCH_TRACE_1
1065       printf("  ---> %d (\"%s\" \"%s\")\n",
1066              pWord->rCost, pWord->zWord, pWord->zCost);
1067 #endif
1068       pOther = amatchAvlInsert(&pCur->pCost, &pWord->sCost);
1069       assert( pOther==0 ); (void)pOther;
1070     }
1071     return;
1072   }
1073   pWord = sqlite3_malloc64( sizeof(*pWord) + nBase + nTail - 1 );
1074   if( pWord==0 ) return;
1075   memset(pWord, 0, sizeof(*pWord));
1076   pWord->rCost = rCost;
1077   pWord->iSeq = pCur->nWord++;
1078   amatchWriteCost(pWord);
1079   pWord->nMatch = (short)nMatch;
1080   pWord->pNext = pCur->pAllWords;
1081   pCur->pAllWords = pWord;
1082   pWord->sCost.zKey = pWord->zCost;
1083   pWord->sCost.pWord = pWord;
1084   pOther = amatchAvlInsert(&pCur->pCost, &pWord->sCost);
1085   assert( pOther==0 ); (void)pOther;
1086   pWord->sWord.zKey = pWord->zWord;
1087   pWord->sWord.pWord = pWord;
1088   amatchStrcpy(pWord->zWord, pCur->zBuf);
1089   pOther = amatchAvlInsert(&pCur->pWord, &pWord->sWord);
1090   assert( pOther==0 ); (void)pOther;
1091 #ifdef AMATCH_TRACE_1
1092   printf("INSERT [%s][%.*s^%s] %d (\"%s\" \"%s\")\n", pWord->zWord+2,
1093        pWord->nMatch, pCur->zInput, pCur->zInput+pWord->nMatch, rCost,
1094        pWord->zWord, pWord->zCost);
1095 #endif
1096 }
1097 
1098 
1099 /*
1100 ** Advance a cursor to its next row of output
1101 */
amatchNext(sqlite3_vtab_cursor * cur)1102 static int amatchNext(sqlite3_vtab_cursor *cur){
1103   amatch_cursor *pCur = (amatch_cursor*)cur;
1104   amatch_word *pWord = 0;
1105   amatch_avl *pNode;
1106   int isMatch = 0;
1107   amatch_vtab *p = pCur->pVtab;
1108   int nWord;
1109   int rc;
1110   int i;
1111   const char *zW;
1112   amatch_rule *pRule;
1113   char *zBuf = 0;
1114   char nBuf = 0;
1115   char zNext[8];
1116   char zNextIn[8];
1117   int nNextIn;
1118 
1119   if( p->pVCheck==0 ){
1120     char *zSql;
1121     if( p->zVocabLang && p->zVocabLang[0] ){
1122       zSql = sqlite3_mprintf(
1123           "SELECT \"%w\" FROM \"%w\"",
1124           " WHERE \"%w\">=?1 AND \"%w\"=?2"
1125           " ORDER BY 1",
1126           p->zVocabWord, p->zVocabTab,
1127           p->zVocabWord, p->zVocabLang
1128       );
1129     }else{
1130       zSql = sqlite3_mprintf(
1131           "SELECT \"%w\" FROM \"%w\""
1132           " WHERE \"%w\">=?1"
1133           " ORDER BY 1",
1134           p->zVocabWord, p->zVocabTab,
1135           p->zVocabWord
1136       );
1137     }
1138     rc = sqlite3_prepare_v2(p->db, zSql, -1, &p->pVCheck, 0);
1139     sqlite3_free(zSql);
1140     if( rc ) return rc;
1141   }
1142   sqlite3_bind_int(p->pVCheck, 2, pCur->iLang);
1143 
1144   do{
1145     pNode = amatchAvlFirst(pCur->pCost);
1146     if( pNode==0 ){
1147       pWord = 0;
1148       break;
1149     }
1150     pWord = pNode->pWord;
1151     amatchAvlRemove(&pCur->pCost, &pWord->sCost);
1152 
1153 #ifdef AMATCH_TRACE_1
1154     printf("PROCESS [%s][%.*s^%s] %d (\"%s\" \"%s\")\n",
1155        pWord->zWord+2, pWord->nMatch, pCur->zInput, pCur->zInput+pWord->nMatch,
1156        pWord->rCost, pWord->zWord, pWord->zCost);
1157 #endif
1158     nWord = (int)strlen(pWord->zWord+2);
1159     if( nWord+20>nBuf ){
1160       nBuf = (char)(nWord+100);
1161       zBuf = sqlite3_realloc(zBuf, nBuf);
1162       if( zBuf==0 ) return SQLITE_NOMEM;
1163     }
1164     amatchStrcpy(zBuf, pWord->zWord+2);
1165     zNext[0] = 0;
1166     zNextIn[0] = pCur->zInput[pWord->nMatch];
1167     if( zNextIn[0] ){
1168       for(i=1; i<=4 && (pCur->zInput[pWord->nMatch+i]&0xc0)==0x80; i++){
1169         zNextIn[i] = pCur->zInput[pWord->nMatch+i];
1170       }
1171       zNextIn[i] = 0;
1172       nNextIn = i;
1173     }else{
1174       nNextIn = 0;
1175     }
1176 
1177     if( zNextIn[0] && zNextIn[0]!='*' ){
1178       sqlite3_reset(p->pVCheck);
1179       amatchStrcat(zBuf, zNextIn);
1180       sqlite3_bind_text(p->pVCheck, 1, zBuf, nWord+nNextIn, SQLITE_STATIC);
1181       rc = sqlite3_step(p->pVCheck);
1182       if( rc==SQLITE_ROW ){
1183         zW = (const char*)sqlite3_column_text(p->pVCheck, 0);
1184         if( strncmp(zBuf, zW, nWord+nNextIn)==0 ){
1185           amatchAddWord(pCur, pWord->rCost, pWord->nMatch+nNextIn, zBuf, "");
1186         }
1187       }
1188       zBuf[nWord] = 0;
1189     }
1190 
1191     while( 1 ){
1192       amatchStrcpy(zBuf+nWord, zNext);
1193       sqlite3_reset(p->pVCheck);
1194       sqlite3_bind_text(p->pVCheck, 1, zBuf, -1, SQLITE_TRANSIENT);
1195       rc = sqlite3_step(p->pVCheck);
1196       if( rc!=SQLITE_ROW ) break;
1197       zW = (const char*)sqlite3_column_text(p->pVCheck, 0);
1198       amatchStrcpy(zBuf+nWord, zNext);
1199       if( strncmp(zW, zBuf, nWord)!=0 ) break;
1200       if( (zNextIn[0]=='*' && zNextIn[1]==0)
1201        || (zNextIn[0]==0 && zW[nWord]==0)
1202       ){
1203         isMatch = 1;
1204         zNextIn[0] = 0;
1205         nNextIn = 0;
1206         break;
1207       }
1208       zNext[0] = zW[nWord];
1209       for(i=1; i<=4 && (zW[nWord+i]&0xc0)==0x80; i++){
1210         zNext[i] = zW[nWord+i];
1211       }
1212       zNext[i] = 0;
1213       zBuf[nWord] = 0;
1214       if( p->rIns>0 ){
1215         amatchAddWord(pCur, pWord->rCost+p->rIns, pWord->nMatch,
1216                       zBuf, zNext);
1217       }
1218       if( p->rSub>0 ){
1219         amatchAddWord(pCur, pWord->rCost+p->rSub, pWord->nMatch+nNextIn,
1220                       zBuf, zNext);
1221       }
1222       if( p->rIns<0 && p->rSub<0 ) break;
1223       zNext[i-1]++;  /* FIX ME */
1224     }
1225     sqlite3_reset(p->pVCheck);
1226 
1227     if( p->rDel>0 ){
1228       zBuf[nWord] = 0;
1229       amatchAddWord(pCur, pWord->rCost+p->rDel, pWord->nMatch+nNextIn,
1230                     zBuf, "");
1231     }
1232 
1233     for(pRule=p->pRule; pRule; pRule=pRule->pNext){
1234       if( pRule->iLang!=pCur->iLang ) continue;
1235       if( strncmp(pRule->zFrom, pCur->zInput+pWord->nMatch, pRule->nFrom)==0 ){
1236         amatchAddWord(pCur, pWord->rCost+pRule->rCost,
1237                       pWord->nMatch+pRule->nFrom, pWord->zWord+2, pRule->zTo);
1238       }
1239     }
1240   }while( !isMatch );
1241   pCur->pCurrent = pWord;
1242   sqlite3_free(zBuf);
1243   return SQLITE_OK;
1244 }
1245 
1246 /*
1247 ** Called to "rewind" a cursor back to the beginning so that
1248 ** it starts its output over again.  Always called at least once
1249 ** prior to any amatchColumn, amatchRowid, or amatchEof call.
1250 */
amatchFilter(sqlite3_vtab_cursor * pVtabCursor,int idxNum,const char * idxStr,int argc,sqlite3_value ** argv)1251 static int amatchFilter(
1252   sqlite3_vtab_cursor *pVtabCursor,
1253   int idxNum, const char *idxStr,
1254   int argc, sqlite3_value **argv
1255 ){
1256   amatch_cursor *pCur = (amatch_cursor *)pVtabCursor;
1257   const char *zWord = "*";
1258   int idx;
1259 
1260   amatchClearCursor(pCur);
1261   idx = 0;
1262   if( idxNum & 1 ){
1263     zWord = (const char*)sqlite3_value_text(argv[0]);
1264     idx++;
1265   }
1266   if( idxNum & 2 ){
1267     pCur->rLimit = (amatch_cost)sqlite3_value_int(argv[idx]);
1268     idx++;
1269   }
1270   if( idxNum & 4 ){
1271     pCur->iLang = (amatch_cost)sqlite3_value_int(argv[idx]);
1272     idx++;
1273   }
1274   pCur->zInput = sqlite3_mprintf("%s", zWord);
1275   if( pCur->zInput==0 ) return SQLITE_NOMEM;
1276   amatchAddWord(pCur, 0, 0, "", "");
1277   amatchNext(pVtabCursor);
1278 
1279   return SQLITE_OK;
1280 }
1281 
1282 /*
1283 ** Only the word and distance columns have values.  All other columns
1284 ** return NULL
1285 */
amatchColumn(sqlite3_vtab_cursor * cur,sqlite3_context * ctx,int i)1286 static int amatchColumn(sqlite3_vtab_cursor *cur, sqlite3_context *ctx, int i){
1287   amatch_cursor *pCur = (amatch_cursor*)cur;
1288   switch( i ){
1289     case AMATCH_COL_WORD: {
1290       sqlite3_result_text(ctx, pCur->pCurrent->zWord+2, -1, SQLITE_STATIC);
1291       break;
1292     }
1293     case AMATCH_COL_DISTANCE: {
1294       sqlite3_result_int(ctx, pCur->pCurrent->rCost);
1295       break;
1296     }
1297     case AMATCH_COL_LANGUAGE: {
1298       sqlite3_result_int(ctx, pCur->iLang);
1299       break;
1300     }
1301     case AMATCH_COL_NWORD: {
1302       sqlite3_result_int(ctx, pCur->nWord);
1303       break;
1304     }
1305     default: {
1306       sqlite3_result_null(ctx);
1307       break;
1308     }
1309   }
1310   return SQLITE_OK;
1311 }
1312 
1313 /*
1314 ** The rowid.
1315 */
amatchRowid(sqlite3_vtab_cursor * cur,sqlite_int64 * pRowid)1316 static int amatchRowid(sqlite3_vtab_cursor *cur, sqlite_int64 *pRowid){
1317   amatch_cursor *pCur = (amatch_cursor*)cur;
1318   *pRowid = pCur->iRowid;
1319   return SQLITE_OK;
1320 }
1321 
1322 /*
1323 ** EOF indicator
1324 */
amatchEof(sqlite3_vtab_cursor * cur)1325 static int amatchEof(sqlite3_vtab_cursor *cur){
1326   amatch_cursor *pCur = (amatch_cursor*)cur;
1327   return pCur->pCurrent==0;
1328 }
1329 
1330 /*
1331 ** Search for terms of these forms:
1332 **
1333 **   (A)    word MATCH $str
1334 **   (B1)   distance < $value
1335 **   (B2)   distance <= $value
1336 **   (C)    language == $language
1337 **
1338 ** The distance< and distance<= are both treated as distance<=.
1339 ** The query plan number is a bit vector:
1340 **
1341 **   bit 1:   Term of the form (A) found
1342 **   bit 2:   Term like (B1) or (B2) found
1343 **   bit 3:   Term like (C) found
1344 **
1345 ** If bit-1 is set, $str is always in filter.argv[0].  If bit-2 is set
1346 ** then $value is in filter.argv[0] if bit-1 is clear and is in
1347 ** filter.argv[1] if bit-1 is set.  If bit-3 is set, then $ruleid is
1348 ** in filter.argv[0] if bit-1 and bit-2 are both zero, is in
1349 ** filter.argv[1] if exactly one of bit-1 and bit-2 are set, and is in
1350 ** filter.argv[2] if both bit-1 and bit-2 are set.
1351 */
amatchBestIndex(sqlite3_vtab * tab,sqlite3_index_info * pIdxInfo)1352 static int amatchBestIndex(
1353   sqlite3_vtab *tab,
1354   sqlite3_index_info *pIdxInfo
1355 ){
1356   int iPlan = 0;
1357   int iDistTerm = -1;
1358   int iLangTerm = -1;
1359   int i;
1360   const struct sqlite3_index_constraint *pConstraint;
1361 
1362   (void)tab;
1363   pConstraint = pIdxInfo->aConstraint;
1364   for(i=0; i<pIdxInfo->nConstraint; i++, pConstraint++){
1365     if( pConstraint->usable==0 ) continue;
1366     if( (iPlan & 1)==0
1367      && pConstraint->iColumn==0
1368      && pConstraint->op==SQLITE_INDEX_CONSTRAINT_MATCH
1369     ){
1370       iPlan |= 1;
1371       pIdxInfo->aConstraintUsage[i].argvIndex = 1;
1372       pIdxInfo->aConstraintUsage[i].omit = 1;
1373     }
1374     if( (iPlan & 2)==0
1375      && pConstraint->iColumn==1
1376      && (pConstraint->op==SQLITE_INDEX_CONSTRAINT_LT
1377            || pConstraint->op==SQLITE_INDEX_CONSTRAINT_LE)
1378     ){
1379       iPlan |= 2;
1380       iDistTerm = i;
1381     }
1382     if( (iPlan & 4)==0
1383      && pConstraint->iColumn==2
1384      && pConstraint->op==SQLITE_INDEX_CONSTRAINT_EQ
1385     ){
1386       iPlan |= 4;
1387       pIdxInfo->aConstraintUsage[i].omit = 1;
1388       iLangTerm = i;
1389     }
1390   }
1391   if( iPlan & 2 ){
1392     pIdxInfo->aConstraintUsage[iDistTerm].argvIndex = 1+((iPlan&1)!=0);
1393   }
1394   if( iPlan & 4 ){
1395     int idx = 1;
1396     if( iPlan & 1 ) idx++;
1397     if( iPlan & 2 ) idx++;
1398     pIdxInfo->aConstraintUsage[iLangTerm].argvIndex = idx;
1399   }
1400   pIdxInfo->idxNum = iPlan;
1401   if( pIdxInfo->nOrderBy==1
1402    && pIdxInfo->aOrderBy[0].iColumn==1
1403    && pIdxInfo->aOrderBy[0].desc==0
1404   ){
1405     pIdxInfo->orderByConsumed = 1;
1406   }
1407   pIdxInfo->estimatedCost = (double)10000;
1408 
1409   return SQLITE_OK;
1410 }
1411 
1412 /*
1413 ** The xUpdate() method.
1414 **
1415 ** This implementation disallows DELETE and UPDATE.  The only thing
1416 ** allowed is INSERT into the "command" column.
1417 */
amatchUpdate(sqlite3_vtab * pVTab,int argc,sqlite3_value ** argv,sqlite_int64 * pRowid)1418 static int amatchUpdate(
1419   sqlite3_vtab *pVTab,
1420   int argc,
1421   sqlite3_value **argv,
1422   sqlite_int64 *pRowid
1423 ){
1424   amatch_vtab *p = (amatch_vtab*)pVTab;
1425   const unsigned char *zCmd;
1426   (void)pRowid;
1427   if( argc==1 ){
1428     pVTab->zErrMsg = sqlite3_mprintf("DELETE from %s is not allowed",
1429                                       p->zSelf);
1430     return SQLITE_ERROR;
1431   }
1432   if( sqlite3_value_type(argv[0])!=SQLITE_NULL ){
1433     pVTab->zErrMsg = sqlite3_mprintf("UPDATE of %s is not allowed",
1434                                       p->zSelf);
1435     return SQLITE_ERROR;
1436   }
1437   if( sqlite3_value_type(argv[2+AMATCH_COL_WORD])!=SQLITE_NULL
1438    || sqlite3_value_type(argv[2+AMATCH_COL_DISTANCE])!=SQLITE_NULL
1439    || sqlite3_value_type(argv[2+AMATCH_COL_LANGUAGE])!=SQLITE_NULL
1440   ){
1441     pVTab->zErrMsg = sqlite3_mprintf(
1442             "INSERT INTO %s allowed for column [command] only", p->zSelf);
1443     return SQLITE_ERROR;
1444   }
1445   zCmd = sqlite3_value_text(argv[2+AMATCH_COL_COMMAND]);
1446   if( zCmd==0 ) return SQLITE_OK;
1447 
1448   return SQLITE_OK;
1449 }
1450 
1451 /*
1452 ** A virtual table module that implements the "approximate_match".
1453 */
1454 static sqlite3_module amatchModule = {
1455   0,                      /* iVersion */
1456   amatchConnect,          /* xCreate */
1457   amatchConnect,          /* xConnect */
1458   amatchBestIndex,        /* xBestIndex */
1459   amatchDisconnect,       /* xDisconnect */
1460   amatchDisconnect,       /* xDestroy */
1461   amatchOpen,             /* xOpen - open a cursor */
1462   amatchClose,            /* xClose - close a cursor */
1463   amatchFilter,           /* xFilter - configure scan constraints */
1464   amatchNext,             /* xNext - advance a cursor */
1465   amatchEof,              /* xEof - check for end of scan */
1466   amatchColumn,           /* xColumn - read data */
1467   amatchRowid,            /* xRowid - read data */
1468   amatchUpdate,           /* xUpdate */
1469   0,                      /* xBegin */
1470   0,                      /* xSync */
1471   0,                      /* xCommit */
1472   0,                      /* xRollback */
1473   0,                      /* xFindMethod */
1474   0,                      /* xRename */
1475   0,                      /* xSavepoint */
1476   0,                      /* xRelease */
1477   0,                      /* xRollbackTo */
1478   0                       /* xShadowName */
1479 };
1480 
1481 #endif /* SQLITE_OMIT_VIRTUALTABLE */
1482 
1483 /*
1484 ** Register the amatch virtual table
1485 */
1486 #ifdef _WIN32
1487 __declspec(dllexport)
1488 #endif
sqlite3_amatch_init(sqlite3 * db,char ** pzErrMsg,const sqlite3_api_routines * pApi)1489 int sqlite3_amatch_init(
1490   sqlite3 *db,
1491   char **pzErrMsg,
1492   const sqlite3_api_routines *pApi
1493 ){
1494   int rc = SQLITE_OK;
1495   SQLITE_EXTENSION_INIT2(pApi);
1496   (void)pzErrMsg;  /* Not used */
1497 #ifndef SQLITE_OMIT_VIRTUALTABLE
1498   rc = sqlite3_create_module(db, "approximate_match", &amatchModule, 0);
1499 #endif /* SQLITE_OMIT_VIRTUALTABLE */
1500   return rc;
1501 }
1502