4 ** The author disclaims copyright to this source code. In place of
5 ** a legal notice, here is a blessing:
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.
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
){
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
){
46 while( fts5_iswhitespace(*p
) ){ 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
){
58 while ( sqlite3Fts5IsBareword(*p
) ) p
++;
63 static int fts5_isdigit(char a
){
64 return (a
>='0' && a
<='9');
69 static const char *fts5ConfigSkipLiteral(const char *pIn
){
73 if( sqlite3_strnicmp("null", p
, 4)==0 ){
84 while( (*p
>='a' && *p
<='f')
85 || (*p
>='A' && *p
<='F')
86 || (*p
>='0' && *p
<='9')
90 if( *p
=='\'' && 0==((p
-pIn
)%2) ){
105 if( *p
!='\'' ) break;
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]) ){
122 while( fts5_isdigit(*p
) ) 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
145 static int fts5Dequote(char *z
){
151 /* Set stack variable q to the close-quote character */
152 assert( q
=='[' || q
=='\'' || q
=='"' || q
=='`' );
153 if( q
=='[' ) q
= ']';
158 /* Character iIn was the close quote. */
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. */
169 z
[iOut
++] = z
[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
190 void sqlite3Fts5Dequote(char *z
){
191 char quote
; /* Quote character (if any ) */
193 assert( 0==fts5_iswhitespace(z
[0]) );
195 if( quote
=='[' || quote
=='\'' || quote
=='"' || quote
=='`' ){
205 typedef struct Fts5Enum Fts5Enum
;
207 static int fts5ConfigSetEnum(
208 const Fts5Enum
*aEnum
,
212 int nEnum
= (int)strlen(zEnum
);
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
;
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(
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 */
244 int nCmd
= (int)strlen(zCmd
);
245 if( sqlite3_strnicmp("prefix", zCmd
, nCmd
)==0 ){
246 const int nByte
= sizeof(int) * FTS5_MAX_PREFIX_INDEXES
;
249 if( pConfig
->aPrefix
==0 ){
250 pConfig
->aPrefix
= sqlite3Fts5MallocZero(&rc
, nByte
);
258 while( p
[0]==' ' ) p
++;
259 if( bFirst
==0 && p
[0]==',' ){
261 while( p
[0]==' ' ) p
++;
262 }else if( p
[0]=='\0' ){
265 if( p
[0]<'0' || p
[0]>'9' ){
266 *pzErr
= sqlite3_mprintf("malformed prefix=... directive");
271 if( pConfig
->nPrefix
==FTS5_MAX_PREFIX_INDEXES
){
272 *pzErr
= sqlite3_mprintf(
273 "too many prefix indexes (max %d)", FTS5_MAX_PREFIX_INDEXES
279 while( p
[0]>='0' && p
[0]<='9' && nPre
<1000 ){
280 nPre
= nPre
*10 + (p
[0] - '0');
284 if( nPre
<=0 || nPre
>=1000 ){
285 *pzErr
= sqlite3_mprintf("prefix length out of range (max 999)");
290 pConfig
->aPrefix
[pConfig
->nPrefix
] = nPre
;
294 assert( pConfig
->nPrefix
<=FTS5_MAX_PREFIX_INDEXES
);
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);
305 if( azArg
&& pSpace
){
307 *pzErr
= sqlite3_mprintf("multiple tokenize=... directives");
310 for(nArg
=0; p
&& *p
; nArg
++){
311 const char *p2
= fts5ConfigSkipWhitespace(p
);
313 p
= fts5ConfigSkipLiteral(p2
);
315 p
= fts5ConfigSkipBareword(p2
);
318 memcpy(pSpace
, p2
, p
-p2
);
319 azArg
[nArg
] = pSpace
;
320 sqlite3Fts5Dequote(pSpace
);
321 pSpace
+= (p
- p2
) + 1;
322 p
= fts5ConfigSkipWhitespace(p
);
326 *pzErr
= sqlite3_mprintf("parse error in tokenize directive");
329 rc
= sqlite3Fts5GetTokenizer(pGlobal
,
330 (const char**)azArg
, (int)nArg
, pConfig
,
342 if( sqlite3_strnicmp("content", zCmd
, nCmd
)==0 ){
343 if( pConfig
->eContent
!=FTS5_CONTENT_NORMAL
){
344 *pzErr
= sqlite3_mprintf("multiple content=... directives");
348 pConfig
->eContent
= FTS5_CONTENT_EXTERNAL
;
349 pConfig
->zContent
= sqlite3Fts5Mprintf(&rc
, "%Q.%Q", pConfig
->zDb
,zArg
);
351 pConfig
->eContent
= FTS5_CONTENT_NONE
;
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");
362 pConfig
->bContentlessDelete
= (zArg
[0]=='1');
367 if( sqlite3_strnicmp("content_rowid", zCmd
, nCmd
)==0 ){
368 if( pConfig
->zContentRowid
){
369 *pzErr
= sqlite3_mprintf("multiple content_rowid=... directives");
372 pConfig
->zContentRowid
= sqlite3Fts5Strndup(&rc
, zArg
, -1);
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");
382 pConfig
->bColumnsize
= (zArg
[0]=='1');
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
},
395 if( (rc
= fts5ConfigSetEnum(aDetail
, zArg
, &pConfig
->eDetail
)) ){
396 *pzErr
= sqlite3_mprintf("malformed detail=... directive");
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");
406 pConfig
->bTokendata
= (zArg
[0]=='1');
411 *pzErr
= sqlite3_mprintf("unrecognized option: \"%.*s\"", nCmd
, zCmd
);
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
);
458 memcpy(zOut
, zIn
, (size_t)(nIn
+1));
459 if( fts5_isopenquote(zOut
[0]) ){
460 int ii
= fts5Dequote(zOut
);
464 zRet
= fts5ConfigSkipBareword(zIn
);
466 zOut
[zRet
-zIn
] = '\0';
480 static int fts5ConfigParseColumn(
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
);
493 if( 0==sqlite3_stricmp(zArg
, "unindexed") ){
494 p
->abUnindexed
[p
->nCol
] = 1;
496 *pzErr
= sqlite3_mprintf("unrecognized column option: %s", zArg
);
501 p
->azCol
[p
->nCol
++] = zCol
;
506 ** Populate the Fts5Config.zContentExprlist string.
508 static int fts5ConfigMakeExprlist(Fts5Config
*p
){
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
]);
519 sqlite3Fts5BufferAppendPrintf(&rc
, &buf
, ", T.c%d", i
);
524 assert( p
->zContentExprlist
==0 );
525 p
->zContentExprlist
= (char*)buf
.p
;
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
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(
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 */
554 *ppOut
= pRet
= (Fts5Config
*)sqlite3_malloc(sizeof(Fts5Config
));
555 if( pRet
==0 ) return SQLITE_NOMEM
;
556 memset(pRet
, 0, sizeof(Fts5Config
));
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
;
568 pRet
->bPrefixIndex
= 1;
570 if( rc
==SQLITE_OK
&& sqlite3_stricmp(pRet
->zName
, FTS5_RANK_NAME
)==0 ){
571 *pzErr
= sqlite3_mprintf("reserved fts5 table name: %s", pRet
->zName
);
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
];
584 z
= fts5ConfigGobbleWord(&rc
, zOrig
, &zOne
, &bMustBeCol
);
585 z
= fts5ConfigSkipWhitespace(z
);
590 if( bMustBeCol
) z
= 0;
592 z
= fts5ConfigSkipWhitespace(z
);
595 z
= fts5ConfigGobbleWord(&rc
, z
, &zTwo
, &bDummy
);
596 if( z
&& z
[0] ) z
= 0;
601 *pzErr
= sqlite3_mprintf("parse error in \"%s\"", zOrig
);
605 rc
= fts5ConfigParseSpecial(pGlobal
, pRet
,
606 ALWAYS(zOne
)?zOne
:"",
611 rc
= fts5ConfigParseColumn(pRet
, zOne
, zTwo
, pzErr
);
621 /* We only allow contentless_delete=1 if the table is indeed contentless. */
623 && pRet
->bContentlessDelete
624 && pRet
->eContent
!=FTS5_CONTENT_NONE
626 *pzErr
= sqlite3_mprintf(
627 "contentless_delete=1 requires a contentless table"
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"
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
){
658 }else if( pRet
->bColumnsize
){
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 */
675 rc
= fts5ConfigMakeExprlist(pRet
);
679 sqlite3Fts5ConfigFree(pRet
);
686 ** Free the configuration object passed as the only argument.
688 void sqlite3Fts5ConfigFree(Fts5Config
*pConfig
){
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
){
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
);
731 rc
= sqlite3_declare_vtab(pConfig
->db
, zSql
);
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
){
784 p
= fts5ConfigSkipWhitespace(p
);
785 p
= fts5ConfigSkipLiteral(p
);
786 p
= fts5ConfigSkipWhitespace(p
);
787 if( p
==0 || *p
==')' ) break;
799 ** Parameter zIn contains a rank() function specification. The format of
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 */
824 p
= fts5ConfigSkipWhitespace(p
);
826 p
= fts5ConfigSkipBareword(p
);
829 zRank
= sqlite3Fts5MallocZero(&rc
, 1 + p
- pRank
);
830 if( zRank
) memcpy(zRank
, pRank
, p
-pRank
);
836 p
= fts5ConfigSkipWhitespace(p
);
837 if( *p
!='(' ) rc
= SQLITE_ERROR
;
842 p
= fts5ConfigSkipWhitespace(p
);
845 p
= fts5ConfigSkipArgs(p
);
849 zRankArgs
= sqlite3Fts5MallocZero(&rc
, 1 + p
- pArgs
);
850 if( zRankArgs
) memcpy(zRankArgs
, pArgs
, p
-pArgs
);
858 assert( zRankArgs
==0 );
861 *pzRankArgs
= zRankArgs
;
866 int sqlite3Fts5ConfigSetValue(
874 if( 0==sqlite3_stricmp(zKey
, "pgsz") ){
876 if( SQLITE_INTEGER
==sqlite3_value_numeric_type(pVal
) ){
877 pgsz
= sqlite3_value_int(pVal
);
879 if( pgsz
<32 || pgsz
>FTS5_MAX_PAGE_SIZE
){
882 pConfig
->pgsz
= pgsz
;
886 else if( 0==sqlite3_stricmp(zKey
, "hashsize") ){
888 if( SQLITE_INTEGER
==sqlite3_value_numeric_type(pVal
) ){
889 nHashSize
= sqlite3_value_int(pVal
);
894 pConfig
->nHashSize
= nHashSize
;
898 else if( 0==sqlite3_stricmp(zKey
, "automerge") ){
900 if( SQLITE_INTEGER
==sqlite3_value_numeric_type(pVal
) ){
901 nAutomerge
= sqlite3_value_int(pVal
);
903 if( nAutomerge
<0 || nAutomerge
>64 ){
906 if( nAutomerge
==1 ) nAutomerge
= FTS5_DEFAULT_AUTOMERGE
;
907 pConfig
->nAutomerge
= nAutomerge
;
911 else if( 0==sqlite3_stricmp(zKey
, "usermerge") ){
913 if( SQLITE_INTEGER
==sqlite3_value_numeric_type(pVal
) ){
914 nUsermerge
= sqlite3_value_int(pVal
);
916 if( nUsermerge
<2 || nUsermerge
>16 ){
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 ){
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") ){
939 if( SQLITE_INTEGER
==sqlite3_value_numeric_type(pVal
) ){
940 nVal
= sqlite3_value_int(pVal
);
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
);
953 rc
= sqlite3Fts5ConfigParseRank(zIn
, &zRank
, &zRankArgs
);
955 sqlite3_free(pConfig
->zRank
);
956 sqlite3_free(pConfig
->zRankArgs
);
957 pConfig
->zRank
= zRank
;
958 pConfig
->zRankArgs
= zRankArgs
;
959 }else if( rc
==SQLITE_ERROR
){
965 else if( 0==sqlite3_stricmp(zKey
, "secure-delete") ){
967 if( SQLITE_INTEGER
==sqlite3_value_numeric_type(pVal
) ){
968 bVal
= sqlite3_value_int(pVal
);
973 pConfig
->bSecureDelete
= (bVal
? 1 : 0);
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'";
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
);
1001 rc
= sqlite3_prepare_v2(pConfig
->db
, zSql
, -1, &p
, 0);
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
);
1014 sqlite3Fts5ConfigSetValue(pConfig
, zK
, pVal
, &bDummy
);
1017 rc
= sqlite3_finalize(p
);
1021 && iVersion
!=FTS5_CURRENT_VERSION
1022 && iVersion
!=FTS5_CURRENT_VERSION_SECUREDELETE
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
1033 pConfig
->iVersion
= iVersion
;
1036 if( rc
==SQLITE_OK
){
1037 pConfig
->iCookie
= iCookie
;