fts3_test.c 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621
  1. /*
  2. ** 2011 Jun 13
  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. ** This file is not part of the production FTS code. It is only used for
  14. ** testing. It contains a Tcl command that can be used to test if a document
  15. ** matches an FTS NEAR expression.
  16. **
  17. ** As of March 2012, it also contains a version 1 tokenizer used for testing
  18. ** that the sqlite3_tokenizer_module.xLanguage() method is invoked correctly.
  19. */
  20. #include "tclsqlite.h"
  21. #include <string.h>
  22. #include <assert.h>
  23. #if defined(SQLITE_TEST)
  24. #if defined(SQLITE_ENABLE_FTS3) || defined(SQLITE_ENABLE_FTS4)
  25. /* Required so that the "ifdef SQLITE_ENABLE_FTS3" below works */
  26. #include "fts3Int.h"
  27. #define NM_MAX_TOKEN 12
  28. typedef struct NearPhrase NearPhrase;
  29. typedef struct NearDocument NearDocument;
  30. typedef struct NearToken NearToken;
  31. struct NearDocument {
  32. int nToken; /* Length of token in bytes */
  33. NearToken *aToken; /* Token array */
  34. };
  35. struct NearToken {
  36. int n; /* Length of token in bytes */
  37. const char *z; /* Pointer to token string */
  38. };
  39. struct NearPhrase {
  40. int nNear; /* Preceding NEAR value */
  41. int nToken; /* Number of tokens in this phrase */
  42. NearToken aToken[NM_MAX_TOKEN]; /* Array of tokens in this phrase */
  43. };
  44. static int nm_phrase_match(
  45. NearPhrase *p,
  46. NearToken *aToken
  47. ){
  48. int ii;
  49. for(ii=0; ii<p->nToken; ii++){
  50. NearToken *pToken = &p->aToken[ii];
  51. if( pToken->n>0 && pToken->z[pToken->n-1]=='*' ){
  52. if( aToken[ii].n<(pToken->n-1) ) return 0;
  53. if( memcmp(aToken[ii].z, pToken->z, pToken->n-1) ) return 0;
  54. }else{
  55. if( aToken[ii].n!=pToken->n ) return 0;
  56. if( memcmp(aToken[ii].z, pToken->z, pToken->n) ) return 0;
  57. }
  58. }
  59. return 1;
  60. }
  61. static int nm_near_chain(
  62. int iDir, /* Direction to iterate through aPhrase[] */
  63. NearDocument *pDoc, /* Document to match against */
  64. int iPos, /* Position at which iPhrase was found */
  65. int nPhrase, /* Size of phrase array */
  66. NearPhrase *aPhrase, /* Phrase array */
  67. int iPhrase /* Index of phrase found */
  68. ){
  69. int iStart;
  70. int iStop;
  71. int ii;
  72. int nNear;
  73. int iPhrase2;
  74. NearPhrase *p;
  75. NearPhrase *pPrev;
  76. assert( iDir==1 || iDir==-1 );
  77. if( iDir==1 ){
  78. if( (iPhrase+1)==nPhrase ) return 1;
  79. nNear = aPhrase[iPhrase+1].nNear;
  80. }else{
  81. if( iPhrase==0 ) return 1;
  82. nNear = aPhrase[iPhrase].nNear;
  83. }
  84. pPrev = &aPhrase[iPhrase];
  85. iPhrase2 = iPhrase+iDir;
  86. p = &aPhrase[iPhrase2];
  87. iStart = iPos - nNear - p->nToken;
  88. iStop = iPos + nNear + pPrev->nToken;
  89. if( iStart<0 ) iStart = 0;
  90. if( iStop > pDoc->nToken - p->nToken ) iStop = pDoc->nToken - p->nToken;
  91. for(ii=iStart; ii<=iStop; ii++){
  92. if( nm_phrase_match(p, &pDoc->aToken[ii]) ){
  93. if( nm_near_chain(iDir, pDoc, ii, nPhrase, aPhrase, iPhrase2) ) return 1;
  94. }
  95. }
  96. return 0;
  97. }
  98. static int nm_match_count(
  99. NearDocument *pDoc, /* Document to match against */
  100. int nPhrase, /* Size of phrase array */
  101. NearPhrase *aPhrase, /* Phrase array */
  102. int iPhrase /* Index of phrase to count matches for */
  103. ){
  104. int nOcc = 0;
  105. int ii;
  106. NearPhrase *p = &aPhrase[iPhrase];
  107. for(ii=0; ii<(pDoc->nToken + 1 - p->nToken); ii++){
  108. if( nm_phrase_match(p, &pDoc->aToken[ii]) ){
  109. /* Test forward NEAR chain (i>iPhrase) */
  110. if( 0==nm_near_chain(1, pDoc, ii, nPhrase, aPhrase, iPhrase) ) continue;
  111. /* Test reverse NEAR chain (i<iPhrase) */
  112. if( 0==nm_near_chain(-1, pDoc, ii, nPhrase, aPhrase, iPhrase) ) continue;
  113. /* This is a real match. Increment the counter. */
  114. nOcc++;
  115. }
  116. }
  117. return nOcc;
  118. }
  119. /*
  120. ** Tclcmd: fts3_near_match DOCUMENT EXPR ?OPTIONS?
  121. */
  122. static int SQLITE_TCLAPI fts3_near_match_cmd(
  123. ClientData clientData,
  124. Tcl_Interp *interp,
  125. int objc,
  126. Tcl_Obj *CONST objv[]
  127. ){
  128. int nTotal = 0;
  129. int rc;
  130. int ii;
  131. int nPhrase;
  132. NearPhrase *aPhrase = 0;
  133. NearDocument doc = {0, 0};
  134. Tcl_Obj **apDocToken;
  135. Tcl_Obj *pRet;
  136. Tcl_Obj *pPhrasecount = 0;
  137. Tcl_Obj **apExprToken;
  138. Tcl_Size nExprToken;
  139. Tcl_Size nn;
  140. UNUSED_PARAMETER(clientData);
  141. /* Must have 3 or more arguments. */
  142. if( objc<3 || (objc%2)==0 ){
  143. Tcl_WrongNumArgs(interp, 1, objv, "DOCUMENT EXPR ?OPTION VALUE?...");
  144. rc = TCL_ERROR;
  145. goto near_match_out;
  146. }
  147. for(ii=3; ii<objc; ii+=2){
  148. enum NM_enum { NM_PHRASECOUNTS };
  149. struct TestnmSubcmd {
  150. char *zName;
  151. enum NM_enum eOpt;
  152. } aOpt[] = {
  153. { "-phrasecountvar", NM_PHRASECOUNTS },
  154. { 0, 0 }
  155. };
  156. int iOpt;
  157. if( Tcl_GetIndexFromObjStruct(
  158. interp, objv[ii], aOpt, sizeof(aOpt[0]), "option", 0, &iOpt)
  159. ){
  160. return TCL_ERROR;
  161. }
  162. switch( aOpt[iOpt].eOpt ){
  163. case NM_PHRASECOUNTS:
  164. pPhrasecount = objv[ii+1];
  165. break;
  166. }
  167. }
  168. rc = Tcl_ListObjGetElements(interp, objv[1], &nn, &apDocToken);
  169. doc.nToken = (int)nn;
  170. if( rc!=TCL_OK ) goto near_match_out;
  171. doc.aToken = (NearToken *)ckalloc(doc.nToken*sizeof(NearToken));
  172. for(ii=0; ii<doc.nToken; ii++){
  173. doc.aToken[ii].z = Tcl_GetStringFromObj(apDocToken[ii], &nn);
  174. doc.aToken[ii].n = (int)nn;
  175. }
  176. rc = Tcl_ListObjGetElements(interp, objv[2], &nExprToken, &apExprToken);
  177. if( rc!=TCL_OK ) goto near_match_out;
  178. nPhrase = (int)(nExprToken + 1) / 2;
  179. aPhrase = (NearPhrase *)ckalloc(nPhrase * sizeof(NearPhrase));
  180. memset(aPhrase, 0, nPhrase * sizeof(NearPhrase));
  181. for(ii=0; ii<nPhrase; ii++){
  182. Tcl_Obj *pPhrase = apExprToken[ii*2];
  183. Tcl_Obj **apToken;
  184. Tcl_Size nToken;
  185. int jj;
  186. rc = Tcl_ListObjGetElements(interp, pPhrase, &nToken, &apToken);
  187. if( rc!=TCL_OK ) goto near_match_out;
  188. if( nToken>NM_MAX_TOKEN ){
  189. Tcl_AppendResult(interp, "Too many tokens in phrase", 0);
  190. rc = TCL_ERROR;
  191. goto near_match_out;
  192. }
  193. for(jj=0; jj<(int)nToken; jj++){
  194. NearToken *pT = &aPhrase[ii].aToken[jj];
  195. pT->z = Tcl_GetStringFromObj(apToken[jj], &nn);
  196. pT->n = (int)nn;
  197. }
  198. aPhrase[ii].nToken = (int)nToken;
  199. }
  200. for(ii=1; ii<nPhrase; ii++){
  201. Tcl_Obj *pNear = apExprToken[2*ii-1];
  202. int nNear;
  203. rc = Tcl_GetIntFromObj(interp, pNear, &nNear);
  204. if( rc!=TCL_OK ) goto near_match_out;
  205. aPhrase[ii].nNear = nNear;
  206. }
  207. pRet = Tcl_NewObj();
  208. Tcl_IncrRefCount(pRet);
  209. for(ii=0; ii<nPhrase; ii++){
  210. int nOcc = nm_match_count(&doc, nPhrase, aPhrase, ii);
  211. Tcl_ListObjAppendElement(interp, pRet, Tcl_NewIntObj(nOcc));
  212. nTotal += nOcc;
  213. }
  214. if( pPhrasecount ){
  215. Tcl_ObjSetVar2(interp, pPhrasecount, 0, pRet, 0);
  216. }
  217. Tcl_DecrRefCount(pRet);
  218. Tcl_SetObjResult(interp, Tcl_NewBooleanObj(nTotal>0));
  219. near_match_out:
  220. ckfree((char *)aPhrase);
  221. ckfree((char *)doc.aToken);
  222. return rc;
  223. }
  224. /*
  225. ** Tclcmd: fts3_configure_incr_load ?CHUNKSIZE THRESHOLD?
  226. **
  227. ** Normally, FTS uses hard-coded values to determine the minimum doclist
  228. ** size eligible for incremental loading, and the size of the chunks loaded
  229. ** when a doclist is incrementally loaded. This command allows the built-in
  230. ** values to be overridden for testing purposes.
  231. **
  232. ** If present, the first argument is the chunksize in bytes to load doclists
  233. ** in. The second argument is the minimum doclist size in bytes to use
  234. ** incremental loading with.
  235. **
  236. ** Whether or not the arguments are present, this command returns a list of
  237. ** two integers - the initial chunksize and threshold when the command is
  238. ** invoked. This can be used to restore the default behavior after running
  239. ** tests. For example:
  240. **
  241. ** # Override incr-load settings for testing:
  242. ** set cfg [fts3_configure_incr_load $new_chunksize $new_threshold]
  243. **
  244. ** .... run tests ....
  245. **
  246. ** # Restore initial incr-load settings:
  247. ** eval fts3_configure_incr_load $cfg
  248. */
  249. static int SQLITE_TCLAPI fts3_configure_incr_load_cmd(
  250. ClientData clientData,
  251. Tcl_Interp *interp,
  252. int objc,
  253. Tcl_Obj *CONST objv[]
  254. ){
  255. #ifdef SQLITE_ENABLE_FTS3
  256. extern int test_fts3_node_chunksize;
  257. extern int test_fts3_node_chunk_threshold;
  258. Tcl_Obj *pRet;
  259. if( objc!=1 && objc!=3 ){
  260. Tcl_WrongNumArgs(interp, 1, objv, "?CHUNKSIZE THRESHOLD?");
  261. return TCL_ERROR;
  262. }
  263. pRet = Tcl_NewObj();
  264. Tcl_IncrRefCount(pRet);
  265. Tcl_ListObjAppendElement(
  266. interp, pRet, Tcl_NewIntObj(test_fts3_node_chunksize));
  267. Tcl_ListObjAppendElement(
  268. interp, pRet, Tcl_NewIntObj(test_fts3_node_chunk_threshold));
  269. if( objc==3 ){
  270. int iArg1;
  271. int iArg2;
  272. if( Tcl_GetIntFromObj(interp, objv[1], &iArg1)
  273. || Tcl_GetIntFromObj(interp, objv[2], &iArg2)
  274. ){
  275. Tcl_DecrRefCount(pRet);
  276. return TCL_ERROR;
  277. }
  278. test_fts3_node_chunksize = iArg1;
  279. test_fts3_node_chunk_threshold = iArg2;
  280. }
  281. Tcl_SetObjResult(interp, pRet);
  282. Tcl_DecrRefCount(pRet);
  283. #endif
  284. UNUSED_PARAMETER(clientData);
  285. return TCL_OK;
  286. }
  287. #ifdef SQLITE_ENABLE_FTS3
  288. /**************************************************************************
  289. ** Beginning of test tokenizer code.
  290. **
  291. ** For language 0, this tokenizer is similar to the default 'simple'
  292. ** tokenizer. For other languages L, the following:
  293. **
  294. ** * Odd numbered languages are case-sensitive. Even numbered
  295. ** languages are not.
  296. **
  297. ** * Language ids 100 or greater are considered an error.
  298. **
  299. ** The implementation assumes that the input contains only ASCII characters
  300. ** (i.e. those that may be encoded in UTF-8 using a single byte).
  301. */
  302. typedef struct test_tokenizer {
  303. sqlite3_tokenizer base;
  304. } test_tokenizer;
  305. typedef struct test_tokenizer_cursor {
  306. sqlite3_tokenizer_cursor base;
  307. const char *aInput; /* Input being tokenized */
  308. int nInput; /* Size of the input in bytes */
  309. int iInput; /* Current offset in aInput */
  310. int iToken; /* Index of next token to be returned */
  311. char *aBuffer; /* Buffer containing current token */
  312. int nBuffer; /* Number of bytes allocated at pToken */
  313. int iLangid; /* Configured language id */
  314. } test_tokenizer_cursor;
  315. static int testTokenizerCreate(
  316. int argc, const char * const *argv,
  317. sqlite3_tokenizer **ppTokenizer
  318. ){
  319. test_tokenizer *pNew;
  320. UNUSED_PARAMETER(argc);
  321. UNUSED_PARAMETER(argv);
  322. pNew = sqlite3_malloc(sizeof(test_tokenizer));
  323. if( !pNew ) return SQLITE_NOMEM;
  324. memset(pNew, 0, sizeof(test_tokenizer));
  325. *ppTokenizer = (sqlite3_tokenizer *)pNew;
  326. return SQLITE_OK;
  327. }
  328. static int testTokenizerDestroy(sqlite3_tokenizer *pTokenizer){
  329. test_tokenizer *p = (test_tokenizer *)pTokenizer;
  330. sqlite3_free(p);
  331. return SQLITE_OK;
  332. }
  333. static int testTokenizerOpen(
  334. sqlite3_tokenizer *pTokenizer, /* The tokenizer */
  335. const char *pInput, int nBytes, /* String to be tokenized */
  336. sqlite3_tokenizer_cursor **ppCursor /* OUT: Tokenization cursor */
  337. ){
  338. int rc = SQLITE_OK; /* Return code */
  339. test_tokenizer_cursor *pCsr; /* New cursor object */
  340. UNUSED_PARAMETER(pTokenizer);
  341. pCsr = (test_tokenizer_cursor *)sqlite3_malloc(sizeof(test_tokenizer_cursor));
  342. if( pCsr==0 ){
  343. rc = SQLITE_NOMEM;
  344. }else{
  345. memset(pCsr, 0, sizeof(test_tokenizer_cursor));
  346. pCsr->aInput = pInput;
  347. if( nBytes<0 ){
  348. pCsr->nInput = (int)strlen(pInput);
  349. }else{
  350. pCsr->nInput = nBytes;
  351. }
  352. }
  353. *ppCursor = (sqlite3_tokenizer_cursor *)pCsr;
  354. return rc;
  355. }
  356. static int testTokenizerClose(sqlite3_tokenizer_cursor *pCursor){
  357. test_tokenizer_cursor *pCsr = (test_tokenizer_cursor *)pCursor;
  358. sqlite3_free(pCsr->aBuffer);
  359. sqlite3_free(pCsr);
  360. return SQLITE_OK;
  361. }
  362. static int testIsTokenChar(char c){
  363. return (c>='a' && c<='z') || (c>='A' && c<='Z');
  364. }
  365. static int testTolower(char c){
  366. char ret = c;
  367. if( ret>='A' && ret<='Z') ret = ret - ('A'-'a');
  368. return ret;
  369. }
  370. static int testTokenizerNext(
  371. sqlite3_tokenizer_cursor *pCursor, /* Cursor returned by testTokenizerOpen */
  372. const char **ppToken, /* OUT: *ppToken is the token text */
  373. int *pnBytes, /* OUT: Number of bytes in token */
  374. int *piStartOffset, /* OUT: Starting offset of token */
  375. int *piEndOffset, /* OUT: Ending offset of token */
  376. int *piPosition /* OUT: Position integer of token */
  377. ){
  378. test_tokenizer_cursor *pCsr = (test_tokenizer_cursor *)pCursor;
  379. int rc = SQLITE_OK;
  380. const char *p;
  381. const char *pEnd;
  382. p = &pCsr->aInput[pCsr->iInput];
  383. pEnd = &pCsr->aInput[pCsr->nInput];
  384. /* Skip past any white-space */
  385. assert( p<=pEnd );
  386. while( p<pEnd && testIsTokenChar(*p)==0 ) p++;
  387. if( p==pEnd ){
  388. rc = SQLITE_DONE;
  389. }else{
  390. /* Advance to the end of the token */
  391. const char *pToken = p;
  392. sqlite3_int64 nToken;
  393. while( p<pEnd && testIsTokenChar(*p) ) p++;
  394. nToken = (sqlite3_int64)(p-pToken);
  395. /* Copy the token into the buffer */
  396. if( nToken>pCsr->nBuffer ){
  397. sqlite3_free(pCsr->aBuffer);
  398. pCsr->aBuffer = sqlite3_malloc64(nToken);
  399. }
  400. if( pCsr->aBuffer==0 ){
  401. rc = SQLITE_NOMEM;
  402. }else{
  403. int i;
  404. if( pCsr->iLangid & 0x00000001 ){
  405. for(i=0; i<nToken; i++) pCsr->aBuffer[i] = pToken[i];
  406. }else{
  407. for(i=0; i<nToken; i++) pCsr->aBuffer[i] = (char)testTolower(pToken[i]);
  408. }
  409. pCsr->iToken++;
  410. pCsr->iInput = (int)(p - pCsr->aInput);
  411. *ppToken = pCsr->aBuffer;
  412. *pnBytes = (int)nToken;
  413. *piStartOffset = (int)(pToken - pCsr->aInput);
  414. *piEndOffset = (int)(p - pCsr->aInput);
  415. *piPosition = pCsr->iToken;
  416. }
  417. }
  418. return rc;
  419. }
  420. static int testTokenizerLanguage(
  421. sqlite3_tokenizer_cursor *pCursor,
  422. int iLangid
  423. ){
  424. int rc = SQLITE_OK;
  425. test_tokenizer_cursor *pCsr = (test_tokenizer_cursor *)pCursor;
  426. pCsr->iLangid = iLangid;
  427. if( pCsr->iLangid>=100 ){
  428. rc = SQLITE_ERROR;
  429. }
  430. return rc;
  431. }
  432. #endif
  433. static int SQLITE_TCLAPI fts3_test_tokenizer_cmd(
  434. ClientData clientData,
  435. Tcl_Interp *interp,
  436. int objc,
  437. Tcl_Obj *CONST objv[]
  438. ){
  439. #ifdef SQLITE_ENABLE_FTS3
  440. static const sqlite3_tokenizer_module testTokenizerModule = {
  441. 1,
  442. testTokenizerCreate,
  443. testTokenizerDestroy,
  444. testTokenizerOpen,
  445. testTokenizerClose,
  446. testTokenizerNext,
  447. testTokenizerLanguage
  448. };
  449. const sqlite3_tokenizer_module *pPtr = &testTokenizerModule;
  450. if( objc!=1 ){
  451. Tcl_WrongNumArgs(interp, 1, objv, "");
  452. return TCL_ERROR;
  453. }
  454. Tcl_SetObjResult(interp, Tcl_NewByteArrayObj(
  455. (const unsigned char *)&pPtr, sizeof(sqlite3_tokenizer_module *)
  456. ));
  457. #endif
  458. UNUSED_PARAMETER(clientData);
  459. return TCL_OK;
  460. }
  461. static int SQLITE_TCLAPI fts3_test_varint_cmd(
  462. ClientData clientData,
  463. Tcl_Interp *interp,
  464. int objc,
  465. Tcl_Obj *CONST objv[]
  466. ){
  467. #ifdef SQLITE_ENABLE_FTS3
  468. char aBuf[24];
  469. int rc;
  470. Tcl_WideInt w;
  471. sqlite3_int64 w2;
  472. int nByte, nByte2;
  473. if( objc!=2 ){
  474. Tcl_WrongNumArgs(interp, 1, objv, "INTEGER");
  475. return TCL_ERROR;
  476. }
  477. rc = Tcl_GetWideIntFromObj(interp, objv[1], &w);
  478. if( rc!=TCL_OK ) return rc;
  479. nByte = sqlite3Fts3PutVarint(aBuf, w);
  480. nByte2 = sqlite3Fts3GetVarint(aBuf, &w2);
  481. if( w!=w2 || nByte!=nByte2 ){
  482. char *zErr = sqlite3_mprintf("error testing %lld", w);
  483. Tcl_ResetResult(interp);
  484. Tcl_AppendResult(interp, zErr, 0);
  485. return TCL_ERROR;
  486. }
  487. if( w<=2147483647 && w>=0 ){
  488. int i;
  489. nByte2 = fts3GetVarint32(aBuf, &i);
  490. if( (int)w!=i || nByte!=nByte2 ){
  491. char *zErr = sqlite3_mprintf("error testing %lld (32-bit)", w);
  492. Tcl_ResetResult(interp);
  493. Tcl_AppendResult(interp, zErr, 0);
  494. return TCL_ERROR;
  495. }
  496. }
  497. #endif
  498. UNUSED_PARAMETER(clientData);
  499. return TCL_OK;
  500. }
  501. /*
  502. ** End of tokenizer code.
  503. **************************************************************************/
  504. /*
  505. ** sqlite3_fts3_may_be_corrupt BOOLEAN
  506. **
  507. ** Set or clear the global "may-be-corrupt" flag. Return the old value.
  508. */
  509. static int SQLITE_TCLAPI fts3_may_be_corrupt(
  510. void * clientData,
  511. Tcl_Interp *interp,
  512. int objc,
  513. Tcl_Obj *CONST objv[]
  514. ){
  515. #ifdef SQLITE_DEBUG
  516. int bOld = sqlite3_fts3_may_be_corrupt;
  517. if( objc!=2 && objc!=1 ){
  518. Tcl_WrongNumArgs(interp, 1, objv, "?BOOLEAN?");
  519. return TCL_ERROR;
  520. }
  521. if( objc==2 ){
  522. int bNew;
  523. if( Tcl_GetBooleanFromObj(interp, objv[1], &bNew) ) return TCL_ERROR;
  524. sqlite3_fts3_may_be_corrupt = bNew;
  525. }
  526. Tcl_SetObjResult(interp, Tcl_NewIntObj(bOld));
  527. #endif
  528. return TCL_OK;
  529. }
  530. int Sqlitetestfts3_Init(Tcl_Interp *interp){
  531. Tcl_CreateObjCommand(interp, "fts3_near_match", fts3_near_match_cmd, 0, 0);
  532. Tcl_CreateObjCommand(interp,
  533. "fts3_configure_incr_load", fts3_configure_incr_load_cmd, 0, 0
  534. );
  535. Tcl_CreateObjCommand(
  536. interp, "fts3_test_tokenizer", fts3_test_tokenizer_cmd, 0, 0
  537. );
  538. Tcl_CreateObjCommand(
  539. interp, "fts3_test_varint", fts3_test_varint_cmd, 0, 0
  540. );
  541. Tcl_CreateObjCommand(
  542. interp, "sqlite3_fts3_may_be_corrupt", fts3_may_be_corrupt, 0, 0
  543. );
  544. return TCL_OK;
  545. }
  546. #endif /* SQLITE_ENABLE_FTS3 || SQLITE_ENABLE_FTS4 */
  547. #endif /* ifdef SQLITE_TEST */