Fossil SCM

timeline json refactoring, fixed ordering, split tags into an Array.

stephan 2011-09-21 15:22 UTC json
Commit d6cbe37b6b63c914167d4461a40325741f24e1f7
1 file changed +121 -51
+121 -51
--- src/json.c
+++ src/json.c
@@ -462,10 +462,80 @@
462462
g.json.param.v = v;
463463
g.json.param.o = cson_value_get_object(v);
464464
json_gc_add("$PARAMS", v, 1);
465465
}
466466
467
+
468
+/*
469
+** Splits zStr (which must not be NULL) into tokens separated by the
470
+** given separator character. If doDeHttp is true then each element
471
+** will be passed through dehttpize(), otherwise they are used
472
+** as-is. Each new element is appended to the given target array
473
+** object, which must not be NULL and ownership of it is not changed
474
+** by this call.
475
+**
476
+** On success, returns the number of items appended to target. On
477
+** error a NEGATIVE number is returned - its absolute value is the
478
+** number of items inserted before the error occurred. There is a
479
+** corner case here if we fail on the 1st element, which will cause 0
480
+** to be returned, which the client cannot immediately distinguish as
481
+** success or error.
482
+**
483
+** Achtung: leading whitespace of elements is NOT elided. We should
484
+** add an option to do that, but don't have one yet.
485
+**
486
+** Achtung: empty elements will be skipped, meaning consecutive
487
+** empty elements are collapsed.
488
+*/
489
+int json_string_split( char const * zStr,
490
+ char separator,
491
+ char doDeHttp,
492
+ cson_array * target ){
493
+ char const * p = zStr /* current byte */;
494
+ char const * head = p /* current start-of-token */;
495
+ unsigned int len = 0 /* current token's length */;
496
+ int rc = 0 /* return code (number of added elements)*/;
497
+ assert( zStr && target );
498
+ for( ; ; ++p){
499
+ if( !*p || (separator == *p) ){
500
+ if( len ){/* append head..(head+len) as next array
501
+ element. */
502
+ cson_value * part = NULL;
503
+ char * zPart = NULL;
504
+ assert( head != p );
505
+ zPart = (char*)malloc(len+1);
506
+ assert( (zPart != NULL) && "malloc failure" );
507
+ memcpy(zPart, head, len);
508
+ zPart[len] = 0;
509
+ if(doDeHttp){
510
+ dehttpize(zPart);
511
+ }
512
+ if( *zPart ){ /* should only fail if someone manages to url-encoded a NUL byte */
513
+ part = cson_value_new_string(zPart, strlen(zPart));
514
+ if( 0 == cson_array_append( target, part ) ){
515
+ ++rc;
516
+ }else{
517
+ cson_value_free(part);
518
+ rc = rc ? -rc : 0;
519
+ break;
520
+ }
521
+ }else{
522
+ assert(0 && "i didn't think this was possible!");
523
+ }
524
+ free(zPart);
525
+ len = 0;
526
+ }
527
+ if( !*p ){
528
+ break;
529
+ }
530
+ head = p+1;
531
+ continue;
532
+ }
533
+ ++len;
534
+ }
535
+ return rc;
536
+}
467537
468538
/*
469539
** Performs some common initialization of JSON-related state. Must be
470540
** called by the json_page_top() and json_cmd_top() dispatching
471541
** functions to set up the JSON stat used by the dispatched functions.
@@ -519,44 +589,12 @@
519589
Note that translating g.argv this way is overkill but allows us to
520590
avoid CLI-only special-case handling in other code, e.g.
521591
json_command_arg().
522592
*/
523593
if( zPath ){/* Either CGI or server mode... */
524
- /* Translate PATH_INFO into JSON for later convenience. */
525
- char const * p = zPath /* current byte */;
526
- char const * head = p /* current start-of-token */;
527
- unsigned int len = 0 /* current token's length */;
528
- assert( g.isHTTP && "g.isHTTP should have been set by now." );
529
- for( ; ; ++p){
530
- if( !*p || ('/' == *p) ){
531
- if( len ){/* append head..(head+len) as next array
532
- element. */
533
- cson_value * part = NULL;
534
- char * zPart = NULL;
535
- assert( head != p );
536
- zPart = (char*)malloc(len+1);
537
- assert( (zPart != NULL) && "malloc failure" );
538
- memcpy(zPart, head, len);
539
- zPart[len] = 0;
540
- dehttpize(zPart);
541
- if( *zPart ){ /* should only fail if someone manages to url-encoded a NUL byte */
542
- part = cson_value_new_string(zPart, strlen(zPart));
543
- cson_array_append( g.json.cmd.a, part );
544
- }else{
545
- assert(0 && "i didn't think this was possible!");
546
- }
547
- free(zPart);
548
- len = 0;
549
- }
550
- if( !*p ){
551
- break;
552
- }
553
- head = p+1;
554
- continue;
555
- }
556
- ++len;
557
- }
594
+ /* Translate PATH_INFO into JSON array for later convenience. */
595
+ json_string_split(zPath, '/', 1, g.json.cmd.a);
558596
}else{/* assume CLI mode */
559597
int i;
560598
char const * arg;
561599
cson_value * part;
562600
for(i = 1/*skip argv[0]*/; i < g.argc; ++i ){
@@ -1404,10 +1442,11 @@
14041442
14051443
/*
14061444
** Create a temporary table suitable for storing timeline data.
14071445
*/
14081446
static void json_timeline_temp_table(void){
1447
+ /* Field order MUST match that from json_timeline_query_XXX()!!! */
14091448
static const char zSql[] =
14101449
@ CREATE TEMP TABLE IF NOT EXISTS json_timeline(
14111450
@ rid INTEGER PRIMARY KEY,
14121451
@ uuid TEXT,
14131452
@ mtime INTEGER,
@@ -1440,11 +1479,11 @@
14401479
@ coalesce(ecomment, comment),
14411480
@ coalesce(euser, user),
14421481
@ blob.rid IN leaf,
14431482
@ bgcolor,
14441483
@ event.type,
1445
- @ (SELECT group_concat(substr(tagname,5), ' ') FROM tag, tagxref
1484
+ @ (SELECT group_concat(substr(tagname,5), ',') FROM tag, tagxref
14461485
@ WHERE tagname GLOB 'sym-*' AND tag.tagid=tagxref.tagid
14471486
@ AND tagxref.rid=blob.rid AND tagxref.tagtype>0),
14481487
@ tagid,
14491488
@ brief
14501489
@ FROM event JOIN blob
@@ -1458,36 +1497,45 @@
14581497
** /json/timeline/ci
14591498
**
14601499
** Far from complete.
14611500
*/
14621501
static cson_value * json_timeline_ci(unsigned int depth){
1502
+ static const int defaultLimit = 10;
14631503
cson_value * payV = NULL;
14641504
cson_object * pay = NULL;
14651505
cson_value * tmp = NULL;
1466
- int limit = json_getenv_int("n",10);
1506
+ cson_value * listV = NULL;
1507
+ cson_array * list = NULL;
1508
+ int limit = json_getenv_int("n",defaultLimit);
1509
+ int check = 0;
14671510
Stmt q;
14681511
Blob sql = empty_blob;
14691512
if( !g.perm.Read/* && !g.perm.RdTkt && !g.perm.RdWiki*/ ){
14701513
g.json.resultCode = FSL_JSON_E_DENIED;
14711514
return NULL;
14721515
}
1473
- if( limit < 0 ) limit = 10;
1516
+ payV = cson_value_new_object();
1517
+ pay = cson_value_get_object(payV);
1518
+ if( limit < 0 ) limit = defaultLimit;
14741519
json_timeline_temp_table();
1475
-
14761520
blob_append(&sql, "INSERT OR IGNORE INTO json_timeline ", -1);
14771521
blob_append(&sql, json_timeline_query_ci(), -1 );
14781522
blob_append(&sql, "AND event.type IN('ci') ", -1);
14791523
blob_append(&sql, "ORDER BY mtime DESC ", -1);
1524
+#define SET(K) if(0!=(check=cson_object_set(pay,K,tmp))){ \
1525
+ g.json.resultCode = (cson_rc.AllocError==check) \
1526
+ ? FSL_JSON_E_ALLOC : FSL_JSON_E_UNKNOWN; \
1527
+ goto error;\
1528
+ }
14801529
if(limit){
14811530
blob_appendf(&sql,"LIMIT %d ",limit);
1531
+ tmp = cson_value_new_integer(limit);
1532
+ SET("limit");
14821533
}
14831534
db_multi_exec(blob_buffer(&sql));
1484
- payV = cson_value_new_object();
1485
- pay = cson_value_get_object(payV);
1486
-#define SET(K) cson_object_set(pay,K,tmp)
14871535
1488
-#if 1
1536
+#if 0
14891537
/* only for testing! */
14901538
tmp = cson_value_new_string(blob_buffer(&sql),strlen(blob_buffer(&sql)));
14911539
SET("timelineSql");
14921540
#endif
14931541
@@ -1503,22 +1551,44 @@
15031551
" eventType AS eventType,"
15041552
" tags AS tags," /*FIXME: split this into
15051553
a JSON array*/
15061554
" tagId AS tagId,"
15071555
" brief AS briefText"
1508
- " FROM json_timeline",-1);
1556
+ " FROM json_timeline"
1557
+ " ORDER BY mtime DESC",
1558
+ -1);
15091559
db_prepare(&q,blob_buffer(&sql));
15101560
tmp = NULL;
1511
- cson_sqlite3_stmt_to_json(q.pStmt, &tmp, 1);
1512
- db_finalize(&q);
1513
- if(tmp){
1514
- cson_value * rows = cson_object_take(cson_value_get_object(tmp),"rows");
1515
- assert(NULL != rows);
1516
- cson_value_free(tmp);
1517
- tmp = rows;
1518
- }
1561
+ listV = cson_value_new_array();
1562
+ list = cson_value_get_array(listV);
1563
+ tmp = listV;
15191564
SET("timeline");
1565
+ while( (SQLITE_ROW == db_step(&q) )){
1566
+ /* convert each row into a JSON object...*/
1567
+ cson_value * rowV = cson_sqlite3_row_to_object(q.pStmt);
1568
+ cson_object * row = cson_value_get_object(rowV);
1569
+ cson_string const * tagsStr = NULL;
1570
+ if(!row){
1571
+ /* need a way of warning about this */
1572
+ continue;
1573
+ }
1574
+
1575
+ /* Split tags string field into JSON Array... */
1576
+ cson_array_append(list, rowV);
1577
+ tagsStr = cson_value_get_string(cson_object_get(row,"tags"));
1578
+ if(tagsStr){
1579
+ cson_value * tagsV = cson_value_new_array();
1580
+ cson_array * tags = cson_value_get_array(tagsV);
1581
+ if( 0 < json_string_split( cson_string_cstr(tagsStr), ',', 0, tags)){
1582
+ cson_object_set(row,"tags",tagsV)
1583
+ /*replaces/deletes old tags value, invalidating tagsStr!*/;
1584
+ }else{
1585
+ cson_value_free(tagsV);
1586
+ }
1587
+ }
1588
+ }
1589
+ db_finalize(&q);
15201590
#undef SET
15211591
goto ok;
15221592
error:
15231593
cson_value_free(payV);
15241594
payV = NULL;
15251595
--- src/json.c
+++ src/json.c
@@ -462,10 +462,80 @@
462 g.json.param.v = v;
463 g.json.param.o = cson_value_get_object(v);
464 json_gc_add("$PARAMS", v, 1);
465 }
466
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
467
468 /*
469 ** Performs some common initialization of JSON-related state. Must be
470 ** called by the json_page_top() and json_cmd_top() dispatching
471 ** functions to set up the JSON stat used by the dispatched functions.
@@ -519,44 +589,12 @@
519 Note that translating g.argv this way is overkill but allows us to
520 avoid CLI-only special-case handling in other code, e.g.
521 json_command_arg().
522 */
523 if( zPath ){/* Either CGI or server mode... */
524 /* Translate PATH_INFO into JSON for later convenience. */
525 char const * p = zPath /* current byte */;
526 char const * head = p /* current start-of-token */;
527 unsigned int len = 0 /* current token's length */;
528 assert( g.isHTTP && "g.isHTTP should have been set by now." );
529 for( ; ; ++p){
530 if( !*p || ('/' == *p) ){
531 if( len ){/* append head..(head+len) as next array
532 element. */
533 cson_value * part = NULL;
534 char * zPart = NULL;
535 assert( head != p );
536 zPart = (char*)malloc(len+1);
537 assert( (zPart != NULL) && "malloc failure" );
538 memcpy(zPart, head, len);
539 zPart[len] = 0;
540 dehttpize(zPart);
541 if( *zPart ){ /* should only fail if someone manages to url-encoded a NUL byte */
542 part = cson_value_new_string(zPart, strlen(zPart));
543 cson_array_append( g.json.cmd.a, part );
544 }else{
545 assert(0 && "i didn't think this was possible!");
546 }
547 free(zPart);
548 len = 0;
549 }
550 if( !*p ){
551 break;
552 }
553 head = p+1;
554 continue;
555 }
556 ++len;
557 }
558 }else{/* assume CLI mode */
559 int i;
560 char const * arg;
561 cson_value * part;
562 for(i = 1/*skip argv[0]*/; i < g.argc; ++i ){
@@ -1404,10 +1442,11 @@
1404
1405 /*
1406 ** Create a temporary table suitable for storing timeline data.
1407 */
1408 static void json_timeline_temp_table(void){
 
1409 static const char zSql[] =
1410 @ CREATE TEMP TABLE IF NOT EXISTS json_timeline(
1411 @ rid INTEGER PRIMARY KEY,
1412 @ uuid TEXT,
1413 @ mtime INTEGER,
@@ -1440,11 +1479,11 @@
1440 @ coalesce(ecomment, comment),
1441 @ coalesce(euser, user),
1442 @ blob.rid IN leaf,
1443 @ bgcolor,
1444 @ event.type,
1445 @ (SELECT group_concat(substr(tagname,5), ' ') FROM tag, tagxref
1446 @ WHERE tagname GLOB 'sym-*' AND tag.tagid=tagxref.tagid
1447 @ AND tagxref.rid=blob.rid AND tagxref.tagtype>0),
1448 @ tagid,
1449 @ brief
1450 @ FROM event JOIN blob
@@ -1458,36 +1497,45 @@
1458 ** /json/timeline/ci
1459 **
1460 ** Far from complete.
1461 */
1462 static cson_value * json_timeline_ci(unsigned int depth){
 
1463 cson_value * payV = NULL;
1464 cson_object * pay = NULL;
1465 cson_value * tmp = NULL;
1466 int limit = json_getenv_int("n",10);
 
 
 
1467 Stmt q;
1468 Blob sql = empty_blob;
1469 if( !g.perm.Read/* && !g.perm.RdTkt && !g.perm.RdWiki*/ ){
1470 g.json.resultCode = FSL_JSON_E_DENIED;
1471 return NULL;
1472 }
1473 if( limit < 0 ) limit = 10;
 
 
1474 json_timeline_temp_table();
1475
1476 blob_append(&sql, "INSERT OR IGNORE INTO json_timeline ", -1);
1477 blob_append(&sql, json_timeline_query_ci(), -1 );
1478 blob_append(&sql, "AND event.type IN('ci') ", -1);
1479 blob_append(&sql, "ORDER BY mtime DESC ", -1);
 
 
 
 
 
1480 if(limit){
1481 blob_appendf(&sql,"LIMIT %d ",limit);
 
 
1482 }
1483 db_multi_exec(blob_buffer(&sql));
1484 payV = cson_value_new_object();
1485 pay = cson_value_get_object(payV);
1486 #define SET(K) cson_object_set(pay,K,tmp)
1487
1488 #if 1
1489 /* only for testing! */
1490 tmp = cson_value_new_string(blob_buffer(&sql),strlen(blob_buffer(&sql)));
1491 SET("timelineSql");
1492 #endif
1493
@@ -1503,22 +1551,44 @@
1503 " eventType AS eventType,"
1504 " tags AS tags," /*FIXME: split this into
1505 a JSON array*/
1506 " tagId AS tagId,"
1507 " brief AS briefText"
1508 " FROM json_timeline",-1);
 
 
1509 db_prepare(&q,blob_buffer(&sql));
1510 tmp = NULL;
1511 cson_sqlite3_stmt_to_json(q.pStmt, &tmp, 1);
1512 db_finalize(&q);
1513 if(tmp){
1514 cson_value * rows = cson_object_take(cson_value_get_object(tmp),"rows");
1515 assert(NULL != rows);
1516 cson_value_free(tmp);
1517 tmp = rows;
1518 }
1519 SET("timeline");
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1520 #undef SET
1521 goto ok;
1522 error:
1523 cson_value_free(payV);
1524 payV = NULL;
1525
--- src/json.c
+++ src/json.c
@@ -462,10 +462,80 @@
462 g.json.param.v = v;
463 g.json.param.o = cson_value_get_object(v);
464 json_gc_add("$PARAMS", v, 1);
465 }
466
467
468 /*
469 ** Splits zStr (which must not be NULL) into tokens separated by the
470 ** given separator character. If doDeHttp is true then each element
471 ** will be passed through dehttpize(), otherwise they are used
472 ** as-is. Each new element is appended to the given target array
473 ** object, which must not be NULL and ownership of it is not changed
474 ** by this call.
475 **
476 ** On success, returns the number of items appended to target. On
477 ** error a NEGATIVE number is returned - its absolute value is the
478 ** number of items inserted before the error occurred. There is a
479 ** corner case here if we fail on the 1st element, which will cause 0
480 ** to be returned, which the client cannot immediately distinguish as
481 ** success or error.
482 **
483 ** Achtung: leading whitespace of elements is NOT elided. We should
484 ** add an option to do that, but don't have one yet.
485 **
486 ** Achtung: empty elements will be skipped, meaning consecutive
487 ** empty elements are collapsed.
488 */
489 int json_string_split( char const * zStr,
490 char separator,
491 char doDeHttp,
492 cson_array * target ){
493 char const * p = zStr /* current byte */;
494 char const * head = p /* current start-of-token */;
495 unsigned int len = 0 /* current token's length */;
496 int rc = 0 /* return code (number of added elements)*/;
497 assert( zStr && target );
498 for( ; ; ++p){
499 if( !*p || (separator == *p) ){
500 if( len ){/* append head..(head+len) as next array
501 element. */
502 cson_value * part = NULL;
503 char * zPart = NULL;
504 assert( head != p );
505 zPart = (char*)malloc(len+1);
506 assert( (zPart != NULL) && "malloc failure" );
507 memcpy(zPart, head, len);
508 zPart[len] = 0;
509 if(doDeHttp){
510 dehttpize(zPart);
511 }
512 if( *zPart ){ /* should only fail if someone manages to url-encoded a NUL byte */
513 part = cson_value_new_string(zPart, strlen(zPart));
514 if( 0 == cson_array_append( target, part ) ){
515 ++rc;
516 }else{
517 cson_value_free(part);
518 rc = rc ? -rc : 0;
519 break;
520 }
521 }else{
522 assert(0 && "i didn't think this was possible!");
523 }
524 free(zPart);
525 len = 0;
526 }
527 if( !*p ){
528 break;
529 }
530 head = p+1;
531 continue;
532 }
533 ++len;
534 }
535 return rc;
536 }
537
538 /*
539 ** Performs some common initialization of JSON-related state. Must be
540 ** called by the json_page_top() and json_cmd_top() dispatching
541 ** functions to set up the JSON stat used by the dispatched functions.
@@ -519,44 +589,12 @@
589 Note that translating g.argv this way is overkill but allows us to
590 avoid CLI-only special-case handling in other code, e.g.
591 json_command_arg().
592 */
593 if( zPath ){/* Either CGI or server mode... */
594 /* Translate PATH_INFO into JSON array for later convenience. */
595 json_string_split(zPath, '/', 1, g.json.cmd.a);
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
596 }else{/* assume CLI mode */
597 int i;
598 char const * arg;
599 cson_value * part;
600 for(i = 1/*skip argv[0]*/; i < g.argc; ++i ){
@@ -1404,10 +1442,11 @@
1442
1443 /*
1444 ** Create a temporary table suitable for storing timeline data.
1445 */
1446 static void json_timeline_temp_table(void){
1447 /* Field order MUST match that from json_timeline_query_XXX()!!! */
1448 static const char zSql[] =
1449 @ CREATE TEMP TABLE IF NOT EXISTS json_timeline(
1450 @ rid INTEGER PRIMARY KEY,
1451 @ uuid TEXT,
1452 @ mtime INTEGER,
@@ -1440,11 +1479,11 @@
1479 @ coalesce(ecomment, comment),
1480 @ coalesce(euser, user),
1481 @ blob.rid IN leaf,
1482 @ bgcolor,
1483 @ event.type,
1484 @ (SELECT group_concat(substr(tagname,5), ',') FROM tag, tagxref
1485 @ WHERE tagname GLOB 'sym-*' AND tag.tagid=tagxref.tagid
1486 @ AND tagxref.rid=blob.rid AND tagxref.tagtype>0),
1487 @ tagid,
1488 @ brief
1489 @ FROM event JOIN blob
@@ -1458,36 +1497,45 @@
1497 ** /json/timeline/ci
1498 **
1499 ** Far from complete.
1500 */
1501 static cson_value * json_timeline_ci(unsigned int depth){
1502 static const int defaultLimit = 10;
1503 cson_value * payV = NULL;
1504 cson_object * pay = NULL;
1505 cson_value * tmp = NULL;
1506 cson_value * listV = NULL;
1507 cson_array * list = NULL;
1508 int limit = json_getenv_int("n",defaultLimit);
1509 int check = 0;
1510 Stmt q;
1511 Blob sql = empty_blob;
1512 if( !g.perm.Read/* && !g.perm.RdTkt && !g.perm.RdWiki*/ ){
1513 g.json.resultCode = FSL_JSON_E_DENIED;
1514 return NULL;
1515 }
1516 payV = cson_value_new_object();
1517 pay = cson_value_get_object(payV);
1518 if( limit < 0 ) limit = defaultLimit;
1519 json_timeline_temp_table();
 
1520 blob_append(&sql, "INSERT OR IGNORE INTO json_timeline ", -1);
1521 blob_append(&sql, json_timeline_query_ci(), -1 );
1522 blob_append(&sql, "AND event.type IN('ci') ", -1);
1523 blob_append(&sql, "ORDER BY mtime DESC ", -1);
1524 #define SET(K) if(0!=(check=cson_object_set(pay,K,tmp))){ \
1525 g.json.resultCode = (cson_rc.AllocError==check) \
1526 ? FSL_JSON_E_ALLOC : FSL_JSON_E_UNKNOWN; \
1527 goto error;\
1528 }
1529 if(limit){
1530 blob_appendf(&sql,"LIMIT %d ",limit);
1531 tmp = cson_value_new_integer(limit);
1532 SET("limit");
1533 }
1534 db_multi_exec(blob_buffer(&sql));
 
 
 
1535
1536 #if 0
1537 /* only for testing! */
1538 tmp = cson_value_new_string(blob_buffer(&sql),strlen(blob_buffer(&sql)));
1539 SET("timelineSql");
1540 #endif
1541
@@ -1503,22 +1551,44 @@
1551 " eventType AS eventType,"
1552 " tags AS tags," /*FIXME: split this into
1553 a JSON array*/
1554 " tagId AS tagId,"
1555 " brief AS briefText"
1556 " FROM json_timeline"
1557 " ORDER BY mtime DESC",
1558 -1);
1559 db_prepare(&q,blob_buffer(&sql));
1560 tmp = NULL;
1561 listV = cson_value_new_array();
1562 list = cson_value_get_array(listV);
1563 tmp = listV;
 
 
 
 
 
1564 SET("timeline");
1565 while( (SQLITE_ROW == db_step(&q) )){
1566 /* convert each row into a JSON object...*/
1567 cson_value * rowV = cson_sqlite3_row_to_object(q.pStmt);
1568 cson_object * row = cson_value_get_object(rowV);
1569 cson_string const * tagsStr = NULL;
1570 if(!row){
1571 /* need a way of warning about this */
1572 continue;
1573 }
1574
1575 /* Split tags string field into JSON Array... */
1576 cson_array_append(list, rowV);
1577 tagsStr = cson_value_get_string(cson_object_get(row,"tags"));
1578 if(tagsStr){
1579 cson_value * tagsV = cson_value_new_array();
1580 cson_array * tags = cson_value_get_array(tagsV);
1581 if( 0 < json_string_split( cson_string_cstr(tagsStr), ',', 0, tags)){
1582 cson_object_set(row,"tags",tagsV)
1583 /*replaces/deletes old tags value, invalidating tagsStr!*/;
1584 }else{
1585 cson_value_free(tagsV);
1586 }
1587 }
1588 }
1589 db_finalize(&q);
1590 #undef SET
1591 goto ok;
1592 error:
1593 cson_value_free(payV);
1594 payV = NULL;
1595

Keyboard Shortcuts

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