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 ******************************************************************************
17 #if defined(INCLUDE_SQLITE_TCL_H)
18 # include "sqlite_tcl.h"
21 # ifndef SQLITE_TCLAPI
22 # define SQLITE_TCLAPI
26 #ifdef SQLITE_ENABLE_FTS5
33 extern int sqlite3_fts5_may_be_corrupt
;
35 extern int sqlite3Fts5TestRegisterMatchinfo(sqlite3
*);
36 extern int sqlite3Fts5TestRegisterTok(sqlite3
*, fts5_api
*);
38 /*************************************************************************
39 ** This is a copy of the first part of the SqliteDb structure in
40 ** tclsqlite.c. We need it here so that the get_sqlite_pointer routine
41 ** can extract the sqlite3* pointer from an existing Tcl SQLite
45 extern const char *sqlite3ErrName(int);
52 ** Decode a pointer to an sqlite3 object.
54 static int f5tDbPointer(Tcl_Interp
*interp
, Tcl_Obj
*pObj
, sqlite3
**ppDb
){
57 char *z
= Tcl_GetString(pObj
);
58 if( Tcl_GetCommandInfo(interp
, z
, &cmdInfo
) ){
59 p
= (struct SqliteDb
*)cmdInfo
.objClientData
;
66 /* End of code that accesses the SqliteDb struct.
67 **************************************************************************/
69 static int f5tResultToErrorCode(const char *zRes
){
74 { SQLITE_DONE
, "SQLITE_DONE" },
75 { SQLITE_ERROR
, "SQLITE_ERROR" },
76 { SQLITE_OK
, "SQLITE_OK" },
81 for(i
=0; i
<sizeof(aErr
)/sizeof(aErr
[0]); i
++){
82 if( 0==sqlite3_stricmp(zRes
, aErr
[i
].zError
) ){
90 static int SQLITE_TCLAPI
f5tDbAndApi(
97 int rc
= f5tDbPointer(interp
, pObj
, &db
);
101 sqlite3_stmt
*pStmt
= 0;
104 rc
= sqlite3_prepare_v2(db
, "SELECT fts5(?1)", -1, &pStmt
, 0);
106 Tcl_AppendResult(interp
, "error: ", sqlite3_errmsg(db
), 0);
109 sqlite3_bind_pointer(pStmt
, 1, (void*)&pApi
, "fts5_api_ptr", 0);
112 if( sqlite3_finalize(pStmt
)!=SQLITE_OK
){
113 Tcl_AppendResult(interp
, "error: ", sqlite3_errmsg(db
), 0);
124 typedef struct F5tFunction F5tFunction
;
130 typedef struct F5tApi F5tApi
;
132 const Fts5ExtensionApi
*pApi
;
137 ** An object of this type is used with the xSetAuxdata() and xGetAuxdata()
138 ** API test wrappers. The tcl interface allows a single tcl value to be
139 ** saved using xSetAuxdata(). Instead of simply storing a pointer to the
140 ** tcl object, the code in this file wraps it in an sqlite3_malloc'd
141 ** instance of the following struct so that if the destructor is not
142 ** correctly invoked it will be reported as an SQLite memory leak.
144 typedef struct F5tAuxData F5tAuxData
;
149 static int xTokenizeCb(
152 const char *zToken
, int nToken
,
155 F5tFunction
*p
= (F5tFunction
*)pCtx
;
156 Tcl_Obj
*pEval
= Tcl_DuplicateObj(p
->pScript
);
159 Tcl_IncrRefCount(pEval
);
160 Tcl_ListObjAppendElement(p
->interp
, pEval
, Tcl_NewStringObj(zToken
, nToken
));
161 Tcl_ListObjAppendElement(p
->interp
, pEval
, Tcl_NewIntObj(iStart
));
162 Tcl_ListObjAppendElement(p
->interp
, pEval
, Tcl_NewIntObj(iEnd
));
164 rc
= Tcl_EvalObjEx(p
->interp
, pEval
, 0);
165 Tcl_DecrRefCount(pEval
);
167 rc
= f5tResultToErrorCode(Tcl_GetStringResult(p
->interp
));
173 static int SQLITE_TCLAPI
xF5tApi(void*, Tcl_Interp
*, int, Tcl_Obj
*CONST
[]);
175 static int xQueryPhraseCb(
176 const Fts5ExtensionApi
*pApi
,
180 F5tFunction
*p
= (F5tFunction
*)pCtx
;
181 static sqlite3_int64 iCmd
= 0;
190 sprintf(zCmd
, "f5t_2_%lld", iCmd
++);
191 Tcl_CreateObjCommand(p
->interp
, zCmd
, xF5tApi
, &sApi
, 0);
193 pEval
= Tcl_DuplicateObj(p
->pScript
);
194 Tcl_IncrRefCount(pEval
);
195 Tcl_ListObjAppendElement(p
->interp
, pEval
, Tcl_NewStringObj(zCmd
, -1));
196 rc
= Tcl_EvalObjEx(p
->interp
, pEval
, 0);
197 Tcl_DecrRefCount(pEval
);
198 Tcl_DeleteCommand(p
->interp
, zCmd
);
201 rc
= f5tResultToErrorCode(Tcl_GetStringResult(p
->interp
));
207 static void xSetAuxdataDestructor(void *p
){
208 F5tAuxData
*pData
= (F5tAuxData
*)p
;
209 Tcl_DecrRefCount(pData
->pObj
);
214 ** api sub-command...
218 static int SQLITE_TCLAPI
xF5tApi(
222 Tcl_Obj
*CONST objv
[]
229 { "xColumnCount", 0, "" }, /* 0 */
230 { "xRowCount", 0, "" }, /* 1 */
231 { "xColumnTotalSize", 1, "COL" }, /* 2 */
232 { "xTokenize", 2, "TEXT SCRIPT" }, /* 3 */
233 { "xPhraseCount", 0, "" }, /* 4 */
234 { "xPhraseSize", 1, "PHRASE" }, /* 5 */
235 { "xInstCount", 0, "" }, /* 6 */
236 { "xInst", 1, "IDX" }, /* 7 */
237 { "xRowid", 0, "" }, /* 8 */
238 { "xColumnText", 1, "COL" }, /* 9 */
239 { "xColumnSize", 1, "COL" }, /* 10 */
240 { "xQueryPhrase", 2, "PHRASE SCRIPT" }, /* 11 */
241 { "xSetAuxdata", 1, "VALUE" }, /* 12 */
242 { "xGetAuxdata", 1, "CLEAR" }, /* 13 */
243 { "xSetAuxdataInt", 1, "INTEGER" }, /* 14 */
244 { "xGetAuxdataInt", 1, "CLEAR" }, /* 15 */
245 { "xPhraseForeach", 4, "IPHRASE COLVAR OFFVAR SCRIPT" }, /* 16 */
246 { "xPhraseColumnForeach", 3, "IPHRASE COLVAR SCRIPT" }, /* 17 */
252 F5tApi
*p
= (F5tApi
*)clientData
;
255 Tcl_WrongNumArgs(interp
, 1, objv
, "SUB-COMMAND");
259 rc
= Tcl_GetIndexFromObjStruct(
260 interp
, objv
[1], aSub
, sizeof(aSub
[0]), "SUB-COMMAND", 0, &iSub
262 if( rc
!=TCL_OK
) return rc
;
263 if( aSub
[iSub
].nArg
!=objc
-2 ){
264 Tcl_WrongNumArgs(interp
, 1, objv
, aSub
[iSub
].zMsg
);
268 #define CASE(i,str) case i: assert( strcmp(aSub[i].zName, str)==0 );
270 CASE(0, "xColumnCount") {
272 nCol
= p
->pApi
->xColumnCount(p
->pFts
);
274 Tcl_SetObjResult(interp
, Tcl_NewIntObj(nCol
));
278 CASE(1, "xRowCount") {
280 rc
= p
->pApi
->xRowCount(p
->pFts
, &nRow
);
282 Tcl_SetObjResult(interp
, Tcl_NewWideIntObj(nRow
));
286 CASE(2, "xColumnTotalSize") {
289 if( Tcl_GetIntFromObj(interp
, objv
[2], &iCol
) ) return TCL_ERROR
;
290 rc
= p
->pApi
->xColumnTotalSize(p
->pFts
, iCol
, &nSize
);
292 Tcl_SetObjResult(interp
, Tcl_NewWideIntObj(nSize
));
296 CASE(3, "xTokenize") {
298 char *zText
= Tcl_GetStringFromObj(objv
[2], &nText
);
301 ctx
.pScript
= objv
[3];
302 rc
= p
->pApi
->xTokenize(p
->pFts
, zText
, nText
, &ctx
, xTokenizeCb
);
304 Tcl_ResetResult(interp
);
308 CASE(4, "xPhraseCount") {
310 nPhrase
= p
->pApi
->xPhraseCount(p
->pFts
);
312 Tcl_SetObjResult(interp
, Tcl_NewIntObj(nPhrase
));
316 CASE(5, "xPhraseSize") {
319 if( Tcl_GetIntFromObj(interp
, objv
[2], &iPhrase
) ){
322 sz
= p
->pApi
->xPhraseSize(p
->pFts
, iPhrase
);
324 Tcl_SetObjResult(interp
, Tcl_NewIntObj(sz
));
328 CASE(6, "xInstCount") {
330 rc
= p
->pApi
->xInstCount(p
->pFts
, &nInst
);
332 Tcl_SetObjResult(interp
, Tcl_NewIntObj(nInst
));
337 int iIdx
, ip
, ic
, io
;
338 if( Tcl_GetIntFromObj(interp
, objv
[2], &iIdx
) ){
341 rc
= p
->pApi
->xInst(p
->pFts
, iIdx
, &ip
, &ic
, &io
);
343 Tcl_Obj
*pList
= Tcl_NewObj();
344 Tcl_ListObjAppendElement(interp
, pList
, Tcl_NewIntObj(ip
));
345 Tcl_ListObjAppendElement(interp
, pList
, Tcl_NewIntObj(ic
));
346 Tcl_ListObjAppendElement(interp
, pList
, Tcl_NewIntObj(io
));
347 Tcl_SetObjResult(interp
, pList
);
352 sqlite3_int64 iRowid
= p
->pApi
->xRowid(p
->pFts
);
353 Tcl_SetObjResult(interp
, Tcl_NewWideIntObj(iRowid
));
356 CASE(9, "xColumnText") {
360 if( Tcl_GetIntFromObj(interp
, objv
[2], &iCol
) ){
363 rc
= p
->pApi
->xColumnText(p
->pFts
, iCol
, &z
, &n
);
365 Tcl_SetObjResult(interp
, Tcl_NewStringObj(z
, n
));
369 CASE(10, "xColumnSize") {
372 if( Tcl_GetIntFromObj(interp
, objv
[2], &iCol
) ){
375 rc
= p
->pApi
->xColumnSize(p
->pFts
, iCol
, &n
);
377 Tcl_SetObjResult(interp
, Tcl_NewIntObj(n
));
381 CASE(11, "xQueryPhrase") {
384 if( Tcl_GetIntFromObj(interp
, objv
[2], &iPhrase
) ){
388 ctx
.pScript
= objv
[3];
389 rc
= p
->pApi
->xQueryPhrase(p
->pFts
, iPhrase
, &ctx
, xQueryPhraseCb
);
391 Tcl_ResetResult(interp
);
395 CASE(12, "xSetAuxdata") {
396 F5tAuxData
*pData
= (F5tAuxData
*)sqlite3_malloc(sizeof(F5tAuxData
));
398 Tcl_AppendResult(interp
, "out of memory", 0);
401 pData
->pObj
= objv
[2];
402 Tcl_IncrRefCount(pData
->pObj
);
403 rc
= p
->pApi
->xSetAuxdata(p
->pFts
, pData
, xSetAuxdataDestructor
);
406 CASE(13, "xGetAuxdata") {
409 if( Tcl_GetBooleanFromObj(interp
, objv
[2], &bClear
) ){
412 pData
= (F5tAuxData
*)p
->pApi
->xGetAuxdata(p
->pFts
, bClear
);
414 Tcl_ResetResult(interp
);
416 Tcl_SetObjResult(interp
, pData
->pObj
);
418 xSetAuxdataDestructor((void*)pData
);
424 /* These two - xSetAuxdataInt and xGetAuxdataInt - are similar to the
425 ** xSetAuxdata and xGetAuxdata methods implemented above. The difference
426 ** is that they may only save an integer value as auxiliary data, and
427 ** do not specify a destructor function. */
428 CASE(14, "xSetAuxdataInt") {
430 if( Tcl_GetIntFromObj(interp
, objv
[2], &iVal
) ) return TCL_ERROR
;
431 rc
= p
->pApi
->xSetAuxdata(p
->pFts
, (void*)((char*)0 + iVal
), 0);
434 CASE(15, "xGetAuxdataInt") {
437 if( Tcl_GetBooleanFromObj(interp
, objv
[2], &bClear
) ) return TCL_ERROR
;
438 iVal
= (int)((char*)p
->pApi
->xGetAuxdata(p
->pFts
, bClear
) - (char*)0);
439 Tcl_SetObjResult(interp
, Tcl_NewIntObj(iVal
));
443 CASE(16, "xPhraseForeach") {
449 Tcl_Obj
*pScript
= objv
[5];
452 if( Tcl_GetIntFromObj(interp
, objv
[2], &iPhrase
) ) return TCL_ERROR
;
453 zColvar
= Tcl_GetString(objv
[3]);
454 zOffvar
= Tcl_GetString(objv
[4]);
456 rc
= p
->pApi
->xPhraseFirst(p
->pFts
, iPhrase
, &iter
, &iCol
, &iOff
);
458 Tcl_AppendResult(interp
, sqlite3ErrName(rc
), 0);
461 for( ;iCol
>=0; p
->pApi
->xPhraseNext(p
->pFts
, &iter
, &iCol
, &iOff
) ){
462 Tcl_SetVar2Ex(interp
, zColvar
, 0, Tcl_NewIntObj(iCol
), 0);
463 Tcl_SetVar2Ex(interp
, zOffvar
, 0, Tcl_NewIntObj(iOff
), 0);
464 rc
= Tcl_EvalObjEx(interp
, pScript
, 0);
465 if( rc
==TCL_CONTINUE
) rc
= TCL_OK
;
467 if( rc
==TCL_BREAK
) rc
= TCL_OK
;
475 CASE(17, "xPhraseColumnForeach") {
479 Tcl_Obj
*pScript
= objv
[4];
482 if( Tcl_GetIntFromObj(interp
, objv
[2], &iPhrase
) ) return TCL_ERROR
;
483 zColvar
= Tcl_GetString(objv
[3]);
485 rc
= p
->pApi
->xPhraseFirstColumn(p
->pFts
, iPhrase
, &iter
, &iCol
);
487 Tcl_SetResult(interp
, (char*)sqlite3ErrName(rc
), TCL_VOLATILE
);
490 for( ; iCol
>=0; p
->pApi
->xPhraseNextColumn(p
->pFts
, &iter
, &iCol
)){
491 Tcl_SetVar2Ex(interp
, zColvar
, 0, Tcl_NewIntObj(iCol
), 0);
492 rc
= Tcl_EvalObjEx(interp
, pScript
, 0);
493 if( rc
==TCL_CONTINUE
) rc
= TCL_OK
;
495 if( rc
==TCL_BREAK
) rc
= TCL_OK
;
510 Tcl_SetResult(interp
, (char*)sqlite3ErrName(rc
), TCL_VOLATILE
);
517 static void xF5tFunction(
518 const Fts5ExtensionApi
*pApi
, /* API offered by current FTS version */
519 Fts5Context
*pFts
, /* First arg to pass to pApi functions */
520 sqlite3_context
*pCtx
, /* Context for returning result/error */
521 int nVal
, /* Number of values in apVal[] array */
522 sqlite3_value
**apVal
/* Array of trailing arguments */
524 F5tFunction
*p
= (F5tFunction
*)pApi
->xUserData(pFts
);
525 Tcl_Obj
*pEval
; /* Script to evaluate */
529 static sqlite3_int64 iCmd
= 0;
535 sprintf(zCmd
, "f5t_%lld", iCmd
++);
536 Tcl_CreateObjCommand(p
->interp
, zCmd
, xF5tApi
, &sApi
, 0);
537 pEval
= Tcl_DuplicateObj(p
->pScript
);
538 Tcl_IncrRefCount(pEval
);
539 Tcl_ListObjAppendElement(p
->interp
, pEval
, Tcl_NewStringObj(zCmd
, -1));
541 for(i
=0; i
<nVal
; i
++){
543 switch( sqlite3_value_type(apVal
[i
]) ){
545 pObj
= Tcl_NewStringObj((const char*)sqlite3_value_text(apVal
[i
]), -1);
548 pObj
= Tcl_NewByteArrayObj(
549 sqlite3_value_blob(apVal
[i
]), sqlite3_value_bytes(apVal
[i
])
553 pObj
= Tcl_NewWideIntObj(sqlite3_value_int64(apVal
[i
]));
556 pObj
= Tcl_NewDoubleObj(sqlite3_value_double(apVal
[i
]));
562 Tcl_ListObjAppendElement(p
->interp
, pEval
, pObj
);
565 rc
= Tcl_EvalObjEx(p
->interp
, pEval
, TCL_GLOBAL_ONLY
);
566 Tcl_DecrRefCount(pEval
);
567 Tcl_DeleteCommand(p
->interp
, zCmd
);
570 sqlite3_result_error(pCtx
, Tcl_GetStringResult(p
->interp
), -1);
572 Tcl_Obj
*pVar
= Tcl_GetObjResult(p
->interp
);
574 const char *zType
= (pVar
->typePtr
? pVar
->typePtr
->name
: "");
576 if( c
=='b' && strcmp(zType
,"bytearray")==0 && pVar
->bytes
==0 ){
577 /* Only return a BLOB type if the Tcl variable is a bytearray and
578 ** has no string representation. */
579 unsigned char *data
= Tcl_GetByteArrayFromObj(pVar
, &n
);
580 sqlite3_result_blob(pCtx
, data
, n
, SQLITE_TRANSIENT
);
581 }else if( c
=='b' && strcmp(zType
,"boolean")==0 ){
582 Tcl_GetIntFromObj(0, pVar
, &n
);
583 sqlite3_result_int(pCtx
, n
);
584 }else if( c
=='d' && strcmp(zType
,"double")==0 ){
586 Tcl_GetDoubleFromObj(0, pVar
, &r
);
587 sqlite3_result_double(pCtx
, r
);
588 }else if( (c
=='w' && strcmp(zType
,"wideInt")==0) ||
589 (c
=='i' && strcmp(zType
,"int")==0) ){
591 Tcl_GetWideIntFromObj(0, pVar
, &v
);
592 sqlite3_result_int64(pCtx
, v
);
594 unsigned char *data
= (unsigned char *)Tcl_GetStringFromObj(pVar
, &n
);
595 sqlite3_result_text(pCtx
, (char *)data
, n
, SQLITE_TRANSIENT
);
600 static void xF5tDestroy(void *pCtx
){
601 F5tFunction
*p
= (F5tFunction
*)pCtx
;
602 Tcl_DecrRefCount(p
->pScript
);
607 ** sqlite3_fts5_create_function DB NAME SCRIPT
611 static int SQLITE_TCLAPI
f5tCreateFunction(
615 Tcl_Obj
*CONST objv
[]
621 F5tFunction
*pCtx
= 0;
625 Tcl_WrongNumArgs(interp
, 1, objv
, "DB NAME SCRIPT");
628 if( f5tDbAndApi(interp
, objv
[1], &db
, &pApi
) ) return TCL_ERROR
;
630 zName
= Tcl_GetString(objv
[2]);
632 pCtx
= (F5tFunction
*)ckalloc(sizeof(F5tFunction
));
633 pCtx
->interp
= interp
;
634 pCtx
->pScript
= pScript
;
635 Tcl_IncrRefCount(pScript
);
637 rc
= pApi
->xCreateFunction(
638 pApi
, zName
, (void*)pCtx
, xF5tFunction
, xF5tDestroy
641 Tcl_AppendResult(interp
, "error: ", sqlite3_errmsg(db
), 0);
648 typedef struct F5tTokenizeCtx F5tTokenizeCtx
;
649 struct F5tTokenizeCtx
{
655 static int xTokenizeCb2(
658 const char *zToken
, int nToken
,
661 F5tTokenizeCtx
*p
= (F5tTokenizeCtx
*)pCtx
;
663 Tcl_ListObjAppendElement(0, p
->pRet
, Tcl_NewStringObj(zToken
, nToken
));
664 Tcl_ListObjAppendElement(
665 0, p
->pRet
, Tcl_NewStringObj(&p
->zInput
[iStart
], iEnd
-iStart
)
668 Tcl_ListObjAppendElement(0, p
->pRet
, Tcl_NewStringObj(zToken
, nToken
));
669 Tcl_ListObjAppendElement(0, p
->pRet
, Tcl_NewIntObj(iStart
));
670 Tcl_ListObjAppendElement(0, p
->pRet
, Tcl_NewIntObj(iEnd
));
677 ** sqlite3_fts5_tokenize DB TOKENIZER TEXT
681 static int SQLITE_TCLAPI
f5tTokenize(
685 Tcl_Obj
*CONST objv
[]
691 Fts5Tokenizer
*pTok
= 0;
692 fts5_tokenizer tokenizer
;
701 if( objc
!=4 && objc
!=5 ){
702 Tcl_WrongNumArgs(interp
, 1, objv
, "?-subst? DB NAME TEXT");
706 char *zOpt
= Tcl_GetString(objv
[1]);
707 if( strcmp("-subst", zOpt
) ){
708 Tcl_AppendResult(interp
, "unrecognized option: ", zOpt
, 0);
712 if( f5tDbAndApi(interp
, objv
[objc
-3], &db
, &pApi
) ) return TCL_ERROR
;
713 if( Tcl_SplitList(interp
, Tcl_GetString(objv
[objc
-2]), &nArg
, &azArg
) ){
717 Tcl_AppendResult(interp
, "no such tokenizer: ", 0);
718 Tcl_Free((void*)azArg
);
721 zText
= Tcl_GetStringFromObj(objv
[objc
-1], &nText
);
723 rc
= pApi
->xFindTokenizer(pApi
, azArg
[0], &pUserdata
, &tokenizer
);
725 Tcl_AppendResult(interp
, "no such tokenizer: ", azArg
[0], 0);
729 rc
= tokenizer
.xCreate(pUserdata
, &azArg
[1], nArg
-1, &pTok
);
731 Tcl_AppendResult(interp
, "error in tokenizer.xCreate()", 0);
736 Tcl_IncrRefCount(pRet
);
737 ctx
.bSubst
= (objc
==5);
740 rc
= tokenizer
.xTokenize(
741 pTok
, (void*)&ctx
, FTS5_TOKENIZE_DOCUMENT
, zText
, nText
, xTokenizeCb2
743 tokenizer
.xDelete(pTok
);
745 Tcl_AppendResult(interp
, "error in tokenizer.xTokenize()", 0);
746 Tcl_DecrRefCount(pRet
);
751 Tcl_Free((void*)azArg
);
752 Tcl_SetObjResult(interp
, pRet
);
753 Tcl_DecrRefCount(pRet
);
757 /*************************************************************************
758 ** Start of tokenizer wrapper.
761 typedef struct F5tTokenizerContext F5tTokenizerContext
;
762 typedef struct F5tTokenizerCb F5tTokenizerCb
;
763 typedef struct F5tTokenizerModule F5tTokenizerModule
;
764 typedef struct F5tTokenizerInstance F5tTokenizerInstance
;
766 struct F5tTokenizerContext
{
768 int (*xToken
)(void*, int, const char*, int, int, int);
771 struct F5tTokenizerModule
{
774 F5tTokenizerContext
*pContext
;
777 struct F5tTokenizerInstance
{
780 F5tTokenizerContext
*pContext
;
783 static int f5tTokenizerCreate(
787 Fts5Tokenizer
**ppOut
789 F5tTokenizerModule
*pMod
= (F5tTokenizerModule
*)pCtx
;
794 pEval
= Tcl_DuplicateObj(pMod
->pScript
);
795 Tcl_IncrRefCount(pEval
);
796 for(i
=0; rc
==TCL_OK
&& i
<nArg
; i
++){
797 Tcl_Obj
*pObj
= Tcl_NewStringObj(azArg
[i
], -1);
798 rc
= Tcl_ListObjAppendElement(pMod
->interp
, pEval
, pObj
);
802 rc
= Tcl_EvalObjEx(pMod
->interp
, pEval
, TCL_GLOBAL_ONLY
);
804 Tcl_DecrRefCount(pEval
);
807 F5tTokenizerInstance
*pInst
;
808 pInst
= (F5tTokenizerInstance
*)ckalloc(sizeof(F5tTokenizerInstance
));
809 memset(pInst
, 0, sizeof(F5tTokenizerInstance
));
810 pInst
->interp
= pMod
->interp
;
811 pInst
->pScript
= Tcl_GetObjResult(pMod
->interp
);
812 pInst
->pContext
= pMod
->pContext
;
813 Tcl_IncrRefCount(pInst
->pScript
);
814 *ppOut
= (Fts5Tokenizer
*)pInst
;
821 static void f5tTokenizerDelete(Fts5Tokenizer
*p
){
822 F5tTokenizerInstance
*pInst
= (F5tTokenizerInstance
*)p
;
823 Tcl_DecrRefCount(pInst
->pScript
);
824 ckfree((char *)pInst
);
827 static int f5tTokenizerTokenize(
831 const char *pText
, int nText
,
832 int (*xToken
)(void*, int, const char*, int, int, int)
834 F5tTokenizerInstance
*pInst
= (F5tTokenizerInstance
*)p
;
836 int (*xOldToken
)(void*, int, const char*, int, int, int);
841 pOldCtx
= pInst
->pContext
->pCtx
;
842 xOldToken
= pInst
->pContext
->xToken
;
844 pInst
->pContext
->pCtx
= pCtx
;
845 pInst
->pContext
->xToken
= xToken
;
848 flags
==FTS5_TOKENIZE_DOCUMENT
849 || flags
==FTS5_TOKENIZE_AUX
850 || flags
==FTS5_TOKENIZE_QUERY
851 || flags
==(FTS5_TOKENIZE_QUERY
| FTS5_TOKENIZE_PREFIX
)
853 pEval
= Tcl_DuplicateObj(pInst
->pScript
);
854 Tcl_IncrRefCount(pEval
);
856 case FTS5_TOKENIZE_DOCUMENT
:
859 case FTS5_TOKENIZE_AUX
:
862 case FTS5_TOKENIZE_QUERY
:
865 case (FTS5_TOKENIZE_PREFIX
| FTS5_TOKENIZE_QUERY
):
866 zFlags
= "prefixquery";
874 Tcl_ListObjAppendElement(pInst
->interp
, pEval
, Tcl_NewStringObj(zFlags
, -1));
875 Tcl_ListObjAppendElement(pInst
->interp
, pEval
, Tcl_NewStringObj(pText
,nText
));
876 rc
= Tcl_EvalObjEx(pInst
->interp
, pEval
, TCL_GLOBAL_ONLY
);
877 Tcl_DecrRefCount(pEval
);
879 pInst
->pContext
->pCtx
= pOldCtx
;
880 pInst
->pContext
->xToken
= xOldToken
;
885 ** sqlite3_fts5_token ?-colocated? TEXT START END
887 static int SQLITE_TCLAPI
f5tTokenizerReturn(
891 Tcl_Obj
*CONST objv
[]
893 F5tTokenizerContext
*p
= (F5tTokenizerContext
*)clientData
;
903 char *zArg
= Tcl_GetStringFromObj(objv
[1], &nArg
);
904 if( nArg
<=10 && nArg
>=2 && memcmp("-colocated", zArg
, nArg
)==0 ){
905 tflags
|= FTS5_TOKEN_COLOCATED
;
913 zToken
= Tcl_GetStringFromObj(objv
[objc
-3], &nToken
);
914 if( Tcl_GetIntFromObj(interp
, objv
[objc
-2], &iStart
)
915 || Tcl_GetIntFromObj(interp
, objv
[objc
-1], &iEnd
)
921 Tcl_AppendResult(interp
,
922 "sqlite3_fts5_token may only be used by tokenizer callback", 0
927 rc
= p
->xToken(p
->pCtx
, tflags
, zToken
, nToken
, iStart
, iEnd
);
928 Tcl_SetResult(interp
, (char*)sqlite3ErrName(rc
), TCL_VOLATILE
);
929 return rc
==SQLITE_OK
? TCL_OK
: TCL_ERROR
;
932 Tcl_WrongNumArgs(interp
, 1, objv
, "?-colocated? TEXT START END");
936 static void f5tDelTokenizer(void *pCtx
){
937 F5tTokenizerModule
*pMod
= (F5tTokenizerModule
*)pCtx
;
938 Tcl_DecrRefCount(pMod
->pScript
);
939 ckfree((char *)pMod
);
943 ** sqlite3_fts5_create_tokenizer DB NAME SCRIPT
945 ** Register a tokenizer named NAME implemented by script SCRIPT. When
946 ** a tokenizer instance is created (fts5_tokenizer.xCreate), any tokenizer
947 ** arguments are appended to SCRIPT and the result executed.
949 ** The value returned by (SCRIPT + args) is itself a tcl script. This
950 ** script - call it SCRIPT2 - is executed to tokenize text using the
951 ** tokenizer instance "returned" by SCRIPT. Specifically, to tokenize
952 ** text SCRIPT2 is invoked with a single argument appended to it - the
955 ** SCRIPT2 should invoke the [sqlite3_fts5_token] command once for each
956 ** token within the tokenized text.
958 static int SQLITE_TCLAPI
f5tCreateTokenizer(
959 ClientData clientData
,
962 Tcl_Obj
*CONST objv
[]
964 F5tTokenizerContext
*pContext
= (F5tTokenizerContext
*)clientData
;
970 F5tTokenizerModule
*pMod
;
974 Tcl_WrongNumArgs(interp
, 1, objv
, "DB NAME SCRIPT");
977 if( f5tDbAndApi(interp
, objv
[1], &db
, &pApi
) ){
980 zName
= Tcl_GetString(objv
[2]);
983 t
.xCreate
= f5tTokenizerCreate
;
984 t
.xTokenize
= f5tTokenizerTokenize
;
985 t
.xDelete
= f5tTokenizerDelete
;
987 pMod
= (F5tTokenizerModule
*)ckalloc(sizeof(F5tTokenizerModule
));
988 pMod
->interp
= interp
;
989 pMod
->pScript
= pScript
;
990 pMod
->pContext
= pContext
;
991 Tcl_IncrRefCount(pScript
);
992 rc
= pApi
->xCreateTokenizer(pApi
, zName
, (void*)pMod
, &t
, f5tDelTokenizer
);
994 Tcl_AppendResult(interp
, "error in fts5_api.xCreateTokenizer()", 0);
1001 static void SQLITE_TCLAPI
xF5tFree(ClientData clientData
){
1006 ** sqlite3_fts5_may_be_corrupt BOOLEAN
1008 ** Set or clear the global "may-be-corrupt" flag. Return the old value.
1010 static int SQLITE_TCLAPI
f5tMayBeCorrupt(
1014 Tcl_Obj
*CONST objv
[]
1017 int bOld
= sqlite3_fts5_may_be_corrupt
;
1019 if( objc
!=2 && objc
!=1 ){
1020 Tcl_WrongNumArgs(interp
, 1, objv
, "?BOOLEAN?");
1025 if( Tcl_GetBooleanFromObj(interp
, objv
[1], &bNew
) ) return TCL_ERROR
;
1026 sqlite3_fts5_may_be_corrupt
= bNew
;
1029 Tcl_SetObjResult(interp
, Tcl_NewIntObj(bOld
));
1035 static unsigned int f5t_fts5HashKey(int nSlot
, const char *p
, int n
){
1037 unsigned int h
= 13;
1038 for(i
=n
-1; i
>=0; i
--){
1039 h
= (h
<< 3) ^ h
^ p
[i
];
1044 static int SQLITE_TCLAPI
f5tTokenHash(
1048 Tcl_Obj
*CONST objv
[]
1056 Tcl_WrongNumArgs(interp
, 1, objv
, "NSLOT TOKEN");
1059 if( Tcl_GetIntFromObj(interp
, objv
[1], &nSlot
) ){
1062 z
= Tcl_GetStringFromObj(objv
[2], &n
);
1064 iVal
= f5t_fts5HashKey(nSlot
, z
, n
);
1065 Tcl_SetObjResult(interp
, Tcl_NewIntObj(iVal
));
1069 static int SQLITE_TCLAPI
f5tRegisterMatchinfo(
1073 Tcl_Obj
*CONST objv
[]
1079 Tcl_WrongNumArgs(interp
, 1, objv
, "DB");
1082 if( f5tDbPointer(interp
, objv
[1], &db
) ){
1086 rc
= sqlite3Fts5TestRegisterMatchinfo(db
);
1087 if( rc
!=SQLITE_OK
){
1088 Tcl_SetResult(interp
, (char*)sqlite3ErrName(rc
), TCL_VOLATILE
);
1094 static int SQLITE_TCLAPI
f5tRegisterTok(
1098 Tcl_Obj
*CONST objv
[]
1105 Tcl_WrongNumArgs(interp
, 1, objv
, "DB");
1108 if( f5tDbAndApi(interp
, objv
[1], &db
, &pApi
) ){
1112 rc
= sqlite3Fts5TestRegisterTok(db
, pApi
);
1113 if( rc
!=SQLITE_OK
){
1114 Tcl_SetResult(interp
, (char*)sqlite3ErrName(rc
), TCL_VOLATILE
);
1123 int Fts5tcl_Init(Tcl_Interp
*interp
){
1126 Tcl_ObjCmdProc
*xProc
;
1129 { "sqlite3_fts5_create_tokenizer", f5tCreateTokenizer
, 1 },
1130 { "sqlite3_fts5_token", f5tTokenizerReturn
, 1 },
1131 { "sqlite3_fts5_tokenize", f5tTokenize
, 0 },
1132 { "sqlite3_fts5_create_function", f5tCreateFunction
, 0 },
1133 { "sqlite3_fts5_may_be_corrupt", f5tMayBeCorrupt
, 0 },
1134 { "sqlite3_fts5_token_hash", f5tTokenHash
, 0 },
1135 { "sqlite3_fts5_register_matchinfo", f5tRegisterMatchinfo
, 0 },
1136 { "sqlite3_fts5_register_fts5tokenize", f5tRegisterTok
, 0 }
1139 F5tTokenizerContext
*pContext
;
1141 pContext
= (F5tTokenizerContext
*)ckalloc(sizeof(F5tTokenizerContext
));
1142 memset(pContext
, 0, sizeof(*pContext
));
1144 for(i
=0; i
<sizeof(aCmd
)/sizeof(aCmd
[0]); i
++){
1145 struct Cmd
*p
= &aCmd
[i
];
1147 if( p
->bTokenizeCtx
) pCtx
= (void*)pContext
;
1148 Tcl_CreateObjCommand(interp
, p
->zName
, p
->xProc
, pCtx
, (i
? 0 : xF5tFree
));
1153 #else /* SQLITE_ENABLE_FTS5 */
1154 int Fts5tcl_Init(Tcl_Interp
*interp
){
1157 #endif /* SQLITE_ENABLE_FTS5 */
1158 #endif /* SQLITE_TEST */