Snapshot of upstream SQLite 3.45.3
[sqlcipher.git] / ext / fts5 / fts5_config.c
blobd2e8309cd2c26fbe27de30ff29eee2823d942f26
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.
11 ******************************************************************************
13 ** This is an SQLite module implementing full-text search.
17 #include "fts5Int.h"
19 #define FTS5_DEFAULT_PAGE_SIZE 4050
20 #define FTS5_DEFAULT_AUTOMERGE 4
21 #define FTS5_DEFAULT_USERMERGE 4
22 #define FTS5_DEFAULT_CRISISMERGE 16
23 #define FTS5_DEFAULT_HASHSIZE (1024*1024)
25 #define FTS5_DEFAULT_DELETE_AUTOMERGE 10 /* default 10% */
27 /* Maximum allowed page size */
28 #define FTS5_MAX_PAGE_SIZE (64*1024)
30 static int fts5_iswhitespace(char x){
31 return (x==' ');
34 static int fts5_isopenquote(char x){
35 return (x=='"' || x=='\'' || x=='[' || x=='`');
39 ** Argument pIn points to a character that is part of a nul-terminated
40 ** string. Return a pointer to the first character following *pIn in
41 ** the string that is not a white-space character.
43 static const char *fts5ConfigSkipWhitespace(const char *pIn){
44 const char *p = pIn;
45 if( p ){
46 while( fts5_iswhitespace(*p) ){ p++; }
48 return p;
52 ** Argument pIn points to a character that is part of a nul-terminated
53 ** string. Return a pointer to the first character following *pIn in
54 ** the string that is not a "bareword" character.
56 static const char *fts5ConfigSkipBareword(const char *pIn){
57 const char *p = pIn;
58 while ( sqlite3Fts5IsBareword(*p) ) p++;
59 if( p==pIn ) p = 0;
60 return p;
63 static int fts5_isdigit(char a){
64 return (a>='0' && a<='9');
69 static const char *fts5ConfigSkipLiteral(const char *pIn){
70 const char *p = pIn;
71 switch( *p ){
72 case 'n': case 'N':
73 if( sqlite3_strnicmp("null", p, 4)==0 ){
74 p = &p[4];
75 }else{
76 p = 0;
78 break;
80 case 'x': case 'X':
81 p++;
82 if( *p=='\'' ){
83 p++;
84 while( (*p>='a' && *p<='f')
85 || (*p>='A' && *p<='F')
86 || (*p>='0' && *p<='9')
88 p++;
90 if( *p=='\'' && 0==((p-pIn)%2) ){
91 p++;
92 }else{
93 p = 0;
95 }else{
96 p = 0;
98 break;
100 case '\'':
101 p++;
102 while( p ){
103 if( *p=='\'' ){
104 p++;
105 if( *p!='\'' ) break;
107 p++;
108 if( *p==0 ) p = 0;
110 break;
112 default:
113 /* maybe a number */
114 if( *p=='+' || *p=='-' ) p++;
115 while( fts5_isdigit(*p) ) p++;
117 /* At this point, if the literal was an integer, the parse is
118 ** finished. Or, if it is a floating point value, it may continue
119 ** with either a decimal point or an 'E' character. */
120 if( *p=='.' && fts5_isdigit(p[1]) ){
121 p += 2;
122 while( fts5_isdigit(*p) ) p++;
124 if( p==pIn ) p = 0;
126 break;
129 return p;
133 ** The first character of the string pointed to by argument z is guaranteed
134 ** to be an open-quote character (see function fts5_isopenquote()).
136 ** This function searches for the corresponding close-quote character within
137 ** the string and, if found, dequotes the string in place and adds a new
138 ** nul-terminator byte.
140 ** If the close-quote is found, the value returned is the byte offset of
141 ** the character immediately following it. Or, if the close-quote is not
142 ** found, -1 is returned. If -1 is returned, the buffer is left in an
143 ** undefined state.
145 static int fts5Dequote(char *z){
146 char q;
147 int iIn = 1;
148 int iOut = 0;
149 q = z[0];
151 /* Set stack variable q to the close-quote character */
152 assert( q=='[' || q=='\'' || q=='"' || q=='`' );
153 if( q=='[' ) q = ']';
155 while( z[iIn] ){
156 if( z[iIn]==q ){
157 if( z[iIn+1]!=q ){
158 /* Character iIn was the close quote. */
159 iIn++;
160 break;
161 }else{
162 /* Character iIn and iIn+1 form an escaped quote character. Skip
163 ** the input cursor past both and copy a single quote character
164 ** to the output buffer. */
165 iIn += 2;
166 z[iOut++] = q;
168 }else{
169 z[iOut++] = z[iIn++];
173 z[iOut] = '\0';
174 return iIn;
178 ** Convert an SQL-style quoted string into a normal string by removing
179 ** the quote characters. The conversion is done in-place. If the
180 ** input does not begin with a quote character, then this routine
181 ** is a no-op.
183 ** Examples:
185 ** "abc" becomes abc
186 ** 'xyz' becomes xyz
187 ** [pqr] becomes pqr
188 ** `mno` becomes mno
190 void sqlite3Fts5Dequote(char *z){
191 char quote; /* Quote character (if any ) */
193 assert( 0==fts5_iswhitespace(z[0]) );
194 quote = z[0];
195 if( quote=='[' || quote=='\'' || quote=='"' || quote=='`' ){
196 fts5Dequote(z);
201 struct Fts5Enum {
202 const char *zName;
203 int eVal;
205 typedef struct Fts5Enum Fts5Enum;
207 static int fts5ConfigSetEnum(
208 const Fts5Enum *aEnum,
209 const char *zEnum,
210 int *peVal
212 int nEnum = (int)strlen(zEnum);
213 int i;
214 int iVal = -1;
216 for(i=0; aEnum[i].zName; i++){
217 if( sqlite3_strnicmp(aEnum[i].zName, zEnum, nEnum)==0 ){
218 if( iVal>=0 ) return SQLITE_ERROR;
219 iVal = aEnum[i].eVal;
223 *peVal = iVal;
224 return iVal<0 ? SQLITE_ERROR : SQLITE_OK;
228 ** Parse a "special" CREATE VIRTUAL TABLE directive and update
229 ** configuration object pConfig as appropriate.
231 ** If successful, object pConfig is updated and SQLITE_OK returned. If
232 ** an error occurs, an SQLite error code is returned and an error message
233 ** may be left in *pzErr. It is the responsibility of the caller to
234 ** eventually free any such error message using sqlite3_free().
236 static int fts5ConfigParseSpecial(
237 Fts5Global *pGlobal,
238 Fts5Config *pConfig, /* Configuration object to update */
239 const char *zCmd, /* Special command to parse */
240 const char *zArg, /* Argument to parse */
241 char **pzErr /* OUT: Error message */
243 int rc = SQLITE_OK;
244 int nCmd = (int)strlen(zCmd);
245 if( sqlite3_strnicmp("prefix", zCmd, nCmd)==0 ){
246 const int nByte = sizeof(int) * FTS5_MAX_PREFIX_INDEXES;
247 const char *p;
248 int bFirst = 1;
249 if( pConfig->aPrefix==0 ){
250 pConfig->aPrefix = sqlite3Fts5MallocZero(&rc, nByte);
251 if( rc ) return rc;
254 p = zArg;
255 while( 1 ){
256 int nPre = 0;
258 while( p[0]==' ' ) p++;
259 if( bFirst==0 && p[0]==',' ){
260 p++;
261 while( p[0]==' ' ) p++;
262 }else if( p[0]=='\0' ){
263 break;
265 if( p[0]<'0' || p[0]>'9' ){
266 *pzErr = sqlite3_mprintf("malformed prefix=... directive");
267 rc = SQLITE_ERROR;
268 break;
271 if( pConfig->nPrefix==FTS5_MAX_PREFIX_INDEXES ){
272 *pzErr = sqlite3_mprintf(
273 "too many prefix indexes (max %d)", FTS5_MAX_PREFIX_INDEXES
275 rc = SQLITE_ERROR;
276 break;
279 while( p[0]>='0' && p[0]<='9' && nPre<1000 ){
280 nPre = nPre*10 + (p[0] - '0');
281 p++;
284 if( nPre<=0 || nPre>=1000 ){
285 *pzErr = sqlite3_mprintf("prefix length out of range (max 999)");
286 rc = SQLITE_ERROR;
287 break;
290 pConfig->aPrefix[pConfig->nPrefix] = nPre;
291 pConfig->nPrefix++;
292 bFirst = 0;
294 assert( pConfig->nPrefix<=FTS5_MAX_PREFIX_INDEXES );
295 return rc;
298 if( sqlite3_strnicmp("tokenize", zCmd, nCmd)==0 ){
299 const char *p = (const char*)zArg;
300 sqlite3_int64 nArg = strlen(zArg) + 1;
301 char **azArg = sqlite3Fts5MallocZero(&rc, sizeof(char*) * nArg);
302 char *pDel = sqlite3Fts5MallocZero(&rc, nArg * 2);
303 char *pSpace = pDel;
305 if( azArg && pSpace ){
306 if( pConfig->pTok ){
307 *pzErr = sqlite3_mprintf("multiple tokenize=... directives");
308 rc = SQLITE_ERROR;
309 }else{
310 for(nArg=0; p && *p; nArg++){
311 const char *p2 = fts5ConfigSkipWhitespace(p);
312 if( *p2=='\'' ){
313 p = fts5ConfigSkipLiteral(p2);
314 }else{
315 p = fts5ConfigSkipBareword(p2);
317 if( p ){
318 memcpy(pSpace, p2, p-p2);
319 azArg[nArg] = pSpace;
320 sqlite3Fts5Dequote(pSpace);
321 pSpace += (p - p2) + 1;
322 p = fts5ConfigSkipWhitespace(p);
325 if( p==0 ){
326 *pzErr = sqlite3_mprintf("parse error in tokenize directive");
327 rc = SQLITE_ERROR;
328 }else{
329 rc = sqlite3Fts5GetTokenizer(pGlobal,
330 (const char**)azArg, (int)nArg, pConfig,
331 pzErr
337 sqlite3_free(azArg);
338 sqlite3_free(pDel);
339 return rc;
342 if( sqlite3_strnicmp("content", zCmd, nCmd)==0 ){
343 if( pConfig->eContent!=FTS5_CONTENT_NORMAL ){
344 *pzErr = sqlite3_mprintf("multiple content=... directives");
345 rc = SQLITE_ERROR;
346 }else{
347 if( zArg[0] ){
348 pConfig->eContent = FTS5_CONTENT_EXTERNAL;
349 pConfig->zContent = sqlite3Fts5Mprintf(&rc, "%Q.%Q", pConfig->zDb,zArg);
350 }else{
351 pConfig->eContent = FTS5_CONTENT_NONE;
354 return rc;
357 if( sqlite3_strnicmp("contentless_delete", zCmd, nCmd)==0 ){
358 if( (zArg[0]!='0' && zArg[0]!='1') || zArg[1]!='\0' ){
359 *pzErr = sqlite3_mprintf("malformed contentless_delete=... directive");
360 rc = SQLITE_ERROR;
361 }else{
362 pConfig->bContentlessDelete = (zArg[0]=='1');
364 return rc;
367 if( sqlite3_strnicmp("content_rowid", zCmd, nCmd)==0 ){
368 if( pConfig->zContentRowid ){
369 *pzErr = sqlite3_mprintf("multiple content_rowid=... directives");
370 rc = SQLITE_ERROR;
371 }else{
372 pConfig->zContentRowid = sqlite3Fts5Strndup(&rc, zArg, -1);
374 return rc;
377 if( sqlite3_strnicmp("columnsize", zCmd, nCmd)==0 ){
378 if( (zArg[0]!='0' && zArg[0]!='1') || zArg[1]!='\0' ){
379 *pzErr = sqlite3_mprintf("malformed columnsize=... directive");
380 rc = SQLITE_ERROR;
381 }else{
382 pConfig->bColumnsize = (zArg[0]=='1');
384 return rc;
387 if( sqlite3_strnicmp("detail", zCmd, nCmd)==0 ){
388 const Fts5Enum aDetail[] = {
389 { "none", FTS5_DETAIL_NONE },
390 { "full", FTS5_DETAIL_FULL },
391 { "columns", FTS5_DETAIL_COLUMNS },
392 { 0, 0 }
395 if( (rc = fts5ConfigSetEnum(aDetail, zArg, &pConfig->eDetail)) ){
396 *pzErr = sqlite3_mprintf("malformed detail=... directive");
398 return rc;
401 if( sqlite3_strnicmp("tokendata", zCmd, nCmd)==0 ){
402 if( (zArg[0]!='0' && zArg[0]!='1') || zArg[1]!='\0' ){
403 *pzErr = sqlite3_mprintf("malformed tokendata=... directive");
404 rc = SQLITE_ERROR;
405 }else{
406 pConfig->bTokendata = (zArg[0]=='1');
408 return rc;
411 *pzErr = sqlite3_mprintf("unrecognized option: \"%.*s\"", nCmd, zCmd);
412 return SQLITE_ERROR;
416 ** Allocate an instance of the default tokenizer ("simple") at
417 ** Fts5Config.pTokenizer. Return SQLITE_OK if successful, or an SQLite error
418 ** code if an error occurs.
420 static int fts5ConfigDefaultTokenizer(Fts5Global *pGlobal, Fts5Config *pConfig){
421 assert( pConfig->pTok==0 && pConfig->pTokApi==0 );
422 return sqlite3Fts5GetTokenizer(pGlobal, 0, 0, pConfig, 0);
426 ** Gobble up the first bareword or quoted word from the input buffer zIn.
427 ** Return a pointer to the character immediately following the last in
428 ** the gobbled word if successful, or a NULL pointer otherwise (failed
429 ** to find close-quote character).
431 ** Before returning, set pzOut to point to a new buffer containing a
432 ** nul-terminated, dequoted copy of the gobbled word. If the word was
433 ** quoted, *pbQuoted is also set to 1 before returning.
435 ** If *pRc is other than SQLITE_OK when this function is called, it is
436 ** a no-op (NULL is returned). Otherwise, if an OOM occurs within this
437 ** function, *pRc is set to SQLITE_NOMEM before returning. *pRc is *not*
438 ** set if a parse error (failed to find close quote) occurs.
440 static const char *fts5ConfigGobbleWord(
441 int *pRc, /* IN/OUT: Error code */
442 const char *zIn, /* Buffer to gobble string/bareword from */
443 char **pzOut, /* OUT: malloc'd buffer containing str/bw */
444 int *pbQuoted /* OUT: Set to true if dequoting required */
446 const char *zRet = 0;
448 sqlite3_int64 nIn = strlen(zIn);
449 char *zOut = sqlite3_malloc64(nIn+1);
451 assert( *pRc==SQLITE_OK );
452 *pbQuoted = 0;
453 *pzOut = 0;
455 if( zOut==0 ){
456 *pRc = SQLITE_NOMEM;
457 }else{
458 memcpy(zOut, zIn, (size_t)(nIn+1));
459 if( fts5_isopenquote(zOut[0]) ){
460 int ii = fts5Dequote(zOut);
461 zRet = &zIn[ii];
462 *pbQuoted = 1;
463 }else{
464 zRet = fts5ConfigSkipBareword(zIn);
465 if( zRet ){
466 zOut[zRet-zIn] = '\0';
471 if( zRet==0 ){
472 sqlite3_free(zOut);
473 }else{
474 *pzOut = zOut;
477 return zRet;
480 static int fts5ConfigParseColumn(
481 Fts5Config *p,
482 char *zCol,
483 char *zArg,
484 char **pzErr
486 int rc = SQLITE_OK;
487 if( 0==sqlite3_stricmp(zCol, FTS5_RANK_NAME)
488 || 0==sqlite3_stricmp(zCol, FTS5_ROWID_NAME)
490 *pzErr = sqlite3_mprintf("reserved fts5 column name: %s", zCol);
491 rc = SQLITE_ERROR;
492 }else if( zArg ){
493 if( 0==sqlite3_stricmp(zArg, "unindexed") ){
494 p->abUnindexed[p->nCol] = 1;
495 }else{
496 *pzErr = sqlite3_mprintf("unrecognized column option: %s", zArg);
497 rc = SQLITE_ERROR;
501 p->azCol[p->nCol++] = zCol;
502 return rc;
506 ** Populate the Fts5Config.zContentExprlist string.
508 static int fts5ConfigMakeExprlist(Fts5Config *p){
509 int i;
510 int rc = SQLITE_OK;
511 Fts5Buffer buf = {0, 0, 0};
513 sqlite3Fts5BufferAppendPrintf(&rc, &buf, "T.%Q", p->zContentRowid);
514 if( p->eContent!=FTS5_CONTENT_NONE ){
515 for(i=0; i<p->nCol; i++){
516 if( p->eContent==FTS5_CONTENT_EXTERNAL ){
517 sqlite3Fts5BufferAppendPrintf(&rc, &buf, ", T.%Q", p->azCol[i]);
518 }else{
519 sqlite3Fts5BufferAppendPrintf(&rc, &buf, ", T.c%d", i);
524 assert( p->zContentExprlist==0 );
525 p->zContentExprlist = (char*)buf.p;
526 return rc;
530 ** Arguments nArg/azArg contain the string arguments passed to the xCreate
531 ** or xConnect method of the virtual table. This function attempts to
532 ** allocate an instance of Fts5Config containing the results of parsing
533 ** those arguments.
535 ** If successful, SQLITE_OK is returned and *ppOut is set to point to the
536 ** new Fts5Config object. If an error occurs, an SQLite error code is
537 ** returned, *ppOut is set to NULL and an error message may be left in
538 ** *pzErr. It is the responsibility of the caller to eventually free any
539 ** such error message using sqlite3_free().
541 int sqlite3Fts5ConfigParse(
542 Fts5Global *pGlobal,
543 sqlite3 *db,
544 int nArg, /* Number of arguments */
545 const char **azArg, /* Array of nArg CREATE VIRTUAL TABLE args */
546 Fts5Config **ppOut, /* OUT: Results of parse */
547 char **pzErr /* OUT: Error message */
549 int rc = SQLITE_OK; /* Return code */
550 Fts5Config *pRet; /* New object to return */
551 int i;
552 sqlite3_int64 nByte;
554 *ppOut = pRet = (Fts5Config*)sqlite3_malloc(sizeof(Fts5Config));
555 if( pRet==0 ) return SQLITE_NOMEM;
556 memset(pRet, 0, sizeof(Fts5Config));
557 pRet->db = db;
558 pRet->iCookie = -1;
560 nByte = nArg * (sizeof(char*) + sizeof(u8));
561 pRet->azCol = (char**)sqlite3Fts5MallocZero(&rc, nByte);
562 pRet->abUnindexed = pRet->azCol ? (u8*)&pRet->azCol[nArg] : 0;
563 pRet->zDb = sqlite3Fts5Strndup(&rc, azArg[1], -1);
564 pRet->zName = sqlite3Fts5Strndup(&rc, azArg[2], -1);
565 pRet->bColumnsize = 1;
566 pRet->eDetail = FTS5_DETAIL_FULL;
567 #ifdef SQLITE_DEBUG
568 pRet->bPrefixIndex = 1;
569 #endif
570 if( rc==SQLITE_OK && sqlite3_stricmp(pRet->zName, FTS5_RANK_NAME)==0 ){
571 *pzErr = sqlite3_mprintf("reserved fts5 table name: %s", pRet->zName);
572 rc = SQLITE_ERROR;
575 assert( (pRet->abUnindexed && pRet->azCol) || rc!=SQLITE_OK );
576 for(i=3; rc==SQLITE_OK && i<nArg; i++){
577 const char *zOrig = azArg[i];
578 const char *z;
579 char *zOne = 0;
580 char *zTwo = 0;
581 int bOption = 0;
582 int bMustBeCol = 0;
584 z = fts5ConfigGobbleWord(&rc, zOrig, &zOne, &bMustBeCol);
585 z = fts5ConfigSkipWhitespace(z);
586 if( z && *z=='=' ){
587 bOption = 1;
588 assert( zOne!=0 );
589 z++;
590 if( bMustBeCol ) z = 0;
592 z = fts5ConfigSkipWhitespace(z);
593 if( z && z[0] ){
594 int bDummy;
595 z = fts5ConfigGobbleWord(&rc, z, &zTwo, &bDummy);
596 if( z && z[0] ) z = 0;
599 if( rc==SQLITE_OK ){
600 if( z==0 ){
601 *pzErr = sqlite3_mprintf("parse error in \"%s\"", zOrig);
602 rc = SQLITE_ERROR;
603 }else{
604 if( bOption ){
605 rc = fts5ConfigParseSpecial(pGlobal, pRet,
606 ALWAYS(zOne)?zOne:"",
607 zTwo?zTwo:"",
608 pzErr
610 }else{
611 rc = fts5ConfigParseColumn(pRet, zOne, zTwo, pzErr);
612 zOne = 0;
617 sqlite3_free(zOne);
618 sqlite3_free(zTwo);
621 /* We only allow contentless_delete=1 if the table is indeed contentless. */
622 if( rc==SQLITE_OK
623 && pRet->bContentlessDelete
624 && pRet->eContent!=FTS5_CONTENT_NONE
626 *pzErr = sqlite3_mprintf(
627 "contentless_delete=1 requires a contentless table"
629 rc = SQLITE_ERROR;
632 /* We only allow contentless_delete=1 if columnsize=0 is not present.
634 ** This restriction may be removed at some point.
636 if( rc==SQLITE_OK && pRet->bContentlessDelete && pRet->bColumnsize==0 ){
637 *pzErr = sqlite3_mprintf(
638 "contentless_delete=1 is incompatible with columnsize=0"
640 rc = SQLITE_ERROR;
643 /* If a tokenizer= option was successfully parsed, the tokenizer has
644 ** already been allocated. Otherwise, allocate an instance of the default
645 ** tokenizer (unicode61) now. */
646 if( rc==SQLITE_OK && pRet->pTok==0 ){
647 rc = fts5ConfigDefaultTokenizer(pGlobal, pRet);
650 /* If no zContent option was specified, fill in the default values. */
651 if( rc==SQLITE_OK && pRet->zContent==0 ){
652 const char *zTail = 0;
653 assert( pRet->eContent==FTS5_CONTENT_NORMAL
654 || pRet->eContent==FTS5_CONTENT_NONE
656 if( pRet->eContent==FTS5_CONTENT_NORMAL ){
657 zTail = "content";
658 }else if( pRet->bColumnsize ){
659 zTail = "docsize";
662 if( zTail ){
663 pRet->zContent = sqlite3Fts5Mprintf(
664 &rc, "%Q.'%q_%s'", pRet->zDb, pRet->zName, zTail
669 if( rc==SQLITE_OK && pRet->zContentRowid==0 ){
670 pRet->zContentRowid = sqlite3Fts5Strndup(&rc, "rowid", -1);
673 /* Formulate the zContentExprlist text */
674 if( rc==SQLITE_OK ){
675 rc = fts5ConfigMakeExprlist(pRet);
678 if( rc!=SQLITE_OK ){
679 sqlite3Fts5ConfigFree(pRet);
680 *ppOut = 0;
682 return rc;
686 ** Free the configuration object passed as the only argument.
688 void sqlite3Fts5ConfigFree(Fts5Config *pConfig){
689 if( pConfig ){
690 int i;
691 if( pConfig->pTok ){
692 pConfig->pTokApi->xDelete(pConfig->pTok);
694 sqlite3_free(pConfig->zDb);
695 sqlite3_free(pConfig->zName);
696 for(i=0; i<pConfig->nCol; i++){
697 sqlite3_free(pConfig->azCol[i]);
699 sqlite3_free(pConfig->azCol);
700 sqlite3_free(pConfig->aPrefix);
701 sqlite3_free(pConfig->zRank);
702 sqlite3_free(pConfig->zRankArgs);
703 sqlite3_free(pConfig->zContent);
704 sqlite3_free(pConfig->zContentRowid);
705 sqlite3_free(pConfig->zContentExprlist);
706 sqlite3_free(pConfig);
711 ** Call sqlite3_declare_vtab() based on the contents of the configuration
712 ** object passed as the only argument. Return SQLITE_OK if successful, or
713 ** an SQLite error code if an error occurs.
715 int sqlite3Fts5ConfigDeclareVtab(Fts5Config *pConfig){
716 int i;
717 int rc = SQLITE_OK;
718 char *zSql;
720 zSql = sqlite3Fts5Mprintf(&rc, "CREATE TABLE x(");
721 for(i=0; zSql && i<pConfig->nCol; i++){
722 const char *zSep = (i==0?"":", ");
723 zSql = sqlite3Fts5Mprintf(&rc, "%z%s%Q", zSql, zSep, pConfig->azCol[i]);
725 zSql = sqlite3Fts5Mprintf(&rc, "%z, %Q HIDDEN, %s HIDDEN)",
726 zSql, pConfig->zName, FTS5_RANK_NAME
729 assert( zSql || rc==SQLITE_NOMEM );
730 if( zSql ){
731 rc = sqlite3_declare_vtab(pConfig->db, zSql);
732 sqlite3_free(zSql);
735 return rc;
739 ** Tokenize the text passed via the second and third arguments.
741 ** The callback is invoked once for each token in the input text. The
742 ** arguments passed to it are, in order:
744 ** void *pCtx // Copy of 4th argument to sqlite3Fts5Tokenize()
745 ** const char *pToken // Pointer to buffer containing token
746 ** int nToken // Size of token in bytes
747 ** int iStart // Byte offset of start of token within input text
748 ** int iEnd // Byte offset of end of token within input text
749 ** int iPos // Position of token in input (first token is 0)
751 ** If the callback returns a non-zero value the tokenization is abandoned
752 ** and no further callbacks are issued.
754 ** This function returns SQLITE_OK if successful or an SQLite error code
755 ** if an error occurs. If the tokenization was abandoned early because
756 ** the callback returned SQLITE_DONE, this is not an error and this function
757 ** still returns SQLITE_OK. Or, if the tokenization was abandoned early
758 ** because the callback returned another non-zero value, it is assumed
759 ** to be an SQLite error code and returned to the caller.
761 int sqlite3Fts5Tokenize(
762 Fts5Config *pConfig, /* FTS5 Configuration object */
763 int flags, /* FTS5_TOKENIZE_* flags */
764 const char *pText, int nText, /* Text to tokenize */
765 void *pCtx, /* Context passed to xToken() */
766 int (*xToken)(void*, int, const char*, int, int, int) /* Callback */
768 if( pText==0 ) return SQLITE_OK;
769 return pConfig->pTokApi->xTokenize(
770 pConfig->pTok, pCtx, flags, pText, nText, xToken
775 ** Argument pIn points to the first character in what is expected to be
776 ** a comma-separated list of SQL literals followed by a ')' character.
777 ** If it actually is this, return a pointer to the ')'. Otherwise, return
778 ** NULL to indicate a parse error.
780 static const char *fts5ConfigSkipArgs(const char *pIn){
781 const char *p = pIn;
783 while( 1 ){
784 p = fts5ConfigSkipWhitespace(p);
785 p = fts5ConfigSkipLiteral(p);
786 p = fts5ConfigSkipWhitespace(p);
787 if( p==0 || *p==')' ) break;
788 if( *p!=',' ){
789 p = 0;
790 break;
792 p++;
795 return p;
799 ** Parameter zIn contains a rank() function specification. The format of
800 ** this is:
802 ** + Bareword (function name)
803 ** + Open parenthesis - "("
804 ** + Zero or more SQL literals in a comma separated list
805 ** + Close parenthesis - ")"
807 int sqlite3Fts5ConfigParseRank(
808 const char *zIn, /* Input string */
809 char **pzRank, /* OUT: Rank function name */
810 char **pzRankArgs /* OUT: Rank function arguments */
812 const char *p = zIn;
813 const char *pRank;
814 char *zRank = 0;
815 char *zRankArgs = 0;
816 int rc = SQLITE_OK;
818 *pzRank = 0;
819 *pzRankArgs = 0;
821 if( p==0 ){
822 rc = SQLITE_ERROR;
823 }else{
824 p = fts5ConfigSkipWhitespace(p);
825 pRank = p;
826 p = fts5ConfigSkipBareword(p);
828 if( p ){
829 zRank = sqlite3Fts5MallocZero(&rc, 1 + p - pRank);
830 if( zRank ) memcpy(zRank, pRank, p-pRank);
831 }else{
832 rc = SQLITE_ERROR;
835 if( rc==SQLITE_OK ){
836 p = fts5ConfigSkipWhitespace(p);
837 if( *p!='(' ) rc = SQLITE_ERROR;
838 p++;
840 if( rc==SQLITE_OK ){
841 const char *pArgs;
842 p = fts5ConfigSkipWhitespace(p);
843 pArgs = p;
844 if( *p!=')' ){
845 p = fts5ConfigSkipArgs(p);
846 if( p==0 ){
847 rc = SQLITE_ERROR;
848 }else{
849 zRankArgs = sqlite3Fts5MallocZero(&rc, 1 + p - pArgs);
850 if( zRankArgs ) memcpy(zRankArgs, pArgs, p-pArgs);
856 if( rc!=SQLITE_OK ){
857 sqlite3_free(zRank);
858 assert( zRankArgs==0 );
859 }else{
860 *pzRank = zRank;
861 *pzRankArgs = zRankArgs;
863 return rc;
866 int sqlite3Fts5ConfigSetValue(
867 Fts5Config *pConfig,
868 const char *zKey,
869 sqlite3_value *pVal,
870 int *pbBadkey
872 int rc = SQLITE_OK;
874 if( 0==sqlite3_stricmp(zKey, "pgsz") ){
875 int pgsz = 0;
876 if( SQLITE_INTEGER==sqlite3_value_numeric_type(pVal) ){
877 pgsz = sqlite3_value_int(pVal);
879 if( pgsz<32 || pgsz>FTS5_MAX_PAGE_SIZE ){
880 *pbBadkey = 1;
881 }else{
882 pConfig->pgsz = pgsz;
886 else if( 0==sqlite3_stricmp(zKey, "hashsize") ){
887 int nHashSize = -1;
888 if( SQLITE_INTEGER==sqlite3_value_numeric_type(pVal) ){
889 nHashSize = sqlite3_value_int(pVal);
891 if( nHashSize<=0 ){
892 *pbBadkey = 1;
893 }else{
894 pConfig->nHashSize = nHashSize;
898 else if( 0==sqlite3_stricmp(zKey, "automerge") ){
899 int nAutomerge = -1;
900 if( SQLITE_INTEGER==sqlite3_value_numeric_type(pVal) ){
901 nAutomerge = sqlite3_value_int(pVal);
903 if( nAutomerge<0 || nAutomerge>64 ){
904 *pbBadkey = 1;
905 }else{
906 if( nAutomerge==1 ) nAutomerge = FTS5_DEFAULT_AUTOMERGE;
907 pConfig->nAutomerge = nAutomerge;
911 else if( 0==sqlite3_stricmp(zKey, "usermerge") ){
912 int nUsermerge = -1;
913 if( SQLITE_INTEGER==sqlite3_value_numeric_type(pVal) ){
914 nUsermerge = sqlite3_value_int(pVal);
916 if( nUsermerge<2 || nUsermerge>16 ){
917 *pbBadkey = 1;
918 }else{
919 pConfig->nUsermerge = nUsermerge;
923 else if( 0==sqlite3_stricmp(zKey, "crisismerge") ){
924 int nCrisisMerge = -1;
925 if( SQLITE_INTEGER==sqlite3_value_numeric_type(pVal) ){
926 nCrisisMerge = sqlite3_value_int(pVal);
928 if( nCrisisMerge<0 ){
929 *pbBadkey = 1;
930 }else{
931 if( nCrisisMerge<=1 ) nCrisisMerge = FTS5_DEFAULT_CRISISMERGE;
932 if( nCrisisMerge>=FTS5_MAX_SEGMENT ) nCrisisMerge = FTS5_MAX_SEGMENT-1;
933 pConfig->nCrisisMerge = nCrisisMerge;
937 else if( 0==sqlite3_stricmp(zKey, "deletemerge") ){
938 int nVal = -1;
939 if( SQLITE_INTEGER==sqlite3_value_numeric_type(pVal) ){
940 nVal = sqlite3_value_int(pVal);
941 }else{
942 *pbBadkey = 1;
944 if( nVal<0 ) nVal = FTS5_DEFAULT_DELETE_AUTOMERGE;
945 if( nVal>100 ) nVal = 0;
946 pConfig->nDeleteMerge = nVal;
949 else if( 0==sqlite3_stricmp(zKey, "rank") ){
950 const char *zIn = (const char*)sqlite3_value_text(pVal);
951 char *zRank;
952 char *zRankArgs;
953 rc = sqlite3Fts5ConfigParseRank(zIn, &zRank, &zRankArgs);
954 if( rc==SQLITE_OK ){
955 sqlite3_free(pConfig->zRank);
956 sqlite3_free(pConfig->zRankArgs);
957 pConfig->zRank = zRank;
958 pConfig->zRankArgs = zRankArgs;
959 }else if( rc==SQLITE_ERROR ){
960 rc = SQLITE_OK;
961 *pbBadkey = 1;
965 else if( 0==sqlite3_stricmp(zKey, "secure-delete") ){
966 int bVal = -1;
967 if( SQLITE_INTEGER==sqlite3_value_numeric_type(pVal) ){
968 bVal = sqlite3_value_int(pVal);
970 if( bVal<0 ){
971 *pbBadkey = 1;
972 }else{
973 pConfig->bSecureDelete = (bVal ? 1 : 0);
975 }else{
976 *pbBadkey = 1;
978 return rc;
982 ** Load the contents of the %_config table into memory.
984 int sqlite3Fts5ConfigLoad(Fts5Config *pConfig, int iCookie){
985 const char *zSelect = "SELECT k, v FROM %Q.'%q_config'";
986 char *zSql;
987 sqlite3_stmt *p = 0;
988 int rc = SQLITE_OK;
989 int iVersion = 0;
991 /* Set default values */
992 pConfig->pgsz = FTS5_DEFAULT_PAGE_SIZE;
993 pConfig->nAutomerge = FTS5_DEFAULT_AUTOMERGE;
994 pConfig->nUsermerge = FTS5_DEFAULT_USERMERGE;
995 pConfig->nCrisisMerge = FTS5_DEFAULT_CRISISMERGE;
996 pConfig->nHashSize = FTS5_DEFAULT_HASHSIZE;
997 pConfig->nDeleteMerge = FTS5_DEFAULT_DELETE_AUTOMERGE;
999 zSql = sqlite3Fts5Mprintf(&rc, zSelect, pConfig->zDb, pConfig->zName);
1000 if( zSql ){
1001 rc = sqlite3_prepare_v2(pConfig->db, zSql, -1, &p, 0);
1002 sqlite3_free(zSql);
1005 assert( rc==SQLITE_OK || p==0 );
1006 if( rc==SQLITE_OK ){
1007 while( SQLITE_ROW==sqlite3_step(p) ){
1008 const char *zK = (const char*)sqlite3_column_text(p, 0);
1009 sqlite3_value *pVal = sqlite3_column_value(p, 1);
1010 if( 0==sqlite3_stricmp(zK, "version") ){
1011 iVersion = sqlite3_value_int(pVal);
1012 }else{
1013 int bDummy = 0;
1014 sqlite3Fts5ConfigSetValue(pConfig, zK, pVal, &bDummy);
1017 rc = sqlite3_finalize(p);
1020 if( rc==SQLITE_OK
1021 && iVersion!=FTS5_CURRENT_VERSION
1022 && iVersion!=FTS5_CURRENT_VERSION_SECUREDELETE
1024 rc = SQLITE_ERROR;
1025 if( pConfig->pzErrmsg ){
1026 assert( 0==*pConfig->pzErrmsg );
1027 *pConfig->pzErrmsg = sqlite3_mprintf("invalid fts5 file format "
1028 "(found %d, expected %d or %d) - run 'rebuild'",
1029 iVersion, FTS5_CURRENT_VERSION, FTS5_CURRENT_VERSION_SECUREDELETE
1032 }else{
1033 pConfig->iVersion = iVersion;
1036 if( rc==SQLITE_OK ){
1037 pConfig->iCookie = iCookie;
1039 return rc;