Fossil SCM

Add the ability to index files.

drh 2014-12-17 18:28 search-using-fts4
Commit 83c67816701851c5765abf9ed4f74ceeddfac6be
+8
--- src/blob.c
+++ src/blob.c
@@ -201,10 +201,18 @@
201201
*/
202202
void blob_reset(Blob *pBlob){
203203
blob_is_init(pBlob);
204204
pBlob->xRealloc(pBlob, 0);
205205
}
206
+
207
+/*
208
+** Return true if the result of blob_str() is a string that can be
209
+** freed using fossil_free.
210
+*/
211
+int blob_is_malloced(Blob *pBlob){
212
+ return pBlob && pBlob->aData && pBlob->xRealloc==blobReallocMalloc;
213
+}
206214
207215
208216
/*
209217
** Return true if the blob has been zeroed - in other words if it contains
210218
** no malloced memory. This only works reliably if the blob has been
211219
--- src/blob.c
+++ src/blob.c
@@ -201,10 +201,18 @@
201 */
202 void blob_reset(Blob *pBlob){
203 blob_is_init(pBlob);
204 pBlob->xRealloc(pBlob, 0);
205 }
 
 
 
 
 
 
 
 
206
207
208 /*
209 ** Return true if the blob has been zeroed - in other words if it contains
210 ** no malloced memory. This only works reliably if the blob has been
211
--- src/blob.c
+++ src/blob.c
@@ -201,10 +201,18 @@
201 */
202 void blob_reset(Blob *pBlob){
203 blob_is_init(pBlob);
204 pBlob->xRealloc(pBlob, 0);
205 }
206
207 /*
208 ** Return true if the result of blob_str() is a string that can be
209 ** freed using fossil_free.
210 */
211 int blob_is_malloced(Blob *pBlob){
212 return pBlob && pBlob->aData && pBlob->xRealloc==blobReallocMalloc;
213 }
214
215
216 /*
217 ** Return true if the blob has been zeroed - in other words if it contains
218 ** no malloced memory. This only works reliably if the blob has been
219
+1
--- src/db.c
+++ src/db.c
@@ -838,10 +838,11 @@
838838
);
839839
if( g.fSqlTrace ) sqlite3_trace(db, db_sql_trace, 0);
840840
re_add_sql_func(db);
841841
foci_register(db);
842842
ftsearch_add_sql_func(db);
843
+ add_content_sql_commands(db);
843844
sqlite3_exec(db, "PRAGMA foreign_keys=OFF;", 0, 0, 0);
844845
return db;
845846
}
846847
847848
848849
--- src/db.c
+++ src/db.c
@@ -838,10 +838,11 @@
838 );
839 if( g.fSqlTrace ) sqlite3_trace(db, db_sql_trace, 0);
840 re_add_sql_func(db);
841 foci_register(db);
842 ftsearch_add_sql_func(db);
 
843 sqlite3_exec(db, "PRAGMA foreign_keys=OFF;", 0, 0, 0);
844 return db;
845 }
846
847
848
--- src/db.c
+++ src/db.c
@@ -838,10 +838,11 @@
838 );
839 if( g.fSqlTrace ) sqlite3_trace(db, db_sql_trace, 0);
840 re_add_sql_func(db);
841 foci_register(db);
842 ftsearch_add_sql_func(db);
843 add_content_sql_commands(db);
844 sqlite3_exec(db, "PRAGMA foreign_keys=OFF;", 0, 0, 0);
845 return db;
846 }
847
848
849
+144 -30
--- src/ftsearch.c
+++ src/ftsearch.c
@@ -60,11 +60,11 @@
6060
if( zDocType[0]==0 || zDocType[1]==0 ) return 0;
6161
zDocId = zDocType + 2;
6262
}
6363
id = atoi(zDocId);
6464
switch( zDocType[0] ){
65
- case 'c': { /* A check-in comment. zDocId is the UUID */
65
+ case 'c': { /* A check-in comment. zDocId is the RID */
6666
zRes = db_text(0,
6767
"SELECT coalesce(ecomment,comment) || char(10) ||"
6868
" 'user: ' || coalesce(euser,user) || char(10) ||"
6969
" 'branch: ' || coalesce((SELECT value FROM tagxref"
7070
" WHERE tagid=%d AND tagtype>0"
@@ -72,38 +72,59 @@
7272
" FROM event"
7373
" WHERE event.objid=%d"
7474
" AND event.type GLOB 'c*'",
7575
TAG_BRANCH, id, id);
7676
break;
77
+ }
78
+ case 'f': { /* A file with zDocId as the filename.fnid */
79
+ zRes = db_text(0,
80
+ "SELECT content(mlink.fid)"
81
+ " FROM filename, mlink, event"
82
+ " WHERE filename.fnid=%d"
83
+ " AND mlink.fnid=filename.fnid"
84
+ " AND event.objid=mlink.mid"
85
+ " ORDER BY event.mtime DESC LIMIT 1",
86
+ id
87
+ );
88
+ break;
7789
}
7890
default: {
7991
/* No-op */
8092
}
8193
}
8294
return zRes;
8395
}
8496
8597
/* Return a human-readable description for the document described by
86
-** the arguments. The returned text is in the Wiki format and contains
87
-** links to the document in question.
98
+** the arguments.
8899
**
89100
** See ftsearch_content() for further information
90101
*/
91
-char *ftsearch_description(const char *zDocType, const char *zDocId){
102
+char *ftsearch_description(
103
+ const char *zDocType,
104
+ const char *zDocId,
105
+ int bLink /* Provide hyperlink in text if true */
106
+){
92107
char *zRes = 0; /* The result to be returned */
93108
int id;
94109
if( zDocId==0 ){
95110
if( zDocType[0]==0 || zDocType[1]==0 ) return 0;
96111
zDocId = zDocType + 2;
97112
}
98113
id = atoi(zDocId);
99114
switch( zDocType[0] ){
100
- case 'c': { /* A check-in comment. zDocId is the UUID */
115
+ case 'c': { /* A check-in comment. zDocId is the RID */
101116
char *zUuid = db_text("","SELECT uuid FROM blob WHERE rid=%d", id);
102117
zRes = mprintf("Check-in [%S]", zUuid);
103118
fossil_free(zUuid);
104119
break;
120
+ }
121
+ case 'f': { /* A file. zDocId is the FNID */
122
+ char *zName = db_text("","SELECT name FROM filename WHERE fnid=%d",id);
123
+ zRes = mprintf("File %s", zName);
124
+ fossil_free(zName);
125
+ break;
105126
}
106127
default: {
107128
/* No-op */
108129
}
109130
}
@@ -125,11 +146,11 @@
125146
db_find_and_open_repository(0, 0);
126147
verify_all_options();
127148
if( g.argc!=3 ) usage("DOCUMENTCODE");
128149
if( strlen(g.argv[2])>3 ){
129150
zContent = ftsearch_content(g.argv[2],0);
130
- zDesc = ftsearch_description(g.argv[2],0);
151
+ zDesc = ftsearch_description(g.argv[2],0,0);
131152
}
132153
if( zDesc ){
133154
fossil_print("Description: %s\n", zDesc);
134155
fossil_free(zDesc);
135156
}
@@ -210,12 +231,19 @@
210231
211232
/*
212233
** Completely rebuild the ftsearch indexes from scratch
213234
*/
214235
void ftsearch_rebuild_all(void){
236
+ const char *zEnables;
215237
db_begin_transaction();
216238
ftsearch_disable_all();
239
+ zEnables = db_get("ftsearch-index-type", "cdeftw");
240
+
241
+ /* If none of the search categories are enabled, then do not
242
+ ** bother constructing the search tables
243
+ */
244
+ if( sqlite3_strglob("*[cdeftw]*", zEnables) ) return;
217245
218246
/* The FTSSEARCHXREF table provides a mapping between the integer
219247
** document-ids in FTS4 to the "document codes" that describe a
220248
** referenced object
221249
*/
@@ -242,18 +270,35 @@
242270
"CREATE VIRTUAL TABLE %s.ftsearch"
243271
" USING fts4(content='ftsearchbody',body);",
244272
db_name("repository")
245273
);
246274
247
- /* Populate the FTSEARCHXREF table with references to all check-in
248
- ** comments currently in the event table
249
- */
250
- db_multi_exec(
251
- "INSERT INTO ftsearchxref(ftsid,mtime)"
252
- " SELECT 'c-' || objid, mtime FROM event"
253
- " WHERE type='ci';"
254
- );
275
+ if( strchr(zEnables, 'c')!=0 ){
276
+ /* Populate the FTSEARCHXREF table with references to all check-in
277
+ ** comments currently in the event table
278
+ */
279
+ db_multi_exec(
280
+ "INSERT INTO ftsearchxref(ftsid,mtime)"
281
+ " SELECT 'c-' || objid, mtime FROM event"
282
+ " WHERE type='ci';"
283
+ );
284
+ }
285
+
286
+ if( strchr(zEnables, 'f')!=0 ){
287
+ /* Populate the FTSEARCHXREF table with references to all files
288
+ */
289
+ db_multi_exec(
290
+ "INSERT INTO ftsearchxref(ftsid,mtime)"
291
+ " SELECT 'f-' || filename.fnid, max(event.mtime)"
292
+ " FROM filename, mlink, event"
293
+ " WHERE mlink.fnid=filename.fnid"
294
+ " AND event.objid=mlink.mid"
295
+ " AND %s"
296
+ " GROUP BY 1",
297
+ glob_expr("filename.name", db_get("search-file-glob","*"))
298
+ );
299
+ }
255300
256301
/* Index every document mentioned in the FTSEARCHXREF table */
257302
db_multi_exec(
258303
"INSERT INTO ftsearch(docid,body)"
259304
" SELECT docid, ftsearch_content(ftsid) FROM ftsearchxref;"
@@ -272,11 +317,23 @@
272317
** enable searching.
273318
**
274319
** The "search-config" is used to setup the search feature of the repository.
275320
** Subcommands are:
276321
**
277
-** fossil search-config setting ?NAME? ?VALUE?
322
+** fossil search-config doclist
323
+**
324
+** List all the documents currently indexed
325
+**
326
+** fossil search-config rebuild
327
+**
328
+** Completely rebuild the search index.
329
+**
330
+** fossil search-config reset
331
+**
332
+** Disable search and remove the search indexes from the repository.
333
+**
334
+** fossil search-config setting NAME ?VALUE?
278335
**
279336
** Set or query a search setting. NAMES are:
280337
** file-glob Comma-separated list of GLOBs for file search
281338
** ticket-expr SQL expression to render TICKET content
282339
** ticketchng-expr SQL expression to render TICKETCHNG content
@@ -293,20 +350,18 @@
293350
** w: wiki pages
294351
**
295352
** It is necessary to run "fossil search-config rebuild" after making
296353
** setting changes in order to reconstruct the search index
297354
**
298
-** fossil search-config rebuild
299
-** fossil search-config optimize
300
-**
301
-** Completely rebuild the search index, or optimize the search index.
302
-**
303
-** fossil search-config reset
304
-**
305
-** Disable search and remove the search indexes from the repository.
355
+** fossil search-config status
356
+**
357
+** Report on the status of the search configuration.
306358
*/
307359
void ftsearch_cmd(void){
360
+ static const char *azSettings[] = {
361
+ "file-glob", "index-type", "ticket-expr", "ticketchng-expr"
362
+ };
308363
const char *zSubCmd;
309364
int nSubCmd;
310365
db_find_and_open_repository(0, 0);
311366
verify_all_options();
312367
if( g.argc<3 ) usage("search PATTERN");
@@ -328,31 +383,90 @@
328383
fossil_fatal("search is disabled - see \"fossil help search\""
329384
" for more information");
330385
}
331386
db_prepare(&q, "SELECT "
332387
" snippet(ftsearch,%Q,%Q,'...'),"
333
- " ftsearchxref.ftsid"
388
+ " ftsearchxref.ftsid,"
389
+ " date(ftsearchxref.mtime)"
334390
" FROM ftsearch, ftsearchxref"
335391
" WHERE ftsearch.body MATCH %Q"
336392
" AND ftsearchxref.docid=ftsearch.docid"
337
- " ORDER BY ftsearchxref.mtime DESC;",
393
+ " ORDER BY ftsearchxref.mtime DESC LIMIT 50;",
338394
zMark1, zMark2, zSubCmd);
339395
while( db_step(&q)==SQLITE_ROW ){
340396
const char *zSnippet = db_column_text(&q,0);
341
- char *zDesc = ftsearch_description(db_column_text(&q,1),0);
397
+ char *zDesc = ftsearch_description(db_column_text(&q,1),0,0);
398
+ const char *zDate = db_column_text(&q,2);
342399
if( i++ > 0 ){
343400
fossil_print("----------------------------------------------------\n");
344401
}
345
- fossil_print("%s\n%s\n", zDesc, zSnippet);
402
+ fossil_print("%s (%s)\n%s\n", zDesc, zDate, zSnippet);
346403
fossil_free(zDesc);
347404
}
348405
db_finalize(&q);
349
- }else if( strncmp(zSubCmd, "settings", nSubCmd)==0 ){
350
-
406
+ }else if( strncmp(zSubCmd, "doclist", nSubCmd)==0 ){
407
+ if( db_table_exists("repository","ftsearch") ){
408
+ Stmt q;
409
+ db_prepare(&q, "SELECT ftsid, date(mtime) FROM ftsearchxref"
410
+ " ORDER BY mtime DESC");
411
+ while( db_step(&q)==SQLITE_ROW ){
412
+ const char *zDate = db_column_text(&q,1);
413
+ const char *zFtsid = db_column_text(&q,0);
414
+ char *zDesc = ftsearch_description(zFtsid,0,0);
415
+ fossil_print("%s (%s)\n", zDesc, zDate);
416
+ fossil_free(zDesc);
417
+ }
418
+ db_finalize(&q);
419
+ }
351420
}else if( strncmp(zSubCmd, "rebuild", nSubCmd)==0 ){
352421
ftsearch_rebuild_all();
353422
}else if( strncmp(zSubCmd, "reset", nSubCmd)==0 ){
354423
ftsearch_disable_all();
355
- }else if( strncmp(zSubCmd, "optimize", nSubCmd)==0 ){
424
+ }else if( strncmp(zSubCmd, "settings", nSubCmd)==0 ){
425
+ const char *zName = g.argv[3];
426
+ const char *zValue = g.argc>=5 ? g.argv[4] : 0;
427
+ char *zFullname;
428
+ int i;
429
+ if( g.argc<4 ) usage("setting NAME ?VALUE?");
430
+ for(i=0; i<count(azSettings); i++){
431
+ if( strcmp(zName, azSettings[i])==0 ) break;
432
+ }
433
+ if( i>=count(azSettings) ){
434
+ Blob x;
435
+ blob_init(&x,0,0);
436
+ for(i=0; i<count(azSettings); i++) blob_appendf(&x," %s", azSettings[i]);
437
+ fossil_fatal("unknown setting \"%s\" - should be one of:%s",
438
+ zName, blob_str(&x));
439
+ }
440
+ zFullname = mprintf("search-%s", zName);
441
+ if( zValue==0 ){
442
+ zValue = db_get(zFullname, 0);
443
+ }else{
444
+ db_set(zFullname, zValue, 0);
445
+ }
446
+ if( zValue==0 ){
447
+ fossil_print("%s is not defined\n", zName);
448
+ }else{
449
+ fossil_print("%s: %s\n", zName, zValue);
450
+ }
451
+ }else if( strncmp(zSubCmd, "status", nSubCmd)==0 ){
452
+ int i;
453
+ fossil_print("search settings:\n");
454
+ for(i=0; i<count(azSettings); i++){
455
+ char *zFullname = mprintf("search-%s", azSettings[i]);
456
+ char *zValue = db_get(zFullname, 0);
457
+ if( zValue==0 ){
458
+ fossil_print(" %s is undefined\n", azSettings[i]);
459
+ }else{
460
+ fossil_print(" %s: %s\n", azSettings[i], zValue);
461
+ }
462
+ fossil_free(zFullname);
463
+ }
464
+ if( db_table_exists("repository","ftsearchxref") ){
465
+ int n = db_int(0, "SELECT count(*) FROM ftsearchxref");
466
+ fossil_print("search is enabled with %d documents indexed\n", n);
467
+ }else{
468
+ fossil_print("search is disabled\n");
469
+ }
356470
}
357471
db_end_transaction(0);
358472
}
359473
--- src/ftsearch.c
+++ src/ftsearch.c
@@ -60,11 +60,11 @@
60 if( zDocType[0]==0 || zDocType[1]==0 ) return 0;
61 zDocId = zDocType + 2;
62 }
63 id = atoi(zDocId);
64 switch( zDocType[0] ){
65 case 'c': { /* A check-in comment. zDocId is the UUID */
66 zRes = db_text(0,
67 "SELECT coalesce(ecomment,comment) || char(10) ||"
68 " 'user: ' || coalesce(euser,user) || char(10) ||"
69 " 'branch: ' || coalesce((SELECT value FROM tagxref"
70 " WHERE tagid=%d AND tagtype>0"
@@ -72,38 +72,59 @@
72 " FROM event"
73 " WHERE event.objid=%d"
74 " AND event.type GLOB 'c*'",
75 TAG_BRANCH, id, id);
76 break;
 
 
 
 
 
 
 
 
 
 
 
 
77 }
78 default: {
79 /* No-op */
80 }
81 }
82 return zRes;
83 }
84
85 /* Return a human-readable description for the document described by
86 ** the arguments. The returned text is in the Wiki format and contains
87 ** links to the document in question.
88 **
89 ** See ftsearch_content() for further information
90 */
91 char *ftsearch_description(const char *zDocType, const char *zDocId){
 
 
 
 
92 char *zRes = 0; /* The result to be returned */
93 int id;
94 if( zDocId==0 ){
95 if( zDocType[0]==0 || zDocType[1]==0 ) return 0;
96 zDocId = zDocType + 2;
97 }
98 id = atoi(zDocId);
99 switch( zDocType[0] ){
100 case 'c': { /* A check-in comment. zDocId is the UUID */
101 char *zUuid = db_text("","SELECT uuid FROM blob WHERE rid=%d", id);
102 zRes = mprintf("Check-in [%S]", zUuid);
103 fossil_free(zUuid);
104 break;
 
 
 
 
 
 
105 }
106 default: {
107 /* No-op */
108 }
109 }
@@ -125,11 +146,11 @@
125 db_find_and_open_repository(0, 0);
126 verify_all_options();
127 if( g.argc!=3 ) usage("DOCUMENTCODE");
128 if( strlen(g.argv[2])>3 ){
129 zContent = ftsearch_content(g.argv[2],0);
130 zDesc = ftsearch_description(g.argv[2],0);
131 }
132 if( zDesc ){
133 fossil_print("Description: %s\n", zDesc);
134 fossil_free(zDesc);
135 }
@@ -210,12 +231,19 @@
210
211 /*
212 ** Completely rebuild the ftsearch indexes from scratch
213 */
214 void ftsearch_rebuild_all(void){
 
215 db_begin_transaction();
216 ftsearch_disable_all();
 
 
 
 
 
 
217
218 /* The FTSSEARCHXREF table provides a mapping between the integer
219 ** document-ids in FTS4 to the "document codes" that describe a
220 ** referenced object
221 */
@@ -242,18 +270,35 @@
242 "CREATE VIRTUAL TABLE %s.ftsearch"
243 " USING fts4(content='ftsearchbody',body);",
244 db_name("repository")
245 );
246
247 /* Populate the FTSEARCHXREF table with references to all check-in
248 ** comments currently in the event table
249 */
250 db_multi_exec(
251 "INSERT INTO ftsearchxref(ftsid,mtime)"
252 " SELECT 'c-' || objid, mtime FROM event"
253 " WHERE type='ci';"
254 );
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
255
256 /* Index every document mentioned in the FTSEARCHXREF table */
257 db_multi_exec(
258 "INSERT INTO ftsearch(docid,body)"
259 " SELECT docid, ftsearch_content(ftsid) FROM ftsearchxref;"
@@ -272,11 +317,23 @@
272 ** enable searching.
273 **
274 ** The "search-config" is used to setup the search feature of the repository.
275 ** Subcommands are:
276 **
277 ** fossil search-config setting ?NAME? ?VALUE?
 
 
 
 
 
 
 
 
 
 
 
 
278 **
279 ** Set or query a search setting. NAMES are:
280 ** file-glob Comma-separated list of GLOBs for file search
281 ** ticket-expr SQL expression to render TICKET content
282 ** ticketchng-expr SQL expression to render TICKETCHNG content
@@ -293,20 +350,18 @@
293 ** w: wiki pages
294 **
295 ** It is necessary to run "fossil search-config rebuild" after making
296 ** setting changes in order to reconstruct the search index
297 **
298 ** fossil search-config rebuild
299 ** fossil search-config optimize
300 **
301 ** Completely rebuild the search index, or optimize the search index.
302 **
303 ** fossil search-config reset
304 **
305 ** Disable search and remove the search indexes from the repository.
306 */
307 void ftsearch_cmd(void){
 
 
 
308 const char *zSubCmd;
309 int nSubCmd;
310 db_find_and_open_repository(0, 0);
311 verify_all_options();
312 if( g.argc<3 ) usage("search PATTERN");
@@ -328,31 +383,90 @@
328 fossil_fatal("search is disabled - see \"fossil help search\""
329 " for more information");
330 }
331 db_prepare(&q, "SELECT "
332 " snippet(ftsearch,%Q,%Q,'...'),"
333 " ftsearchxref.ftsid"
 
334 " FROM ftsearch, ftsearchxref"
335 " WHERE ftsearch.body MATCH %Q"
336 " AND ftsearchxref.docid=ftsearch.docid"
337 " ORDER BY ftsearchxref.mtime DESC;",
338 zMark1, zMark2, zSubCmd);
339 while( db_step(&q)==SQLITE_ROW ){
340 const char *zSnippet = db_column_text(&q,0);
341 char *zDesc = ftsearch_description(db_column_text(&q,1),0);
 
342 if( i++ > 0 ){
343 fossil_print("----------------------------------------------------\n");
344 }
345 fossil_print("%s\n%s\n", zDesc, zSnippet);
346 fossil_free(zDesc);
347 }
348 db_finalize(&q);
349 }else if( strncmp(zSubCmd, "settings", nSubCmd)==0 ){
350
 
 
 
 
 
 
 
 
 
 
 
 
351 }else if( strncmp(zSubCmd, "rebuild", nSubCmd)==0 ){
352 ftsearch_rebuild_all();
353 }else if( strncmp(zSubCmd, "reset", nSubCmd)==0 ){
354 ftsearch_disable_all();
355 }else if( strncmp(zSubCmd, "optimize", nSubCmd)==0 ){
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
356 }
357 db_end_transaction(0);
358 }
359
--- src/ftsearch.c
+++ src/ftsearch.c
@@ -60,11 +60,11 @@
60 if( zDocType[0]==0 || zDocType[1]==0 ) return 0;
61 zDocId = zDocType + 2;
62 }
63 id = atoi(zDocId);
64 switch( zDocType[0] ){
65 case 'c': { /* A check-in comment. zDocId is the RID */
66 zRes = db_text(0,
67 "SELECT coalesce(ecomment,comment) || char(10) ||"
68 " 'user: ' || coalesce(euser,user) || char(10) ||"
69 " 'branch: ' || coalesce((SELECT value FROM tagxref"
70 " WHERE tagid=%d AND tagtype>0"
@@ -72,38 +72,59 @@
72 " FROM event"
73 " WHERE event.objid=%d"
74 " AND event.type GLOB 'c*'",
75 TAG_BRANCH, id, id);
76 break;
77 }
78 case 'f': { /* A file with zDocId as the filename.fnid */
79 zRes = db_text(0,
80 "SELECT content(mlink.fid)"
81 " FROM filename, mlink, event"
82 " WHERE filename.fnid=%d"
83 " AND mlink.fnid=filename.fnid"
84 " AND event.objid=mlink.mid"
85 " ORDER BY event.mtime DESC LIMIT 1",
86 id
87 );
88 break;
89 }
90 default: {
91 /* No-op */
92 }
93 }
94 return zRes;
95 }
96
97 /* Return a human-readable description for the document described by
98 ** the arguments.
 
99 **
100 ** See ftsearch_content() for further information
101 */
102 char *ftsearch_description(
103 const char *zDocType,
104 const char *zDocId,
105 int bLink /* Provide hyperlink in text if true */
106 ){
107 char *zRes = 0; /* The result to be returned */
108 int id;
109 if( zDocId==0 ){
110 if( zDocType[0]==0 || zDocType[1]==0 ) return 0;
111 zDocId = zDocType + 2;
112 }
113 id = atoi(zDocId);
114 switch( zDocType[0] ){
115 case 'c': { /* A check-in comment. zDocId is the RID */
116 char *zUuid = db_text("","SELECT uuid FROM blob WHERE rid=%d", id);
117 zRes = mprintf("Check-in [%S]", zUuid);
118 fossil_free(zUuid);
119 break;
120 }
121 case 'f': { /* A file. zDocId is the FNID */
122 char *zName = db_text("","SELECT name FROM filename WHERE fnid=%d",id);
123 zRes = mprintf("File %s", zName);
124 fossil_free(zName);
125 break;
126 }
127 default: {
128 /* No-op */
129 }
130 }
@@ -125,11 +146,11 @@
146 db_find_and_open_repository(0, 0);
147 verify_all_options();
148 if( g.argc!=3 ) usage("DOCUMENTCODE");
149 if( strlen(g.argv[2])>3 ){
150 zContent = ftsearch_content(g.argv[2],0);
151 zDesc = ftsearch_description(g.argv[2],0,0);
152 }
153 if( zDesc ){
154 fossil_print("Description: %s\n", zDesc);
155 fossil_free(zDesc);
156 }
@@ -210,12 +231,19 @@
231
232 /*
233 ** Completely rebuild the ftsearch indexes from scratch
234 */
235 void ftsearch_rebuild_all(void){
236 const char *zEnables;
237 db_begin_transaction();
238 ftsearch_disable_all();
239 zEnables = db_get("ftsearch-index-type", "cdeftw");
240
241 /* If none of the search categories are enabled, then do not
242 ** bother constructing the search tables
243 */
244 if( sqlite3_strglob("*[cdeftw]*", zEnables) ) return;
245
246 /* The FTSSEARCHXREF table provides a mapping between the integer
247 ** document-ids in FTS4 to the "document codes" that describe a
248 ** referenced object
249 */
@@ -242,18 +270,35 @@
270 "CREATE VIRTUAL TABLE %s.ftsearch"
271 " USING fts4(content='ftsearchbody',body);",
272 db_name("repository")
273 );
274
275 if( strchr(zEnables, 'c')!=0 ){
276 /* Populate the FTSEARCHXREF table with references to all check-in
277 ** comments currently in the event table
278 */
279 db_multi_exec(
280 "INSERT INTO ftsearchxref(ftsid,mtime)"
281 " SELECT 'c-' || objid, mtime FROM event"
282 " WHERE type='ci';"
283 );
284 }
285
286 if( strchr(zEnables, 'f')!=0 ){
287 /* Populate the FTSEARCHXREF table with references to all files
288 */
289 db_multi_exec(
290 "INSERT INTO ftsearchxref(ftsid,mtime)"
291 " SELECT 'f-' || filename.fnid, max(event.mtime)"
292 " FROM filename, mlink, event"
293 " WHERE mlink.fnid=filename.fnid"
294 " AND event.objid=mlink.mid"
295 " AND %s"
296 " GROUP BY 1",
297 glob_expr("filename.name", db_get("search-file-glob","*"))
298 );
299 }
300
301 /* Index every document mentioned in the FTSEARCHXREF table */
302 db_multi_exec(
303 "INSERT INTO ftsearch(docid,body)"
304 " SELECT docid, ftsearch_content(ftsid) FROM ftsearchxref;"
@@ -272,11 +317,23 @@
317 ** enable searching.
318 **
319 ** The "search-config" is used to setup the search feature of the repository.
320 ** Subcommands are:
321 **
322 ** fossil search-config doclist
323 **
324 ** List all the documents currently indexed
325 **
326 ** fossil search-config rebuild
327 **
328 ** Completely rebuild the search index.
329 **
330 ** fossil search-config reset
331 **
332 ** Disable search and remove the search indexes from the repository.
333 **
334 ** fossil search-config setting NAME ?VALUE?
335 **
336 ** Set or query a search setting. NAMES are:
337 ** file-glob Comma-separated list of GLOBs for file search
338 ** ticket-expr SQL expression to render TICKET content
339 ** ticketchng-expr SQL expression to render TICKETCHNG content
@@ -293,20 +350,18 @@
350 ** w: wiki pages
351 **
352 ** It is necessary to run "fossil search-config rebuild" after making
353 ** setting changes in order to reconstruct the search index
354 **
355 ** fossil search-config status
356 **
357 ** Report on the status of the search configuration.
 
 
 
 
 
358 */
359 void ftsearch_cmd(void){
360 static const char *azSettings[] = {
361 "file-glob", "index-type", "ticket-expr", "ticketchng-expr"
362 };
363 const char *zSubCmd;
364 int nSubCmd;
365 db_find_and_open_repository(0, 0);
366 verify_all_options();
367 if( g.argc<3 ) usage("search PATTERN");
@@ -328,31 +383,90 @@
383 fossil_fatal("search is disabled - see \"fossil help search\""
384 " for more information");
385 }
386 db_prepare(&q, "SELECT "
387 " snippet(ftsearch,%Q,%Q,'...'),"
388 " ftsearchxref.ftsid,"
389 " date(ftsearchxref.mtime)"
390 " FROM ftsearch, ftsearchxref"
391 " WHERE ftsearch.body MATCH %Q"
392 " AND ftsearchxref.docid=ftsearch.docid"
393 " ORDER BY ftsearchxref.mtime DESC LIMIT 50;",
394 zMark1, zMark2, zSubCmd);
395 while( db_step(&q)==SQLITE_ROW ){
396 const char *zSnippet = db_column_text(&q,0);
397 char *zDesc = ftsearch_description(db_column_text(&q,1),0,0);
398 const char *zDate = db_column_text(&q,2);
399 if( i++ > 0 ){
400 fossil_print("----------------------------------------------------\n");
401 }
402 fossil_print("%s (%s)\n%s\n", zDesc, zDate, zSnippet);
403 fossil_free(zDesc);
404 }
405 db_finalize(&q);
406 }else if( strncmp(zSubCmd, "doclist", nSubCmd)==0 ){
407 if( db_table_exists("repository","ftsearch") ){
408 Stmt q;
409 db_prepare(&q, "SELECT ftsid, date(mtime) FROM ftsearchxref"
410 " ORDER BY mtime DESC");
411 while( db_step(&q)==SQLITE_ROW ){
412 const char *zDate = db_column_text(&q,1);
413 const char *zFtsid = db_column_text(&q,0);
414 char *zDesc = ftsearch_description(zFtsid,0,0);
415 fossil_print("%s (%s)\n", zDesc, zDate);
416 fossil_free(zDesc);
417 }
418 db_finalize(&q);
419 }
420 }else if( strncmp(zSubCmd, "rebuild", nSubCmd)==0 ){
421 ftsearch_rebuild_all();
422 }else if( strncmp(zSubCmd, "reset", nSubCmd)==0 ){
423 ftsearch_disable_all();
424 }else if( strncmp(zSubCmd, "settings", nSubCmd)==0 ){
425 const char *zName = g.argv[3];
426 const char *zValue = g.argc>=5 ? g.argv[4] : 0;
427 char *zFullname;
428 int i;
429 if( g.argc<4 ) usage("setting NAME ?VALUE?");
430 for(i=0; i<count(azSettings); i++){
431 if( strcmp(zName, azSettings[i])==0 ) break;
432 }
433 if( i>=count(azSettings) ){
434 Blob x;
435 blob_init(&x,0,0);
436 for(i=0; i<count(azSettings); i++) blob_appendf(&x," %s", azSettings[i]);
437 fossil_fatal("unknown setting \"%s\" - should be one of:%s",
438 zName, blob_str(&x));
439 }
440 zFullname = mprintf("search-%s", zName);
441 if( zValue==0 ){
442 zValue = db_get(zFullname, 0);
443 }else{
444 db_set(zFullname, zValue, 0);
445 }
446 if( zValue==0 ){
447 fossil_print("%s is not defined\n", zName);
448 }else{
449 fossil_print("%s: %s\n", zName, zValue);
450 }
451 }else if( strncmp(zSubCmd, "status", nSubCmd)==0 ){
452 int i;
453 fossil_print("search settings:\n");
454 for(i=0; i<count(azSettings); i++){
455 char *zFullname = mprintf("search-%s", azSettings[i]);
456 char *zValue = db_get(zFullname, 0);
457 if( zValue==0 ){
458 fossil_print(" %s is undefined\n", azSettings[i]);
459 }else{
460 fossil_print(" %s: %s\n", azSettings[i], zValue);
461 }
462 fossil_free(zFullname);
463 }
464 if( db_table_exists("repository","ftsearchxref") ){
465 int n = db_int(0, "SELECT count(*) FROM ftsearchxref");
466 fossil_print("search is enabled with %d documents indexed\n", n);
467 }else{
468 fossil_print("search is disabled\n");
469 }
470 }
471 db_end_transaction(0);
472 }
473
+10 -7
--- src/sqlcmd.c
+++ src/sqlcmd.c
@@ -40,20 +40,23 @@
4040
){
4141
int rid;
4242
Blob cx;
4343
const char *zName;
4444
assert( argc==1 );
45
- zName = (const char*)sqlite3_value_text(argv[0]);
46
- if( zName==0 ) return;
47
- g.db = sqlite3_context_db_handle(context);
48
- g.repositoryOpen = 1;
49
- rid = name_to_rid(zName);
45
+ if( sqlite3_value_type(argv[0])==SQLITE_INTEGER ){
46
+ rid = sqlite3_value_int(argv[0]);
47
+ }else{
48
+ zName = (const char*)sqlite3_value_text(argv[0]);
49
+ if( zName==0 ) return;
50
+ g.db = sqlite3_context_db_handle(context);
51
+ g.repositoryOpen = 1;
52
+ rid = name_to_rid(zName);
53
+ }
5054
if( rid==0 ) return;
5155
if( content_get(rid, &cx) ){
5256
sqlite3_result_blob(context, blob_buffer(&cx), blob_size(&cx),
53
- SQLITE_TRANSIENT);
54
- blob_reset(&cx);
57
+ blob_is_malloced(&cx) ? fossil_free : SQLITE_TRANSIENT);
5558
}
5659
}
5760
5861
/*
5962
** Implementation of the "compress(X)" SQL function. The input X is
6063
--- src/sqlcmd.c
+++ src/sqlcmd.c
@@ -40,20 +40,23 @@
40 ){
41 int rid;
42 Blob cx;
43 const char *zName;
44 assert( argc==1 );
45 zName = (const char*)sqlite3_value_text(argv[0]);
46 if( zName==0 ) return;
47 g.db = sqlite3_context_db_handle(context);
48 g.repositoryOpen = 1;
49 rid = name_to_rid(zName);
 
 
 
 
50 if( rid==0 ) return;
51 if( content_get(rid, &cx) ){
52 sqlite3_result_blob(context, blob_buffer(&cx), blob_size(&cx),
53 SQLITE_TRANSIENT);
54 blob_reset(&cx);
55 }
56 }
57
58 /*
59 ** Implementation of the "compress(X)" SQL function. The input X is
60
--- src/sqlcmd.c
+++ src/sqlcmd.c
@@ -40,20 +40,23 @@
40 ){
41 int rid;
42 Blob cx;
43 const char *zName;
44 assert( argc==1 );
45 if( sqlite3_value_type(argv[0])==SQLITE_INTEGER ){
46 rid = sqlite3_value_int(argv[0]);
47 }else{
48 zName = (const char*)sqlite3_value_text(argv[0]);
49 if( zName==0 ) return;
50 g.db = sqlite3_context_db_handle(context);
51 g.repositoryOpen = 1;
52 rid = name_to_rid(zName);
53 }
54 if( rid==0 ) return;
55 if( content_get(rid, &cx) ){
56 sqlite3_result_blob(context, blob_buffer(&cx), blob_size(&cx),
57 blob_is_malloced(&cx) ? fossil_free : SQLITE_TRANSIENT);
 
58 }
59 }
60
61 /*
62 ** Implementation of the "compress(X)" SQL function. The input X is
63

Keyboard Shortcuts

Open search /
Next entry (timeline) j
Previous entry (timeline) k
Open focused entry Enter
Show this help ?
Toggle theme Top nav button