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

Side by Side Diff: third_party/sqlite/sqlite-src-3100200/ext/fts5/fts5_config.c

Issue 1610543003: [sql] Import reference version of SQLite 3.10.2. (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: Created 4 years, 11 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 ** 2014 Jun 09
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 is an SQLite module implementing full-text search.
14 */
15
16
17
18 #include "fts5Int.h"
19
20 #define FTS5_DEFAULT_PAGE_SIZE 4050
21 #define FTS5_DEFAULT_AUTOMERGE 4
22 #define FTS5_DEFAULT_CRISISMERGE 16
23 #define FTS5_DEFAULT_HASHSIZE (1024*1024)
24
25 /* Maximum allowed page size */
26 #define FTS5_MAX_PAGE_SIZE (128*1024)
27
28 static int fts5_iswhitespace(char x){
29 return (x==' ');
30 }
31
32 static int fts5_isopenquote(char x){
33 return (x=='"' || x=='\'' || x=='[' || x=='`');
34 }
35
36 /*
37 ** Argument pIn points to a character that is part of a nul-terminated
38 ** string. Return a pointer to the first character following *pIn in
39 ** the string that is not a white-space character.
40 */
41 static const char *fts5ConfigSkipWhitespace(const char *pIn){
42 const char *p = pIn;
43 if( p ){
44 while( fts5_iswhitespace(*p) ){ p++; }
45 }
46 return p;
47 }
48
49 /*
50 ** Argument pIn points to a character that is part of a nul-terminated
51 ** string. Return a pointer to the first character following *pIn in
52 ** the string that is not a "bareword" character.
53 */
54 static const char *fts5ConfigSkipBareword(const char *pIn){
55 const char *p = pIn;
56 while ( sqlite3Fts5IsBareword(*p) ) p++;
57 if( p==pIn ) p = 0;
58 return p;
59 }
60
61 static int fts5_isdigit(char a){
62 return (a>='0' && a<='9');
63 }
64
65
66
67 static const char *fts5ConfigSkipLiteral(const char *pIn){
68 const char *p = pIn;
69 switch( *p ){
70 case 'n': case 'N':
71 if( sqlite3_strnicmp("null", p, 4)==0 ){
72 p = &p[4];
73 }else{
74 p = 0;
75 }
76 break;
77
78 case 'x': case 'X':
79 p++;
80 if( *p=='\'' ){
81 p++;
82 while( (*p>='a' && *p<='f')
83 || (*p>='A' && *p<='F')
84 || (*p>='0' && *p<='9')
85 ){
86 p++;
87 }
88 if( *p=='\'' && 0==((p-pIn)%2) ){
89 p++;
90 }else{
91 p = 0;
92 }
93 }else{
94 p = 0;
95 }
96 break;
97
98 case '\'':
99 p++;
100 while( p ){
101 if( *p=='\'' ){
102 p++;
103 if( *p!='\'' ) break;
104 }
105 p++;
106 if( *p==0 ) p = 0;
107 }
108 break;
109
110 default:
111 /* maybe a number */
112 if( *p=='+' || *p=='-' ) p++;
113 while( fts5_isdigit(*p) ) p++;
114
115 /* At this point, if the literal was an integer, the parse is
116 ** finished. Or, if it is a floating point value, it may continue
117 ** with either a decimal point or an 'E' character. */
118 if( *p=='.' && fts5_isdigit(p[1]) ){
119 p += 2;
120 while( fts5_isdigit(*p) ) p++;
121 }
122 if( p==pIn ) p = 0;
123
124 break;
125 }
126
127 return p;
128 }
129
130 /*
131 ** The first character of the string pointed to by argument z is guaranteed
132 ** to be an open-quote character (see function fts5_isopenquote()).
133 **
134 ** This function searches for the corresponding close-quote character within
135 ** the string and, if found, dequotes the string in place and adds a new
136 ** nul-terminator byte.
137 **
138 ** If the close-quote is found, the value returned is the byte offset of
139 ** the character immediately following it. Or, if the close-quote is not
140 ** found, -1 is returned. If -1 is returned, the buffer is left in an
141 ** undefined state.
142 */
143 static int fts5Dequote(char *z){
144 char q;
145 int iIn = 1;
146 int iOut = 0;
147 q = z[0];
148
149 /* Set stack variable q to the close-quote character */
150 assert( q=='[' || q=='\'' || q=='"' || q=='`' );
151 if( q=='[' ) q = ']';
152
153 while( ALWAYS(z[iIn]) ){
154 if( z[iIn]==q ){
155 if( z[iIn+1]!=q ){
156 /* Character iIn was the close quote. */
157 iIn++;
158 break;
159 }else{
160 /* Character iIn and iIn+1 form an escaped quote character. Skip
161 ** the input cursor past both and copy a single quote character
162 ** to the output buffer. */
163 iIn += 2;
164 z[iOut++] = q;
165 }
166 }else{
167 z[iOut++] = z[iIn++];
168 }
169 }
170
171 z[iOut] = '\0';
172 return iIn;
173 }
174
175 /*
176 ** Convert an SQL-style quoted string into a normal string by removing
177 ** the quote characters. The conversion is done in-place. If the
178 ** input does not begin with a quote character, then this routine
179 ** is a no-op.
180 **
181 ** Examples:
182 **
183 ** "abc" becomes abc
184 ** 'xyz' becomes xyz
185 ** [pqr] becomes pqr
186 ** `mno` becomes mno
187 */
188 void sqlite3Fts5Dequote(char *z){
189 char quote; /* Quote character (if any ) */
190
191 assert( 0==fts5_iswhitespace(z[0]) );
192 quote = z[0];
193 if( quote=='[' || quote=='\'' || quote=='"' || quote=='`' ){
194 fts5Dequote(z);
195 }
196 }
197
198 /*
199 ** Parse a "special" CREATE VIRTUAL TABLE directive and update
200 ** configuration object pConfig as appropriate.
201 **
202 ** If successful, object pConfig is updated and SQLITE_OK returned. If
203 ** an error occurs, an SQLite error code is returned and an error message
204 ** may be left in *pzErr. It is the responsibility of the caller to
205 ** eventually free any such error message using sqlite3_free().
206 */
207 static int fts5ConfigParseSpecial(
208 Fts5Global *pGlobal,
209 Fts5Config *pConfig, /* Configuration object to update */
210 const char *zCmd, /* Special command to parse */
211 const char *zArg, /* Argument to parse */
212 char **pzErr /* OUT: Error message */
213 ){
214 int rc = SQLITE_OK;
215 int nCmd = (int)strlen(zCmd);
216 if( sqlite3_strnicmp("prefix", zCmd, nCmd)==0 ){
217 const int nByte = sizeof(int) * FTS5_MAX_PREFIX_INDEXES;
218 const char *p;
219 int bFirst = 1;
220 if( pConfig->aPrefix==0 ){
221 pConfig->aPrefix = sqlite3Fts5MallocZero(&rc, nByte);
222 if( rc ) return rc;
223 }
224
225 p = zArg;
226 while( 1 ){
227 int nPre = 0;
228
229 while( p[0]==' ' ) p++;
230 if( bFirst==0 && p[0]==',' ){
231 p++;
232 while( p[0]==' ' ) p++;
233 }else if( p[0]=='\0' ){
234 break;
235 }
236 if( p[0]<'0' || p[0]>'9' ){
237 *pzErr = sqlite3_mprintf("malformed prefix=... directive");
238 rc = SQLITE_ERROR;
239 break;
240 }
241
242 if( pConfig->nPrefix==FTS5_MAX_PREFIX_INDEXES ){
243 *pzErr = sqlite3_mprintf(
244 "too many prefix indexes (max %d)", FTS5_MAX_PREFIX_INDEXES
245 );
246 rc = SQLITE_ERROR;
247 break;
248 }
249
250 while( p[0]>='0' && p[0]<='9' && nPre<1000 ){
251 nPre = nPre*10 + (p[0] - '0');
252 p++;
253 }
254
255 if( rc==SQLITE_OK && (nPre<=0 || nPre>=1000) ){
256 *pzErr = sqlite3_mprintf("prefix length out of range (max 999)");
257 rc = SQLITE_ERROR;
258 break;
259 }
260
261 pConfig->aPrefix[pConfig->nPrefix] = nPre;
262 pConfig->nPrefix++;
263 bFirst = 0;
264 }
265 assert( pConfig->nPrefix<=FTS5_MAX_PREFIX_INDEXES );
266 return rc;
267 }
268
269 if( sqlite3_strnicmp("tokenize", zCmd, nCmd)==0 ){
270 const char *p = (const char*)zArg;
271 int nArg = (int)strlen(zArg) + 1;
272 char **azArg = sqlite3Fts5MallocZero(&rc, sizeof(char*) * nArg);
273 char *pDel = sqlite3Fts5MallocZero(&rc, nArg * 2);
274 char *pSpace = pDel;
275
276 if( azArg && pSpace ){
277 if( pConfig->pTok ){
278 *pzErr = sqlite3_mprintf("multiple tokenize=... directives");
279 rc = SQLITE_ERROR;
280 }else{
281 for(nArg=0; p && *p; nArg++){
282 const char *p2 = fts5ConfigSkipWhitespace(p);
283 if( *p2=='\'' ){
284 p = fts5ConfigSkipLiteral(p2);
285 }else{
286 p = fts5ConfigSkipBareword(p2);
287 }
288 if( p ){
289 memcpy(pSpace, p2, p-p2);
290 azArg[nArg] = pSpace;
291 sqlite3Fts5Dequote(pSpace);
292 pSpace += (p - p2) + 1;
293 p = fts5ConfigSkipWhitespace(p);
294 }
295 }
296 if( p==0 ){
297 *pzErr = sqlite3_mprintf("parse error in tokenize directive");
298 rc = SQLITE_ERROR;
299 }else{
300 rc = sqlite3Fts5GetTokenizer(pGlobal,
301 (const char**)azArg, nArg, &pConfig->pTok, &pConfig->pTokApi,
302 pzErr
303 );
304 }
305 }
306 }
307
308 sqlite3_free(azArg);
309 sqlite3_free(pDel);
310 return rc;
311 }
312
313 if( sqlite3_strnicmp("content", zCmd, nCmd)==0 ){
314 if( pConfig->eContent!=FTS5_CONTENT_NORMAL ){
315 *pzErr = sqlite3_mprintf("multiple content=... directives");
316 rc = SQLITE_ERROR;
317 }else{
318 if( zArg[0] ){
319 pConfig->eContent = FTS5_CONTENT_EXTERNAL;
320 pConfig->zContent = sqlite3Fts5Mprintf(&rc, "%Q.%Q", pConfig->zDb,zArg);
321 }else{
322 pConfig->eContent = FTS5_CONTENT_NONE;
323 }
324 }
325 return rc;
326 }
327
328 if( sqlite3_strnicmp("content_rowid", zCmd, nCmd)==0 ){
329 if( pConfig->zContentRowid ){
330 *pzErr = sqlite3_mprintf("multiple content_rowid=... directives");
331 rc = SQLITE_ERROR;
332 }else{
333 pConfig->zContentRowid = sqlite3Fts5Strndup(&rc, zArg, -1);
334 }
335 return rc;
336 }
337
338 if( sqlite3_strnicmp("columnsize", zCmd, nCmd)==0 ){
339 if( (zArg[0]!='0' && zArg[0]!='1') || zArg[1]!='\0' ){
340 *pzErr = sqlite3_mprintf("malformed columnsize=... directive");
341 rc = SQLITE_ERROR;
342 }else{
343 pConfig->bColumnsize = (zArg[0]=='1');
344 }
345 return rc;
346 }
347
348 *pzErr = sqlite3_mprintf("unrecognized option: \"%.*s\"", nCmd, zCmd);
349 return SQLITE_ERROR;
350 }
351
352 /*
353 ** Allocate an instance of the default tokenizer ("simple") at
354 ** Fts5Config.pTokenizer. Return SQLITE_OK if successful, or an SQLite error
355 ** code if an error occurs.
356 */
357 static int fts5ConfigDefaultTokenizer(Fts5Global *pGlobal, Fts5Config *pConfig){
358 assert( pConfig->pTok==0 && pConfig->pTokApi==0 );
359 return sqlite3Fts5GetTokenizer(
360 pGlobal, 0, 0, &pConfig->pTok, &pConfig->pTokApi, 0
361 );
362 }
363
364 /*
365 ** Gobble up the first bareword or quoted word from the input buffer zIn.
366 ** Return a pointer to the character immediately following the last in
367 ** the gobbled word if successful, or a NULL pointer otherwise (failed
368 ** to find close-quote character).
369 **
370 ** Before returning, set pzOut to point to a new buffer containing a
371 ** nul-terminated, dequoted copy of the gobbled word. If the word was
372 ** quoted, *pbQuoted is also set to 1 before returning.
373 **
374 ** If *pRc is other than SQLITE_OK when this function is called, it is
375 ** a no-op (NULL is returned). Otherwise, if an OOM occurs within this
376 ** function, *pRc is set to SQLITE_NOMEM before returning. *pRc is *not*
377 ** set if a parse error (failed to find close quote) occurs.
378 */
379 static const char *fts5ConfigGobbleWord(
380 int *pRc, /* IN/OUT: Error code */
381 const char *zIn, /* Buffer to gobble string/bareword from */
382 char **pzOut, /* OUT: malloc'd buffer containing str/bw */
383 int *pbQuoted /* OUT: Set to true if dequoting required */
384 ){
385 const char *zRet = 0;
386
387 int nIn = (int)strlen(zIn);
388 char *zOut = sqlite3_malloc(nIn+1);
389
390 assert( *pRc==SQLITE_OK );
391 *pbQuoted = 0;
392 *pzOut = 0;
393
394 if( zOut==0 ){
395 *pRc = SQLITE_NOMEM;
396 }else{
397 memcpy(zOut, zIn, nIn+1);
398 if( fts5_isopenquote(zOut[0]) ){
399 int ii = fts5Dequote(zOut);
400 zRet = &zIn[ii];
401 *pbQuoted = 1;
402 }else{
403 zRet = fts5ConfigSkipBareword(zIn);
404 zOut[zRet-zIn] = '\0';
405 }
406 }
407
408 if( zRet==0 ){
409 sqlite3_free(zOut);
410 }else{
411 *pzOut = zOut;
412 }
413
414 return zRet;
415 }
416
417 static int fts5ConfigParseColumn(
418 Fts5Config *p,
419 char *zCol,
420 char *zArg,
421 char **pzErr
422 ){
423 int rc = SQLITE_OK;
424 if( 0==sqlite3_stricmp(zCol, FTS5_RANK_NAME)
425 || 0==sqlite3_stricmp(zCol, FTS5_ROWID_NAME)
426 ){
427 *pzErr = sqlite3_mprintf("reserved fts5 column name: %s", zCol);
428 rc = SQLITE_ERROR;
429 }else if( zArg ){
430 if( 0==sqlite3_stricmp(zArg, "unindexed") ){
431 p->abUnindexed[p->nCol] = 1;
432 }else{
433 *pzErr = sqlite3_mprintf("unrecognized column option: %s", zArg);
434 rc = SQLITE_ERROR;
435 }
436 }
437
438 p->azCol[p->nCol++] = zCol;
439 return rc;
440 }
441
442 /*
443 ** Populate the Fts5Config.zContentExprlist string.
444 */
445 static int fts5ConfigMakeExprlist(Fts5Config *p){
446 int i;
447 int rc = SQLITE_OK;
448 Fts5Buffer buf = {0, 0, 0};
449
450 sqlite3Fts5BufferAppendPrintf(&rc, &buf, "T.%Q", p->zContentRowid);
451 if( p->eContent!=FTS5_CONTENT_NONE ){
452 for(i=0; i<p->nCol; i++){
453 if( p->eContent==FTS5_CONTENT_EXTERNAL ){
454 sqlite3Fts5BufferAppendPrintf(&rc, &buf, ", T.%Q", p->azCol[i]);
455 }else{
456 sqlite3Fts5BufferAppendPrintf(&rc, &buf, ", T.c%d", i);
457 }
458 }
459 }
460
461 assert( p->zContentExprlist==0 );
462 p->zContentExprlist = (char*)buf.p;
463 return rc;
464 }
465
466 /*
467 ** Arguments nArg/azArg contain the string arguments passed to the xCreate
468 ** or xConnect method of the virtual table. This function attempts to
469 ** allocate an instance of Fts5Config containing the results of parsing
470 ** those arguments.
471 **
472 ** If successful, SQLITE_OK is returned and *ppOut is set to point to the
473 ** new Fts5Config object. If an error occurs, an SQLite error code is
474 ** returned, *ppOut is set to NULL and an error message may be left in
475 ** *pzErr. It is the responsibility of the caller to eventually free any
476 ** such error message using sqlite3_free().
477 */
478 int sqlite3Fts5ConfigParse(
479 Fts5Global *pGlobal,
480 sqlite3 *db,
481 int nArg, /* Number of arguments */
482 const char **azArg, /* Array of nArg CREATE VIRTUAL TABLE args */
483 Fts5Config **ppOut, /* OUT: Results of parse */
484 char **pzErr /* OUT: Error message */
485 ){
486 int rc = SQLITE_OK; /* Return code */
487 Fts5Config *pRet; /* New object to return */
488 int i;
489 int nByte;
490
491 *ppOut = pRet = (Fts5Config*)sqlite3_malloc(sizeof(Fts5Config));
492 if( pRet==0 ) return SQLITE_NOMEM;
493 memset(pRet, 0, sizeof(Fts5Config));
494 pRet->db = db;
495 pRet->iCookie = -1;
496
497 nByte = nArg * (sizeof(char*) + sizeof(u8));
498 pRet->azCol = (char**)sqlite3Fts5MallocZero(&rc, nByte);
499 pRet->abUnindexed = (u8*)&pRet->azCol[nArg];
500 pRet->zDb = sqlite3Fts5Strndup(&rc, azArg[1], -1);
501 pRet->zName = sqlite3Fts5Strndup(&rc, azArg[2], -1);
502 pRet->bColumnsize = 1;
503 #ifdef SQLITE_DEBUG
504 pRet->bPrefixIndex = 1;
505 #endif
506 if( rc==SQLITE_OK && sqlite3_stricmp(pRet->zName, FTS5_RANK_NAME)==0 ){
507 *pzErr = sqlite3_mprintf("reserved fts5 table name: %s", pRet->zName);
508 rc = SQLITE_ERROR;
509 }
510
511 for(i=3; rc==SQLITE_OK && i<nArg; i++){
512 const char *zOrig = azArg[i];
513 const char *z;
514 char *zOne = 0;
515 char *zTwo = 0;
516 int bOption = 0;
517 int bMustBeCol = 0;
518
519 z = fts5ConfigGobbleWord(&rc, zOrig, &zOne, &bMustBeCol);
520 z = fts5ConfigSkipWhitespace(z);
521 if( z && *z=='=' ){
522 bOption = 1;
523 z++;
524 if( bMustBeCol ) z = 0;
525 }
526 z = fts5ConfigSkipWhitespace(z);
527 if( z && z[0] ){
528 int bDummy;
529 z = fts5ConfigGobbleWord(&rc, z, &zTwo, &bDummy);
530 if( z && z[0] ) z = 0;
531 }
532
533 if( rc==SQLITE_OK ){
534 if( z==0 ){
535 *pzErr = sqlite3_mprintf("parse error in \"%s\"", zOrig);
536 rc = SQLITE_ERROR;
537 }else{
538 if( bOption ){
539 rc = fts5ConfigParseSpecial(pGlobal, pRet, zOne, zTwo?zTwo:"", pzErr);
540 }else{
541 rc = fts5ConfigParseColumn(pRet, zOne, zTwo, pzErr);
542 zOne = 0;
543 }
544 }
545 }
546
547 sqlite3_free(zOne);
548 sqlite3_free(zTwo);
549 }
550
551 /* If a tokenizer= option was successfully parsed, the tokenizer has
552 ** already been allocated. Otherwise, allocate an instance of the default
553 ** tokenizer (unicode61) now. */
554 if( rc==SQLITE_OK && pRet->pTok==0 ){
555 rc = fts5ConfigDefaultTokenizer(pGlobal, pRet);
556 }
557
558 /* If no zContent option was specified, fill in the default values. */
559 if( rc==SQLITE_OK && pRet->zContent==0 ){
560 const char *zTail = 0;
561 assert( pRet->eContent==FTS5_CONTENT_NORMAL
562 || pRet->eContent==FTS5_CONTENT_NONE
563 );
564 if( pRet->eContent==FTS5_CONTENT_NORMAL ){
565 zTail = "content";
566 }else if( pRet->bColumnsize ){
567 zTail = "docsize";
568 }
569
570 if( zTail ){
571 pRet->zContent = sqlite3Fts5Mprintf(
572 &rc, "%Q.'%q_%s'", pRet->zDb, pRet->zName, zTail
573 );
574 }
575 }
576
577 if( rc==SQLITE_OK && pRet->zContentRowid==0 ){
578 pRet->zContentRowid = sqlite3Fts5Strndup(&rc, "rowid", -1);
579 }
580
581 /* Formulate the zContentExprlist text */
582 if( rc==SQLITE_OK ){
583 rc = fts5ConfigMakeExprlist(pRet);
584 }
585
586 if( rc!=SQLITE_OK ){
587 sqlite3Fts5ConfigFree(pRet);
588 *ppOut = 0;
589 }
590 return rc;
591 }
592
593 /*
594 ** Free the configuration object passed as the only argument.
595 */
596 void sqlite3Fts5ConfigFree(Fts5Config *pConfig){
597 if( pConfig ){
598 int i;
599 if( pConfig->pTok ){
600 pConfig->pTokApi->xDelete(pConfig->pTok);
601 }
602 sqlite3_free(pConfig->zDb);
603 sqlite3_free(pConfig->zName);
604 for(i=0; i<pConfig->nCol; i++){
605 sqlite3_free(pConfig->azCol[i]);
606 }
607 sqlite3_free(pConfig->azCol);
608 sqlite3_free(pConfig->aPrefix);
609 sqlite3_free(pConfig->zRank);
610 sqlite3_free(pConfig->zRankArgs);
611 sqlite3_free(pConfig->zContent);
612 sqlite3_free(pConfig->zContentRowid);
613 sqlite3_free(pConfig->zContentExprlist);
614 sqlite3_free(pConfig);
615 }
616 }
617
618 /*
619 ** Call sqlite3_declare_vtab() based on the contents of the configuration
620 ** object passed as the only argument. Return SQLITE_OK if successful, or
621 ** an SQLite error code if an error occurs.
622 */
623 int sqlite3Fts5ConfigDeclareVtab(Fts5Config *pConfig){
624 int i;
625 int rc = SQLITE_OK;
626 char *zSql;
627
628 zSql = sqlite3Fts5Mprintf(&rc, "CREATE TABLE x(");
629 for(i=0; zSql && i<pConfig->nCol; i++){
630 const char *zSep = (i==0?"":", ");
631 zSql = sqlite3Fts5Mprintf(&rc, "%z%s%Q", zSql, zSep, pConfig->azCol[i]);
632 }
633 zSql = sqlite3Fts5Mprintf(&rc, "%z, %Q HIDDEN, %s HIDDEN)",
634 zSql, pConfig->zName, FTS5_RANK_NAME
635 );
636
637 assert( zSql || rc==SQLITE_NOMEM );
638 if( zSql ){
639 rc = sqlite3_declare_vtab(pConfig->db, zSql);
640 sqlite3_free(zSql);
641 }
642
643 return rc;
644 }
645
646 /*
647 ** Tokenize the text passed via the second and third arguments.
648 **
649 ** The callback is invoked once for each token in the input text. The
650 ** arguments passed to it are, in order:
651 **
652 ** void *pCtx // Copy of 4th argument to sqlite3Fts5Tokenize()
653 ** const char *pToken // Pointer to buffer containing token
654 ** int nToken // Size of token in bytes
655 ** int iStart // Byte offset of start of token within input text
656 ** int iEnd // Byte offset of end of token within input text
657 ** int iPos // Position of token in input (first token is 0)
658 **
659 ** If the callback returns a non-zero value the tokenization is abandoned
660 ** and no further callbacks are issued.
661 **
662 ** This function returns SQLITE_OK if successful or an SQLite error code
663 ** if an error occurs. If the tokenization was abandoned early because
664 ** the callback returned SQLITE_DONE, this is not an error and this function
665 ** still returns SQLITE_OK. Or, if the tokenization was abandoned early
666 ** because the callback returned another non-zero value, it is assumed
667 ** to be an SQLite error code and returned to the caller.
668 */
669 int sqlite3Fts5Tokenize(
670 Fts5Config *pConfig, /* FTS5 Configuration object */
671 int flags, /* FTS5_TOKENIZE_* flags */
672 const char *pText, int nText, /* Text to tokenize */
673 void *pCtx, /* Context passed to xToken() */
674 int (*xToken)(void*, int, const char*, int, int, int) /* Callback */
675 ){
676 if( pText==0 ) return SQLITE_OK;
677 return pConfig->pTokApi->xTokenize(
678 pConfig->pTok, pCtx, flags, pText, nText, xToken
679 );
680 }
681
682 /*
683 ** Argument pIn points to the first character in what is expected to be
684 ** a comma-separated list of SQL literals followed by a ')' character.
685 ** If it actually is this, return a pointer to the ')'. Otherwise, return
686 ** NULL to indicate a parse error.
687 */
688 static const char *fts5ConfigSkipArgs(const char *pIn){
689 const char *p = pIn;
690
691 while( 1 ){
692 p = fts5ConfigSkipWhitespace(p);
693 p = fts5ConfigSkipLiteral(p);
694 p = fts5ConfigSkipWhitespace(p);
695 if( p==0 || *p==')' ) break;
696 if( *p!=',' ){
697 p = 0;
698 break;
699 }
700 p++;
701 }
702
703 return p;
704 }
705
706 /*
707 ** Parameter zIn contains a rank() function specification. The format of
708 ** this is:
709 **
710 ** + Bareword (function name)
711 ** + Open parenthesis - "("
712 ** + Zero or more SQL literals in a comma separated list
713 ** + Close parenthesis - ")"
714 */
715 int sqlite3Fts5ConfigParseRank(
716 const char *zIn, /* Input string */
717 char **pzRank, /* OUT: Rank function name */
718 char **pzRankArgs /* OUT: Rank function arguments */
719 ){
720 const char *p = zIn;
721 const char *pRank;
722 char *zRank = 0;
723 char *zRankArgs = 0;
724 int rc = SQLITE_OK;
725
726 *pzRank = 0;
727 *pzRankArgs = 0;
728
729 if( p==0 ){
730 rc = SQLITE_ERROR;
731 }else{
732 p = fts5ConfigSkipWhitespace(p);
733 pRank = p;
734 p = fts5ConfigSkipBareword(p);
735
736 if( p ){
737 zRank = sqlite3Fts5MallocZero(&rc, 1 + p - pRank);
738 if( zRank ) memcpy(zRank, pRank, p-pRank);
739 }else{
740 rc = SQLITE_ERROR;
741 }
742
743 if( rc==SQLITE_OK ){
744 p = fts5ConfigSkipWhitespace(p);
745 if( *p!='(' ) rc = SQLITE_ERROR;
746 p++;
747 }
748 if( rc==SQLITE_OK ){
749 const char *pArgs;
750 p = fts5ConfigSkipWhitespace(p);
751 pArgs = p;
752 if( *p!=')' ){
753 p = fts5ConfigSkipArgs(p);
754 if( p==0 ){
755 rc = SQLITE_ERROR;
756 }else{
757 zRankArgs = sqlite3Fts5MallocZero(&rc, 1 + p - pArgs);
758 if( zRankArgs ) memcpy(zRankArgs, pArgs, p-pArgs);
759 }
760 }
761 }
762 }
763
764 if( rc!=SQLITE_OK ){
765 sqlite3_free(zRank);
766 assert( zRankArgs==0 );
767 }else{
768 *pzRank = zRank;
769 *pzRankArgs = zRankArgs;
770 }
771 return rc;
772 }
773
774 int sqlite3Fts5ConfigSetValue(
775 Fts5Config *pConfig,
776 const char *zKey,
777 sqlite3_value *pVal,
778 int *pbBadkey
779 ){
780 int rc = SQLITE_OK;
781
782 if( 0==sqlite3_stricmp(zKey, "pgsz") ){
783 int pgsz = 0;
784 if( SQLITE_INTEGER==sqlite3_value_numeric_type(pVal) ){
785 pgsz = sqlite3_value_int(pVal);
786 }
787 if( pgsz<=0 || pgsz>FTS5_MAX_PAGE_SIZE ){
788 *pbBadkey = 1;
789 }else{
790 pConfig->pgsz = pgsz;
791 }
792 }
793
794 else if( 0==sqlite3_stricmp(zKey, "hashsize") ){
795 int nHashSize = -1;
796 if( SQLITE_INTEGER==sqlite3_value_numeric_type(pVal) ){
797 nHashSize = sqlite3_value_int(pVal);
798 }
799 if( nHashSize<=0 ){
800 *pbBadkey = 1;
801 }else{
802 pConfig->nHashSize = nHashSize;
803 }
804 }
805
806 else if( 0==sqlite3_stricmp(zKey, "automerge") ){
807 int nAutomerge = -1;
808 if( SQLITE_INTEGER==sqlite3_value_numeric_type(pVal) ){
809 nAutomerge = sqlite3_value_int(pVal);
810 }
811 if( nAutomerge<0 || nAutomerge>64 ){
812 *pbBadkey = 1;
813 }else{
814 if( nAutomerge==1 ) nAutomerge = FTS5_DEFAULT_AUTOMERGE;
815 pConfig->nAutomerge = nAutomerge;
816 }
817 }
818
819 else if( 0==sqlite3_stricmp(zKey, "crisismerge") ){
820 int nCrisisMerge = -1;
821 if( SQLITE_INTEGER==sqlite3_value_numeric_type(pVal) ){
822 nCrisisMerge = sqlite3_value_int(pVal);
823 }
824 if( nCrisisMerge<0 ){
825 *pbBadkey = 1;
826 }else{
827 if( nCrisisMerge<=1 ) nCrisisMerge = FTS5_DEFAULT_CRISISMERGE;
828 pConfig->nCrisisMerge = nCrisisMerge;
829 }
830 }
831
832 else if( 0==sqlite3_stricmp(zKey, "rank") ){
833 const char *zIn = (const char*)sqlite3_value_text(pVal);
834 char *zRank;
835 char *zRankArgs;
836 rc = sqlite3Fts5ConfigParseRank(zIn, &zRank, &zRankArgs);
837 if( rc==SQLITE_OK ){
838 sqlite3_free(pConfig->zRank);
839 sqlite3_free(pConfig->zRankArgs);
840 pConfig->zRank = zRank;
841 pConfig->zRankArgs = zRankArgs;
842 }else if( rc==SQLITE_ERROR ){
843 rc = SQLITE_OK;
844 *pbBadkey = 1;
845 }
846 }else{
847 *pbBadkey = 1;
848 }
849 return rc;
850 }
851
852 /*
853 ** Load the contents of the %_config table into memory.
854 */
855 int sqlite3Fts5ConfigLoad(Fts5Config *pConfig, int iCookie){
856 const char *zSelect = "SELECT k, v FROM %Q.'%q_config'";
857 char *zSql;
858 sqlite3_stmt *p = 0;
859 int rc = SQLITE_OK;
860 int iVersion = 0;
861
862 /* Set default values */
863 pConfig->pgsz = FTS5_DEFAULT_PAGE_SIZE;
864 pConfig->nAutomerge = FTS5_DEFAULT_AUTOMERGE;
865 pConfig->nCrisisMerge = FTS5_DEFAULT_CRISISMERGE;
866 pConfig->nHashSize = FTS5_DEFAULT_HASHSIZE;
867
868 zSql = sqlite3Fts5Mprintf(&rc, zSelect, pConfig->zDb, pConfig->zName);
869 if( zSql ){
870 rc = sqlite3_prepare_v2(pConfig->db, zSql, -1, &p, 0);
871 sqlite3_free(zSql);
872 }
873
874 assert( rc==SQLITE_OK || p==0 );
875 if( rc==SQLITE_OK ){
876 while( SQLITE_ROW==sqlite3_step(p) ){
877 const char *zK = (const char*)sqlite3_column_text(p, 0);
878 sqlite3_value *pVal = sqlite3_column_value(p, 1);
879 if( 0==sqlite3_stricmp(zK, "version") ){
880 iVersion = sqlite3_value_int(pVal);
881 }else{
882 int bDummy = 0;
883 sqlite3Fts5ConfigSetValue(pConfig, zK, pVal, &bDummy);
884 }
885 }
886 rc = sqlite3_finalize(p);
887 }
888
889 if( rc==SQLITE_OK && iVersion!=FTS5_CURRENT_VERSION ){
890 rc = SQLITE_ERROR;
891 if( pConfig->pzErrmsg ){
892 assert( 0==*pConfig->pzErrmsg );
893 *pConfig->pzErrmsg = sqlite3_mprintf(
894 "invalid fts5 file format (found %d, expected %d) - run 'rebuild'",
895 iVersion, FTS5_CURRENT_VERSION
896 );
897 }
898 }
899
900 if( rc==SQLITE_OK ){
901 pConfig->iCookie = iCookie;
902 }
903 return rc;
904 }
905
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698