Index: third_party/sqlite/src/ext/misc/spellfix.c |
diff --git a/third_party/sqlite/src/ext/misc/spellfix.c b/third_party/sqlite/src/ext/misc/spellfix.c |
index 2a26e08391dee26edfb09435943cae897a80f094..c0da6b339bfcdc6df1fcd4b87b655602bc172595 100644 |
--- a/third_party/sqlite/src/ext/misc/spellfix.c |
+++ b/third_party/sqlite/src/ext/misc/spellfix.c |
@@ -356,7 +356,7 @@ static int substituteCost(char cPrev, char cFrom, char cTo){ |
static int editdist1(const char *zA, const char *zB, int *pnMatch){ |
int nA, nB; /* Number of characters in zA[] and zB[] */ |
int xA, xB; /* Loop counters for zA[] and zB[] */ |
- char cA, cB; /* Current character of zA and zB */ |
+ char cA = 0, cB; /* Current character of zA and zB */ |
char cAprev, cBprev; /* Previous character of zA and zB */ |
char cAnext, cBnext; /* Next character in zA and zB */ |
int d; /* North-west cost value */ |
@@ -1717,17 +1717,23 @@ static void scriptCodeSqlFunc( |
# define SCRIPT_LATIN 0x0001 |
# define SCRIPT_CYRILLIC 0x0002 |
# define SCRIPT_GREEK 0x0004 |
+# define SCRIPT_HEBREW 0x0008 |
+# define SCRIPT_ARABIC 0x0010 |
while( nIn>0 ){ |
c = utf8Read(zIn, nIn, &sz); |
zIn += sz; |
nIn -= sz; |
- if( c<0x02af ){ |
+ if( c<0x02af && (c>=0x80 || midClass[c&0x7f]<CCLASS_DIGIT) ){ |
scriptMask |= SCRIPT_LATIN; |
}else if( c>=0x0400 && c<=0x04ff ){ |
scriptMask |= SCRIPT_CYRILLIC; |
}else if( c>=0x0386 && c<=0x03ce ){ |
scriptMask |= SCRIPT_GREEK; |
+ }else if( c>=0x0590 && c<=0x05ff ){ |
+ scriptMask |= SCRIPT_HEBREW; |
+ }else if( c>=0x0600 && c<=0x06ff ){ |
+ scriptMask |= SCRIPT_ARABIC; |
} |
} |
switch( scriptMask ){ |
@@ -1735,6 +1741,8 @@ static void scriptCodeSqlFunc( |
case SCRIPT_LATIN: res = 215; break; |
case SCRIPT_CYRILLIC: res = 220; break; |
case SCRIPT_GREEK: res = 200; break; |
+ case SCRIPT_HEBREW: res = 125; break; |
+ case SCRIPT_ARABIC: res = 160; break; |
default: res = 998; break; |
} |
sqlite3_result_int(context, res); |
@@ -1770,6 +1778,7 @@ struct spellfix1_cursor { |
sqlite3_vtab_cursor base; /* Base class - must be first */ |
spellfix1_vtab *pVTab; /* The table to which this cursor belongs */ |
char *zPattern; /* rhs of MATCH clause */ |
+ int idxNum; /* idxNum value passed to xFilter() */ |
int nRow; /* Number of rows of content */ |
int nAlloc; /* Number of allocated rows */ |
int iRow; /* Current row of content */ |
@@ -1850,7 +1859,7 @@ static char *spellfix1Dequote(const char *zIn){ |
char *zOut; |
int i, j; |
char c; |
- while( isspace(zIn[0]) ) zIn++; |
+ while( isspace((unsigned char)zIn[0]) ) zIn++; |
zOut = sqlite3_mprintf("%s", zIn); |
if( zOut==0 ) return 0; |
i = (int)strlen(zOut); |
@@ -2040,26 +2049,19 @@ static int spellfix1Close(sqlite3_vtab_cursor *cur){ |
return SQLITE_OK; |
} |
+#define SPELLFIX_IDXNUM_MATCH 0x01 /* word MATCH $str */ |
+#define SPELLFIX_IDXNUM_LANGID 0x02 /* langid == $langid */ |
+#define SPELLFIX_IDXNUM_TOP 0x04 /* top = $top */ |
+#define SPELLFIX_IDXNUM_SCOPE 0x08 /* scope = $scope */ |
+#define SPELLFIX_IDXNUM_DISTLT 0x10 /* distance < $distance */ |
+#define SPELLFIX_IDXNUM_DISTLE 0x20 /* distance <= $distance */ |
+#define SPELLFIX_IDXNUM_ROWID 0x40 /* rowid = $rowid */ |
+#define SPELLFIX_IDXNUM_DIST (0x10|0x20) /* DISTLT and DISTLE */ |
+ |
/* |
-** Search for terms of these forms: |
-** |
-** (A) word MATCH $str |
-** (B) langid == $langid |
-** (C) top = $top |
-** (D) scope = $scope |
-** (E) distance < $distance |
-** (F) distance <= $distance |
-** (G) rowid = $rowid |
-** |
-** The plan number is a bit mask formed with these bits: |
** |
-** 0x01 (A) is found |
-** 0x02 (B) is found |
-** 0x04 (C) is found |
-** 0x08 (D) is found |
-** 0x10 (E) is found |
-** 0x20 (F) is found |
-** 0x40 (G) is found |
+** The plan number is a bitmask of the SPELLFIX_IDXNUM_* values defined |
+** above. |
** |
** filter.argv[*] values contains $str, $langid, $top, $scope and $rowid |
** if specified and in that order. |
@@ -2078,62 +2080,66 @@ static int spellfix1BestIndex(sqlite3_vtab *tab, sqlite3_index_info *pIdxInfo){ |
if( pConstraint->usable==0 ) continue; |
/* Terms of the form: word MATCH $str */ |
- if( (iPlan & 1)==0 |
+ if( (iPlan & SPELLFIX_IDXNUM_MATCH)==0 |
&& pConstraint->iColumn==SPELLFIX_COL_WORD |
&& pConstraint->op==SQLITE_INDEX_CONSTRAINT_MATCH |
){ |
- iPlan |= 1; |
+ iPlan |= SPELLFIX_IDXNUM_MATCH; |
pIdxInfo->aConstraintUsage[i].argvIndex = 1; |
pIdxInfo->aConstraintUsage[i].omit = 1; |
} |
/* Terms of the form: langid = $langid */ |
- if( (iPlan & 2)==0 |
+ if( (iPlan & SPELLFIX_IDXNUM_LANGID)==0 |
&& pConstraint->iColumn==SPELLFIX_COL_LANGID |
&& pConstraint->op==SQLITE_INDEX_CONSTRAINT_EQ |
){ |
- iPlan |= 2; |
+ iPlan |= SPELLFIX_IDXNUM_LANGID; |
iLangTerm = i; |
} |
/* Terms of the form: top = $top */ |
- if( (iPlan & 4)==0 |
+ if( (iPlan & SPELLFIX_IDXNUM_TOP)==0 |
&& pConstraint->iColumn==SPELLFIX_COL_TOP |
&& pConstraint->op==SQLITE_INDEX_CONSTRAINT_EQ |
){ |
- iPlan |= 4; |
+ iPlan |= SPELLFIX_IDXNUM_TOP; |
iTopTerm = i; |
} |
/* Terms of the form: scope = $scope */ |
- if( (iPlan & 8)==0 |
+ if( (iPlan & SPELLFIX_IDXNUM_SCOPE)==0 |
&& pConstraint->iColumn==SPELLFIX_COL_SCOPE |
&& pConstraint->op==SQLITE_INDEX_CONSTRAINT_EQ |
){ |
- iPlan |= 8; |
+ iPlan |= SPELLFIX_IDXNUM_SCOPE; |
iScopeTerm = i; |
} |
/* Terms of the form: distance < $dist or distance <= $dist */ |
- if( (iPlan & (16|32))==0 |
+ if( (iPlan & SPELLFIX_IDXNUM_DIST)==0 |
&& pConstraint->iColumn==SPELLFIX_COL_DISTANCE |
&& (pConstraint->op==SQLITE_INDEX_CONSTRAINT_LT |
|| pConstraint->op==SQLITE_INDEX_CONSTRAINT_LE) |
){ |
- iPlan |= pConstraint->op==SQLITE_INDEX_CONSTRAINT_LT ? 16 : 32; |
+ if( pConstraint->op==SQLITE_INDEX_CONSTRAINT_LT ){ |
+ iPlan |= SPELLFIX_IDXNUM_DISTLT; |
+ }else{ |
+ iPlan |= SPELLFIX_IDXNUM_DISTLE; |
+ } |
iDistTerm = i; |
} |
/* Terms of the form: distance < $dist or distance <= $dist */ |
- if( (iPlan & 64)==0 |
+ if( (iPlan & SPELLFIX_IDXNUM_ROWID)==0 |
&& pConstraint->iColumn<0 |
&& pConstraint->op==SQLITE_INDEX_CONSTRAINT_EQ |
){ |
- iPlan |= 64; |
+ iPlan |= SPELLFIX_IDXNUM_ROWID; |
iRowidTerm = i; |
} |
} |
- if( iPlan&1 ){ |
+ if( iPlan&SPELLFIX_IDXNUM_MATCH ){ |
int idx = 2; |
pIdxInfo->idxNum = iPlan; |
if( pIdxInfo->nOrderBy==1 |
@@ -2142,25 +2148,25 @@ static int spellfix1BestIndex(sqlite3_vtab *tab, sqlite3_index_info *pIdxInfo){ |
){ |
pIdxInfo->orderByConsumed = 1; /* Default order by iScore */ |
} |
- if( iPlan&2 ){ |
+ if( iPlan&SPELLFIX_IDXNUM_LANGID ){ |
pIdxInfo->aConstraintUsage[iLangTerm].argvIndex = idx++; |
pIdxInfo->aConstraintUsage[iLangTerm].omit = 1; |
} |
- if( iPlan&4 ){ |
+ if( iPlan&SPELLFIX_IDXNUM_TOP ){ |
pIdxInfo->aConstraintUsage[iTopTerm].argvIndex = idx++; |
pIdxInfo->aConstraintUsage[iTopTerm].omit = 1; |
} |
- if( iPlan&8 ){ |
+ if( iPlan&SPELLFIX_IDXNUM_SCOPE ){ |
pIdxInfo->aConstraintUsage[iScopeTerm].argvIndex = idx++; |
pIdxInfo->aConstraintUsage[iScopeTerm].omit = 1; |
} |
- if( iPlan&(16|32) ){ |
+ if( iPlan&SPELLFIX_IDXNUM_DIST ){ |
pIdxInfo->aConstraintUsage[iDistTerm].argvIndex = idx++; |
pIdxInfo->aConstraintUsage[iDistTerm].omit = 1; |
} |
pIdxInfo->estimatedCost = 1e5; |
- }else if( (iPlan & 64) ){ |
- pIdxInfo->idxNum = 64; |
+ }else if( (iPlan & SPELLFIX_IDXNUM_ROWID) ){ |
+ pIdxInfo->idxNum = SPELLFIX_IDXNUM_ROWID; |
pIdxInfo->aConstraintUsage[iRowidTerm].argvIndex = 1; |
pIdxInfo->aConstraintUsage[iRowidTerm].omit = 1; |
pIdxInfo->estimatedCost = 5; |
@@ -2311,15 +2317,24 @@ static void spellfix1RunQuery(MatchQuery *p, const char *zQuery, int nQuery){ |
break; |
} |
pCur->nSearch++; |
- iScore = spellfix1Score(iDist,iRank); |
+ |
+ /* If there is a "distance < $dist" or "distance <= $dist" constraint, |
+ ** check if this row meets it. If not, jump back up to the top of the |
+ ** loop to process the next row. Otherwise, if the row does match the |
+ ** distance constraint, check if the pCur->a[] array is already full. |
+ ** If it is and no explicit "top = ?" constraint was present in the |
+ ** query, grow the array to ensure there is room for the new entry. */ |
+ assert( (p->iMaxDist>=0)==((pCur->idxNum & SPELLFIX_IDXNUM_DIST) ? 1 : 0) ); |
if( p->iMaxDist>=0 ){ |
if( iDist>p->iMaxDist ) continue; |
- if( pCur->nRow>=pCur->nAlloc-1 ){ |
+ if( pCur->nRow>=pCur->nAlloc && (pCur->idxNum & SPELLFIX_IDXNUM_TOP)==0 ){ |
spellfix1ResizeCursor(pCur, pCur->nAlloc*2 + 10); |
if( pCur->a==0 ) break; |
} |
- idx = pCur->nRow; |
- }else if( pCur->nRow<pCur->nAlloc ){ |
+ } |
+ |
+ iScore = spellfix1Score(iDist,iRank); |
+ if( pCur->nRow<pCur->nAlloc ){ |
idx = pCur->nRow; |
}else if( iScore<iWorst ){ |
idx = idxWorst; |
@@ -2327,6 +2342,7 @@ static void spellfix1RunQuery(MatchQuery *p, const char *zQuery, int nQuery){ |
}else{ |
continue; |
} |
+ |
pCur->a[idx].zWord = sqlite3_mprintf("%s", sqlite3_column_text(pStmt, 1)); |
if( pCur->a[idx].zWord==0 ){ |
p->rc = SQLITE_NOMEM; |
@@ -2361,10 +2377,10 @@ static void spellfix1RunQuery(MatchQuery *p, const char *zQuery, int nQuery){ |
*/ |
static int spellfix1FilterForMatch( |
spellfix1_cursor *pCur, |
- int idxNum, |
int argc, |
sqlite3_value **argv |
){ |
+ int idxNum = pCur->idxNum; |
const unsigned char *zMatchThis; /* RHS of the MATCH operator */ |
EditDist3FromString *pMatchStr3 = 0; /* zMatchThis as an editdist string */ |
char *zPattern; /* Transliteration of zMatchThis */ |
@@ -2476,11 +2492,11 @@ filter_exit: |
*/ |
static int spellfix1FilterForFullScan( |
spellfix1_cursor *pCur, |
- int idxNum, |
int argc, |
sqlite3_value **argv |
){ |
int rc = SQLITE_OK; |
+ int idxNum = pCur->idxNum; |
char *zSql; |
spellfix1_vtab *pVTab = pCur->pVTab; |
spellfix1ResetCursor(pCur); |
@@ -2521,10 +2537,11 @@ static int spellfix1Filter( |
){ |
spellfix1_cursor *pCur = (spellfix1_cursor *)cur; |
int rc; |
+ pCur->idxNum = idxNum; |
if( idxNum & 1 ){ |
- rc = spellfix1FilterForMatch(pCur, idxNum, argc, argv); |
+ rc = spellfix1FilterForMatch(pCur, argc, argv); |
}else{ |
- rc = spellfix1FilterForFullScan(pCur, idxNum, argc, argv); |
+ rc = spellfix1FilterForFullScan(pCur, argc, argv); |
} |
return rc; |
} |
@@ -2656,6 +2673,31 @@ static int spellfix1Rowid(sqlite3_vtab_cursor *cur, sqlite_int64 *pRowid){ |
} |
/* |
+** This function is called by the xUpdate() method. It returns a string |
+** containing the conflict mode that xUpdate() should use for the current |
+** operation. One of: "ROLLBACK", "IGNORE", "ABORT" or "REPLACE". |
+*/ |
+static const char *spellfix1GetConflict(sqlite3 *db){ |
+ static const char *azConflict[] = { |
+ /* Note: Instead of "FAIL" - "ABORT". */ |
+ "ROLLBACK", "IGNORE", "ABORT", "ABORT", "REPLACE" |
+ }; |
+ int eConflict = sqlite3_vtab_on_conflict(db); |
+ |
+ assert( eConflict==SQLITE_ROLLBACK || eConflict==SQLITE_IGNORE |
+ || eConflict==SQLITE_FAIL || eConflict==SQLITE_ABORT |
+ || eConflict==SQLITE_REPLACE |
+ ); |
+ assert( SQLITE_ROLLBACK==1 ); |
+ assert( SQLITE_IGNORE==2 ); |
+ assert( SQLITE_FAIL==3 ); |
+ assert( SQLITE_ABORT==4 ); |
+ assert( SQLITE_REPLACE==5 ); |
+ |
+ return azConflict[eConflict-1]; |
+} |
+ |
+/* |
** The xUpdate() method. |
*/ |
static int spellfix1Update( |
@@ -2686,6 +2728,7 @@ static int spellfix1Update( |
char *zK1, *zK2; |
int i; |
char c; |
+ const char *zConflict = spellfix1GetConflict(db); |
if( zWord==0 ){ |
/* Inserts of the form: INSERT INTO table(command) VALUES('xyzzy'); |
@@ -2746,10 +2789,10 @@ static int spellfix1Update( |
}else{ |
newRowid = sqlite3_value_int64(argv[1]); |
spellfix1DbExec(&rc, db, |
- "INSERT INTO \"%w\".\"%w_vocab\"(id,rank,langid,word,k1,k2) " |
- "VALUES(%lld,%d,%d,%Q,%Q,%Q)", |
- p->zDbName, p->zTableName, |
- newRowid, iRank, iLang, zWord, zK1, zK2 |
+ "INSERT OR %s INTO \"%w\".\"%w_vocab\"(id,rank,langid,word,k1,k2) " |
+ "VALUES(%lld,%d,%d,%Q,%Q,%Q)", |
+ zConflict, p->zDbName, p->zTableName, |
+ newRowid, iRank, iLang, zWord, zK1, zK2 |
); |
} |
*pRowid = sqlite3_last_insert_rowid(db); |
@@ -2757,9 +2800,9 @@ static int spellfix1Update( |
rowid = sqlite3_value_int64(argv[0]); |
newRowid = *pRowid = sqlite3_value_int64(argv[1]); |
spellfix1DbExec(&rc, db, |
- "UPDATE \"%w\".\"%w_vocab\" SET id=%lld, rank=%d, langid=%d," |
+ "UPDATE OR %s \"%w\".\"%w_vocab\" SET id=%lld, rank=%d, langid=%d," |
" word=%Q, k1=%Q, k2=%Q WHERE id=%lld", |
- p->zDbName, p->zTableName, newRowid, iRank, iLang, |
+ zConflict, p->zDbName, p->zTableName, newRowid, iRank, iLang, |
zWord, zK1, zK2, rowid |
); |
} |