fts5_tcl.c 46 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658165916601661166216631664166516661667166816691670167116721673167416751676167716781679168016811682168316841685168616871688168916901691169216931694169516961697169816991700170117021703170417051706170717081709171017111712171317141715171617171718171917201721172217231724172517261727172817291730173117321733173417351736173717381739174017411742
  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. #ifdef SQLITE_TEST
  15. #include "tclsqlite.h"
  16. #ifdef SQLITE_ENABLE_FTS5
  17. #include "fts5.h"
  18. #include <string.h>
  19. #include <assert.h>
  20. #include <stdlib.h>
  21. #ifdef SQLITE_DEBUG
  22. extern int sqlite3_fts5_may_be_corrupt;
  23. #endif
  24. extern int sqlite3Fts5TestRegisterMatchinfo(sqlite3*);
  25. extern int sqlite3Fts5TestRegisterTok(sqlite3*, fts5_api*);
  26. /*************************************************************************
  27. ** This is a copy of the first part of the SqliteDb structure in
  28. ** tclsqlite.c. We need it here so that the get_sqlite_pointer routine
  29. ** can extract the sqlite3* pointer from an existing Tcl SQLite
  30. ** connection.
  31. */
  32. extern const char *sqlite3ErrName(int);
  33. struct SqliteDb {
  34. sqlite3 *db;
  35. };
  36. /*
  37. ** Decode a pointer to an sqlite3 object.
  38. */
  39. static int f5tDbPointer(Tcl_Interp *interp, Tcl_Obj *pObj, sqlite3 **ppDb){
  40. struct SqliteDb *p;
  41. Tcl_CmdInfo cmdInfo;
  42. char *z = Tcl_GetString(pObj);
  43. if( Tcl_GetCommandInfo(interp, z, &cmdInfo) ){
  44. p = (struct SqliteDb*)cmdInfo.objClientData;
  45. *ppDb = p->db;
  46. return TCL_OK;
  47. }
  48. return TCL_ERROR;
  49. }
  50. /* End of code that accesses the SqliteDb struct.
  51. **************************************************************************/
  52. static int f5tResultToErrorCode(const char *zRes){
  53. struct ErrorCode {
  54. int rc;
  55. const char *zError;
  56. } aErr[] = {
  57. { SQLITE_DONE, "SQLITE_DONE" },
  58. { SQLITE_ERROR, "SQLITE_ERROR" },
  59. { SQLITE_OK, "SQLITE_OK" },
  60. { SQLITE_OK, "" },
  61. };
  62. int i;
  63. for(i=0; i<sizeof(aErr)/sizeof(aErr[0]); i++){
  64. if( 0==sqlite3_stricmp(zRes, aErr[i].zError) ){
  65. return aErr[i].rc;
  66. }
  67. }
  68. return SQLITE_ERROR;
  69. }
  70. static int SQLITE_TCLAPI f5tDbAndApi(
  71. Tcl_Interp *interp,
  72. Tcl_Obj *pObj,
  73. sqlite3 **ppDb,
  74. fts5_api **ppApi
  75. ){
  76. sqlite3 *db = 0;
  77. int rc = f5tDbPointer(interp, pObj, &db);
  78. if( rc!=TCL_OK ){
  79. return TCL_ERROR;
  80. }else{
  81. sqlite3_stmt *pStmt = 0;
  82. fts5_api *pApi = 0;
  83. rc = sqlite3_prepare_v2(db, "SELECT fts5(?1)", -1, &pStmt, 0);
  84. if( rc!=SQLITE_OK ){
  85. Tcl_AppendResult(interp, "error: ", sqlite3_errmsg(db), (char*)0);
  86. return TCL_ERROR;
  87. }
  88. sqlite3_bind_pointer(pStmt, 1, (void*)&pApi, "fts5_api_ptr", 0);
  89. sqlite3_step(pStmt);
  90. if( sqlite3_finalize(pStmt)!=SQLITE_OK ){
  91. Tcl_AppendResult(interp, "error: ", sqlite3_errmsg(db), (char*)0);
  92. return TCL_ERROR;
  93. }
  94. *ppDb = db;
  95. *ppApi = pApi;
  96. }
  97. return TCL_OK;
  98. }
  99. typedef struct F5tFunction F5tFunction;
  100. struct F5tFunction {
  101. Tcl_Interp *interp;
  102. Tcl_Obj *pScript;
  103. };
  104. typedef struct F5tApi F5tApi;
  105. struct F5tApi {
  106. const Fts5ExtensionApi *pApi;
  107. Fts5Context *pFts;
  108. };
  109. /*
  110. ** An object of this type is used with the xSetAuxdata() and xGetAuxdata()
  111. ** API test wrappers. The tcl interface allows a single tcl value to be
  112. ** saved using xSetAuxdata(). Instead of simply storing a pointer to the
  113. ** tcl object, the code in this file wraps it in an sqlite3_malloc'd
  114. ** instance of the following struct so that if the destructor is not
  115. ** correctly invoked it will be reported as an SQLite memory leak.
  116. */
  117. typedef struct F5tAuxData F5tAuxData;
  118. struct F5tAuxData {
  119. Tcl_Obj *pObj;
  120. };
  121. static int xTokenizeCb(
  122. void *pCtx,
  123. int tflags,
  124. const char *zToken, int nToken,
  125. int iStart, int iEnd
  126. ){
  127. F5tFunction *p = (F5tFunction*)pCtx;
  128. Tcl_Obj *pEval = Tcl_DuplicateObj(p->pScript);
  129. int rc;
  130. Tcl_IncrRefCount(pEval);
  131. Tcl_ListObjAppendElement(p->interp, pEval, Tcl_NewStringObj(zToken, nToken));
  132. Tcl_ListObjAppendElement(p->interp, pEval, Tcl_NewIntObj(iStart));
  133. Tcl_ListObjAppendElement(p->interp, pEval, Tcl_NewIntObj(iEnd));
  134. rc = Tcl_EvalObjEx(p->interp, pEval, 0);
  135. Tcl_DecrRefCount(pEval);
  136. if( rc==TCL_OK ){
  137. rc = f5tResultToErrorCode(Tcl_GetStringResult(p->interp));
  138. }
  139. return rc;
  140. }
  141. static int SQLITE_TCLAPI xF5tApi(void*, Tcl_Interp*, int, Tcl_Obj *CONST []);
  142. static int xQueryPhraseCb(
  143. const Fts5ExtensionApi *pApi,
  144. Fts5Context *pFts,
  145. void *pCtx
  146. ){
  147. F5tFunction *p = (F5tFunction*)pCtx;
  148. static sqlite3_int64 iCmd = 0;
  149. Tcl_Obj *pEval;
  150. int rc;
  151. char zCmd[64];
  152. F5tApi sApi;
  153. sApi.pApi = pApi;
  154. sApi.pFts = pFts;
  155. sprintf(zCmd, "f5t_2_%lld", iCmd++);
  156. Tcl_CreateObjCommand(p->interp, zCmd, xF5tApi, &sApi, 0);
  157. pEval = Tcl_DuplicateObj(p->pScript);
  158. Tcl_IncrRefCount(pEval);
  159. Tcl_ListObjAppendElement(p->interp, pEval, Tcl_NewStringObj(zCmd, -1));
  160. rc = Tcl_EvalObjEx(p->interp, pEval, 0);
  161. Tcl_DecrRefCount(pEval);
  162. Tcl_DeleteCommand(p->interp, zCmd);
  163. if( rc==TCL_OK ){
  164. rc = f5tResultToErrorCode(Tcl_GetStringResult(p->interp));
  165. }
  166. return rc;
  167. }
  168. static void xSetAuxdataDestructor(void *p){
  169. F5tAuxData *pData = (F5tAuxData*)p;
  170. Tcl_DecrRefCount(pData->pObj);
  171. sqlite3_free(pData);
  172. }
  173. /*
  174. ** api sub-command...
  175. **
  176. ** Description...
  177. */
  178. static int SQLITE_TCLAPI xF5tApi(
  179. void * clientData,
  180. Tcl_Interp *interp,
  181. int objc,
  182. Tcl_Obj *CONST objv[]
  183. ){
  184. struct Sub {
  185. const char *zName;
  186. int nArg;
  187. const char *zMsg;
  188. } aSub[] = {
  189. { "xColumnCount", 0, "" }, /* 0 */
  190. { "xRowCount", 0, "" }, /* 1 */
  191. { "xColumnTotalSize", 1, "COL" }, /* 2 */
  192. { "xTokenize", 2, "TEXT SCRIPT" }, /* 3 */
  193. { "xPhraseCount", 0, "" }, /* 4 */
  194. { "xPhraseSize", 1, "PHRASE" }, /* 5 */
  195. { "xInstCount", 0, "" }, /* 6 */
  196. { "xInst", 1, "IDX" }, /* 7 */
  197. { "xRowid", 0, "" }, /* 8 */
  198. { "xColumnText", 1, "COL" }, /* 9 */
  199. { "xColumnSize", 1, "COL" }, /* 10 */
  200. { "xQueryPhrase", 2, "PHRASE SCRIPT" }, /* 11 */
  201. { "xSetAuxdata", 1, "VALUE" }, /* 12 */
  202. { "xGetAuxdata", 1, "CLEAR" }, /* 13 */
  203. { "xSetAuxdataInt", 1, "INTEGER" }, /* 14 */
  204. { "xGetAuxdataInt", 1, "CLEAR" }, /* 15 */
  205. { "xPhraseForeach", 4, "IPHRASE COLVAR OFFVAR SCRIPT" }, /* 16 */
  206. { "xPhraseColumnForeach", 3, "IPHRASE COLVAR SCRIPT" }, /* 17 */
  207. { "xQueryToken", 2, "IPHRASE ITERM" }, /* 18 */
  208. { "xInstToken", 2, "IDX ITERM" }, /* 19 */
  209. { "xColumnLocale", 1, "COL" }, /* 20 */
  210. { 0, 0, 0}
  211. };
  212. int rc;
  213. int iSub = 0;
  214. F5tApi *p = (F5tApi*)clientData;
  215. if( objc<2 ){
  216. Tcl_WrongNumArgs(interp, 1, objv, "SUB-COMMAND");
  217. return TCL_ERROR;
  218. }
  219. rc = Tcl_GetIndexFromObjStruct(
  220. interp, objv[1], aSub, sizeof(aSub[0]), "SUB-COMMAND", 0, &iSub
  221. );
  222. if( rc!=TCL_OK ) return rc;
  223. if( aSub[iSub].nArg!=objc-2 ){
  224. Tcl_WrongNumArgs(interp, 1, objv, aSub[iSub].zMsg);
  225. return TCL_ERROR;
  226. }
  227. #define CASE(i,str) case i: assert( strcmp(aSub[i].zName, str)==0 );
  228. switch( iSub ){
  229. CASE(0, "xColumnCount") {
  230. int nCol;
  231. nCol = p->pApi->xColumnCount(p->pFts);
  232. if( rc==SQLITE_OK ){
  233. Tcl_SetObjResult(interp, Tcl_NewIntObj(nCol));
  234. }
  235. break;
  236. }
  237. CASE(1, "xRowCount") {
  238. sqlite3_int64 nRow;
  239. rc = p->pApi->xRowCount(p->pFts, &nRow);
  240. if( rc==SQLITE_OK ){
  241. Tcl_SetObjResult(interp, Tcl_NewWideIntObj(nRow));
  242. }
  243. break;
  244. }
  245. CASE(2, "xColumnTotalSize") {
  246. int iCol;
  247. sqlite3_int64 nSize;
  248. if( Tcl_GetIntFromObj(interp, objv[2], &iCol) ) return TCL_ERROR;
  249. rc = p->pApi->xColumnTotalSize(p->pFts, iCol, &nSize);
  250. if( rc==SQLITE_OK ){
  251. Tcl_SetObjResult(interp, Tcl_NewWideIntObj(nSize));
  252. }
  253. break;
  254. }
  255. CASE(3, "xTokenize") {
  256. Tcl_Size nText;
  257. char *zText = Tcl_GetStringFromObj(objv[2], &nText);
  258. F5tFunction ctx;
  259. ctx.interp = interp;
  260. ctx.pScript = objv[3];
  261. rc = p->pApi->xTokenize(p->pFts, zText, (int)nText, &ctx, xTokenizeCb);
  262. if( rc==SQLITE_OK ){
  263. Tcl_ResetResult(interp);
  264. }
  265. return rc;
  266. }
  267. CASE(4, "xPhraseCount") {
  268. int nPhrase;
  269. nPhrase = p->pApi->xPhraseCount(p->pFts);
  270. if( rc==SQLITE_OK ){
  271. Tcl_SetObjResult(interp, Tcl_NewIntObj(nPhrase));
  272. }
  273. break;
  274. }
  275. CASE(5, "xPhraseSize") {
  276. int iPhrase;
  277. int sz;
  278. if( Tcl_GetIntFromObj(interp, objv[2], &iPhrase) ){
  279. return TCL_ERROR;
  280. }
  281. sz = p->pApi->xPhraseSize(p->pFts, iPhrase);
  282. if( rc==SQLITE_OK ){
  283. Tcl_SetObjResult(interp, Tcl_NewIntObj(sz));
  284. }
  285. break;
  286. }
  287. CASE(6, "xInstCount") {
  288. int nInst;
  289. rc = p->pApi->xInstCount(p->pFts, &nInst);
  290. if( rc==SQLITE_OK ){
  291. Tcl_SetObjResult(interp, Tcl_NewIntObj(nInst));
  292. }
  293. break;
  294. }
  295. CASE(7, "xInst") {
  296. int iIdx, ip, ic, io;
  297. if( Tcl_GetIntFromObj(interp, objv[2], &iIdx) ){
  298. return TCL_ERROR;
  299. }
  300. rc = p->pApi->xInst(p->pFts, iIdx, &ip, &ic, &io);
  301. if( rc==SQLITE_OK ){
  302. Tcl_Obj *pList = Tcl_NewObj();
  303. Tcl_ListObjAppendElement(interp, pList, Tcl_NewIntObj(ip));
  304. Tcl_ListObjAppendElement(interp, pList, Tcl_NewIntObj(ic));
  305. Tcl_ListObjAppendElement(interp, pList, Tcl_NewIntObj(io));
  306. Tcl_SetObjResult(interp, pList);
  307. }
  308. break;
  309. }
  310. CASE(8, "xRowid") {
  311. sqlite3_int64 iRowid = p->pApi->xRowid(p->pFts);
  312. Tcl_SetObjResult(interp, Tcl_NewWideIntObj(iRowid));
  313. break;
  314. }
  315. CASE(9, "xColumnText") {
  316. const char *z = 0;
  317. int n = 0;
  318. int iCol;
  319. if( Tcl_GetIntFromObj(interp, objv[2], &iCol) ){
  320. return TCL_ERROR;
  321. }
  322. rc = p->pApi->xColumnText(p->pFts, iCol, &z, &n);
  323. if( rc==SQLITE_OK ){
  324. Tcl_SetObjResult(interp, Tcl_NewStringObj(z, n));
  325. }
  326. break;
  327. }
  328. CASE(10, "xColumnSize") {
  329. int n = 0;
  330. int iCol;
  331. if( Tcl_GetIntFromObj(interp, objv[2], &iCol) ){
  332. return TCL_ERROR;
  333. }
  334. rc = p->pApi->xColumnSize(p->pFts, iCol, &n);
  335. if( rc==SQLITE_OK ){
  336. Tcl_SetObjResult(interp, Tcl_NewIntObj(n));
  337. }
  338. break;
  339. }
  340. CASE(11, "xQueryPhrase") {
  341. int iPhrase;
  342. F5tFunction ctx;
  343. if( Tcl_GetIntFromObj(interp, objv[2], &iPhrase) ){
  344. return TCL_ERROR;
  345. }
  346. ctx.interp = interp;
  347. ctx.pScript = objv[3];
  348. rc = p->pApi->xQueryPhrase(p->pFts, iPhrase, &ctx, xQueryPhraseCb);
  349. if( rc==SQLITE_OK ){
  350. Tcl_ResetResult(interp);
  351. }
  352. break;
  353. }
  354. CASE(12, "xSetAuxdata") {
  355. F5tAuxData *pData = (F5tAuxData*)sqlite3_malloc(sizeof(F5tAuxData));
  356. if( pData==0 ){
  357. Tcl_AppendResult(interp, "out of memory", (char*)0);
  358. return TCL_ERROR;
  359. }
  360. pData->pObj = objv[2];
  361. Tcl_IncrRefCount(pData->pObj);
  362. rc = p->pApi->xSetAuxdata(p->pFts, pData, xSetAuxdataDestructor);
  363. break;
  364. }
  365. CASE(13, "xGetAuxdata") {
  366. F5tAuxData *pData;
  367. int bClear;
  368. if( Tcl_GetBooleanFromObj(interp, objv[2], &bClear) ){
  369. return TCL_ERROR;
  370. }
  371. pData = (F5tAuxData*)p->pApi->xGetAuxdata(p->pFts, bClear);
  372. if( pData==0 ){
  373. Tcl_ResetResult(interp);
  374. }else{
  375. Tcl_SetObjResult(interp, pData->pObj);
  376. if( bClear ){
  377. xSetAuxdataDestructor((void*)pData);
  378. }
  379. }
  380. break;
  381. }
  382. /* These two - xSetAuxdataInt and xGetAuxdataInt - are similar to the
  383. ** xSetAuxdata and xGetAuxdata methods implemented above. The difference
  384. ** is that they may only save an integer value as auxiliary data, and
  385. ** do not specify a destructor function. */
  386. CASE(14, "xSetAuxdataInt") {
  387. int iVal;
  388. if( Tcl_GetIntFromObj(interp, objv[2], &iVal) ) return TCL_ERROR;
  389. rc = p->pApi->xSetAuxdata(p->pFts, (void*)((char*)0 + iVal), 0);
  390. break;
  391. }
  392. CASE(15, "xGetAuxdataInt") {
  393. int iVal;
  394. int bClear;
  395. if( Tcl_GetBooleanFromObj(interp, objv[2], &bClear) ) return TCL_ERROR;
  396. iVal = (int)((char*)p->pApi->xGetAuxdata(p->pFts, bClear) - (char*)0);
  397. Tcl_SetObjResult(interp, Tcl_NewIntObj(iVal));
  398. break;
  399. }
  400. CASE(16, "xPhraseForeach") {
  401. int iPhrase;
  402. int iCol;
  403. int iOff;
  404. const char *zColvar;
  405. const char *zOffvar;
  406. Tcl_Obj *pScript = objv[5];
  407. Fts5PhraseIter iter;
  408. if( Tcl_GetIntFromObj(interp, objv[2], &iPhrase) ) return TCL_ERROR;
  409. zColvar = Tcl_GetString(objv[3]);
  410. zOffvar = Tcl_GetString(objv[4]);
  411. rc = p->pApi->xPhraseFirst(p->pFts, iPhrase, &iter, &iCol, &iOff);
  412. if( rc!=SQLITE_OK ){
  413. Tcl_AppendResult(interp, sqlite3ErrName(rc), (char*)0);
  414. return TCL_ERROR;
  415. }
  416. for( ;iCol>=0; p->pApi->xPhraseNext(p->pFts, &iter, &iCol, &iOff) ){
  417. Tcl_SetVar2Ex(interp, zColvar, 0, Tcl_NewIntObj(iCol), 0);
  418. Tcl_SetVar2Ex(interp, zOffvar, 0, Tcl_NewIntObj(iOff), 0);
  419. rc = Tcl_EvalObjEx(interp, pScript, 0);
  420. if( rc==TCL_CONTINUE ) rc = TCL_OK;
  421. if( rc!=TCL_OK ){
  422. if( rc==TCL_BREAK ) rc = TCL_OK;
  423. break;
  424. }
  425. }
  426. break;
  427. }
  428. CASE(17, "xPhraseColumnForeach") {
  429. int iPhrase;
  430. int iCol;
  431. const char *zColvar;
  432. Tcl_Obj *pScript = objv[4];
  433. Fts5PhraseIter iter;
  434. if( Tcl_GetIntFromObj(interp, objv[2], &iPhrase) ) return TCL_ERROR;
  435. zColvar = Tcl_GetString(objv[3]);
  436. rc = p->pApi->xPhraseFirstColumn(p->pFts, iPhrase, &iter, &iCol);
  437. if( rc!=SQLITE_OK ){
  438. Tcl_SetResult(interp, (char*)sqlite3ErrName(rc), TCL_VOLATILE);
  439. return TCL_ERROR;
  440. }
  441. for( ; iCol>=0; p->pApi->xPhraseNextColumn(p->pFts, &iter, &iCol)){
  442. Tcl_SetVar2Ex(interp, zColvar, 0, Tcl_NewIntObj(iCol), 0);
  443. rc = Tcl_EvalObjEx(interp, pScript, 0);
  444. if( rc==TCL_CONTINUE ) rc = TCL_OK;
  445. if( rc!=TCL_OK ){
  446. if( rc==TCL_BREAK ) rc = TCL_OK;
  447. break;
  448. }
  449. }
  450. break;
  451. }
  452. CASE(18, "xQueryToken") {
  453. const char *pTerm = 0;
  454. int nTerm = 0;
  455. int iPhrase = 0;
  456. int iTerm = 0;
  457. if( Tcl_GetIntFromObj(interp, objv[2], &iPhrase) ) return TCL_ERROR;
  458. if( Tcl_GetIntFromObj(interp, objv[3], &iTerm) ) return TCL_ERROR;
  459. rc = p->pApi->xQueryToken(p->pFts, iPhrase, iTerm, &pTerm, &nTerm);
  460. if( rc==SQLITE_OK ){
  461. Tcl_SetObjResult(interp, Tcl_NewStringObj(pTerm, nTerm));
  462. }
  463. break;
  464. }
  465. CASE(19, "xInstToken") {
  466. const char *pTerm = 0;
  467. int nTerm = 0;
  468. int iIdx = 0;
  469. int iTerm = 0;
  470. if( Tcl_GetIntFromObj(interp, objv[2], &iIdx) ) return TCL_ERROR;
  471. if( Tcl_GetIntFromObj(interp, objv[3], &iTerm) ) return TCL_ERROR;
  472. rc = p->pApi->xInstToken(p->pFts, iIdx, iTerm, &pTerm, &nTerm);
  473. if( rc==SQLITE_OK ){
  474. Tcl_SetObjResult(interp, Tcl_NewStringObj(pTerm, nTerm));
  475. }
  476. break;
  477. }
  478. CASE(20, "xColumnLocale") {
  479. const char *z = 0;
  480. int n = 0;
  481. int iCol;
  482. if( Tcl_GetIntFromObj(interp, objv[2], &iCol) ){
  483. return TCL_ERROR;
  484. }
  485. rc = p->pApi->xColumnLocale(p->pFts, iCol, &z, &n);
  486. if( rc==SQLITE_OK && z ){
  487. Tcl_SetObjResult(interp, Tcl_NewStringObj(z, n));
  488. }
  489. break;
  490. }
  491. default:
  492. assert( 0 );
  493. break;
  494. }
  495. #undef CASE
  496. if( rc!=SQLITE_OK ){
  497. Tcl_SetResult(interp, (char*)sqlite3ErrName(rc), TCL_VOLATILE);
  498. return TCL_ERROR;
  499. }
  500. return TCL_OK;
  501. }
  502. static void xF5tFunction(
  503. const Fts5ExtensionApi *pApi, /* API offered by current FTS version */
  504. Fts5Context *pFts, /* First arg to pass to pApi functions */
  505. sqlite3_context *pCtx, /* Context for returning result/error */
  506. int nVal, /* Number of values in apVal[] array */
  507. sqlite3_value **apVal /* Array of trailing arguments */
  508. ){
  509. F5tFunction *p = (F5tFunction*)pApi->xUserData(pFts);
  510. Tcl_Obj *pEval; /* Script to evaluate */
  511. int i;
  512. int rc;
  513. static sqlite3_int64 iCmd = 0;
  514. char zCmd[64];
  515. F5tApi sApi;
  516. sApi.pApi = pApi;
  517. sApi.pFts = pFts;
  518. sprintf(zCmd, "f5t_%lld", iCmd++);
  519. Tcl_CreateObjCommand(p->interp, zCmd, xF5tApi, &sApi, 0);
  520. pEval = Tcl_DuplicateObj(p->pScript);
  521. Tcl_IncrRefCount(pEval);
  522. Tcl_ListObjAppendElement(p->interp, pEval, Tcl_NewStringObj(zCmd, -1));
  523. for(i=0; i<nVal; i++){
  524. Tcl_Obj *pObj = 0;
  525. switch( sqlite3_value_type(apVal[i]) ){
  526. case SQLITE_TEXT:
  527. pObj = Tcl_NewStringObj((const char*)sqlite3_value_text(apVal[i]), -1);
  528. break;
  529. case SQLITE_BLOB:
  530. pObj = Tcl_NewByteArrayObj(
  531. sqlite3_value_blob(apVal[i]), sqlite3_value_bytes(apVal[i])
  532. );
  533. break;
  534. case SQLITE_INTEGER:
  535. pObj = Tcl_NewWideIntObj(sqlite3_value_int64(apVal[i]));
  536. break;
  537. case SQLITE_FLOAT:
  538. pObj = Tcl_NewDoubleObj(sqlite3_value_double(apVal[i]));
  539. break;
  540. default:
  541. pObj = Tcl_NewObj();
  542. break;
  543. }
  544. Tcl_ListObjAppendElement(p->interp, pEval, pObj);
  545. }
  546. rc = Tcl_EvalObjEx(p->interp, pEval, TCL_GLOBAL_ONLY);
  547. Tcl_DecrRefCount(pEval);
  548. Tcl_DeleteCommand(p->interp, zCmd);
  549. if( rc!=TCL_OK ){
  550. sqlite3_result_error(pCtx, Tcl_GetStringResult(p->interp), -1);
  551. }else{
  552. Tcl_Obj *pVar = Tcl_GetObjResult(p->interp);
  553. const char *zType = (pVar->typePtr ? pVar->typePtr->name : "");
  554. char c = zType[0];
  555. if( c=='b' && strcmp(zType,"bytearray")==0 && pVar->bytes==0 ){
  556. /* Only return a BLOB type if the Tcl variable is a bytearray and
  557. ** has no string representation. */
  558. Tcl_Size nn;
  559. unsigned char *data = Tcl_GetByteArrayFromObj(pVar, &nn);
  560. sqlite3_result_blob(pCtx, data, (int)nn, SQLITE_TRANSIENT);
  561. }else if( c=='b' && strcmp(zType,"boolean")==0 ){
  562. int n;
  563. Tcl_GetIntFromObj(0, pVar, &n);
  564. sqlite3_result_int(pCtx, n);
  565. }else if( c=='d' && strcmp(zType,"double")==0 ){
  566. double r;
  567. Tcl_GetDoubleFromObj(0, pVar, &r);
  568. sqlite3_result_double(pCtx, r);
  569. }else if( (c=='w' && strcmp(zType,"wideInt")==0) ||
  570. (c=='i' && strcmp(zType,"int")==0) ){
  571. Tcl_WideInt v;
  572. Tcl_GetWideIntFromObj(0, pVar, &v);
  573. sqlite3_result_int64(pCtx, v);
  574. }else{
  575. Tcl_Size nn;
  576. unsigned char *data = (unsigned char *)Tcl_GetStringFromObj(pVar, &nn);
  577. sqlite3_result_text(pCtx, (char*)data, (int)nn, SQLITE_TRANSIENT);
  578. }
  579. }
  580. }
  581. static void xF5tDestroy(void *pCtx){
  582. F5tFunction *p = (F5tFunction*)pCtx;
  583. Tcl_DecrRefCount(p->pScript);
  584. ckfree((char *)p);
  585. }
  586. /*
  587. ** sqlite3_fts5_create_function DB NAME SCRIPT
  588. **
  589. ** Description...
  590. */
  591. static int SQLITE_TCLAPI f5tCreateFunction(
  592. void * clientData,
  593. Tcl_Interp *interp,
  594. int objc,
  595. Tcl_Obj *CONST objv[]
  596. ){
  597. char *zName;
  598. Tcl_Obj *pScript;
  599. sqlite3 *db = 0;
  600. fts5_api *pApi = 0;
  601. F5tFunction *pCtx = 0;
  602. int rc;
  603. if( objc!=4 ){
  604. Tcl_WrongNumArgs(interp, 1, objv, "DB NAME SCRIPT");
  605. return TCL_ERROR;
  606. }
  607. if( f5tDbAndApi(interp, objv[1], &db, &pApi) ) return TCL_ERROR;
  608. zName = Tcl_GetString(objv[2]);
  609. pScript = objv[3];
  610. pCtx = (F5tFunction*)ckalloc(sizeof(F5tFunction));
  611. pCtx->interp = interp;
  612. pCtx->pScript = pScript;
  613. Tcl_IncrRefCount(pScript);
  614. rc = pApi->xCreateFunction(
  615. pApi, zName, (void*)pCtx, xF5tFunction, xF5tDestroy
  616. );
  617. if( rc!=SQLITE_OK ){
  618. Tcl_AppendResult(interp, "error: ", sqlite3_errmsg(db), (char*)0);
  619. return TCL_ERROR;
  620. }
  621. return TCL_OK;
  622. }
  623. typedef struct F5tTokenizeCtx F5tTokenizeCtx;
  624. struct F5tTokenizeCtx {
  625. Tcl_Obj *pRet;
  626. int bSubst;
  627. const char *zInput;
  628. };
  629. static int xTokenizeCb2(
  630. void *pCtx,
  631. int tflags,
  632. const char *zToken, int nToken,
  633. int iStart, int iEnd
  634. ){
  635. F5tTokenizeCtx *p = (F5tTokenizeCtx*)pCtx;
  636. if( p->bSubst ){
  637. Tcl_ListObjAppendElement(0, p->pRet, Tcl_NewStringObj(zToken, nToken));
  638. Tcl_ListObjAppendElement(
  639. 0, p->pRet, Tcl_NewStringObj(&p->zInput[iStart], iEnd-iStart)
  640. );
  641. }else{
  642. Tcl_ListObjAppendElement(0, p->pRet, Tcl_NewStringObj(zToken, nToken));
  643. Tcl_ListObjAppendElement(0, p->pRet, Tcl_NewIntObj(iStart));
  644. Tcl_ListObjAppendElement(0, p->pRet, Tcl_NewIntObj(iEnd));
  645. }
  646. return SQLITE_OK;
  647. }
  648. /*
  649. ** sqlite3_fts5_tokenize DB TOKENIZER TEXT
  650. **
  651. ** Description...
  652. */
  653. static int SQLITE_TCLAPI f5tTokenize(
  654. void * clientData,
  655. Tcl_Interp *interp,
  656. int objc,
  657. Tcl_Obj *CONST objv[]
  658. ){
  659. char *pCopy = 0;
  660. char *zText = 0;
  661. Tcl_Size nText = 0;
  662. sqlite3 *db = 0;
  663. fts5_api *pApi = 0;
  664. Fts5Tokenizer *pTok = 0;
  665. fts5_tokenizer tokenizer;
  666. Tcl_Obj *pRet = 0;
  667. void *pUserdata;
  668. int rc;
  669. Tcl_Size nArg;
  670. const char **azArg;
  671. F5tTokenizeCtx ctx;
  672. if( objc!=4 && objc!=5 ){
  673. Tcl_WrongNumArgs(interp, 1, objv, "?-subst? DB NAME TEXT");
  674. return TCL_ERROR;
  675. }
  676. if( objc==5 ){
  677. char *zOpt = Tcl_GetString(objv[1]);
  678. if( strcmp("-subst", zOpt) ){
  679. Tcl_AppendResult(interp, "unrecognized option: ", zOpt, (char*)0);
  680. return TCL_ERROR;
  681. }
  682. }
  683. if( f5tDbAndApi(interp, objv[objc-3], &db, &pApi) ) return TCL_ERROR;
  684. if( Tcl_SplitList(interp, Tcl_GetString(objv[objc-2]), &nArg, &azArg) ){
  685. return TCL_ERROR;
  686. }
  687. if( nArg==0 ){
  688. Tcl_AppendResult(interp, "no such tokenizer: ", (char*)0);
  689. Tcl_Free((void*)azArg);
  690. return TCL_ERROR;
  691. }
  692. zText = Tcl_GetStringFromObj(objv[objc-1], &nText);
  693. rc = pApi->xFindTokenizer(pApi, azArg[0], &pUserdata, &tokenizer);
  694. if( rc!=SQLITE_OK ){
  695. Tcl_AppendResult(interp, "no such tokenizer: ", azArg[0], (char*)0);
  696. return TCL_ERROR;
  697. }
  698. rc = tokenizer.xCreate(pUserdata, &azArg[1], (int)(nArg-1), &pTok);
  699. if( rc!=SQLITE_OK ){
  700. Tcl_AppendResult(interp, "error in tokenizer.xCreate()", (char*)0);
  701. return TCL_ERROR;
  702. }
  703. if( nText>0 ){
  704. pCopy = sqlite3_malloc(nText);
  705. if( pCopy==0 ){
  706. tokenizer.xDelete(pTok);
  707. Tcl_AppendResult(interp, "error in sqlite3_malloc()", (char*)0);
  708. return TCL_ERROR;
  709. }else{
  710. memcpy(pCopy, zText, nText);
  711. }
  712. }
  713. pRet = Tcl_NewObj();
  714. Tcl_IncrRefCount(pRet);
  715. ctx.bSubst = (objc==5);
  716. ctx.pRet = pRet;
  717. ctx.zInput = pCopy;
  718. rc = tokenizer.xTokenize(
  719. pTok, (void*)&ctx, FTS5_TOKENIZE_DOCUMENT, pCopy,(int)nText, xTokenizeCb2
  720. );
  721. tokenizer.xDelete(pTok);
  722. sqlite3_free(pCopy);
  723. if( rc!=SQLITE_OK ){
  724. Tcl_AppendResult(interp, "error in tokenizer.xTokenize()", (char*)0);
  725. Tcl_DecrRefCount(pRet);
  726. return TCL_ERROR;
  727. }
  728. Tcl_Free((void*)azArg);
  729. Tcl_SetObjResult(interp, pRet);
  730. Tcl_DecrRefCount(pRet);
  731. return TCL_OK;
  732. }
  733. /*************************************************************************
  734. ** Start of tokenizer wrapper.
  735. */
  736. typedef struct F5tTokenizerContext F5tTokenizerContext;
  737. typedef struct F5tTokenizerCb F5tTokenizerCb;
  738. typedef struct F5tTokenizerModule F5tTokenizerModule;
  739. typedef struct F5tTokenizerInstance F5tTokenizerInstance;
  740. struct F5tTokenizerContext {
  741. void *pCtx;
  742. int (*xToken)(void*, int, const char*, int, int, int);
  743. F5tTokenizerInstance *pInst;
  744. };
  745. struct F5tTokenizerModule {
  746. Tcl_Interp *interp;
  747. Tcl_Obj *pScript;
  748. void *pParentCtx;
  749. fts5_tokenizer_v2 parent_v2;
  750. fts5_tokenizer parent;
  751. F5tTokenizerContext *pContext;
  752. };
  753. /*
  754. ** zLocale:
  755. ** Within a call to xTokenize_v2(), pLocale/nLocale store the locale
  756. ** passed to the call by fts5. This can be retrieved by a Tcl tokenize
  757. ** script using [sqlite3_fts5_locale].
  758. */
  759. struct F5tTokenizerInstance {
  760. Tcl_Interp *interp;
  761. Tcl_Obj *pScript;
  762. F5tTokenizerModule *pModule;
  763. Fts5Tokenizer *pParent;
  764. F5tTokenizerContext *pContext;
  765. const char *pLocale;
  766. int nLocale;
  767. };
  768. static int f5tTokenizerCreate(
  769. void *pCtx,
  770. const char **azArg,
  771. int nArg,
  772. Fts5Tokenizer **ppOut
  773. ){
  774. Fts5Tokenizer *pParent = 0;
  775. F5tTokenizerModule *pMod = (F5tTokenizerModule*)pCtx;
  776. Tcl_Obj *pEval;
  777. int rc = TCL_OK;
  778. int i;
  779. assert( pMod->parent_v2.xCreate==0 || pMod->parent.xCreate==0 );
  780. if( pMod->parent_v2.xCreate ){
  781. rc = pMod->parent_v2.xCreate(pMod->pParentCtx, 0, 0, &pParent);
  782. }
  783. if( pMod->parent.xCreate ){
  784. rc = pMod->parent.xCreate(pMod->pParentCtx, 0, 0, &pParent);
  785. }
  786. pEval = Tcl_DuplicateObj(pMod->pScript);
  787. Tcl_IncrRefCount(pEval);
  788. for(i=0; rc==TCL_OK && i<nArg; i++){
  789. Tcl_Obj *pObj = Tcl_NewStringObj(azArg[i], -1);
  790. rc = Tcl_ListObjAppendElement(pMod->interp, pEval, pObj);
  791. }
  792. if( rc==TCL_OK ){
  793. rc = Tcl_EvalObjEx(pMod->interp, pEval, TCL_GLOBAL_ONLY);
  794. }
  795. Tcl_DecrRefCount(pEval);
  796. if( rc==TCL_OK ){
  797. F5tTokenizerInstance *pInst;
  798. pInst = (F5tTokenizerInstance*)ckalloc(sizeof(F5tTokenizerInstance));
  799. memset(pInst, 0, sizeof(F5tTokenizerInstance));
  800. pInst->interp = pMod->interp;
  801. pInst->pScript = Tcl_GetObjResult(pMod->interp);
  802. pInst->pContext = pMod->pContext;
  803. pInst->pParent = pParent;
  804. pInst->pModule = pMod;
  805. Tcl_IncrRefCount(pInst->pScript);
  806. *ppOut = (Fts5Tokenizer*)pInst;
  807. }
  808. return rc;
  809. }
  810. static void f5tTokenizerDelete(Fts5Tokenizer *p){
  811. F5tTokenizerInstance *pInst = (F5tTokenizerInstance*)p;
  812. if( pInst ){
  813. if( pInst->pParent ){
  814. if( pInst->pModule->parent_v2.xDelete ){
  815. pInst->pModule->parent_v2.xDelete(pInst->pParent);
  816. }else{
  817. pInst->pModule->parent.xDelete(pInst->pParent);
  818. }
  819. }
  820. Tcl_DecrRefCount(pInst->pScript);
  821. ckfree((char *)pInst);
  822. }
  823. }
  824. static int f5tTokenizerReallyTokenize(
  825. Fts5Tokenizer *p,
  826. void *pCtx,
  827. int flags,
  828. const char *pText, int nText,
  829. int (*xToken)(void*, int, const char*, int, int, int)
  830. ){
  831. F5tTokenizerInstance *pInst = (F5tTokenizerInstance*)p;
  832. F5tTokenizerInstance *pOldInst = 0;
  833. void *pOldCtx;
  834. int (*xOldToken)(void*, int, const char*, int, int, int);
  835. Tcl_Obj *pEval;
  836. int rc;
  837. const char *zFlags;
  838. pOldCtx = pInst->pContext->pCtx;
  839. xOldToken = pInst->pContext->xToken;
  840. pOldInst = pInst->pContext->pInst;
  841. pInst->pContext->pCtx = pCtx;
  842. pInst->pContext->xToken = xToken;
  843. pInst->pContext->pInst = pInst;
  844. assert(
  845. flags==FTS5_TOKENIZE_DOCUMENT
  846. || flags==FTS5_TOKENIZE_AUX
  847. || flags==FTS5_TOKENIZE_QUERY
  848. || flags==(FTS5_TOKENIZE_QUERY | FTS5_TOKENIZE_PREFIX)
  849. );
  850. pEval = Tcl_DuplicateObj(pInst->pScript);
  851. Tcl_IncrRefCount(pEval);
  852. switch( flags ){
  853. case FTS5_TOKENIZE_DOCUMENT:
  854. zFlags = "document";
  855. break;
  856. case FTS5_TOKENIZE_AUX:
  857. zFlags = "aux";
  858. break;
  859. case FTS5_TOKENIZE_QUERY:
  860. zFlags = "query";
  861. break;
  862. case (FTS5_TOKENIZE_PREFIX | FTS5_TOKENIZE_QUERY):
  863. zFlags = "prefixquery";
  864. break;
  865. default:
  866. assert( 0 );
  867. zFlags = "invalid";
  868. break;
  869. }
  870. Tcl_ListObjAppendElement(pInst->interp, pEval, Tcl_NewStringObj(zFlags, -1));
  871. Tcl_ListObjAppendElement(pInst->interp, pEval, Tcl_NewStringObj(pText,nText));
  872. rc = Tcl_EvalObjEx(pInst->interp, pEval, TCL_GLOBAL_ONLY);
  873. Tcl_DecrRefCount(pEval);
  874. pInst->pContext->pCtx = pOldCtx;
  875. pInst->pContext->xToken = xOldToken;
  876. pInst->pContext->pInst = pOldInst;
  877. return rc;
  878. }
  879. typedef struct CallbackCtx CallbackCtx;
  880. struct CallbackCtx {
  881. Fts5Tokenizer *p;
  882. void *pCtx;
  883. int flags;
  884. int (*xToken)(void*, int, const char*, int, int, int);
  885. };
  886. static int f5tTokenizeCallback(
  887. void *pCtx,
  888. int tflags,
  889. const char *z, int n,
  890. int iStart, int iEnd
  891. ){
  892. CallbackCtx *p = (CallbackCtx*)pCtx;
  893. return f5tTokenizerReallyTokenize(p->p, p->pCtx, p->flags, z, n, p->xToken);
  894. }
  895. static int f5tTokenizerTokenize_v2(
  896. Fts5Tokenizer *p,
  897. void *pCtx,
  898. int flags,
  899. const char *pText, int nText,
  900. const char *pLoc, int nLoc,
  901. int (*xToken)(void*, int, const char*, int, int, int)
  902. ){
  903. int rc = SQLITE_OK;
  904. F5tTokenizerInstance *pInst = (F5tTokenizerInstance*)p;
  905. pInst->pLocale = pLoc;
  906. pInst->nLocale = nLoc;
  907. if( pInst->pParent ){
  908. CallbackCtx ctx;
  909. ctx.p = p;
  910. ctx.pCtx = pCtx;
  911. ctx.flags = flags;
  912. ctx.xToken = xToken;
  913. if( pInst->pModule->parent_v2.xTokenize ){
  914. rc = pInst->pModule->parent_v2.xTokenize(
  915. pInst->pParent, (void*)&ctx, flags, pText, nText,
  916. pLoc, nLoc, f5tTokenizeCallback
  917. );
  918. }else{
  919. rc = pInst->pModule->parent.xTokenize(
  920. pInst->pParent, (void*)&ctx, flags, pText, nText, f5tTokenizeCallback
  921. );
  922. }
  923. }else{
  924. rc = f5tTokenizerReallyTokenize(p, pCtx, flags, pText, nText, xToken);
  925. }
  926. pInst->pLocale = 0;
  927. pInst->nLocale = 0;
  928. return rc;
  929. }
  930. static int f5tTokenizerTokenize(
  931. Fts5Tokenizer *p,
  932. void *pCtx,
  933. int flags,
  934. const char *pText, int nText,
  935. int (*xToken)(void*, int, const char*, int, int, int)
  936. ){
  937. return f5tTokenizerTokenize_v2(p, pCtx, flags, pText, nText, 0, 0, xToken);
  938. }
  939. /*
  940. ** sqlite3_fts5_locale
  941. */
  942. static int SQLITE_TCLAPI f5tTokenizerLocale(
  943. void * clientData,
  944. Tcl_Interp *interp,
  945. int objc,
  946. Tcl_Obj *CONST objv[]
  947. ){
  948. F5tTokenizerContext *p = (F5tTokenizerContext*)clientData;
  949. if( objc!=1 ){
  950. Tcl_WrongNumArgs(interp, 1, objv, "");
  951. return TCL_ERROR;
  952. }
  953. if( p->xToken==0 ){
  954. Tcl_AppendResult(interp,
  955. "sqlite3_fts5_locale may only be used by tokenizer callback", (char*)0
  956. );
  957. return TCL_ERROR;
  958. }
  959. Tcl_SetObjResult(interp,
  960. Tcl_NewStringObj(p->pInst->pLocale, p->pInst->nLocale)
  961. );
  962. return TCL_OK;
  963. }
  964. /*
  965. ** sqlite3_fts5_token ?-colocated? TEXT START END
  966. */
  967. static int SQLITE_TCLAPI f5tTokenizerReturn(
  968. void * clientData,
  969. Tcl_Interp *interp,
  970. int objc,
  971. Tcl_Obj *CONST objv[]
  972. ){
  973. F5tTokenizerContext *p = (F5tTokenizerContext*)clientData;
  974. int iStart;
  975. int iEnd;
  976. Tcl_Size nToken;
  977. int tflags = 0;
  978. char *zToken;
  979. int rc;
  980. if( objc==5 ){
  981. Tcl_Size nArg;
  982. char *zArg = Tcl_GetStringFromObj(objv[1], &nArg);
  983. if( nArg<=10 && nArg>=2 && memcmp("-colocated", zArg, nArg)==0 ){
  984. tflags |= FTS5_TOKEN_COLOCATED;
  985. }else{
  986. goto usage;
  987. }
  988. }else if( objc!=4 ){
  989. goto usage;
  990. }
  991. zToken = Tcl_GetStringFromObj(objv[objc-3], &nToken);
  992. if( Tcl_GetIntFromObj(interp, objv[objc-2], &iStart)
  993. || Tcl_GetIntFromObj(interp, objv[objc-1], &iEnd)
  994. ){
  995. return TCL_ERROR;
  996. }
  997. if( p->xToken==0 ){
  998. Tcl_AppendResult(interp,
  999. "sqlite3_fts5_token may only be used by tokenizer callback", (char*)0
  1000. );
  1001. return TCL_ERROR;
  1002. }
  1003. rc = p->xToken(p->pCtx, tflags, zToken, (int)nToken, iStart, iEnd);
  1004. Tcl_SetResult(interp, (char*)sqlite3ErrName(rc), TCL_VOLATILE);
  1005. return rc==SQLITE_OK ? TCL_OK : TCL_ERROR;
  1006. usage:
  1007. Tcl_WrongNumArgs(interp, 1, objv, "?-colocated? TEXT START END");
  1008. return TCL_ERROR;
  1009. }
  1010. static void f5tDelTokenizer(void *pCtx){
  1011. F5tTokenizerModule *pMod = (F5tTokenizerModule*)pCtx;
  1012. Tcl_DecrRefCount(pMod->pScript);
  1013. ckfree((char *)pMod);
  1014. }
  1015. /*
  1016. ** sqlite3_fts5_create_tokenizer DB NAME SCRIPT
  1017. **
  1018. ** Register a tokenizer named NAME implemented by script SCRIPT. When
  1019. ** a tokenizer instance is created (fts5_tokenizer.xCreate), any tokenizer
  1020. ** arguments are appended to SCRIPT and the result executed.
  1021. **
  1022. ** The value returned by (SCRIPT + args) is itself a tcl script. This
  1023. ** script - call it SCRIPT2 - is executed to tokenize text using the
  1024. ** tokenizer instance "returned" by SCRIPT. Specifically, to tokenize
  1025. ** text SCRIPT2 is invoked with a single argument appended to it - the
  1026. ** text to tokenize.
  1027. **
  1028. ** SCRIPT2 should invoke the [sqlite3_fts5_token] command once for each
  1029. ** token within the tokenized text.
  1030. */
  1031. static int SQLITE_TCLAPI f5tCreateTokenizer(
  1032. ClientData clientData,
  1033. Tcl_Interp *interp,
  1034. int objc,
  1035. Tcl_Obj *CONST objv[]
  1036. ){
  1037. F5tTokenizerContext *pContext = (F5tTokenizerContext*)clientData;
  1038. sqlite3 *db;
  1039. fts5_api *pApi;
  1040. char *zName;
  1041. Tcl_Obj *pScript;
  1042. F5tTokenizerModule *pMod;
  1043. int rc = SQLITE_OK;
  1044. int bV2 = 0; /* True to use _v2 API */
  1045. int iVersion = 2; /* Value for _v2.iVersion */
  1046. const char *zParent = 0; /* Name of parent tokenizer, if any */
  1047. int ii = 0;
  1048. if( objc<4 ){
  1049. Tcl_WrongNumArgs(interp, 1, objv, "?OPTIONS? DB NAME SCRIPT");
  1050. return TCL_ERROR;
  1051. }
  1052. /* Parse any options. Set stack variables bV2 and zParent. */
  1053. for(ii=1; ii<objc-3; ii++){
  1054. int iOpt = 0;
  1055. const char *azOpt[] = { "-v2", "-parent", "-version", 0 };
  1056. if( Tcl_GetIndexFromObj(interp, objv[ii], azOpt, "OPTION", 0, &iOpt) ){
  1057. return TCL_ERROR;
  1058. }
  1059. switch( iOpt ){
  1060. case 0: /* -v2 */ {
  1061. bV2 = 1;
  1062. break;
  1063. }
  1064. case 1: /* -parent */ {
  1065. ii++;
  1066. if( ii==objc-3 ){
  1067. Tcl_AppendResult(
  1068. interp, "option requires an argument: -parent", (char*)0
  1069. );
  1070. return TCL_ERROR;
  1071. }
  1072. zParent = Tcl_GetString(objv[ii]);
  1073. break;
  1074. }
  1075. case 2: /* -version */ {
  1076. ii++;
  1077. if( ii==objc-3 ){
  1078. Tcl_AppendResult(
  1079. interp, "option requires an argument: -version", (char*)0
  1080. );
  1081. return TCL_ERROR;
  1082. }
  1083. if( Tcl_GetIntFromObj(interp, objv[ii], &iVersion) ){
  1084. return TCL_ERROR;
  1085. }
  1086. break;
  1087. }
  1088. default:
  1089. assert( 0 );
  1090. break;
  1091. }
  1092. }
  1093. if( f5tDbAndApi(interp, objv[objc-3], &db, &pApi) ){
  1094. return TCL_ERROR;
  1095. }
  1096. zName = Tcl_GetString(objv[objc-2]);
  1097. pScript = objv[objc-1];
  1098. pMod = (F5tTokenizerModule*)ckalloc(sizeof(F5tTokenizerModule));
  1099. memset(pMod, 0, sizeof(F5tTokenizerModule));
  1100. pMod->interp = interp;
  1101. pMod->pScript = pScript;
  1102. Tcl_IncrRefCount(pScript);
  1103. pMod->pContext = pContext;
  1104. if( zParent ){
  1105. if( bV2 ){
  1106. fts5_tokenizer_v2 *pParent = 0;
  1107. rc = pApi->xFindTokenizer_v2(pApi, zParent, &pMod->pParentCtx, &pParent);
  1108. if( rc==SQLITE_OK ){
  1109. memcpy(&pMod->parent_v2, pParent, sizeof(fts5_tokenizer_v2));
  1110. pMod->parent_v2.xDelete(0);
  1111. }
  1112. }else{
  1113. rc = pApi->xFindTokenizer(pApi, zParent, &pMod->pParentCtx,&pMod->parent);
  1114. if( rc==SQLITE_OK ){
  1115. pMod->parent.xDelete(0);
  1116. }
  1117. }
  1118. }
  1119. if( rc==SQLITE_OK ){
  1120. void *pModCtx = (void*)pMod;
  1121. if( bV2==0 ){
  1122. fts5_tokenizer t;
  1123. t.xCreate = f5tTokenizerCreate;
  1124. t.xTokenize = f5tTokenizerTokenize;
  1125. t.xDelete = f5tTokenizerDelete;
  1126. rc = pApi->xCreateTokenizer(pApi, zName, pModCtx, &t, f5tDelTokenizer);
  1127. }else{
  1128. fts5_tokenizer_v2 t2;
  1129. memset(&t2, 0, sizeof(t2));
  1130. t2.iVersion = iVersion;
  1131. t2.xCreate = f5tTokenizerCreate;
  1132. t2.xTokenize = f5tTokenizerTokenize_v2;
  1133. t2.xDelete = f5tTokenizerDelete;
  1134. rc = pApi->xCreateTokenizer_v2(pApi, zName, pModCtx, &t2,f5tDelTokenizer);
  1135. }
  1136. }
  1137. if( rc!=SQLITE_OK ){
  1138. Tcl_AppendResult(interp, (
  1139. bV2 ? "error in fts5_api.xCreateTokenizer_v2()"
  1140. : "error in fts5_api.xCreateTokenizer()"
  1141. ), (char*)0);
  1142. return TCL_ERROR;
  1143. }
  1144. return TCL_OK;
  1145. }
  1146. static void SQLITE_TCLAPI xF5tFree(ClientData clientData){
  1147. ckfree(clientData);
  1148. }
  1149. /*
  1150. ** sqlite3_fts5_may_be_corrupt BOOLEAN
  1151. **
  1152. ** Set or clear the global "may-be-corrupt" flag. Return the old value.
  1153. */
  1154. static int SQLITE_TCLAPI f5tMayBeCorrupt(
  1155. void * clientData,
  1156. Tcl_Interp *interp,
  1157. int objc,
  1158. Tcl_Obj *CONST objv[]
  1159. ){
  1160. #ifdef SQLITE_DEBUG
  1161. int bOld = sqlite3_fts5_may_be_corrupt;
  1162. if( objc!=2 && objc!=1 ){
  1163. Tcl_WrongNumArgs(interp, 1, objv, "?BOOLEAN?");
  1164. return TCL_ERROR;
  1165. }
  1166. if( objc==2 ){
  1167. int bNew;
  1168. if( Tcl_GetBooleanFromObj(interp, objv[1], &bNew) ) return TCL_ERROR;
  1169. sqlite3_fts5_may_be_corrupt = bNew;
  1170. }
  1171. Tcl_SetObjResult(interp, Tcl_NewIntObj(bOld));
  1172. #endif
  1173. return TCL_OK;
  1174. }
  1175. static unsigned int f5t_fts5HashKey(int nSlot, const char *p, int n){
  1176. int i;
  1177. unsigned int h = 13;
  1178. for(i=n-1; i>=0; i--){
  1179. h = (h << 3) ^ h ^ p[i];
  1180. }
  1181. return (h % nSlot);
  1182. }
  1183. static int SQLITE_TCLAPI f5tTokenHash(
  1184. void * clientData,
  1185. Tcl_Interp *interp,
  1186. int objc,
  1187. Tcl_Obj *CONST objv[]
  1188. ){
  1189. char *z;
  1190. Tcl_Size n;
  1191. unsigned int iVal;
  1192. int nSlot;
  1193. if( objc!=3 ){
  1194. Tcl_WrongNumArgs(interp, 1, objv, "NSLOT TOKEN");
  1195. return TCL_ERROR;
  1196. }
  1197. if( Tcl_GetIntFromObj(interp, objv[1], &nSlot) ){
  1198. return TCL_ERROR;
  1199. }
  1200. z = Tcl_GetStringFromObj(objv[2], &n);
  1201. iVal = f5t_fts5HashKey(nSlot, z, (int)n);
  1202. Tcl_SetObjResult(interp, Tcl_NewIntObj(iVal));
  1203. return TCL_OK;
  1204. }
  1205. static int SQLITE_TCLAPI f5tRegisterMatchinfo(
  1206. void * clientData,
  1207. Tcl_Interp *interp,
  1208. int objc,
  1209. Tcl_Obj *CONST objv[]
  1210. ){
  1211. int rc;
  1212. sqlite3 *db = 0;
  1213. if( objc!=2 ){
  1214. Tcl_WrongNumArgs(interp, 1, objv, "DB");
  1215. return TCL_ERROR;
  1216. }
  1217. if( f5tDbPointer(interp, objv[1], &db) ){
  1218. return TCL_ERROR;
  1219. }
  1220. rc = sqlite3Fts5TestRegisterMatchinfo(db);
  1221. if( rc!=SQLITE_OK ){
  1222. Tcl_SetResult(interp, (char*)sqlite3ErrName(rc), TCL_VOLATILE);
  1223. return TCL_ERROR;
  1224. }
  1225. return TCL_OK;
  1226. }
  1227. static int SQLITE_TCLAPI f5tRegisterTok(
  1228. void * clientData,
  1229. Tcl_Interp *interp,
  1230. int objc,
  1231. Tcl_Obj *CONST objv[]
  1232. ){
  1233. int rc;
  1234. sqlite3 *db = 0;
  1235. fts5_api *pApi = 0;
  1236. if( objc!=2 ){
  1237. Tcl_WrongNumArgs(interp, 1, objv, "DB");
  1238. return TCL_ERROR;
  1239. }
  1240. if( f5tDbAndApi(interp, objv[1], &db, &pApi) ){
  1241. return TCL_ERROR;
  1242. }
  1243. rc = sqlite3Fts5TestRegisterTok(db, pApi);
  1244. if( rc!=SQLITE_OK ){
  1245. Tcl_SetResult(interp, (char*)sqlite3ErrName(rc), TCL_VOLATILE);
  1246. return TCL_ERROR;
  1247. }
  1248. return TCL_OK;
  1249. }
  1250. typedef struct OriginTextCtx OriginTextCtx;
  1251. struct OriginTextCtx {
  1252. sqlite3 *db;
  1253. fts5_api *pApi;
  1254. };
  1255. typedef struct OriginTextTokenizer OriginTextTokenizer;
  1256. struct OriginTextTokenizer {
  1257. Fts5Tokenizer *pTok; /* Underlying tokenizer object */
  1258. fts5_tokenizer tokapi; /* API implementation for pTok */
  1259. };
  1260. /*
  1261. ** Delete the OriginTextCtx object indicated by the only argument.
  1262. */
  1263. static void f5tOrigintextTokenizerDelete(void *pCtx){
  1264. OriginTextCtx *p = (OriginTextCtx*)pCtx;
  1265. ckfree((char*)p);
  1266. }
  1267. static int f5tOrigintextCreate(
  1268. void *pCtx,
  1269. const char **azArg,
  1270. int nArg,
  1271. Fts5Tokenizer **ppOut
  1272. ){
  1273. OriginTextCtx *p = (OriginTextCtx*)pCtx;
  1274. OriginTextTokenizer *pTok = 0;
  1275. void *pTokCtx = 0;
  1276. int rc = SQLITE_OK;
  1277. pTok = (OriginTextTokenizer*)sqlite3_malloc(sizeof(OriginTextTokenizer));
  1278. if( pTok==0 ){
  1279. rc = SQLITE_NOMEM;
  1280. }else if( nArg<1 ){
  1281. rc = SQLITE_ERROR;
  1282. }else{
  1283. /* Locate the underlying tokenizer */
  1284. rc = p->pApi->xFindTokenizer(p->pApi, azArg[0], &pTokCtx, &pTok->tokapi);
  1285. }
  1286. /* Create the new tokenizer instance */
  1287. if( rc==SQLITE_OK ){
  1288. rc = pTok->tokapi.xCreate(pTokCtx, &azArg[1], nArg-1, &pTok->pTok);
  1289. }
  1290. if( rc!=SQLITE_OK ){
  1291. sqlite3_free(pTok);
  1292. pTok = 0;
  1293. }
  1294. *ppOut = (Fts5Tokenizer*)pTok;
  1295. return rc;
  1296. }
  1297. static void f5tOrigintextDelete(Fts5Tokenizer *pTokenizer){
  1298. OriginTextTokenizer *p = (OriginTextTokenizer*)pTokenizer;
  1299. if( p->pTok ){
  1300. p->tokapi.xDelete(p->pTok);
  1301. }
  1302. sqlite3_free(p);
  1303. }
  1304. typedef struct OriginTextCb OriginTextCb;
  1305. struct OriginTextCb {
  1306. void *pCtx;
  1307. const char *pText;
  1308. int nText;
  1309. int (*xToken)(void *, int, const char *, int, int, int);
  1310. char *aBuf; /* Buffer to use */
  1311. int nBuf; /* Allocated size of aBuf[] */
  1312. };
  1313. static int xOriginToken(
  1314. void *pCtx, /* Copy of 2nd argument to xTokenize() */
  1315. int tflags, /* Mask of FTS5_TOKEN_* flags */
  1316. const char *pToken, /* Pointer to buffer containing token */
  1317. int nToken, /* Size of token in bytes */
  1318. int iStart, /* Byte offset of token within input text */
  1319. int iEnd /* Byte offset of end of token within input */
  1320. ){
  1321. OriginTextCb *p = (OriginTextCb*)pCtx;
  1322. int ret = 0;
  1323. if( nToken==(iEnd-iStart) && 0==memcmp(pToken, &p->pText[iStart], nToken) ){
  1324. /* Token exactly matches document text. Pass it through as is. */
  1325. ret = p->xToken(p->pCtx, tflags, pToken, nToken, iStart, iEnd);
  1326. }else{
  1327. int nReq = nToken + 1 + (iEnd-iStart);
  1328. if( nReq>p->nBuf ){
  1329. sqlite3_free(p->aBuf);
  1330. p->aBuf = sqlite3_malloc(nReq*2);
  1331. if( p->aBuf==0 ) return SQLITE_NOMEM;
  1332. p->nBuf = nReq*2;
  1333. }
  1334. memcpy(p->aBuf, pToken, nToken);
  1335. p->aBuf[nToken] = '\0';
  1336. memcpy(&p->aBuf[nToken+1], &p->pText[iStart], iEnd-iStart);
  1337. ret = p->xToken(p->pCtx, tflags, p->aBuf, nReq, iStart, iEnd);
  1338. }
  1339. return ret;
  1340. }
  1341. static int f5tOrigintextTokenize(
  1342. Fts5Tokenizer *pTokenizer,
  1343. void *pCtx,
  1344. int flags, /* Mask of FTS5_TOKENIZE_* flags */
  1345. const char *pText, int nText,
  1346. int (*xToken)(void *, int, const char *, int, int, int)
  1347. ){
  1348. OriginTextTokenizer *p = (OriginTextTokenizer*)pTokenizer;
  1349. OriginTextCb cb;
  1350. int ret;
  1351. memset(&cb, 0, sizeof(cb));
  1352. cb.pCtx = pCtx;
  1353. cb.pText = pText;
  1354. cb.nText = nText;
  1355. cb.xToken = xToken;
  1356. ret = p->tokapi.xTokenize(p->pTok,(void*)&cb,flags,pText,nText,xOriginToken);
  1357. sqlite3_free(cb.aBuf);
  1358. return ret;
  1359. }
  1360. /*
  1361. ** sqlite3_fts5_register_origintext DB
  1362. **
  1363. ** Description...
  1364. */
  1365. static int SQLITE_TCLAPI f5tRegisterOriginText(
  1366. void * clientData,
  1367. Tcl_Interp *interp,
  1368. int objc,
  1369. Tcl_Obj *CONST objv[]
  1370. ){
  1371. sqlite3 *db = 0;
  1372. fts5_api *pApi = 0;
  1373. int rc;
  1374. fts5_tokenizer tok = {0, 0, 0};
  1375. OriginTextCtx *pCtx = 0;
  1376. if( objc!=2 ){
  1377. Tcl_WrongNumArgs(interp, 1, objv, "DB");
  1378. return TCL_ERROR;
  1379. }
  1380. if( f5tDbAndApi(interp, objv[1], &db, &pApi) ) return TCL_ERROR;
  1381. pCtx = (OriginTextCtx*)ckalloc(sizeof(OriginTextCtx));
  1382. pCtx->db = db;
  1383. pCtx->pApi = pApi;
  1384. tok.xCreate = f5tOrigintextCreate;
  1385. tok.xDelete = f5tOrigintextDelete;
  1386. tok.xTokenize = f5tOrigintextTokenize;
  1387. rc = pApi->xCreateTokenizer(
  1388. pApi, "origintext", (void*)pCtx, &tok, f5tOrigintextTokenizerDelete
  1389. );
  1390. Tcl_ResetResult(interp);
  1391. if( rc!=SQLITE_OK ){
  1392. Tcl_AppendResult(interp, "error: ", sqlite3_errmsg(db), (void*)0);
  1393. return TCL_ERROR;
  1394. }
  1395. return TCL_OK;
  1396. }
  1397. /*
  1398. ** This function is used to DROP an fts5 table. It works even if the data
  1399. ** structures fts5 stores within the database are corrupt, which sometimes
  1400. ** prevents a straight "DROP TABLE" command from succeeding.
  1401. **
  1402. ** The first parameter is the database handle to use for the DROP TABLE
  1403. ** operation. The second is the name of the database to drop the fts5 table
  1404. ** from (i.e. "main", "temp" or the name of an attached database). The
  1405. ** third parameter is the name of the fts5 table to drop.
  1406. **
  1407. ** SQLITE_OK is returned if the table is successfully dropped. Or, if an
  1408. ** error occurs, an SQLite error code.
  1409. */
  1410. static int sqlite3_fts5_drop_corrupt_table(
  1411. sqlite3 *db, /* Database handle */
  1412. const char *zDb, /* Database name ("main", "temp" etc.) */
  1413. const char *zTab /* Name of fts5 table to drop */
  1414. ){
  1415. int rc = SQLITE_OK;
  1416. int bDef = 0;
  1417. rc = sqlite3_db_config(db, SQLITE_DBCONFIG_DEFENSIVE, -1, &bDef);
  1418. if( rc==SQLITE_OK ){
  1419. char *zScript = sqlite3_mprintf(
  1420. "DELETE FROM %Q.'%q_data';"
  1421. "DELETE FROM %Q.'%q_config';"
  1422. "INSERT INTO %Q.'%q_data' VALUES(10, X'0000000000');"
  1423. "INSERT INTO %Q.'%q_config' VALUES('version', 4);"
  1424. "DROP TABLE %Q.'%q';",
  1425. zDb, zTab, zDb, zTab, zDb, zTab, zDb, zTab, zDb, zTab
  1426. );
  1427. if( zScript==0 ){
  1428. rc = SQLITE_NOMEM;
  1429. }else{
  1430. if( bDef ) sqlite3_db_config(db, SQLITE_DBCONFIG_DEFENSIVE, 0, 0);
  1431. rc = sqlite3_exec(db, zScript, 0, 0, 0);
  1432. if( bDef ) sqlite3_db_config(db, SQLITE_DBCONFIG_DEFENSIVE, 1, 0);
  1433. sqlite3_free(zScript);
  1434. }
  1435. }
  1436. return rc;
  1437. }
  1438. /*
  1439. ** sqlite3_fts5_drop_corrupt_table DB DATABASE TABLE
  1440. **
  1441. ** Description...
  1442. */
  1443. static int SQLITE_TCLAPI f5tDropCorruptTable(
  1444. void * clientData,
  1445. Tcl_Interp *interp,
  1446. int objc,
  1447. Tcl_Obj *CONST objv[]
  1448. ){
  1449. sqlite3 *db = 0;
  1450. const char *zDb = 0;
  1451. const char *zTab = 0;
  1452. int rc = SQLITE_OK;
  1453. if( objc!=4 ){
  1454. Tcl_WrongNumArgs(interp, 1, objv, "DB DATABASE TABLE");
  1455. return TCL_ERROR;
  1456. }
  1457. if( f5tDbPointer(interp, objv[1], &db) ){
  1458. return TCL_ERROR;
  1459. }
  1460. zDb = Tcl_GetString(objv[2]);
  1461. zTab = Tcl_GetString(objv[3]);
  1462. rc = sqlite3_fts5_drop_corrupt_table(db, zDb, zTab);
  1463. if( rc!=SQLITE_OK ){
  1464. Tcl_AppendResult(interp, "error: ", sqlite3_errmsg(db), (void*)0);
  1465. return TCL_ERROR;
  1466. }
  1467. return TCL_OK;
  1468. }
  1469. /*
  1470. ** Free a buffer returned to SQLite by the str() function.
  1471. */
  1472. void f5tFree(void *p){
  1473. char *x = (char *)p;
  1474. ckfree(&x[-8]);
  1475. }
  1476. /*
  1477. ** Implementation of str().
  1478. */
  1479. void f5tStrFunc(sqlite3_context *pCtx, int nArg, sqlite3_value **apArg){
  1480. const char *zText = 0;
  1481. assert( nArg==1 );
  1482. zText = (const char*)sqlite3_value_text(apArg[0]);
  1483. if( zText ){
  1484. sqlite3_int64 nText = strlen(zText);
  1485. char *zCopy = (char*)ckalloc(nText+8);
  1486. if( zCopy==0 ){
  1487. sqlite3_result_error_nomem(pCtx);
  1488. }else{
  1489. zCopy += 8;
  1490. memcpy(zCopy, zText, nText);
  1491. sqlite3_result_text64(pCtx, zCopy, nText, f5tFree, SQLITE_UTF8);
  1492. }
  1493. }
  1494. }
  1495. /*
  1496. ** sqlite3_fts5_register_str DB
  1497. **
  1498. ** Register the str() function with database handle DB. str() interprets
  1499. ** its only argument as text and returns a copy of the value in a
  1500. ** non-nul-terminated buffer.
  1501. */
  1502. static int SQLITE_TCLAPI f5tRegisterStr(
  1503. void * clientData,
  1504. Tcl_Interp *interp,
  1505. int objc,
  1506. Tcl_Obj *CONST objv[]
  1507. ){
  1508. sqlite3 *db = 0;
  1509. if( objc!=2 ){
  1510. Tcl_WrongNumArgs(interp, 1, objv, "DB");
  1511. return TCL_ERROR;
  1512. }
  1513. if( f5tDbPointer(interp, objv[1], &db) ){
  1514. return TCL_ERROR;
  1515. }
  1516. sqlite3_create_function(db, "str", 1, SQLITE_UTF8, 0, f5tStrFunc, 0, 0);
  1517. return TCL_OK;
  1518. }
  1519. /*
  1520. ** Entry point.
  1521. */
  1522. int Fts5tcl_Init(Tcl_Interp *interp){
  1523. static struct Cmd {
  1524. char *zName;
  1525. Tcl_ObjCmdProc *xProc;
  1526. int bTokenizeCtx;
  1527. } aCmd[] = {
  1528. { "sqlite3_fts5_create_tokenizer", f5tCreateTokenizer, 1 },
  1529. { "sqlite3_fts5_token", f5tTokenizerReturn, 1 },
  1530. { "sqlite3_fts5_locale", f5tTokenizerLocale, 1 },
  1531. { "sqlite3_fts5_tokenize", f5tTokenize, 0 },
  1532. { "sqlite3_fts5_create_function", f5tCreateFunction, 0 },
  1533. { "sqlite3_fts5_may_be_corrupt", f5tMayBeCorrupt, 0 },
  1534. { "sqlite3_fts5_token_hash", f5tTokenHash, 0 },
  1535. { "sqlite3_fts5_register_matchinfo", f5tRegisterMatchinfo, 0 },
  1536. { "sqlite3_fts5_register_fts5tokenize", f5tRegisterTok, 0 },
  1537. { "sqlite3_fts5_register_origintext",f5tRegisterOriginText, 0 },
  1538. { "sqlite3_fts5_drop_corrupt_table", f5tDropCorruptTable, 0 },
  1539. { "sqlite3_fts5_register_str", f5tRegisterStr, 0 }
  1540. };
  1541. int i;
  1542. F5tTokenizerContext *pContext;
  1543. pContext = (F5tTokenizerContext*)ckalloc(sizeof(F5tTokenizerContext));
  1544. memset(pContext, 0, sizeof(*pContext));
  1545. for(i=0; i<sizeof(aCmd)/sizeof(aCmd[0]); i++){
  1546. struct Cmd *p = &aCmd[i];
  1547. void *pCtx = 0;
  1548. if( p->bTokenizeCtx ) pCtx = (void*)pContext;
  1549. Tcl_CreateObjCommand(interp, p->zName, p->xProc, pCtx, (i ? 0 : xF5tFree));
  1550. }
  1551. return TCL_OK;
  1552. }
  1553. #else /* SQLITE_ENABLE_FTS5 */
  1554. int Fts5tcl_Init(Tcl_Interp *interp){
  1555. return TCL_OK;
  1556. }
  1557. #endif /* SQLITE_ENABLE_FTS5 */
  1558. #endif /* SQLITE_TEST */