Fossil

Check-in [19276984bd]
Login

Check-in [19276984bd]

Many hyperlinks are disabled.
Use anonymous login to enable hyperlinks.

Overview
Comment:Fixed a minor denkfehler in the "bump <1ms runtime to 1ms" logic. Changed g.json.gc to an array (lower memory and better performance for what we use it for).
Downloads: Tarball | ZIP archive
Timelines: family | ancestors | descendants | both | json-multitag-test | json
Files: files | file ages | folders
SHA1: 19276984bde340079137e0ddb8498c449b41de6a
User & Date: stephan 2011-10-07 02:58:47.515
Context
2011-10-07
03:09
fixed whoami requests in demo apps to notice when the user has been logged out (and logs him in with the new auth token). ... (check-in: ccc90734e0 user: stephan tags: json-multitag-test, json)
02:58
Fixed a minor denkfehler in the "bump <1ms runtime to 1ms" logic. Changed g.json.gc to an array (lower memory and better performance for what we use it for). ... (check-in: 19276984bd user: stephan tags: json-multitag-test, json)
02:16
merged in trunk [4b0f813b8c]. ... (check-in: 39d9f83781 user: stephan tags: json-multitag-test, json)
Changes
Unified Diff Ignore Whitespace Patch
Changes to ajax/index.html.
235
236
237
238
239
240
241




242
243
244
245
246
247
248
<input type='button' value='user/list' onclick='TheApp.cgi.sendCommand("/json/user/list")' />
<input type='button' value='user/get' onclick='TheApp.cgi.sendCommand("/json/user/get?name=anonymous")' />
<input type='button' value='resultCodes' onclick='TheApp.cgi.sendCommand("/json/resultCodes")' />
<input type='button' value='tag/list' onclick='TheApp.cgi.sendCommand("/json/tag/list?includeTickets=false&raw=false")' />
<input type='button' value='tag/list/json' onclick='TheApp.cgi.sendCommand("/json/tag/list/json?raw=false")' />
<input type='button' value='tag/add'
onclick='TheApp.cgi.sendCommand("/json/tag/add",{name:"json-add-tag-test",checkin:"json",value:"tag test",propagate:false,raw:false})' />




<!-- not yet ready...
<input type='button' value='artifact/XYZ' onclick='TheApp.cgi.sendCommand("/json/artifact?uuid=json")' />
-->

<!--
<input type='button' value='get whiki' onclick='TheApp.cgi.getPages("whiki")' />
<input type='button' value='get more' onclick='TheApp.cgi.getPages("HelloWorld/WhikiNews")' />







>
>
>
>







235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
<input type='button' value='user/list' onclick='TheApp.cgi.sendCommand("/json/user/list")' />
<input type='button' value='user/get' onclick='TheApp.cgi.sendCommand("/json/user/get?name=anonymous")' />
<input type='button' value='resultCodes' onclick='TheApp.cgi.sendCommand("/json/resultCodes")' />
<input type='button' value='tag/list' onclick='TheApp.cgi.sendCommand("/json/tag/list?includeTickets=false&raw=false")' />
<input type='button' value='tag/list/json' onclick='TheApp.cgi.sendCommand("/json/tag/list/json?raw=false")' />
<input type='button' value='tag/add'
onclick='TheApp.cgi.sendCommand("/json/tag/add",{name:"json-add-tag-test",checkin:"json",value:"tag test",propagate:false,raw:false})' />
<input type='button' value='tag/cancel'
onclick='TheApp.cgi.sendCommand("/json/tag/cancel",{name:"json-add-tag-test",checkin:"json",raw:false})' />
<input type='button' value='tag/find'
onclick='TheApp.cgi.sendCommand("/json/tag/find",{name:"json",type:"*",raw:false,limit:5})' />
<!-- not yet ready...
<input type='button' value='artifact/XYZ' onclick='TheApp.cgi.sendCommand("/json/artifact?uuid=json")' />
-->

<!--
<input type='button' value='get whiki' onclick='TheApp.cgi.getPages("whiki")' />
<input type='button' value='get more' onclick='TheApp.cgi.getPages("HelloWorld/WhikiNews")' />
Changes to src/json.c.
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361

