OLD | NEW |
1 /* | 1 /* |
2 ** 2013-03-14 | 2 ** 2013-03-14 |
3 ** | 3 ** |
4 ** The author disclaims copyright to this source code. In place of | 4 ** The author disclaims copyright to this source code. In place of |
5 ** a legal notice, here is a blessing: | 5 ** a legal notice, here is a blessing: |
6 ** | 6 ** |
7 ** May you do good and not evil. | 7 ** May you do good and not evil. |
8 ** May you find forgiveness for yourself and forgive others. | 8 ** May you find forgiveness for yourself and forgive others. |
9 ** May you share freely, never taking more than you give. | 9 ** May you share freely, never taking more than you give. |
10 ** | 10 ** |
(...skipping 380 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
391 /* assert( amatchAvlIntegrity(*ppHead) ); */ | 391 /* assert( amatchAvlIntegrity(*ppHead) ); */ |
392 /* assert( amatchAvlIntegrity2(*ppHead) ); */ | 392 /* assert( amatchAvlIntegrity2(*ppHead) ); */ |
393 return 0; | 393 return 0; |
394 } | 394 } |
395 | 395 |
396 /* Remove node pOld from the tree. pOld must be an element of the tree or | 396 /* Remove node pOld from the tree. pOld must be an element of the tree or |
397 ** the AVL tree will become corrupt. | 397 ** the AVL tree will become corrupt. |
398 */ | 398 */ |
399 static void amatchAvlRemove(amatch_avl **ppHead, amatch_avl *pOld){ | 399 static void amatchAvlRemove(amatch_avl **ppHead, amatch_avl *pOld){ |
400 amatch_avl **ppParent; | 400 amatch_avl **ppParent; |
401 amatch_avl *pBalance; | 401 amatch_avl *pBalance = 0; |
402 /* assert( amatchAvlSearch(*ppHead, pOld->zKey)==pOld ); */ | 402 /* assert( amatchAvlSearch(*ppHead, pOld->zKey)==pOld ); */ |
403 ppParent = amatchAvlFromPtr(pOld, ppHead); | 403 ppParent = amatchAvlFromPtr(pOld, ppHead); |
404 if( pOld->pBefore==0 && pOld->pAfter==0 ){ | 404 if( pOld->pBefore==0 && pOld->pAfter==0 ){ |
405 *ppParent = 0; | 405 *ppParent = 0; |
406 pBalance = pOld->pUp; | 406 pBalance = pOld->pUp; |
407 }else if( pOld->pBefore && pOld->pAfter ){ | 407 }else if( pOld->pBefore && pOld->pAfter ){ |
408 amatch_avl *pX, *pY; | 408 amatch_avl *pX, *pY; |
409 pX = amatchAvlFirst(pOld->pAfter); | 409 pX = amatchAvlFirst(pOld->pAfter); |
410 *amatchAvlFromPtr(pX, 0) = pX->pAfter; | 410 *amatchAvlFromPtr(pX, 0) = pX->pAfter; |
411 if( pX->pAfter ) pX->pAfter->pUp = pX->pUp; | 411 if( pX->pAfter ) pX->pAfter->pUp = pX->pUp; |
(...skipping 397 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
809 ** | 809 ** |
810 ** If it is, return a pointer to the first character of VALUE. | 810 ** If it is, return a pointer to the first character of VALUE. |
811 ** If not, return NULL. Spaces around the = are ignored. | 811 ** If not, return NULL. Spaces around the = are ignored. |
812 */ | 812 */ |
813 static const char *amatchValueOfKey(const char *zKey, const char *zStr){ | 813 static const char *amatchValueOfKey(const char *zKey, const char *zStr){ |
814 int nKey = (int)strlen(zKey); | 814 int nKey = (int)strlen(zKey); |
815 int nStr = (int)strlen(zStr); | 815 int nStr = (int)strlen(zStr); |
816 int i; | 816 int i; |
817 if( nStr<nKey+1 ) return 0; | 817 if( nStr<nKey+1 ) return 0; |
818 if( memcmp(zStr, zKey, nKey)!=0 ) return 0; | 818 if( memcmp(zStr, zKey, nKey)!=0 ) return 0; |
819 for(i=nKey; isspace(zStr[i]); i++){} | 819 for(i=nKey; isspace((unsigned char)zStr[i]); i++){} |
820 if( zStr[i]!='=' ) return 0; | 820 if( zStr[i]!='=' ) return 0; |
821 i++; | 821 i++; |
822 while( isspace(zStr[i]) ){ i++; } | 822 while( isspace((unsigned char)zStr[i]) ){ i++; } |
823 return zStr+i; | 823 return zStr+i; |
824 } | 824 } |
825 | 825 |
826 /* | 826 /* |
827 ** xConnect/xCreate method for the amatch module. Arguments are: | 827 ** xConnect/xCreate method for the amatch module. Arguments are: |
828 ** | 828 ** |
829 ** argv[0] -> module name ("approximate_match") | 829 ** argv[0] -> module name ("approximate_match") |
830 ** argv[1] -> database name | 830 ** argv[1] -> database name |
831 ** argv[2] -> table name | 831 ** argv[2] -> table name |
832 ** argv[3...] -> arguments | 832 ** argv[3...] -> arguments |
(...skipping 158 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
991 | 991 |
992 /* | 992 /* |
993 ** Write the zCost[] field for a amatch_word object | 993 ** Write the zCost[] field for a amatch_word object |
994 */ | 994 */ |
995 static void amatchWriteCost(amatch_word *pWord){ | 995 static void amatchWriteCost(amatch_word *pWord){ |
996 amatchEncodeInt(pWord->rCost, pWord->zCost); | 996 amatchEncodeInt(pWord->rCost, pWord->zCost); |
997 amatchEncodeInt(pWord->iSeq, pWord->zCost+4); | 997 amatchEncodeInt(pWord->iSeq, pWord->zCost+4); |
998 pWord->zCost[8] = 0; | 998 pWord->zCost[8] = 0; |
999 } | 999 } |
1000 | 1000 |
| 1001 /* Circumvent compiler warnings about the use of strcpy() by supplying |
| 1002 ** our own implementation. |
| 1003 */ |
| 1004 #if defined(__OpenBSD__) |
| 1005 static void amatchStrcpy(char *dest, const char *src){ |
| 1006 while( (*(dest++) = *(src++))!=0 ){} |
| 1007 } |
| 1008 static void amatchStrcat(char *dest, const char *src){ |
| 1009 while( *dest ) dest++; |
| 1010 amatchStrcpy(dest, src); |
| 1011 } |
| 1012 #else |
| 1013 # define amatchStrcpy strcpy |
| 1014 # define amatchStrcat strcat |
| 1015 #endif |
| 1016 |
| 1017 |
1001 /* | 1018 /* |
1002 ** Add a new amatch_word object to the queue. | 1019 ** Add a new amatch_word object to the queue. |
1003 ** | 1020 ** |
1004 ** If a prior amatch_word object with the same zWord, and nMatch | 1021 ** If a prior amatch_word object with the same zWord, and nMatch |
1005 ** already exists, update its rCost (if the new rCost is less) but | 1022 ** already exists, update its rCost (if the new rCost is less) but |
1006 ** otherwise leave it unchanged. Do not add a duplicate. | 1023 ** otherwise leave it unchanged. Do not add a duplicate. |
1007 ** | 1024 ** |
1008 ** Do nothing if the cost exceeds threshold. | 1025 ** Do nothing if the cost exceeds threshold. |
1009 */ | 1026 */ |
1010 static void amatchAddWord( | 1027 static void amatchAddWord( |
(...skipping 55 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1066 amatchWriteCost(pWord); | 1083 amatchWriteCost(pWord); |
1067 pWord->nMatch = nMatch; | 1084 pWord->nMatch = nMatch; |
1068 pWord->pNext = pCur->pAllWords; | 1085 pWord->pNext = pCur->pAllWords; |
1069 pCur->pAllWords = pWord; | 1086 pCur->pAllWords = pWord; |
1070 pWord->sCost.zKey = pWord->zCost; | 1087 pWord->sCost.zKey = pWord->zCost; |
1071 pWord->sCost.pWord = pWord; | 1088 pWord->sCost.pWord = pWord; |
1072 pOther = amatchAvlInsert(&pCur->pCost, &pWord->sCost); | 1089 pOther = amatchAvlInsert(&pCur->pCost, &pWord->sCost); |
1073 assert( pOther==0 ); (void)pOther; | 1090 assert( pOther==0 ); (void)pOther; |
1074 pWord->sWord.zKey = pWord->zWord; | 1091 pWord->sWord.zKey = pWord->zWord; |
1075 pWord->sWord.pWord = pWord; | 1092 pWord->sWord.pWord = pWord; |
1076 strcpy(pWord->zWord, pCur->zBuf); | 1093 amatchStrcpy(pWord->zWord, pCur->zBuf); |
1077 pOther = amatchAvlInsert(&pCur->pWord, &pWord->sWord); | 1094 pOther = amatchAvlInsert(&pCur->pWord, &pWord->sWord); |
1078 assert( pOther==0 ); (void)pOther; | 1095 assert( pOther==0 ); (void)pOther; |
1079 #ifdef AMATCH_TRACE_1 | 1096 #ifdef AMATCH_TRACE_1 |
1080 printf("INSERT [%s][%.*s^%s] %d (\"%s\" \"%s\")\n", pWord->zWord+2, | 1097 printf("INSERT [%s][%.*s^%s] %d (\"%s\" \"%s\")\n", pWord->zWord+2, |
1081 pWord->nMatch, pCur->zInput, pCur->zInput+pWord->nMatch, rCost, | 1098 pWord->nMatch, pCur->zInput, pCur->zInput+pWord->nMatch, rCost, |
1082 pWord->zWord, pWord->zCost); | 1099 pWord->zWord, pWord->zCost); |
1083 #endif | 1100 #endif |
1084 } | 1101 } |
1085 | 1102 |
| 1103 |
1086 /* | 1104 /* |
1087 ** Advance a cursor to its next row of output | 1105 ** Advance a cursor to its next row of output |
1088 */ | 1106 */ |
1089 static int amatchNext(sqlite3_vtab_cursor *cur){ | 1107 static int amatchNext(sqlite3_vtab_cursor *cur){ |
1090 amatch_cursor *pCur = (amatch_cursor*)cur; | 1108 amatch_cursor *pCur = (amatch_cursor*)cur; |
1091 amatch_word *pWord = 0; | 1109 amatch_word *pWord = 0; |
1092 amatch_avl *pNode; | 1110 amatch_avl *pNode; |
1093 int isMatch = 0; | 1111 int isMatch = 0; |
1094 amatch_vtab *p = pCur->pVtab; | 1112 amatch_vtab *p = pCur->pVtab; |
1095 int nWord; | 1113 int nWord; |
(...skipping 45 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1141 printf("PROCESS [%s][%.*s^%s] %d (\"%s\" \"%s\")\n", | 1159 printf("PROCESS [%s][%.*s^%s] %d (\"%s\" \"%s\")\n", |
1142 pWord->zWord+2, pWord->nMatch, pCur->zInput, pCur->zInput+pWord->nMatch, | 1160 pWord->zWord+2, pWord->nMatch, pCur->zInput, pCur->zInput+pWord->nMatch, |
1143 pWord->rCost, pWord->zWord, pWord->zCost); | 1161 pWord->rCost, pWord->zWord, pWord->zCost); |
1144 #endif | 1162 #endif |
1145 nWord = (int)strlen(pWord->zWord+2); | 1163 nWord = (int)strlen(pWord->zWord+2); |
1146 if( nWord+20>nBuf ){ | 1164 if( nWord+20>nBuf ){ |
1147 nBuf = nWord+100; | 1165 nBuf = nWord+100; |
1148 zBuf = sqlite3_realloc(zBuf, nBuf); | 1166 zBuf = sqlite3_realloc(zBuf, nBuf); |
1149 if( zBuf==0 ) return SQLITE_NOMEM; | 1167 if( zBuf==0 ) return SQLITE_NOMEM; |
1150 } | 1168 } |
1151 strcpy(zBuf, pWord->zWord+2); | 1169 amatchStrcpy(zBuf, pWord->zWord+2); |
1152 zNext[0] = 0; | 1170 zNext[0] = 0; |
1153 zNextIn[0] = pCur->zInput[pWord->nMatch]; | 1171 zNextIn[0] = pCur->zInput[pWord->nMatch]; |
1154 if( zNextIn[0] ){ | 1172 if( zNextIn[0] ){ |
1155 for(i=1; i<=4 && (pCur->zInput[pWord->nMatch+i]&0xc0)==0x80; i++){ | 1173 for(i=1; i<=4 && (pCur->zInput[pWord->nMatch+i]&0xc0)==0x80; i++){ |
1156 zNextIn[i] = pCur->zInput[pWord->nMatch+i]; | 1174 zNextIn[i] = pCur->zInput[pWord->nMatch+i]; |
1157 } | 1175 } |
1158 zNextIn[i] = 0; | 1176 zNextIn[i] = 0; |
1159 nNextIn = i; | 1177 nNextIn = i; |
1160 }else{ | 1178 }else{ |
1161 nNextIn = 0; | 1179 nNextIn = 0; |
1162 } | 1180 } |
1163 | 1181 |
1164 if( zNextIn[0] && zNextIn[0]!='*' ){ | 1182 if( zNextIn[0] && zNextIn[0]!='*' ){ |
1165 sqlite3_reset(p->pVCheck); | 1183 sqlite3_reset(p->pVCheck); |
1166 strcat(zBuf, zNextIn); | 1184 amatchStrcat(zBuf, zNextIn); |
1167 sqlite3_bind_text(p->pVCheck, 1, zBuf, nWord+nNextIn, SQLITE_STATIC); | 1185 sqlite3_bind_text(p->pVCheck, 1, zBuf, nWord+nNextIn, SQLITE_STATIC); |
1168 rc = sqlite3_step(p->pVCheck); | 1186 rc = sqlite3_step(p->pVCheck); |
1169 if( rc==SQLITE_ROW ){ | 1187 if( rc==SQLITE_ROW ){ |
1170 zW = (const char*)sqlite3_column_text(p->pVCheck, 0); | 1188 zW = (const char*)sqlite3_column_text(p->pVCheck, 0); |
1171 if( strncmp(zBuf, zW, nWord+nNextIn)==0 ){ | 1189 if( strncmp(zBuf, zW, nWord+nNextIn)==0 ){ |
1172 amatchAddWord(pCur, pWord->rCost, pWord->nMatch+nNextIn, zBuf, ""); | 1190 amatchAddWord(pCur, pWord->rCost, pWord->nMatch+nNextIn, zBuf, ""); |
1173 } | 1191 } |
1174 } | 1192 } |
1175 zBuf[nWord] = 0; | 1193 zBuf[nWord] = 0; |
1176 } | 1194 } |
1177 | 1195 |
1178 while( 1 ){ | 1196 while( 1 ){ |
1179 strcpy(zBuf+nWord, zNext); | 1197 amatchStrcpy(zBuf+nWord, zNext); |
1180 sqlite3_reset(p->pVCheck); | 1198 sqlite3_reset(p->pVCheck); |
1181 sqlite3_bind_text(p->pVCheck, 1, zBuf, -1, SQLITE_TRANSIENT); | 1199 sqlite3_bind_text(p->pVCheck, 1, zBuf, -1, SQLITE_TRANSIENT); |
1182 rc = sqlite3_step(p->pVCheck); | 1200 rc = sqlite3_step(p->pVCheck); |
1183 if( rc!=SQLITE_ROW ) break; | 1201 if( rc!=SQLITE_ROW ) break; |
1184 zW = (const char*)sqlite3_column_text(p->pVCheck, 0); | 1202 zW = (const char*)sqlite3_column_text(p->pVCheck, 0); |
1185 strcpy(zBuf+nWord, zNext); | 1203 amatchStrcpy(zBuf+nWord, zNext); |
1186 if( strncmp(zW, zBuf, nWord)!=0 ) break; | 1204 if( strncmp(zW, zBuf, nWord)!=0 ) break; |
1187 if( (zNextIn[0]=='*' && zNextIn[1]==0) | 1205 if( (zNextIn[0]=='*' && zNextIn[1]==0) |
1188 || (zNextIn[0]==0 && zW[nWord]==0) | 1206 || (zNextIn[0]==0 && zW[nWord]==0) |
1189 ){ | 1207 ){ |
1190 isMatch = 1; | 1208 isMatch = 1; |
1191 zNextIn[0] = 0; | 1209 zNextIn[0] = 0; |
1192 nNextIn = 0; | 1210 nNextIn = 0; |
1193 break; | 1211 break; |
1194 } | 1212 } |
1195 zNext[0] = zW[nWord]; | 1213 zNext[0] = zW[nWord]; |
(...skipping 282 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1478 const sqlite3_api_routines *pApi | 1496 const sqlite3_api_routines *pApi |
1479 ){ | 1497 ){ |
1480 int rc = SQLITE_OK; | 1498 int rc = SQLITE_OK; |
1481 SQLITE_EXTENSION_INIT2(pApi); | 1499 SQLITE_EXTENSION_INIT2(pApi); |
1482 (void)pzErrMsg; /* Not used */ | 1500 (void)pzErrMsg; /* Not used */ |
1483 #ifndef SQLITE_OMIT_VIRTUALTABLE | 1501 #ifndef SQLITE_OMIT_VIRTUALTABLE |
1484 rc = sqlite3_create_module(db, "approximate_match", &amatchModule, 0); | 1502 rc = sqlite3_create_module(db, "approximate_match", &amatchModule, 0); |
1485 #endif /* SQLITE_OMIT_VIRTUALTABLE */ | 1503 #endif /* SQLITE_OMIT_VIRTUALTABLE */ |
1486 return rc; | 1504 return rc; |
1487 } | 1505 } |
OLD | NEW |