Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(65)

Side by Side Diff: third_party/sqlite/sqlite-src-3100200/ext/fts5/fts5_tcl.c

Issue 1610543003: [sql] Import reference version of SQLite 3.10.2. (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: Created 4 years, 11 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
(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 */
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698