362
363
364
365
366
367
368

369
370
371
372
373
374
375
  static char buf[BufSize] = {'F','O','S','S','I','L','-',0};
  assert((code >= 1000) && (code <= 9999) && "Invalid Fossil/JSON code.");
  sprintf(buf+7,"%04d", code);
  return buf;
}

/*
** Adds v to the API-internal cleanup mechanism. key must be a unique
** key for the given element. Adding another item with that key may
** free the previous one (depending on its reference count). If
** freeOnError is true then v is passed to cson_value_free() if the
** key cannot be inserted, otherweise ownership of v is not changed on
** error. Failure to insert a key may be caused by any of the
** following:
**
** - Allocation error.
** - g.json.gc.o is NULL
** - key is NULL or empty.
**
** Returns 0 on success.
**
** On success, ownership of v is transfered to (or shared with)
** g.json.gc, and v will be valid until that object is cleaned up or
** its key is replaced via another call to this function.

*/
int json_gc_add( char const * key, cson_value * v, char freeOnError ){
  int const rc = cson_object_set( g.json.gc.o, key, v );
  assert( NULL != g.json.gc.o );
  if( (0 != rc) && freeOnError ){
    cson_value_free( v );
  }

  return rc;
}


/*
** Returns the value of json_rc_cstr(code) as a new JSON
** string, which is owned by the caller and must eventually







|
<
<
|
|
|
|


|






|
>


|
|



>







338
339
340
341
342
343
344
345


346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
  static char buf[BufSize] = {'F','O','S','S','I','L','-',0};
  assert((code >= 1000) && (code <= 9999) && "Invalid Fossil/JSON code.");
  sprintf(buf+7,"%04d", code);
  return buf;
}

/*
** Adds v to the API-internal cleanup mechanism. key is ingored


** (legacy).  If freeOnError is true then v is passed to
** cson_value_free() if the key cannot be inserted, otherweise
** ownership of v is not changed on error. Failure to insert an item
** may be caused by any of the following:
**
** - Allocation error.
** - g.json.gc.a is NULL
** - key is NULL or empty.
**
** Returns 0 on success.
**
** On success, ownership of v is transfered to (or shared with)
** g.json.gc, and v will be valid until that object is cleaned up or
** some internal code incorrectly removes it from the gc (which we
** never do).
*/
int json_gc_add( char const * key, cson_value * v, char freeOnError ){
  int const rc = cson_array_append( g.json.gc.a, v );
  assert( NULL != g.json.gc.a );
  if( (0 != rc) && freeOnError ){
    cson_value_free( v );
  }
  assert( (0==rc) && "Adding item to GC failed." );
  return rc;
}


/*
** Returns the value of json_rc_cstr(code) as a new JSON
** string, which is owned by the caller and must eventually
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
  cson_value * v;
  assert( (NULL == g.json.gc.v) && "cgi_json_bootstrap() was called twice!" );

  /* g.json.gc is our "garbage collector" - where we put JSON values
     which need a long lifetime but don't have a logical parent to put
     them in.
  */
  v = cson_value_new_object();
  g.json.gc.v = v;
  g.json.gc.o = cson_value_get_object(v);

  /*
    g.json.param holds the JSONized counterpart of fossil's
    cgi_parameter_xxx() family of data. We store them as JSON, as
    opposed to using fossil's data directly, because we can retain
    full type information for data this way (as opposed to it always
    being of type string).







|

|







779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
  cson_value * v;
  assert( (NULL == g.json.gc.v) && "cgi_json_bootstrap() was called twice!" );

  /* g.json.gc is our "garbage collector" - where we put JSON values
     which need a long lifetime but don't have a logical parent to put
     them in.
  */
  v = cson_value_new_array();
  g.json.gc.v = v;
  g.json.gc.a = cson_value_get_array(v);

  /*
    g.json.param holds the JSONized counterpart of fossil's
    cgi_parameter_xxx() family of data. We store them as JSON, as
    opposed to using fossil's data directly, because we can retain
    full type information for data this way (as opposed to it always
    being of type string).
1410
1411
1412
1413
1414
1415
1416
1417
1418
1419
1420
1421
1422
1423
1424
       just before they end).
    */
    double span;
    span = END_TIMER;
    /* i'm actually seeing sub-ms runtimes in some tests, but a time of
       0 is "just wrong", so we'll bump that up to 1ms.
    */
    cson_object_set(o,"procTimeMs", cson_value_new_integer((cson_int_t)((span>0.0)?span:1)));
  }
  if(g.json.warnings.v){
    tmp = g.json.warnings.v;
    SET("warnings");
  }
  
  /* Only add the payload to SUCCESS responses. Else delete it. */







