Fossil SCM

fossil-scm / src / json_tag.c
Blame History Raw 480 lines
1
#ifdef FOSSIL_ENABLE_JSON
2
/*
3
** Copyright (c) 2011 D. Richard Hipp
4
**
5
** This program is free software; you can redistribute it and/or
6
** modify it under the terms of the Simplified BSD License (also
7
** known as the "2-Clause License" or "FreeBSD License".)
8
**
9
** This program is distributed in the hope that it will be useful,
10
** but without any warranty; without even the implied warranty of
11
** merchantability or fitness for a particular purpose.
12
**
13
** Author contact information:
14
** [email protected]
15
** http://www.hwaci.com/drh/
16
**
17
*/
18
#include "VERSION.h"
19
#include "config.h"
20
#include "json_tag.h"
21
22
#if INTERFACE
23
#include "json_detail.h"
24
#endif
25
26
27
static cson_value * json_tag_add(void);
28
static cson_value * json_tag_cancel(void);
29
static cson_value * json_tag_find(void);
30
static cson_value * json_tag_list(void);
31
/*
32
** Mapping of /json/tag/XXX commands/paths to callbacks.
33
*/
34
static const JsonPageDef JsonPageDefs_Tag[] = {
35
{"add", json_tag_add, 0},
36
{"cancel", json_tag_cancel, 0},
37
{"find", json_tag_find, 0},
38
{"list", json_tag_list, 0},
39
/* Last entry MUST have a NULL name. */
40
{NULL,NULL,0}
41
};
42
43
/*
44
** Implements the /json/tag family of pages/commands.
45
**
46
*/
47
cson_value * json_page_tag(void){
48
return json_page_dispatch_helper(&JsonPageDefs_Tag[0]);
49
}
50
51
52
/*
53
** Impl of /json/tag/add.
54
*/
55
static cson_value * json_tag_add(void){
56
cson_value * payV = NULL;
57
cson_object * pay = NULL;
58
char const * zName = NULL;
59
char const * zCheckin = NULL;
60
char fRaw = 0;
61
char fPropagate = 0;
62
char const * zValue = NULL;
63
const char *zPrefix = NULL;
64
65
if( !g.perm.Write ){
66
json_set_err(FSL_JSON_E_DENIED,
67
"Requires 'i' permissions.");
68
return NULL;
69
}
70
fRaw = json_find_option_bool("raw",NULL,NULL,0);
71
fPropagate = json_find_option_bool("propagate",NULL,NULL,0);
72
zName = json_find_option_cstr("name",NULL,NULL);
73
zPrefix = fRaw ? "" : "sym-";
74
if(!zName || !*zName){
75
if(!fossil_has_json()){
76
zName = json_command_arg(3);
77
}
78
if(!zName || !*zName){
79
json_set_err(FSL_JSON_E_MISSING_ARGS,
80
"'name' parameter is missing.");
81
return NULL;
82
}
83
}
84
85
zCheckin = json_find_option_cstr("checkin",NULL,NULL);
86
if( !zCheckin ){
87
if(!fossil_has_json()){
88
zCheckin = json_command_arg(4);
89
}
90
if(!zCheckin || !*zCheckin){
91
json_set_err(FSL_JSON_E_MISSING_ARGS,
92
"'checkin' parameter is missing.");
93
return NULL;
94
}
95
}
96
97
98
zValue = json_find_option_cstr("value",NULL,NULL);
99
if(!zValue && !fossil_has_json()){
100
zValue = json_command_arg(5);
101
}
102
103
db_begin_transaction();
104
tag_add_artifact(zPrefix, zName, zCheckin, zValue,
105
1+fPropagate,NULL/*DateOvrd*/,NULL/*UserOvrd*/);
106
db_end_transaction(0);
107
108
payV = cson_value_new_object();
109
pay = cson_value_get_object(payV);
110
cson_object_set(pay, "name", json_new_string(zName) );
111
cson_object_set(pay, "value", (zValue&&*zValue)
112
? json_new_string(zValue)
113
: cson_value_null());
114
cson_object_set(pay, "propagate", cson_value_new_bool(fPropagate));
115
cson_object_set(pay, "raw", cson_value_new_bool(fRaw));
116
{
117
Blob uu = empty_blob;
118
int rc;
119
blob_append(&uu, zName, -1);
120
rc = name_to_uuid(&uu, 9, "*");
121
if(0!=rc){
122
json_set_err(FSL_JSON_E_UNKNOWN,
123
"Could not convert name back to artifact hash!");
124
blob_reset(&uu);
125
goto error;
126
}
127
cson_object_set(pay, "appliedTo", json_new_string(blob_buffer(&uu)));
128
blob_reset(&uu);
129
}
130
131
goto ok;
132
error:
133
assert( 0 != g.json.resultCode );
134
cson_value_free(payV);
135
payV = NULL;
136
ok:
137
return payV;
138
}
139
140
141
/*
142
** Impl of /json/tag/cancel.
143
*/
144
static cson_value * json_tag_cancel(void){
145
char const * zName = NULL;
146
char const * zCheckin = NULL;
147
char fRaw = 0;
148
const char *zPrefix = NULL;
149
150
if( !g.perm.Write ){
151
json_set_err(FSL_JSON_E_DENIED,
152
"Requires 'i' permissions.");
153
return NULL;
154
}
155
156
fRaw = json_find_option_bool("raw",NULL,NULL,0);
157
zPrefix = fRaw ? "" : "sym-";
158
zName = json_find_option_cstr("name",NULL,NULL);
159
if(!zName || !*zName){
160
if(!fossil_has_json()){
161
zName = json_command_arg(3);
162
}
163
if(!zName || !*zName){
164
json_set_err(FSL_JSON_E_MISSING_ARGS,
165
"'name' parameter is missing.");
166
return NULL;
167
}
168
}
169
170
zCheckin = json_find_option_cstr("checkin",NULL,NULL);
171
if( !zCheckin ){
172
if(!fossil_has_json()){
173
zCheckin = json_command_arg(4);
174
}
175
if(!zCheckin || !*zCheckin){
176
json_set_err(FSL_JSON_E_MISSING_ARGS,
177
"'checkin' parameter is missing.");
178
return NULL;
179
}
180
}
181
/* FIXME?: verify that the tag is currently active. We have no real
182
error case unless we do that.
183
*/
184
db_begin_transaction();
185
tag_add_artifact(zPrefix, zName, zCheckin, NULL, 0, 0, 0);
186
db_end_transaction(0);
187
return NULL;
188
}
189
190
191
/*
192
** Impl of /json/tag/find.
193
*/
194
static cson_value * json_tag_find(void){
195
cson_value * payV = NULL;
196
cson_object * pay = NULL;
197
cson_value * listV = NULL;
198
cson_array * list = NULL;
199
char const * zName = NULL;
200
char const * zType = NULL;
201
char const * zType2 = NULL;
202
char fRaw = 0;
203
Stmt q = empty_Stmt;
204
int limit = 0;
205
int tagid = 0;
206
207
if( !g.perm.Read ){
208
json_set_err(FSL_JSON_E_DENIED,
209
"Requires 'o' permissions.");
210
return NULL;
211
}
212
zName = json_find_option_cstr("name",NULL,NULL);
213
if(!zName || !*zName){
214
if(!fossil_has_json()){
215
zName = json_command_arg(3);
216
}
217
if(!zName || !*zName){
218
json_set_err(FSL_JSON_E_MISSING_ARGS,
219
"'name' parameter is missing.");
220
return NULL;
221
}
222
}
223
zType = json_find_option_cstr("type",NULL,"t");
224
if(!zType || !*zType){
225
zType = "*";
226
zType2 = zType;
227
}else{
228
switch(*zType){
229
case 'c': zType = "ci"; zType2 = "checkin"; break;
230
case 'e': zType = "e"; zType2 = "event"; break;
231
case 'w': zType = "w"; zType2 = "wiki"; break;
232
case 't': zType = "t"; zType2 = "ticket"; break;
233
}
234
}
235
236
limit = json_find_option_int("limit",NULL,"n",0);
237
fRaw = json_find_option_bool("raw",NULL,NULL,0);
238
239
tagid = db_int(0, "SELECT tagid FROM tag WHERE tagname='%s' || %Q",
240
fRaw ? "" : "sym-",
241
zName);
242
243
payV = cson_value_new_object();
244
pay = cson_value_get_object(payV);
245
cson_object_set(pay, "name", json_new_string(zName));
246
cson_object_set(pay, "raw", cson_value_new_bool(fRaw));
247
cson_object_set(pay, "type", json_new_string(zType2));
248
cson_object_set(pay, "limit", json_new_int(limit));
249
250
#if 1
251
if( tagid<=0 ){
252
cson_object_set(pay,"artifacts", cson_value_null());
253
json_warn(FSL_JSON_W_TAG_NOT_FOUND, "Tag not found.");
254
return payV;
255
}
256
#endif
257
258
if( fRaw ){
259
db_prepare(&q,
260
"SELECT blob.uuid FROM tagxref, blob"
261
" WHERE tagid=(SELECT tagid FROM tag WHERE tagname=%Q)"
262
" AND tagxref.tagtype>0"
263
" AND blob.rid=tagxref.rid"
264
"%s LIMIT %d",
265
zName,
266
(limit>0)?"":"--", limit
267
);
268
while( db_step(&q)==SQLITE_ROW ){
269
if(!listV){
270
listV = cson_value_new_array();
271
list = cson_value_get_array(listV);
272
}
273
cson_array_append(list, cson_sqlite3_column_to_value(q.pStmt,0));
274
}
275
db_finalize(&q);
276
}else{
277
char const * zSqlBase = /*modified from timeline_query_for_tty()*/
278
" SELECT"
279
#if 0
280
" blob.rid AS rid,"
281
#endif
282
" uuid AS uuid,"
283
" cast(strftime('%s',event.mtime) as int) AS timestamp,"
284
" coalesce(ecomment,comment) AS comment,"
285
" coalesce(euser,user) AS user,"
286
" CASE event.type"
287
" WHEN 'ci' THEN 'checkin'"
288
" WHEN 'w' THEN 'wiki'"
289
" WHEN 'e' THEN 'event'"
290
" WHEN 't' THEN 'ticket'"
291
" ELSE 'unknown'"
292
" END"
293
" AS eventType"
294
" FROM event, blob"
295
" WHERE blob.rid=event.objid"
296
;
297
/* FIXME: re-add tags. */
298
db_prepare(&q,
299
"%s"
300
" AND event.type GLOB '%q'"
301
" AND blob.rid IN ("
302
" SELECT rid FROM tagxref"
303
" WHERE tagtype>0 AND tagid=%d"
304
" )"
305
" ORDER BY event.mtime DESC"
306
"%s LIMIT %d",
307
zSqlBase /*safe-for-%s*/, zType, tagid,
308
(limit>0)?"":"--", limit
309
);
310
listV = json_stmt_to_array_of_obj(&q, NULL);
311
db_finalize(&q);
312
}
313
314
if(!listV) {
315
listV = cson_value_null();
316
}
317
cson_object_set(pay, "artifacts", listV);
318
return payV;
319
}
320
321
322
/*
323
** Impl for /json/tag/list
324
**
325
** TODOs:
326
**
327
** Add -type TYPE (ci, w, e, t)
328
*/
329
static cson_value * json_tag_list(void){
330
cson_value * payV = NULL;
331
cson_object * pay = NULL;
332
cson_value const * tagsVal = NULL;
333
char const * zCheckin = NULL;
334
char fRaw = 0;
335
char fTicket = 0;
336
Stmt q = empty_Stmt;
337
338
if( !g.perm.Read ){
339
json_set_err(FSL_JSON_E_DENIED,
340
"Requires 'o' permissions.");
341
return NULL;
342
}
343
344
fRaw = json_find_option_bool("raw",NULL,NULL,0);
345
fTicket = json_find_option_bool("includeTickets","tkt","t",0);
346
zCheckin = json_find_option_cstr("checkin",NULL,NULL);
347
if( !zCheckin ){
348
zCheckin = json_command_arg( g.json.dispatchDepth + 1);
349
if( !zCheckin && cson_value_is_string(g.json.reqPayload.v) ){
350
zCheckin = cson_string_cstr(cson_value_get_string(g.json.reqPayload.v));
351
assert(zCheckin);
352
}
353
}
354
payV = cson_value_new_object();
355
pay = cson_value_get_object(payV);
356
cson_object_set(pay, "raw", cson_value_new_bool(fRaw) );
357
if( zCheckin ){
358
/**
359
Tags for a specific check-in. Output format:
360
361
RAW mode:
362
363
{
364
"sym-tagname": (value || null),
365
...other tags...
366
}
367
368
Non-raw:
369
370
{
371
"tagname": (value || null),
372
...other tags...
373
}
374
*/
375
cson_value * objV = NULL;
376
cson_object * obj = NULL;
377
int const rid = name_to_rid(zCheckin);
378
if(0==rid){
379
json_set_err(FSL_JSON_E_UNRESOLVED_UUID,
380
"Could not find artifact for check-in [%s].",
381
zCheckin);
382
goto error;
383
}
384
cson_object_set(pay, "checkin", json_new_string(zCheckin));
385
db_prepare(&q,
386
"SELECT tagname, value FROM tagxref, tag"
387
" WHERE tagxref.rid=%d AND tagxref.tagid=tag.tagid"
388
" AND tagtype>%d"
389
" ORDER BY tagname",
390
rid,
391
fRaw ? -1 : 0
392
);
393
while( SQLITE_ROW == db_step(&q) ){
394
const char *zName = db_column_text(&q, 0);
395
const char *zValue = db_column_text(&q, 1);
396
if( fRaw==0 ){
397
if( 0!=strncmp(zName, "sym-", 4) ) continue;
398
zName += 4;
399
assert( *zName );
400
}
401
if(NULL==objV){
402
objV = cson_value_new_object();
403
obj = cson_value_get_object(objV);
404
tagsVal = objV;
405
cson_object_set( pay, "tags", objV );
406
}
407
if( zValue && zValue[0] ){
408
cson_object_set( obj, zName, json_new_string(zValue) );
409
}else{
410
cson_object_set( obj, zName, cson_value_null() );
411
}
412
}
413
db_finalize(&q);
414
}else{/* all tags */
415
/* Output format:
416
417
RAW mode:
418
419
["tagname", "sym-tagname2",...]
420
421
Non-raw:
422
423
["tagname", "tagname2",...]
424
425
i don't really like the discrepancy in the format but this list
426
can get really long and (A) most tags don't have values, (B) i
427
don't want to bloat it more, and (C) cson_object_set() is O(N)
428
(N=current number of properties) because it uses an unsorted list
429
internally (for memory reasons), so this can slow down appreciably
430
on a long list. The culprit is really tkt- tags, as there is one
431
for each ticket (941 in the main fossil repo as of this writing).
432
*/
433
Blob sql = empty_blob;
434
cson_value * arV = NULL;
435
cson_array * ar = NULL;
436
blob_append(&sql,
437
"SELECT tagname FROM tag"
438
" WHERE EXISTS(SELECT 1 FROM tagxref"
439
" WHERE tagid=tag.tagid"
440
" AND tagtype>0)",
441
-1
442
);
443
if(!fTicket){
444
blob_append(&sql, " AND tagname NOT GLOB('tkt-*') ", -1);
445
}
446
blob_append(&sql,
447
" ORDER BY tagname", -1);
448
db_prepare(&q, "%s", blob_sql_text(&sql));
449
blob_reset(&sql);
450
cson_object_set(pay, "includeTickets", cson_value_new_bool(fTicket) );
451
while( SQLITE_ROW == db_step(&q) ){
452
const char *zName = db_column_text(&q, 0);
453
if(NULL==arV){
454
arV = cson_value_new_array();
455
ar = cson_value_get_array(arV);
456
cson_object_set(pay, "tags", arV);
457
tagsVal = arV;
458
}
459
else if( !fRaw && (0==strncmp(zName, "sym-", 4))){
460
zName += 4;
461
assert( *zName );
462
}
463
cson_array_append(ar, json_new_string(zName));
464
}
465
db_finalize(&q);
466
}
467
468
goto end;
469
error:
470
assert(0 != g.json.resultCode);
471
cson_value_free(payV);
472
payV = NULL;
473
end:
474
if( payV && !tagsVal ){
475
cson_object_set( pay, "tags", cson_value_null() );
476
}
477
return payV;
478
}
479
#endif /* FOSSIL_ENABLE_JSON */
480

Keyboard Shortcuts

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