| OLD | NEW |
| (Empty) |
| 1 /* | |
| 2 ** 2014 Dec 01 | |
| 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 */ | |
| 14 | |
| 15 | |
| 16 #ifdef SQLITE_TEST | |
| 17 #include <tcl.h> | |
| 18 | |
| 19 #ifdef SQLITE_ENABLE_FTS5 | |
| 20 | |
| 21 #include "fts5.h" | |
| 22 #include <string.h> | |
| 23 #include <assert.h> | |
| 24 | |
| 25 extern int sqlite3_fts5_may_be_corrupt; | |
| 26 extern int sqlite3Fts5TestRegisterMatchinfo(sqlite3 *); | |
| 27 | |
| 28 /************************************************************************* | |
| 29 ** This is a copy of the first part of the SqliteDb structure in | |
| 30 ** tclsqlite.c. We need it here so that the get_sqlite_pointer routine | |
| 31 ** can extract the sqlite3* pointer from an existing Tcl SQLite | |
| 32 ** connection. | |
| 33 */ | |
| 34 | |
| 35 extern const char *sqlite3ErrName(int); | |
| 36 | |
| 37 struct SqliteDb { | |
| 38 sqlite3 *db; | |
| 39 }; | |
| 40 | |
| 41 /* | |
| 42 ** Decode a pointer to an sqlite3 object. | |
| 43 */ | |
| 44 static int f5tDbPointer(Tcl_Interp *interp, Tcl_Obj *pObj, sqlite3 **ppDb){ | |
| 45 struct SqliteDb *p; | |
| 46 Tcl_CmdInfo cmdInfo; | |
| 47 char *z = Tcl_GetString(pObj); | |
| 48 if( Tcl_GetCommandInfo(interp, z, &cmdInfo) ){ | |
| 49 p = (struct SqliteDb*)cmdInfo.objClientData; | |
| 50 *ppDb = p->db; | |
| 51 return TCL_OK; | |
| 52 } | |
| 53 return TCL_ERROR; | |
| 54 } | |
| 55 | |
| 56 /* End of code that accesses the SqliteDb struct. | |
| 57 **************************************************************************/ | |
| 58 | |
| 59 static int f5tResultToErrorCode(const char *zRes){ | |
| 60 struct ErrorCode { | |
| 61 int rc; | |
| 62 const char *zError; | |
| 63 } aErr[] = { | |
| 64 { SQLITE_DONE, "SQLITE_DONE" }, | |
| 65 { SQLITE_ERROR, "SQLITE_ERROR" }, | |
| 66 { SQLITE_OK, "SQLITE_OK" }, | |
| 67 { SQLITE_OK, "" }, | |
| 68 }; | |
| 69 int i; | |
| 70 | |
| 71 for(i=0; i<sizeof(aErr)/sizeof(aErr[0]); i++){ | |
| 72 if( 0==sqlite3_stricmp(zRes, aErr[i].zError) ){ | |
| 73 return aErr[i].rc; | |
| 74 } | |
| 75 } | |
| 76 | |
| 77 return SQLITE_ERROR; | |
| 78 } | |
| 79 | |
| 80 static int f5tDbAndApi( | |
| 81 Tcl_Interp *interp, | |
| 82 Tcl_Obj *pObj, | |
| 83 sqlite3 **ppDb, | |
| 84 fts5_api **ppApi | |
| 85 ){ | |
| 86 sqlite3 *db = 0; | |
| 87 int rc = f5tDbPointer(interp, pObj, &db); | |
| 88 if( rc!=TCL_OK ){ | |
| 89 return TCL_ERROR; | |
| 90 }else{ | |
| 91 sqlite3_stmt *pStmt = 0; | |
| 92 fts5_api *pApi = 0; | |
| 93 | |
| 94 rc = sqlite3_prepare_v2(db, "SELECT fts5()", -1, &pStmt, 0); | |
| 95 if( rc!=SQLITE_OK ){ | |
| 96 Tcl_AppendResult(interp, "error: ", sqlite3_errmsg(db), 0); | |
| 97 return TCL_ERROR; | |
| 98 } | |
| 99 | |
| 100 if( SQLITE_ROW==sqlite3_step(pStmt) ){ | |
| 101 const void *pPtr = sqlite3_column_blob(pStmt, 0); | |
| 102 memcpy((void*)&pApi, pPtr, sizeof(pApi)); | |
| 103 } | |
| 104 | |
| 105 if( sqlite3_finalize(pStmt)!=SQLITE_OK ){ | |
| 106 Tcl_AppendResult(interp, "error: ", sqlite3_errmsg(db), 0); | |
| 107 return TCL_ERROR; | |
| 108 } | |
| 109 | |
| 110 *ppDb = db; | |
| 111 *ppApi = pApi; | |
| 112 } | |
| 113 | |
| 114 return TCL_OK; | |
| 115 } | |
| 116 | |
| 117 typedef struct F5tFunction F5tFunction; | |
| 118 struct F5tFunction { | |
| 119 Tcl_Interp *interp; | |
| 120 Tcl_Obj *pScript; | |
| 121 }; | |
| 122 | |
| 123 typedef struct F5tApi F5tApi; | |
| 124 struct F5tApi { | |
| 125 const Fts5ExtensionApi *pApi; | |
| 126 Fts5Context *pFts; | |
| 127 }; | |
| 128 | |
| 129 /* | |
| 130 ** An object of this type is used with the xSetAuxdata() and xGetAuxdata() | |
| 131 ** API test wrappers. The tcl interface allows a single tcl value to be | |
| 132 ** saved using xSetAuxdata(). Instead of simply storing a pointer to the | |
| 133 ** tcl object, the code in this file wraps it in an sqlite3_malloc'd | |
| 134 ** instance of the following struct so that if the destructor is not | |
| 135 ** correctly invoked it will be reported as an SQLite memory leak. | |
| 136 */ | |
| 137 typedef struct F5tAuxData F5tAuxData; | |
| 138 struct F5tAuxData { | |
| 139 Tcl_Obj *pObj; | |
| 140 }; | |
| 141 | |
| 142 static int xTokenizeCb( | |
| 143 void *pCtx, | |
| 144 int tflags, | |
| 145 const char *zToken, int nToken, | |
| 146 int iStart, int iEnd | |
| 147 ){ | |
| 148 F5tFunction *p = (F5tFunction*)pCtx; | |
| 149 Tcl_Obj *pEval = Tcl_DuplicateObj(p->pScript); | |
| 150 int rc; | |
| 151 | |
| 152 Tcl_IncrRefCount(pEval); | |
| 153 Tcl_ListObjAppendElement(p->interp, pEval, Tcl_NewStringObj(zToken, nToken)); | |
| 154 Tcl_ListObjAppendElement(p->interp, pEval, Tcl_NewIntObj(iStart)); | |
| 155 Tcl_ListObjAppendElement(p->interp, pEval, Tcl_NewIntObj(iEnd)); | |
| 156 | |
| 157 rc = Tcl_EvalObjEx(p->interp, pEval, 0); | |
| 158 Tcl_DecrRefCount(pEval); | |
| 159 if( rc==TCL_OK ){ | |
| 160 rc = f5tResultToErrorCode(Tcl_GetStringResult(p->interp)); | |
| 161 } | |
| 162 | |
| 163 return rc; | |
| 164 } | |
| 165 | |
| 166 static int xF5tApi(void*, Tcl_Interp*, int, Tcl_Obj *CONST []); | |
| 167 | |
| 168 static int xQueryPhraseCb( | |
| 169 const Fts5ExtensionApi *pApi, | |
| 170 Fts5Context *pFts, | |
| 171 void *pCtx | |
| 172 ){ | |
| 173 F5tFunction *p = (F5tFunction*)pCtx; | |
| 174 static sqlite3_int64 iCmd = 0; | |
| 175 Tcl_Obj *pEval; | |
| 176 int rc; | |
| 177 | |
| 178 char zCmd[64]; | |
| 179 F5tApi sApi; | |
| 180 | |
| 181 sApi.pApi = pApi; | |
| 182 sApi.pFts = pFts; | |
| 183 sprintf(zCmd, "f5t_2_%lld", iCmd++); | |
| 184 Tcl_CreateObjCommand(p->interp, zCmd, xF5tApi, &sApi, 0); | |
| 185 | |
| 186 pEval = Tcl_DuplicateObj(p->pScript); | |
| 187 Tcl_IncrRefCount(pEval); | |
| 188 Tcl_ListObjAppendElement(p->interp, pEval, Tcl_NewStringObj(zCmd, -1)); | |
| 189 rc = Tcl_EvalObjEx(p->interp, pEval, 0); | |
| 190 Tcl_DecrRefCount(pEval); | |
| 191 Tcl_DeleteCommand(p->interp, zCmd); | |
| 192 | |
| 193 if( rc==TCL_OK ){ | |
| 194 rc = f5tResultToErrorCode(Tcl_GetStringResult(p->interp)); | |
| 195 } | |
| 196 | |
| 197 return rc; | |
| 198 } | |
| 199 | |
| 200 static void xSetAuxdataDestructor(void *p){ | |
| 201 F5tAuxData *pData = (F5tAuxData*)p; | |
| 202 Tcl_DecrRefCount(pData->pObj); | |
| 203 sqlite3_free(pData); | |
| 204 } | |
| 205 | |
| 206 /* | |
| 207 ** api sub-command... | |
| 208 ** | |
| 209 ** Description... | |
| 210 */ | |
| 211 static int xF5tApi( | |
| 212 void * clientData, | |
| 213 Tcl_Interp *interp, | |
| 214 int objc, | |
| 215 Tcl_Obj *CONST objv[] | |
| 216 ){ | |
| 217 struct Sub { | |
| 218 const char *zName; | |
| 219 int nArg; | |
| 220 const char *zMsg; | |
| 221 } aSub[] = { | |
| 222 { "xColumnCount", 0, "" }, /* 0 */ | |
| 223 { "xRowCount", 0, "" }, /* 1 */ | |
| 224 { "xColumnTotalSize", 1, "COL" }, /* 2 */ | |
| 225 { "xTokenize", 2, "TEXT SCRIPT" }, /* 3 */ | |
| 226 { "xPhraseCount", 0, "" }, /* 4 */ | |
| 227 { "xPhraseSize", 1, "PHRASE" }, /* 5 */ | |
| 228 { "xInstCount", 0, "" }, /* 6 */ | |
| 229 { "xInst", 1, "IDX" }, /* 7 */ | |
| 230 { "xRowid", 0, "" }, /* 8 */ | |
| 231 { "xColumnText", 1, "COL" }, /* 9 */ | |
| 232 { "xColumnSize", 1, "COL" }, /* 10 */ | |
| 233 { "xQueryPhrase", 2, "PHRASE SCRIPT" }, /* 11 */ | |
| 234 { "xSetAuxdata", 1, "VALUE" }, /* 12 */ | |
| 235 { "xGetAuxdata", 1, "CLEAR" }, /* 13 */ | |
| 236 { "xSetAuxdataInt", 1, "INTEGER" }, /* 14 */ | |
| 237 { "xGetAuxdataInt", 1, "CLEAR" }, /* 15 */ | |
| 238 { 0, 0, 0} | |
| 239 }; | |
| 240 | |
| 241 int rc; | |
| 242 int iSub = 0; | |
| 243 F5tApi *p = (F5tApi*)clientData; | |
| 244 | |
| 245 if( objc<2 ){ | |
| 246 Tcl_WrongNumArgs(interp, 1, objv, "SUB-COMMAND"); | |
| 247 return TCL_ERROR; | |
| 248 } | |
| 249 | |
| 250 rc = Tcl_GetIndexFromObjStruct( | |
| 251 interp, objv[1], aSub, sizeof(aSub[0]), "SUB-COMMAND", 0, &iSub | |
| 252 ); | |
| 253 if( rc!=TCL_OK ) return rc; | |
| 254 if( aSub[iSub].nArg!=objc-2 ){ | |
| 255 Tcl_WrongNumArgs(interp, 1, objv, aSub[iSub].zMsg); | |
| 256 return TCL_ERROR; | |
| 257 } | |
| 258 | |
| 259 #define CASE(i,str) case i: assert( strcmp(aSub[i].zName, str)==0 ); | |
| 260 switch( iSub ){ | |
| 261 CASE(0, "xColumnCount") { | |
| 262 int nCol; | |
| 263 nCol = p->pApi->xColumnCount(p->pFts); | |
| 264 if( rc==SQLITE_OK ){ | |
| 265 Tcl_SetObjResult(interp, Tcl_NewIntObj(nCol)); | |
| 266 } | |
| 267 break; | |
| 268 } | |
| 269 CASE(1, "xRowCount") { | |
| 270 sqlite3_int64 nRow; | |
| 271 rc = p->pApi->xRowCount(p->pFts, &nRow); | |
| 272 if( rc==SQLITE_OK ){ | |
| 273 Tcl_SetObjResult(interp, Tcl_NewWideIntObj(nRow)); | |
| 274 } | |
| 275 break; | |
| 276 } | |
| 277 CASE(2, "xColumnTotalSize") { | |
| 278 int iCol; | |
| 279 sqlite3_int64 nSize; | |
| 280 if( Tcl_GetIntFromObj(interp, objv[2], &iCol) ) return TCL_ERROR; | |
| 281 rc = p->pApi->xColumnTotalSize(p->pFts, iCol, &nSize); | |
| 282 if( rc==SQLITE_OK ){ | |
| 283 Tcl_SetObjResult(interp, Tcl_NewWideIntObj(nSize)); | |
| 284 } | |
| 285 break; | |
| 286 } | |
| 287 CASE(3, "xTokenize") { | |
| 288 int nText; | |
| 289 char *zText = Tcl_GetStringFromObj(objv[2], &nText); | |
| 290 F5tFunction ctx; | |
| 291 ctx.interp = interp; | |
| 292 ctx.pScript = objv[3]; | |
| 293 rc = p->pApi->xTokenize(p->pFts, zText, nText, &ctx, xTokenizeCb); | |
| 294 if( rc==SQLITE_OK ){ | |
| 295 Tcl_ResetResult(interp); | |
| 296 } | |
| 297 return rc; | |
| 298 } | |
| 299 CASE(4, "xPhraseCount") { | |
| 300 int nPhrase; | |
| 301 nPhrase = p->pApi->xPhraseCount(p->pFts); | |
| 302 if( rc==SQLITE_OK ){ | |
| 303 Tcl_SetObjResult(interp, Tcl_NewIntObj(nPhrase)); | |
| 304 } | |
| 305 break; | |
| 306 } | |
| 307 CASE(5, "xPhraseSize") { | |
| 308 int iPhrase; | |
| 309 int sz; | |
| 310 if( Tcl_GetIntFromObj(interp, objv[2], &iPhrase) ){ | |
| 311 return TCL_ERROR; | |
| 312 } | |
| 313 sz = p->pApi->xPhraseSize(p->pFts, iPhrase); | |
| 314 if( rc==SQLITE_OK ){ | |
| 315 Tcl_SetObjResult(interp, Tcl_NewIntObj(sz)); | |
| 316 } | |
| 317 break; | |
| 318 } | |
| 319 CASE(6, "xInstCount") { | |
| 320 int nInst; | |
| 321 rc = p->pApi->xInstCount(p->pFts, &nInst); | |
| 322 if( rc==SQLITE_OK ){ | |
| 323 Tcl_SetObjResult(interp, Tcl_NewIntObj(nInst)); | |
| 324 } | |
| 325 break; | |
| 326 } | |
| 327 CASE(7, "xInst") { | |
| 328 int iIdx, ip, ic, io; | |
| 329 if( Tcl_GetIntFromObj(interp, objv[2], &iIdx) ){ | |
| 330 return TCL_ERROR; | |
| 331 } | |
| 332 rc = p->pApi->xInst(p->pFts, iIdx, &ip, &ic, &io); | |
| 333 if( rc==SQLITE_OK ){ | |
| 334 Tcl_Obj *pList = Tcl_NewObj(); | |
| 335 Tcl_ListObjAppendElement(interp, pList, Tcl_NewIntObj(ip)); | |
| 336 Tcl_ListObjAppendElement(interp, pList, Tcl_NewIntObj(ic)); | |
| 337 Tcl_ListObjAppendElement(interp, pList, Tcl_NewIntObj(io)); | |
| 338 Tcl_SetObjResult(interp, pList); | |
| 339 } | |
| 340 break; | |
| 341 } | |
| 342 CASE(8, "xRowid") { | |
| 343 sqlite3_int64 iRowid = p->pApi->xRowid(p->pFts); | |
| 344 Tcl_SetObjResult(interp, Tcl_NewWideIntObj(iRowid)); | |
| 345 break; | |
| 346 } | |
| 347 CASE(9, "xColumnText") { | |
| 348 const char *z = 0; | |
| 349 int n = 0; | |
| 350 int iCol; | |
| 351 if( Tcl_GetIntFromObj(interp, objv[2], &iCol) ){ | |
| 352 return TCL_ERROR; | |
| 353 } | |
| 354 rc = p->pApi->xColumnText(p->pFts, iCol, &z, &n); | |
| 355 if( rc==SQLITE_OK ){ | |
| 356 Tcl_SetObjResult(interp, Tcl_NewStringObj(z, n)); | |
| 357 } | |
| 358 break; | |
| 359 } | |
| 360 CASE(10, "xColumnSize") { | |
| 361 int n = 0; | |
| 362 int iCol; | |
| 363 if( Tcl_GetIntFromObj(interp, objv[2], &iCol) ){ | |
| 364 return TCL_ERROR; | |
| 365 } | |
| 366 rc = p->pApi->xColumnSize(p->pFts, iCol, &n); | |
| 367 if( rc==SQLITE_OK ){ | |
| 368 Tcl_SetObjResult(interp, Tcl_NewIntObj(n)); | |
| 369 } | |
| 370 break; | |
| 371 } | |
| 372 CASE(11, "xQueryPhrase") { | |
| 373 int iPhrase; | |
| 374 F5tFunction ctx; | |
| 375 if( Tcl_GetIntFromObj(interp, objv[2], &iPhrase) ){ | |
| 376 return TCL_ERROR; | |
| 377 } | |
| 378 ctx.interp = interp; | |
| 379 ctx.pScript = objv[3]; | |
| 380 rc = p->pApi->xQueryPhrase(p->pFts, iPhrase, &ctx, xQueryPhraseCb); | |
| 381 if( rc==SQLITE_OK ){ | |
| 382 Tcl_ResetResult(interp); | |
| 383 } | |
| 384 break; | |
| 385 } | |
| 386 CASE(12, "xSetAuxdata") { | |
| 387 F5tAuxData *pData = (F5tAuxData*)sqlite3_malloc(sizeof(F5tAuxData)); | |
| 388 if( pData==0 ){ | |
| 389 Tcl_AppendResult(interp, "out of memory", 0); | |
| 390 return TCL_ERROR; | |
| 391 } | |
| 392 pData->pObj = objv[2]; | |
| 393 Tcl_IncrRefCount(pData->pObj); | |
| 394 rc = p->pApi->xSetAuxdata(p->pFts, pData, xSetAuxdataDestructor); | |
| 395 break; | |
| 396 } | |
| 397 CASE(13, "xGetAuxdata") { | |
| 398 F5tAuxData *pData; | |
| 399 int bClear; | |
| 400 if( Tcl_GetBooleanFromObj(interp, objv[2], &bClear) ){ | |
| 401 return TCL_ERROR; | |
| 402 } | |
| 403 pData = (F5tAuxData*)p->pApi->xGetAuxdata(p->pFts, bClear); | |
| 404 if( pData==0 ){ | |
| 405 Tcl_ResetResult(interp); | |
| 406 }else{ | |
| 407 Tcl_SetObjResult(interp, pData->pObj); | |
| 408 if( bClear ){ | |
| 409 xSetAuxdataDestructor((void*)pData); | |
| 410 } | |
| 411 } | |
| 412 break; | |
| 413 } | |
| 414 | |
| 415 /* These two - xSetAuxdataInt and xGetAuxdataInt - are similar to the | |
| 416 ** xSetAuxdata and xGetAuxdata methods implemented above. The difference | |
| 417 ** is that they may only save an integer value as auxiliary data, and | |
| 418 ** do not specify a destructor function. */ | |
| 419 CASE(14, "xSetAuxdataInt") { | |
| 420 int iVal; | |
| 421 if( Tcl_GetIntFromObj(interp, objv[2], &iVal) ) return TCL_ERROR; | |
| 422 rc = p->pApi->xSetAuxdata(p->pFts, (void*)((char*)0 + iVal), 0); | |
| 423 break; | |
| 424 } | |
| 425 CASE(15, "xGetAuxdataInt") { | |
| 426 int iVal; | |
| 427 int bClear; | |
| 428 if( Tcl_GetBooleanFromObj(interp, objv[2], &bClear) ) return TCL_ERROR; | |
| 429 iVal = ((char*)p->pApi->xGetAuxdata(p->pFts, bClear) - (char*)0); | |
| 430 Tcl_SetObjResult(interp, Tcl_NewIntObj(iVal)); | |
| 431 break; | |
| 432 } | |
| 433 | |
| 434 default: | |
| 435 assert( 0 ); | |
| 436 break; | |
| 437 } | |
| 438 #undef CASE | |
| 439 | |
| 440 if( rc!=SQLITE_OK ){ | |
| 441 Tcl_SetResult(interp, (char*)sqlite3ErrName(rc), TCL_VOLATILE); | |
| 442 return TCL_ERROR; | |
| 443 } | |
| 444 | |
| 445 return TCL_OK; | |
| 446 } | |
| 447 | |
| 448 static void xF5tFunction( | |
| 449 const Fts5ExtensionApi *pApi, /* API offered by current FTS version */ | |
| 450 Fts5Context *pFts, /* First arg to pass to pApi functions */ | |
| 451 sqlite3_context *pCtx, /* Context for returning result/error */ | |
| 452 int nVal, /* Number of values in apVal[] array */ | |
| 453 sqlite3_value **apVal /* Array of trailing arguments */ | |
| 454 ){ | |
| 455 F5tFunction *p = (F5tFunction*)pApi->xUserData(pFts); | |
| 456 Tcl_Obj *pEval; /* Script to evaluate */ | |
| 457 int i; | |
| 458 int rc; | |
| 459 | |
| 460 static sqlite3_int64 iCmd = 0; | |
| 461 char zCmd[64]; | |
| 462 F5tApi sApi; | |
| 463 sApi.pApi = pApi; | |
| 464 sApi.pFts = pFts; | |
| 465 | |
| 466 sprintf(zCmd, "f5t_%lld", iCmd++); | |
| 467 Tcl_CreateObjCommand(p->interp, zCmd, xF5tApi, &sApi, 0); | |
| 468 pEval = Tcl_DuplicateObj(p->pScript); | |
| 469 Tcl_IncrRefCount(pEval); | |
| 470 Tcl_ListObjAppendElement(p->interp, pEval, Tcl_NewStringObj(zCmd, -1)); | |
| 471 | |
| 472 for(i=0; i<nVal; i++){ | |
| 473 Tcl_Obj *pObj = 0; | |
| 474 switch( sqlite3_value_type(apVal[i]) ){ | |
| 475 case SQLITE_TEXT: | |
| 476 pObj = Tcl_NewStringObj((const char*)sqlite3_value_text(apVal[i]), -1); | |
| 477 break; | |
| 478 case SQLITE_BLOB: | |
| 479 pObj = Tcl_NewByteArrayObj( | |
| 480 sqlite3_value_blob(apVal[i]), sqlite3_value_bytes(apVal[i]) | |
| 481 ); | |
| 482 break; | |
| 483 case SQLITE_INTEGER: | |
| 484 pObj = Tcl_NewWideIntObj(sqlite3_value_int64(apVal[i])); | |
| 485 break; | |
| 486 case SQLITE_FLOAT: | |
| 487 pObj = Tcl_NewDoubleObj(sqlite3_value_double(apVal[i])); | |
| 488 break; | |
| 489 default: | |
| 490 pObj = Tcl_NewObj(); | |
| 491 break; | |
| 492 } | |
| 493 Tcl_ListObjAppendElement(p->interp, pEval, pObj); | |
| 494 } | |
| 495 | |
| 496 rc = Tcl_EvalObjEx(p->interp, pEval, TCL_GLOBAL_ONLY); | |
| 497 Tcl_DecrRefCount(pEval); | |
| 498 Tcl_DeleteCommand(p->interp, zCmd); | |
| 499 | |
| 500 if( rc!=TCL_OK ){ | |
| 501 sqlite3_result_error(pCtx, Tcl_GetStringResult(p->interp), -1); | |
| 502 }else{ | |
| 503 Tcl_Obj *pVar = Tcl_GetObjResult(p->interp); | |
| 504 int n; | |
| 505 const char *zType = (pVar->typePtr ? pVar->typePtr->name : ""); | |
| 506 char c = zType[0]; | |
| 507 if( c=='b' && strcmp(zType,"bytearray")==0 && pVar->bytes==0 ){ | |
| 508 /* Only return a BLOB type if the Tcl variable is a bytearray and | |
| 509 ** has no string representation. */ | |
| 510 unsigned char *data = Tcl_GetByteArrayFromObj(pVar, &n); | |
| 511 sqlite3_result_blob(pCtx, data, n, SQLITE_TRANSIENT); | |
| 512 }else if( c=='b' && strcmp(zType,"boolean")==0 ){ | |
| 513 Tcl_GetIntFromObj(0, pVar, &n); | |
| 514 sqlite3_result_int(pCtx, n); | |
| 515 }else if( c=='d' && strcmp(zType,"double")==0 ){ | |
| 516 double r; | |
| 517 Tcl_GetDoubleFromObj(0, pVar, &r); | |
| 518 sqlite3_result_double(pCtx, r); | |
| 519 }else if( (c=='w' && strcmp(zType,"wideInt")==0) || | |
| 520 (c=='i' && strcmp(zType,"int")==0) ){ | |
| 521 Tcl_WideInt v; | |
| 522 Tcl_GetWideIntFromObj(0, pVar, &v); | |
| 523 sqlite3_result_int64(pCtx, v); | |
| 524 }else{ | |
| 525 unsigned char *data = (unsigned char *)Tcl_GetStringFromObj(pVar, &n); | |
| 526 sqlite3_result_text(pCtx, (char *)data, n, SQLITE_TRANSIENT); | |
| 527 } | |
| 528 } | |
| 529 } | |
| 530 | |
| 531 static void xF5tDestroy(void *pCtx){ | |
| 532 F5tFunction *p = (F5tFunction*)pCtx; | |
| 533 Tcl_DecrRefCount(p->pScript); | |
| 534 ckfree((char *)p); | |
| 535 } | |
| 536 | |
| 537 /* | |
| 538 ** sqlite3_fts5_create_function DB NAME SCRIPT | |
| 539 ** | |
| 540 ** Description... | |
| 541 */ | |
| 542 static int f5tCreateFunction( | |
| 543 void * clientData, | |
| 544 Tcl_Interp *interp, | |
| 545 int objc, | |
| 546 Tcl_Obj *CONST objv[] | |
| 547 ){ | |
| 548 char *zName; | |
| 549 Tcl_Obj *pScript; | |
| 550 sqlite3 *db = 0; | |
| 551 fts5_api *pApi = 0; | |
| 552 F5tFunction *pCtx = 0; | |
| 553 int rc; | |
| 554 | |
| 555 if( objc!=4 ){ | |
| 556 Tcl_WrongNumArgs(interp, 1, objv, "DB NAME SCRIPT"); | |
| 557 return TCL_ERROR; | |
| 558 } | |
| 559 if( f5tDbAndApi(interp, objv[1], &db, &pApi) ) return TCL_ERROR; | |
| 560 | |
| 561 zName = Tcl_GetString(objv[2]); | |
| 562 pScript = objv[3]; | |
| 563 pCtx = (F5tFunction*)ckalloc(sizeof(F5tFunction)); | |
| 564 pCtx->interp = interp; | |
| 565 pCtx->pScript = pScript; | |
| 566 Tcl_IncrRefCount(pScript); | |
| 567 | |
| 568 rc = pApi->xCreateFunction( | |
| 569 pApi, zName, (void*)pCtx, xF5tFunction, xF5tDestroy | |
| 570 ); | |
| 571 if( rc!=SQLITE_OK ){ | |
| 572 Tcl_AppendResult(interp, "error: ", sqlite3_errmsg(db), 0); | |
| 573 return TCL_ERROR; | |
| 574 } | |
| 575 | |
| 576 return TCL_OK; | |
| 577 } | |
| 578 | |
| 579 typedef struct F5tTokenizeCtx F5tTokenizeCtx; | |
| 580 struct F5tTokenizeCtx { | |
| 581 Tcl_Obj *pRet; | |
| 582 int bSubst; | |
| 583 const char *zInput; | |
| 584 }; | |
| 585 | |
| 586 static int xTokenizeCb2( | |
| 587 void *pCtx, | |
| 588 int tflags, | |
| 589 const char *zToken, int nToken, | |
| 590 int iStart, int iEnd | |
| 591 ){ | |
| 592 F5tTokenizeCtx *p = (F5tTokenizeCtx*)pCtx; | |
| 593 if( p->bSubst ){ | |
| 594 Tcl_ListObjAppendElement(0, p->pRet, Tcl_NewStringObj(zToken, nToken)); | |
| 595 Tcl_ListObjAppendElement( | |
| 596 0, p->pRet, Tcl_NewStringObj(&p->zInput[iStart], iEnd-iStart) | |
| 597 ); | |
| 598 }else{ | |
| 599 Tcl_ListObjAppendElement(0, p->pRet, Tcl_NewStringObj(zToken, nToken)); | |
| 600 Tcl_ListObjAppendElement(0, p->pRet, Tcl_NewIntObj(iStart)); | |
| 601 Tcl_ListObjAppendElement(0, p->pRet, Tcl_NewIntObj(iEnd)); | |
| 602 } | |
| 603 return SQLITE_OK; | |
| 604 } | |
| 605 | |
| 606 | |
| 607 /* | |
| 608 ** sqlite3_fts5_tokenize DB TOKENIZER TEXT | |
| 609 ** | |
| 610 ** Description... | |
| 611 */ | |
| 612 static int f5tTokenize( | |
| 613 void * clientData, | |
| 614 Tcl_Interp *interp, | |
| 615 int objc, | |
| 616 Tcl_Obj *CONST objv[] | |
| 617 ){ | |
| 618 char *zText; | |
| 619 int nText; | |
| 620 sqlite3 *db = 0; | |
| 621 fts5_api *pApi = 0; | |
| 622 Fts5Tokenizer *pTok = 0; | |
| 623 fts5_tokenizer tokenizer; | |
| 624 Tcl_Obj *pRet = 0; | |
| 625 void *pUserdata; | |
| 626 int rc; | |
| 627 | |
| 628 int nArg; | |
| 629 const char **azArg; | |
| 630 F5tTokenizeCtx ctx; | |
| 631 | |
| 632 if( objc!=4 && objc!=5 ){ | |
| 633 Tcl_WrongNumArgs(interp, 1, objv, "?-subst? DB NAME TEXT"); | |
| 634 return TCL_ERROR; | |
| 635 } | |
| 636 if( objc==5 ){ | |
| 637 char *zOpt = Tcl_GetString(objv[1]); | |
| 638 if( strcmp("-subst", zOpt) ){ | |
| 639 Tcl_AppendResult(interp, "unrecognized option: ", zOpt, 0); | |
| 640 return TCL_ERROR; | |
| 641 } | |
| 642 } | |
| 643 if( f5tDbAndApi(interp, objv[objc-3], &db, &pApi) ) return TCL_ERROR; | |
| 644 if( Tcl_SplitList(interp, Tcl_GetString(objv[objc-2]), &nArg, &azArg) ){ | |
| 645 return TCL_ERROR; | |
| 646 } | |
| 647 if( nArg==0 ){ | |
| 648 Tcl_AppendResult(interp, "no such tokenizer: ", 0); | |
| 649 Tcl_Free((void*)azArg); | |
| 650 return TCL_ERROR; | |
| 651 } | |
| 652 zText = Tcl_GetStringFromObj(objv[objc-1], &nText); | |
| 653 | |
| 654 rc = pApi->xFindTokenizer(pApi, azArg[0], &pUserdata, &tokenizer); | |
| 655 if( rc!=SQLITE_OK ){ | |
| 656 Tcl_AppendResult(interp, "no such tokenizer: ", azArg[0], 0); | |
| 657 return TCL_ERROR; | |
| 658 } | |
| 659 | |
| 660 rc = tokenizer.xCreate(pUserdata, &azArg[1], nArg-1, &pTok); | |
| 661 if( rc!=SQLITE_OK ){ | |
| 662 Tcl_AppendResult(interp, "error in tokenizer.xCreate()", 0); | |
| 663 return TCL_ERROR; | |
| 664 } | |
| 665 | |
| 666 pRet = Tcl_NewObj(); | |
| 667 Tcl_IncrRefCount(pRet); | |
| 668 ctx.bSubst = (objc==5); | |
| 669 ctx.pRet = pRet; | |
| 670 ctx.zInput = zText; | |
| 671 rc = tokenizer.xTokenize( | |
| 672 pTok, (void*)&ctx, FTS5_TOKENIZE_DOCUMENT, zText, nText, xTokenizeCb2 | |
| 673 ); | |
| 674 tokenizer.xDelete(pTok); | |
| 675 if( rc!=SQLITE_OK ){ | |
| 676 Tcl_AppendResult(interp, "error in tokenizer.xTokenize()", 0); | |
| 677 Tcl_DecrRefCount(pRet); | |
| 678 return TCL_ERROR; | |
| 679 } | |
| 680 | |
| 681 | |
| 682 Tcl_Free((void*)azArg); | |
| 683 Tcl_SetObjResult(interp, pRet); | |
| 684 Tcl_DecrRefCount(pRet); | |
| 685 return TCL_OK; | |
| 686 } | |
| 687 | |
| 688 /************************************************************************* | |
| 689 ** Start of tokenizer wrapper. | |
| 690 */ | |
| 691 | |
| 692 typedef struct F5tTokenizerContext F5tTokenizerContext; | |
| 693 typedef struct F5tTokenizerCb F5tTokenizerCb; | |
| 694 typedef struct F5tTokenizerModule F5tTokenizerModule; | |
| 695 typedef struct F5tTokenizerInstance F5tTokenizerInstance; | |
| 696 | |
| 697 struct F5tTokenizerContext { | |
| 698 void *pCtx; | |
| 699 int (*xToken)(void*, int, const char*, int, int, int); | |
| 700 }; | |
| 701 | |
| 702 struct F5tTokenizerModule { | |
| 703 Tcl_Interp *interp; | |
| 704 Tcl_Obj *pScript; | |
| 705 F5tTokenizerContext *pContext; | |
| 706 }; | |
| 707 | |
| 708 struct F5tTokenizerInstance { | |
| 709 Tcl_Interp *interp; | |
| 710 Tcl_Obj *pScript; | |
| 711 F5tTokenizerContext *pContext; | |
| 712 }; | |
| 713 | |
| 714 static int f5tTokenizerCreate( | |
| 715 void *pCtx, | |
| 716 const char **azArg, | |
| 717 int nArg, | |
| 718 Fts5Tokenizer **ppOut | |
| 719 ){ | |
| 720 F5tTokenizerModule *pMod = (F5tTokenizerModule*)pCtx; | |
| 721 Tcl_Obj *pEval; | |
| 722 int rc = TCL_OK; | |
| 723 int i; | |
| 724 | |
| 725 pEval = Tcl_DuplicateObj(pMod->pScript); | |
| 726 Tcl_IncrRefCount(pEval); | |
| 727 for(i=0; rc==TCL_OK && i<nArg; i++){ | |
| 728 Tcl_Obj *pObj = Tcl_NewStringObj(azArg[i], -1); | |
| 729 rc = Tcl_ListObjAppendElement(pMod->interp, pEval, pObj); | |
| 730 } | |
| 731 | |
| 732 if( rc==TCL_OK ){ | |
| 733 rc = Tcl_EvalObjEx(pMod->interp, pEval, TCL_GLOBAL_ONLY); | |
| 734 } | |
| 735 Tcl_DecrRefCount(pEval); | |
| 736 | |
| 737 if( rc==TCL_OK ){ | |
| 738 F5tTokenizerInstance *pInst; | |
| 739 pInst = (F5tTokenizerInstance*)ckalloc(sizeof(F5tTokenizerInstance)); | |
| 740 memset(pInst, 0, sizeof(F5tTokenizerInstance)); | |
| 741 pInst->interp = pMod->interp; | |
| 742 pInst->pScript = Tcl_GetObjResult(pMod->interp); | |
| 743 pInst->pContext = pMod->pContext; | |
| 744 Tcl_IncrRefCount(pInst->pScript); | |
| 745 *ppOut = (Fts5Tokenizer*)pInst; | |
| 746 } | |
| 747 | |
| 748 return rc; | |
| 749 } | |
| 750 | |
| 751 | |
| 752 static void f5tTokenizerDelete(Fts5Tokenizer *p){ | |
| 753 F5tTokenizerInstance *pInst = (F5tTokenizerInstance*)p; | |
| 754 Tcl_DecrRefCount(pInst->pScript); | |
| 755 ckfree((char *)pInst); | |
| 756 } | |
| 757 | |
| 758 static int f5tTokenizerTokenize( | |
| 759 Fts5Tokenizer *p, | |
| 760 void *pCtx, | |
| 761 int flags, | |
| 762 const char *pText, int nText, | |
| 763 int (*xToken)(void*, int, const char*, int, int, int) | |
| 764 ){ | |
| 765 F5tTokenizerInstance *pInst = (F5tTokenizerInstance*)p; | |
| 766 void *pOldCtx; | |
| 767 int (*xOldToken)(void*, int, const char*, int, int, int); | |
| 768 Tcl_Obj *pEval; | |
| 769 int rc; | |
| 770 const char *zFlags; | |
| 771 | |
| 772 pOldCtx = pInst->pContext->pCtx; | |
| 773 xOldToken = pInst->pContext->xToken; | |
| 774 | |
| 775 pInst->pContext->pCtx = pCtx; | |
| 776 pInst->pContext->xToken = xToken; | |
| 777 | |
| 778 assert( | |
| 779 flags==FTS5_TOKENIZE_DOCUMENT | |
| 780 || flags==FTS5_TOKENIZE_AUX | |
| 781 || flags==FTS5_TOKENIZE_QUERY | |
| 782 || flags==(FTS5_TOKENIZE_QUERY | FTS5_TOKENIZE_PREFIX) | |
| 783 ); | |
| 784 pEval = Tcl_DuplicateObj(pInst->pScript); | |
| 785 Tcl_IncrRefCount(pEval); | |
| 786 switch( flags ){ | |
| 787 case FTS5_TOKENIZE_DOCUMENT: | |
| 788 zFlags = "document"; | |
| 789 break; | |
| 790 case FTS5_TOKENIZE_AUX: | |
| 791 zFlags = "aux"; | |
| 792 break; | |
| 793 case FTS5_TOKENIZE_QUERY: | |
| 794 zFlags = "query"; | |
| 795 break; | |
| 796 case (FTS5_TOKENIZE_PREFIX | FTS5_TOKENIZE_QUERY): | |
| 797 zFlags = "prefixquery"; | |
| 798 break; | |
| 799 default: | |
| 800 assert( 0 ); | |
| 801 zFlags = "invalid"; | |
| 802 break; | |
| 803 } | |
| 804 | |
| 805 Tcl_ListObjAppendElement(pInst->interp, pEval, Tcl_NewStringObj(zFlags, -1)); | |
| 806 Tcl_ListObjAppendElement(pInst->interp, pEval, Tcl_NewStringObj(pText,nText)); | |
| 807 rc = Tcl_EvalObjEx(pInst->interp, pEval, TCL_GLOBAL_ONLY); | |
| 808 Tcl_DecrRefCount(pEval); | |
| 809 | |
| 810 pInst->pContext->pCtx = pOldCtx; | |
| 811 pInst->pContext->xToken = xOldToken; | |
| 812 return rc; | |
| 813 } | |
| 814 | |
| 815 /* | |
| 816 ** sqlite3_fts5_token ?-colocated? TEXT START END | |
| 817 */ | |
| 818 static int f5tTokenizerReturn( | |
| 819 void * clientData, | |
| 820 Tcl_Interp *interp, | |
| 821 int objc, | |
| 822 Tcl_Obj *CONST objv[] | |
| 823 ){ | |
| 824 F5tTokenizerContext *p = (F5tTokenizerContext*)clientData; | |
| 825 int iStart; | |
| 826 int iEnd; | |
| 827 int nToken; | |
| 828 int tflags = 0; | |
| 829 char *zToken; | |
| 830 int rc; | |
| 831 | |
| 832 if( objc==5 ){ | |
| 833 int nArg; | |
| 834 char *zArg = Tcl_GetStringFromObj(objv[1], &nArg); | |
| 835 if( nArg<=10 && nArg>=2 && memcmp("-colocated", zArg, nArg)==0 ){ | |
| 836 tflags |= FTS5_TOKEN_COLOCATED; | |
| 837 }else{ | |
| 838 goto usage; | |
| 839 } | |
| 840 }else if( objc!=4 ){ | |
| 841 goto usage; | |
| 842 } | |
| 843 | |
| 844 zToken = Tcl_GetStringFromObj(objv[objc-3], &nToken); | |
| 845 if( Tcl_GetIntFromObj(interp, objv[objc-2], &iStart) | |
| 846 || Tcl_GetIntFromObj(interp, objv[objc-1], &iEnd) | |
| 847 ){ | |
| 848 return TCL_ERROR; | |
| 849 } | |
| 850 | |
| 851 if( p->xToken==0 ){ | |
| 852 Tcl_AppendResult(interp, | |
| 853 "sqlite3_fts5_token may only be used by tokenizer callback", 0 | |
| 854 ); | |
| 855 return TCL_ERROR; | |
| 856 } | |
| 857 | |
| 858 rc = p->xToken(p->pCtx, tflags, zToken, nToken, iStart, iEnd); | |
| 859 Tcl_SetResult(interp, (char*)sqlite3ErrName(rc), TCL_VOLATILE); | |
| 860 return TCL_OK; | |
| 861 | |
| 862 usage: | |
| 863 Tcl_WrongNumArgs(interp, 1, objv, "?-colocated? TEXT START END"); | |
| 864 return TCL_ERROR; | |
| 865 } | |
| 866 | |
| 867 static void f5tDelTokenizer(void *pCtx){ | |
| 868 F5tTokenizerModule *pMod = (F5tTokenizerModule*)pCtx; | |
| 869 Tcl_DecrRefCount(pMod->pScript); | |
| 870 ckfree((char *)pMod); | |
| 871 } | |
| 872 | |
| 873 /* | |
| 874 ** sqlite3_fts5_create_tokenizer DB NAME SCRIPT | |
| 875 ** | |
| 876 ** Register a tokenizer named NAME implemented by script SCRIPT. When | |
| 877 ** a tokenizer instance is created (fts5_tokenizer.xCreate), any tokenizer | |
| 878 ** arguments are appended to SCRIPT and the result executed. | |
| 879 ** | |
| 880 ** The value returned by (SCRIPT + args) is itself a tcl script. This | |
| 881 ** script - call it SCRIPT2 - is executed to tokenize text using the | |
| 882 ** tokenizer instance "returned" by SCRIPT. Specifically, to tokenize | |
| 883 ** text SCRIPT2 is invoked with a single argument appended to it - the | |
| 884 ** text to tokenize. | |
| 885 ** | |
| 886 ** SCRIPT2 should invoke the [sqlite3_fts5_token] command once for each | |
| 887 ** token within the tokenized text. | |
| 888 */ | |
| 889 static int f5tCreateTokenizer( | |
| 890 ClientData clientData, | |
| 891 Tcl_Interp *interp, | |
| 892 int objc, | |
| 893 Tcl_Obj *CONST objv[] | |
| 894 ){ | |
| 895 F5tTokenizerContext *pContext = (F5tTokenizerContext*)clientData; | |
| 896 sqlite3 *db; | |
| 897 fts5_api *pApi; | |
| 898 char *zName; | |
| 899 Tcl_Obj *pScript; | |
| 900 fts5_tokenizer t; | |
| 901 F5tTokenizerModule *pMod; | |
| 902 int rc; | |
| 903 | |
| 904 if( objc!=4 ){ | |
| 905 Tcl_WrongNumArgs(interp, 1, objv, "DB NAME SCRIPT"); | |
| 906 return TCL_ERROR; | |
| 907 } | |
| 908 if( f5tDbAndApi(interp, objv[1], &db, &pApi) ){ | |
| 909 return TCL_ERROR; | |
| 910 } | |
| 911 zName = Tcl_GetString(objv[2]); | |
| 912 pScript = objv[3]; | |
| 913 | |
| 914 t.xCreate = f5tTokenizerCreate; | |
| 915 t.xTokenize = f5tTokenizerTokenize; | |
| 916 t.xDelete = f5tTokenizerDelete; | |
| 917 | |
| 918 pMod = (F5tTokenizerModule*)ckalloc(sizeof(F5tTokenizerModule)); | |
| 919 pMod->interp = interp; | |
| 920 pMod->pScript = pScript; | |
| 921 pMod->pContext = pContext; | |
| 922 Tcl_IncrRefCount(pScript); | |
| 923 rc = pApi->xCreateTokenizer(pApi, zName, (void*)pMod, &t, f5tDelTokenizer); | |
| 924 if( rc!=SQLITE_OK ){ | |
| 925 Tcl_AppendResult(interp, "error in fts5_api.xCreateTokenizer()", 0); | |
| 926 return TCL_ERROR; | |
| 927 } | |
| 928 | |
| 929 return TCL_OK; | |
| 930 } | |
| 931 | |
| 932 static void xF5tFree(ClientData clientData){ | |
| 933 ckfree(clientData); | |
| 934 } | |
| 935 | |
| 936 /* | |
| 937 ** sqlite3_fts5_may_be_corrupt BOOLEAN | |
| 938 ** | |
| 939 ** Set or clear the global "may-be-corrupt" flag. Return the old value. | |
| 940 */ | |
| 941 static int f5tMayBeCorrupt( | |
| 942 void * clientData, | |
| 943 Tcl_Interp *interp, | |
| 944 int objc, | |
| 945 Tcl_Obj *CONST objv[] | |
| 946 ){ | |
| 947 int bOld = sqlite3_fts5_may_be_corrupt; | |
| 948 | |
| 949 if( objc!=2 && objc!=1 ){ | |
| 950 Tcl_WrongNumArgs(interp, 1, objv, "?BOOLEAN?"); | |
| 951 return TCL_ERROR; | |
| 952 } | |
| 953 if( objc==2 ){ | |
| 954 int bNew; | |
| 955 if( Tcl_GetBooleanFromObj(interp, objv[1], &bNew) ) return TCL_ERROR; | |
| 956 sqlite3_fts5_may_be_corrupt = bNew; | |
| 957 } | |
| 958 | |
| 959 Tcl_SetObjResult(interp, Tcl_NewIntObj(bOld)); | |
| 960 return TCL_OK; | |
| 961 } | |
| 962 | |
| 963 | |
| 964 static unsigned int f5t_fts5HashKey(int nSlot, const char *p, int n){ | |
| 965 int i; | |
| 966 unsigned int h = 13; | |
| 967 for(i=n-1; i>=0; i--){ | |
| 968 h = (h << 3) ^ h ^ p[i]; | |
| 969 } | |
| 970 return (h % nSlot); | |
| 971 } | |
| 972 | |
| 973 static int f5tTokenHash( | |
| 974 void * clientData, | |
| 975 Tcl_Interp *interp, | |
| 976 int objc, | |
| 977 Tcl_Obj *CONST objv[] | |
| 978 ){ | |
| 979 char *z; | |
| 980 int n; | |
| 981 unsigned int iVal; | |
| 982 int nSlot; | |
| 983 | |
| 984 if( objc!=3 ){ | |
| 985 Tcl_WrongNumArgs(interp, 1, objv, "NSLOT TOKEN"); | |
| 986 return TCL_ERROR; | |
| 987 } | |
| 988 if( Tcl_GetIntFromObj(interp, objv[1], &nSlot) ){ | |
| 989 return TCL_ERROR; | |
| 990 } | |
| 991 z = Tcl_GetStringFromObj(objv[2], &n); | |
| 992 | |
| 993 iVal = f5t_fts5HashKey(nSlot, z, n); | |
| 994 Tcl_SetObjResult(interp, Tcl_NewIntObj(iVal)); | |
| 995 return TCL_OK; | |
| 996 } | |
| 997 | |
| 998 static int f5tRegisterMatchinfo( | |
| 999 void * clientData, | |
| 1000 Tcl_Interp *interp, | |
| 1001 int objc, | |
| 1002 Tcl_Obj *CONST objv[] | |
| 1003 ){ | |
| 1004 int rc; | |
| 1005 sqlite3 *db = 0; | |
| 1006 | |
| 1007 if( objc!=2 ){ | |
| 1008 Tcl_WrongNumArgs(interp, 1, objv, "DB"); | |
| 1009 return TCL_ERROR; | |
| 1010 } | |
| 1011 if( f5tDbPointer(interp, objv[1], &db) ){ | |
| 1012 return TCL_ERROR; | |
| 1013 } | |
| 1014 | |
| 1015 rc = sqlite3Fts5TestRegisterMatchinfo(db); | |
| 1016 if( rc!=SQLITE_OK ){ | |
| 1017 Tcl_SetResult(interp, (char*)sqlite3ErrName(rc), TCL_VOLATILE); | |
| 1018 return TCL_ERROR; | |
| 1019 } | |
| 1020 return TCL_OK; | |
| 1021 } | |
| 1022 | |
| 1023 /* | |
| 1024 ** Entry point. | |
| 1025 */ | |
| 1026 int Fts5tcl_Init(Tcl_Interp *interp){ | |
| 1027 static struct Cmd { | |
| 1028 char *zName; | |
| 1029 Tcl_ObjCmdProc *xProc; | |
| 1030 int bTokenizeCtx; | |
| 1031 } aCmd[] = { | |
| 1032 { "sqlite3_fts5_create_tokenizer", f5tCreateTokenizer, 1 }, | |
| 1033 { "sqlite3_fts5_token", f5tTokenizerReturn, 1 }, | |
| 1034 { "sqlite3_fts5_tokenize", f5tTokenize, 0 }, | |
| 1035 { "sqlite3_fts5_create_function", f5tCreateFunction, 0 }, | |
| 1036 { "sqlite3_fts5_may_be_corrupt", f5tMayBeCorrupt, 0 }, | |
| 1037 { "sqlite3_fts5_token_hash", f5tTokenHash, 0 }, | |
| 1038 { "sqlite3_fts5_register_matchinfo", f5tRegisterMatchinfo, 0 } | |
| 1039 }; | |
| 1040 int i; | |
| 1041 F5tTokenizerContext *pContext; | |
| 1042 | |
| 1043 pContext = (F5tTokenizerContext*)ckalloc(sizeof(F5tTokenizerContext)); | |
| 1044 memset(pContext, 0, sizeof(*pContext)); | |
| 1045 | |
| 1046 for(i=0; i<sizeof(aCmd)/sizeof(aCmd[0]); i++){ | |
| 1047 struct Cmd *p = &aCmd[i]; | |
| 1048 void *pCtx = 0; | |
| 1049 if( p->bTokenizeCtx ) pCtx = (void*)pContext; | |
| 1050 Tcl_CreateObjCommand(interp, p->zName, p->xProc, pCtx, (i ? 0 : xF5tFree)); | |
| 1051 } | |
| 1052 | |
| 1053 return TCL_OK; | |
| 1054 } | |
| 1055 #else /* SQLITE_ENABLE_FTS5 */ | |
| 1056 int Fts5tcl_Init(Tcl_Interp *interp){ | |
| 1057 return TCL_OK; | |
| 1058 } | |
| 1059 #endif /* SQLITE_ENABLE_FTS5 */ | |
| 1060 #endif /* SQLITE_TEST */ | |
| OLD | NEW |