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

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

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

Powered by Google App Engine
This is Rietveld 408576698