Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(4)

Side by Side Diff: third_party/sqlite/sqlite-src-3170000/ext/fts5/fts5_test_mi.c

Issue 2747283002: [sql] Import reference version of SQLite 3.17.. (Closed)
Patch Set: Created 3 years, 9 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
(Empty)
1 /*
2 ** 2015 Aug 04
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 test code only, it is not included in release
14 ** versions of FTS5. It contains the implementation of an FTS5 auxiliary
15 ** function very similar to the FTS4 function matchinfo():
16 **
17 ** https://www.sqlite.org/fts3.html#matchinfo
18 **
19 ** Known differences are that:
20 **
21 ** 1) this function uses the FTS5 definition of "matchable phrase", which
22 ** excludes any phrases that are part of an expression sub-tree that
23 ** does not match the current row. This comes up for MATCH queries
24 ** such as:
25 **
26 ** "a OR (b AND c)"
27 **
28 ** In FTS4, if a single row contains instances of tokens "a" and "c",
29 ** but not "b", all instances of "c" are considered matches. In FTS5,
30 ** they are not (as the "b AND c" sub-tree does not match the current
31 ** row.
32 **
33 ** 2) For the values returned by 'x' that apply to all rows of the table,
34 ** NEAR constraints are not considered. But for the number of hits in
35 ** the current row, they are.
36 **
37 ** This file exports a single function that may be called to register the
38 ** matchinfo() implementation with a database handle:
39 **
40 ** int sqlite3Fts5TestRegisterMatchinfo(sqlite3 *db);
41 */
42
43
44 #ifdef SQLITE_ENABLE_FTS5
45
46 #include "fts5.h"
47 #include <assert.h>
48 #include <string.h>
49
50 typedef struct Fts5MatchinfoCtx Fts5MatchinfoCtx;
51
52 #ifndef SQLITE_AMALGAMATION
53 typedef unsigned int u32;
54 #endif
55
56 struct Fts5MatchinfoCtx {
57 int nCol; /* Number of cols in FTS5 table */
58 int nPhrase; /* Number of phrases in FTS5 query */
59 char *zArg; /* nul-term'd copy of 2nd arg */
60 int nRet; /* Number of elements in aRet[] */
61 u32 *aRet; /* Array of 32-bit unsigned ints to return */
62 };
63
64
65
66 /*
67 ** Return a pointer to the fts5_api pointer for database connection db.
68 ** If an error occurs, return NULL and leave an error in the database
69 ** handle (accessible using sqlite3_errcode()/errmsg()).
70 */
71 static int fts5_api_from_db(sqlite3 *db, fts5_api **ppApi){
72 sqlite3_stmt *pStmt = 0;
73 int rc;
74
75 *ppApi = 0;
76 rc = sqlite3_prepare(db, "SELECT fts5()", -1, &pStmt, 0);
77 if( rc==SQLITE_OK ){
78 if( SQLITE_ROW==sqlite3_step(pStmt)
79 && sizeof(fts5_api*)==sqlite3_column_bytes(pStmt, 0)
80 ){
81 memcpy(ppApi, sqlite3_column_blob(pStmt, 0), sizeof(fts5_api*));
82 }
83 rc = sqlite3_finalize(pStmt);
84 }
85
86 return rc;
87 }
88
89
90 /*
91 ** Argument f should be a flag accepted by matchinfo() (a valid character
92 ** in the string passed as the second argument). If it is not, -1 is
93 ** returned. Otherwise, if f is a valid matchinfo flag, the value returned
94 ** is the number of 32-bit integers added to the output array if the
95 ** table has nCol columns and the query nPhrase phrases.
96 */
97 static int fts5MatchinfoFlagsize(int nCol, int nPhrase, char f){
98 int ret = -1;
99 switch( f ){
100 case 'p': ret = 1; break;
101 case 'c': ret = 1; break;
102 case 'x': ret = 3 * nCol * nPhrase; break;
103 case 'y': ret = nCol * nPhrase; break;
104 case 'b': ret = ((nCol + 31) / 32) * nPhrase; break;
105 case 'n': ret = 1; break;
106 case 'a': ret = nCol; break;
107 case 'l': ret = nCol; break;
108 case 's': ret = nCol; break;
109 }
110 return ret;
111 }
112
113 static int fts5MatchinfoIter(
114 const Fts5ExtensionApi *pApi, /* API offered by current FTS version */
115 Fts5Context *pFts, /* First arg to pass to pApi functions */
116 Fts5MatchinfoCtx *p,
117 int(*x)(const Fts5ExtensionApi*,Fts5Context*,Fts5MatchinfoCtx*,char,u32*)
118 ){
119 int i;
120 int n = 0;
121 int rc = SQLITE_OK;
122 char f;
123 for(i=0; (f = p->zArg[i]); i++){
124 rc = x(pApi, pFts, p, f, &p->aRet[n]);
125 if( rc!=SQLITE_OK ) break;
126 n += fts5MatchinfoFlagsize(p->nCol, p->nPhrase, f);
127 }
128 return rc;
129 }
130
131 static int fts5MatchinfoXCb(
132 const Fts5ExtensionApi *pApi,
133 Fts5Context *pFts,
134 void *pUserData
135 ){
136 Fts5PhraseIter iter;
137 int iCol, iOff;
138 u32 *aOut = (u32*)pUserData;
139 int iPrev = -1;
140
141 for(pApi->xPhraseFirst(pFts, 0, &iter, &iCol, &iOff);
142 iCol>=0;
143 pApi->xPhraseNext(pFts, &iter, &iCol, &iOff)
144 ){
145 aOut[iCol*3+1]++;
146 if( iCol!=iPrev ) aOut[iCol*3 + 2]++;
147 iPrev = iCol;
148 }
149
150 return SQLITE_OK;
151 }
152
153 static int fts5MatchinfoGlobalCb(
154 const Fts5ExtensionApi *pApi,
155 Fts5Context *pFts,
156 Fts5MatchinfoCtx *p,
157 char f,
158 u32 *aOut
159 ){
160 int rc = SQLITE_OK;
161 switch( f ){
162 case 'p':
163 aOut[0] = p->nPhrase;
164 break;
165
166 case 'c':
167 aOut[0] = p->nCol;
168 break;
169
170 case 'x': {
171 int i;
172 for(i=0; i<p->nPhrase && rc==SQLITE_OK; i++){
173 void *pPtr = (void*)&aOut[i * p->nCol * 3];
174 rc = pApi->xQueryPhrase(pFts, i, pPtr, fts5MatchinfoXCb);
175 }
176 break;
177 }
178
179 case 'n': {
180 sqlite3_int64 nRow;
181 rc = pApi->xRowCount(pFts, &nRow);
182 aOut[0] = (u32)nRow;
183 break;
184 }
185
186 case 'a': {
187 sqlite3_int64 nRow = 0;
188 rc = pApi->xRowCount(pFts, &nRow);
189 if( nRow==0 ){
190 memset(aOut, 0, sizeof(u32) * p->nCol);
191 }else{
192 int i;
193 for(i=0; rc==SQLITE_OK && i<p->nCol; i++){
194 sqlite3_int64 nToken;
195 rc = pApi->xColumnTotalSize(pFts, i, &nToken);
196 if( rc==SQLITE_OK){
197 aOut[i] = (u32)((2*nToken + nRow) / (2*nRow));
198 }
199 }
200 }
201 break;
202 }
203
204 }
205 return rc;
206 }
207
208 static int fts5MatchinfoLocalCb(
209 const Fts5ExtensionApi *pApi,
210 Fts5Context *pFts,
211 Fts5MatchinfoCtx *p,
212 char f,
213 u32 *aOut
214 ){
215 int i;
216 int rc = SQLITE_OK;
217
218 switch( f ){
219 case 'b': {
220 int iPhrase;
221 int nInt = ((p->nCol + 31) / 32) * p->nPhrase;
222 for(i=0; i<nInt; i++) aOut[i] = 0;
223
224 for(iPhrase=0; iPhrase<p->nPhrase; iPhrase++){
225 Fts5PhraseIter iter;
226 int iCol;
227 for(pApi->xPhraseFirstColumn(pFts, iPhrase, &iter, &iCol);
228 iCol>=0;
229 pApi->xPhraseNextColumn(pFts, &iter, &iCol)
230 ){
231 aOut[iPhrase * ((p->nCol+31)/32) + iCol/32] |= ((u32)1 << iCol%32);
232 }
233 }
234
235 break;
236 }
237
238 case 'x':
239 case 'y': {
240 int nMul = (f=='x' ? 3 : 1);
241 int iPhrase;
242
243 for(i=0; i<(p->nCol*p->nPhrase); i++) aOut[i*nMul] = 0;
244
245 for(iPhrase=0; iPhrase<p->nPhrase; iPhrase++){
246 Fts5PhraseIter iter;
247 int iOff, iCol;
248 for(pApi->xPhraseFirst(pFts, iPhrase, &iter, &iCol, &iOff);
249 iOff>=0;
250 pApi->xPhraseNext(pFts, &iter, &iCol, &iOff)
251 ){
252 aOut[nMul * (iCol + iPhrase * p->nCol)]++;
253 }
254 }
255
256 break;
257 }
258
259 case 'l': {
260 for(i=0; rc==SQLITE_OK && i<p->nCol; i++){
261 int nToken;
262 rc = pApi->xColumnSize(pFts, i, &nToken);
263 aOut[i] = (u32)nToken;
264 }
265 break;
266 }
267
268 case 's': {
269 int nInst;
270
271 memset(aOut, 0, sizeof(u32) * p->nCol);
272
273 rc = pApi->xInstCount(pFts, &nInst);
274 for(i=0; rc==SQLITE_OK && i<nInst; i++){
275 int iPhrase, iOff, iCol = 0;
276 int iNextPhrase;
277 int iNextOff;
278 u32 nSeq = 1;
279 int j;
280
281 rc = pApi->xInst(pFts, i, &iPhrase, &iCol, &iOff);
282 iNextPhrase = iPhrase+1;
283 iNextOff = iOff+pApi->xPhraseSize(pFts, 0);
284 for(j=i+1; rc==SQLITE_OK && j<nInst; j++){
285 int ip, ic, io;
286 rc = pApi->xInst(pFts, j, &ip, &ic, &io);
287 if( ic!=iCol || io>iNextOff ) break;
288 if( ip==iNextPhrase && io==iNextOff ){
289 nSeq++;
290 iNextPhrase = ip+1;
291 iNextOff = io + pApi->xPhraseSize(pFts, ip);
292 }
293 }
294
295 if( nSeq>aOut[iCol] ) aOut[iCol] = nSeq;
296 }
297
298 break;
299 }
300 }
301 return rc;
302 }
303
304 static Fts5MatchinfoCtx *fts5MatchinfoNew(
305 const Fts5ExtensionApi *pApi, /* API offered by current FTS version */
306 Fts5Context *pFts, /* First arg to pass to pApi functions */
307 sqlite3_context *pCtx, /* Context for returning error message */
308 const char *zArg /* Matchinfo flag string */
309 ){
310 Fts5MatchinfoCtx *p;
311 int nCol;
312 int nPhrase;
313 int i;
314 int nInt;
315 int nByte;
316 int rc;
317
318 nCol = pApi->xColumnCount(pFts);
319 nPhrase = pApi->xPhraseCount(pFts);
320
321 nInt = 0;
322 for(i=0; zArg[i]; i++){
323 int n = fts5MatchinfoFlagsize(nCol, nPhrase, zArg[i]);
324 if( n<0 ){
325 char *zErr = sqlite3_mprintf("unrecognized matchinfo flag: %c", zArg[i]);
326 sqlite3_result_error(pCtx, zErr, -1);
327 sqlite3_free(zErr);
328 return 0;
329 }
330 nInt += n;
331 }
332
333 nByte = sizeof(Fts5MatchinfoCtx) /* The struct itself */
334 + sizeof(u32) * nInt /* The p->aRet[] array */
335 + (i+1); /* The p->zArg string */
336 p = (Fts5MatchinfoCtx*)sqlite3_malloc(nByte);
337 if( p==0 ){
338 sqlite3_result_error_nomem(pCtx);
339 return 0;
340 }
341 memset(p, 0, nByte);
342
343 p->nCol = nCol;
344 p->nPhrase = nPhrase;
345 p->aRet = (u32*)&p[1];
346 p->nRet = nInt;
347 p->zArg = (char*)&p->aRet[nInt];
348 memcpy(p->zArg, zArg, i);
349
350 rc = fts5MatchinfoIter(pApi, pFts, p, fts5MatchinfoGlobalCb);
351 if( rc!=SQLITE_OK ){
352 sqlite3_result_error_code(pCtx, rc);
353 sqlite3_free(p);
354 p = 0;
355 }
356
357 return p;
358 }
359
360 static void fts5MatchinfoFunc(
361 const Fts5ExtensionApi *pApi, /* API offered by current FTS version */
362 Fts5Context *pFts, /* First arg to pass to pApi functions */
363 sqlite3_context *pCtx, /* Context for returning result/error */
364 int nVal, /* Number of values in apVal[] array */
365 sqlite3_value **apVal /* Array of trailing arguments */
366 ){
367 const char *zArg;
368 Fts5MatchinfoCtx *p;
369 int rc = SQLITE_OK;
370
371 if( nVal>0 ){
372 zArg = (const char*)sqlite3_value_text(apVal[0]);
373 }else{
374 zArg = "pcx";
375 }
376
377 p = (Fts5MatchinfoCtx*)pApi->xGetAuxdata(pFts, 0);
378 if( p==0 || sqlite3_stricmp(zArg, p->zArg) ){
379 p = fts5MatchinfoNew(pApi, pFts, pCtx, zArg);
380 if( p==0 ){
381 rc = SQLITE_NOMEM;
382 }else{
383 rc = pApi->xSetAuxdata(pFts, p, sqlite3_free);
384 }
385 }
386
387 if( rc==SQLITE_OK ){
388 rc = fts5MatchinfoIter(pApi, pFts, p, fts5MatchinfoLocalCb);
389 }
390 if( rc!=SQLITE_OK ){
391 sqlite3_result_error_code(pCtx, rc);
392 }else{
393 /* No errors has occured, so return a copy of the array of integers. */
394 int nByte = p->nRet * sizeof(u32);
395 sqlite3_result_blob(pCtx, (void*)p->aRet, nByte, SQLITE_TRANSIENT);
396 }
397 }
398
399 int sqlite3Fts5TestRegisterMatchinfo(sqlite3 *db){
400 int rc; /* Return code */
401 fts5_api *pApi; /* FTS5 API functions */
402
403 /* Extract the FTS5 API pointer from the database handle. The
404 ** fts5_api_from_db() function above is copied verbatim from the
405 ** FTS5 documentation. Refer there for details. */
406 rc = fts5_api_from_db(db, &pApi);
407 if( rc!=SQLITE_OK ) return rc;
408
409 /* If fts5_api_from_db() returns NULL, then either FTS5 is not registered
410 ** with this database handle, or an error (OOM perhaps?) has occurred.
411 **
412 ** Also check that the fts5_api object is version 2 or newer.
413 */
414 if( pApi==0 || pApi->iVersion<2 ){
415 return SQLITE_ERROR;
416 }
417
418 /* Register the implementation of matchinfo() */
419 rc = pApi->xCreateFunction(pApi, "matchinfo", 0, fts5MatchinfoFunc, 0);
420
421 return rc;
422 }
423
424 #endif /* SQLITE_ENABLE_FTS5 */
425
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698