|







1410
1411
1412
1413
1414
1415
1416
1417
1418
1419
1420
1421
1422
1423
1424
       just before they end).
    */
    double span;
    span = END_TIMER;
    /* i'm actually seeing sub-ms runtimes in some tests, but a time of
       0 is "just wrong", so we'll bump that up to 1ms.
    */
    cson_object_set(o,"procTimeMs", cson_value_new_integer((cson_int_t)((span>1.0)?span:1)));
  }
  if(g.json.warnings.v){
    tmp = g.json.warnings.v;
    SET("warnings");
  }
  
  /* Only add the payload to SUCCESS responses. Else delete it. */
Changes to src/json_tag.c.
171
172
173
174
175
176
177



178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194

195
196
197
198
199
200
201
    }
    if(!zCheckin || !*zCheckin){
      json_set_err(FSL_JSON_E_MISSING_ARGS,
                   "'checkin' parameter is missing.");
      return NULL;
    }
  }



  db_begin_transaction();
  tag_add_artifact(zPrefix, zName, zCheckin, NULL, 0, 0, 0);
  db_end_transaction(0);
  return NULL;
}


/*
** Impl of /json/tag/find.
*/
static cson_value * json_tag_find(){
  cson_value * payV = NULL;
  cson_object * pay = NULL;
  cson_value * listV = NULL;
  cson_array * list = NULL;
  char const * zName = NULL;
  char const * zType = NULL;

  char fRaw = 0;
  Stmt q = empty_Stmt;
  int limit = 0;
  int tagid = 0;

  if( !g.perm.Read ){
    json_set_err(FSL_JSON_E_DENIED,







>
>
>

















>







171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
    }
    if(!zCheckin || !*zCheckin){
      json_set_err(FSL_JSON_E_MISSING_ARGS,
                   "'checkin' parameter is missing.");
      return NULL;
    }
  }
  /* FIXME?: verify that the tag is currently active. We have no real
     error case unless we do that.
  */
  db_begin_transaction();
  tag_add_artifact(zPrefix, zName, zCheckin, NULL, 0, 0, 0);
  db_end_transaction(0);
  return NULL;
}


/*
** Impl of /json/tag/find.
*/
static cson_value * json_tag_find(){
  cson_value * payV = NULL;
  cson_object * pay = NULL;
  cson_value * listV = NULL;
  cson_array * list = NULL;
  char const * zName = NULL;
  char const * zType = NULL;
  char const * zType2 = NULL;
  char fRaw = 0;
  Stmt q = empty_Stmt;
  int limit = 0;
  int tagid = 0;

  if( !g.perm.Read ){
    json_set_err(FSL_JSON_E_DENIED,
212
213
214
215
216
217
218








219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
                   "'name' parameter is missing.");
      return NULL;
    }
  }
  zType = json_find_option_cstr("type",NULL,"t");
  if(!zType || !*zType){
    zType = "*";








  }

  limit = json_find_option_int("limit",NULL,"n",0);
  fRaw = json_find_option_bool("raw",NULL,NULL,0);
  
  tagid = db_int(0, "SELECT tagid FROM tag WHERE tagname='%s' || %Q",
                 fRaw ? "" : "sym-",
                 zName);
  
  payV = cson_value_new_object();
  pay = cson_value_get_object(payV);
  cson_object_set(pay, "name", json_new_string(zName));
  cson_object_set(pay, "raw", cson_value_new_bool(fRaw));
  cson_object_set(pay, "type", json_new_string(zType));
  cson_object_set(pay, "limit", json_new_int(limit));

#if 1
  if( tagid<=0 ){
    cson_object_set(pay,"artifacts", cson_value_null());
    json_warn(FSL_JSON_W_TAG_NOT_FOUND, "Tag not found.");
    return payV;







>
>
>
>
>
>
>
>













|







216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
                   "'name' parameter is missing.");
      return NULL;
    }
  }
  zType = json_find_option_cstr("type",NULL,"t");
  if(!zType || !*zType){
    zType = "*";
    zType2 = zType;
  }else{
    switch(*zType){
      case 'c': zType = "ci"; zType2 = "checkin"; break;
      case 'e': zType = "e"; zType2 = "event"; break;
      case 'w': zType = "w"; zType2 = "wiki"; break;
      case 't': zType = "t"; zType2 = "ticket"; break;
    }
  }

  limit = json_find_option_int("limit",NULL,"n",0);
  fRaw = json_find_option_bool("raw",NULL,NULL,0);
  
  tagid = db_int(0, "SELECT tagid FROM tag WHERE tagname='%s' || %Q",
                 fRaw ? "" : "sym-",
                 zName);
  
  payV = cson_value_new_object();
  pay = cson_value_get_object(payV);
  cson_object_set(pay, "name", json_new_string(zName));
  cson_object_set(pay, "raw", cson_value_new_bool(fRaw));
  cson_object_set(pay, "type", json_new_string(zType2));
  cson_object_set(pay, "limit", json_new_int(limit));

#if 1
  if( tagid<=0 ){
    cson_object_set(pay,"artifacts", cson_value_null());
    json_warn(FSL_JSON_W_TAG_NOT_FOUND, "Tag not found.");
    return payV;
359
360
361
362
363
364
365

366
367
368
369
370
371
372
    int const rid = name_to_rid(zCheckin);
    if(0==rid){
      json_set_err(FSL_JSON_E_UNRESOLVED_UUID,
                   "Could not find artifact for checkin [%s].",
                   zCheckin);
      goto error;
    }

    db_prepare(&q,
               "SELECT tagname, value FROM tagxref, tag"
               " WHERE tagxref.rid=%d AND tagxref.tagid=tag.tagid"
               "   AND tagtype>%d"
               " ORDER BY tagname",
               rid,
               fRaw ? -1 : 0







>







371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
    int const rid = name_to_rid(zCheckin);
    if(0==rid){
      json_set_err(FSL_JSON_E_UNRESOLVED_UUID,
                   "Could not find artifact for checkin [%s].",
                   zCheckin);
      goto error;
    }
    cson_object_set(pay, "checkin", json_new_string(zCheckin));
    db_prepare(&q,
               "SELECT tagname, value FROM tagxref, tag"
               " WHERE tagxref.rid=%d AND tagxref.tagid=tag.tagid"
               "   AND tagtype>%d"
               " ORDER BY tagname",
               rid,
               fRaw ? -1 : 0
Changes to src/main.c.
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
    unsigned char dispatchDepth /* Tells JSON command dispatching
                                   which argument we are currently
                                   working on. For this purpose, arg#0
                                   is the "json" path/CLI arg.
                                */;
    struct {                   /* "garbage collector" */
      cson_value * v;
      cson_object * o;
    } gc;
    struct {                   /* JSON POST data. */
      cson_value * v;
      cson_array * a;
      int offset;              /* Tells us which PATH_INFO/CLI args
                                  part holds the "json" command, so
                                  that we can account for sub-repos







|







188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
    unsigned char dispatchDepth /* Tells JSON command dispatching
                                   which argument we are currently
                                   working on. For this purpose, arg#0
                                   is the "json" path/CLI arg.
                                */;
    struct {                   /* "garbage collector" */
      cson_value * v;
      cson_array * a;
    } gc;
    struct {                   /* JSON POST data. */
      cson_value * v;
      cson_array * a;
      int offset;              /* Tells us which PATH_INFO/CLI args
                                  part holds the "json" command, so
                                  that we can account for sub-repos