Fossil SCM
Started adding /json/timeline support, but this is gonna be a doozie. Breaking it down into separate calls for ci/wiki/ticket, e.g. /json/timeline/ci because the structures will be different for each.
Commit
eff3f7d92960b0a0af2599bf19bd238afcd4d3b8
Parent
6ce6b5e63ff041a…
2 files changed
+136
-5
+12
-12
+136
-5
| --- src/json.c | ||
| +++ src/json.c | ||
| @@ -1289,30 +1289,50 @@ | ||
| 1289 | 1289 | /* Last entry MUST have a NULL name. */ |
| 1290 | 1290 | {NULL,NULL,0} |
| 1291 | 1291 | }; |
| 1292 | 1292 | |
| 1293 | 1293 | /* |
| 1294 | -** Implements the /json/wiki family of pages/commands. Far from | |
| 1295 | -** complete. | |
| 1294 | +** A page/command dispatch helper for fossil_json_f() implementations. | |
| 1295 | +** depth should be the depth parameter passed to the fossil_json_f(). | |
| 1296 | +** pages must be an array of JsonPageDef commands which we can | |
| 1297 | +** dispatch. The final item in the array MUST have a NULL name | |
| 1298 | +** element. | |
| 1299 | +** | |
| 1300 | +** This function takes json_comand_arg(1+depth) and searches pages | |
| 1301 | +** for a matching name. If found then that page's func() is called | |
| 1302 | +** to fetch the payload, which is returned to the caller. | |
| 1296 | 1303 | ** |
| 1304 | +** On error, g.json.resultCode is set to one of the FossilJsonCodes | |
| 1305 | +** values. | |
| 1297 | 1306 | */ |
| 1298 | -static cson_value * json_page_wiki(unsigned int depth){ | |
| 1307 | +static cson_value * json_page_dispatch_helper(unsigned int depth, | |
| 1308 | + JsonPageDef const * pages){ | |
| 1299 | 1309 | JsonPageDef const * def; |
| 1300 | 1310 | char const * cmd = json_command_arg(1+depth); |
| 1311 | + assert( NULL != pages ); | |
| 1301 | 1312 | if( ! cmd ){ |
| 1302 | 1313 | g.json.resultCode = FSL_JSON_E_MISSING_ARGS; |
| 1303 | 1314 | return NULL; |
| 1304 | 1315 | } |
| 1305 | - def = json_handler_for_name( cmd, &JsonPageDefs_Wiki[0] ); | |
| 1316 | + def = json_handler_for_name( cmd, pages ); | |
| 1306 | 1317 | if(!def){ |
| 1307 | 1318 | g.json.resultCode = FSL_JSON_E_UNKNOWN_COMMAND; |
| 1308 | 1319 | return NULL; |
| 1309 | 1320 | } |
| 1310 | 1321 | else{ |
| 1311 | 1322 | return (*def->func)(1+depth); |
| 1312 | 1323 | } |
| 1313 | 1324 | } |
| 1325 | + | |
| 1326 | +/* | |
| 1327 | +** Implements the /json/wiki family of pages/commands. Far from | |
| 1328 | +** complete. | |
| 1329 | +** | |
| 1330 | +*/ | |
| 1331 | +static cson_value * json_page_wiki(unsigned int depth){ | |
| 1332 | + return json_page_dispatch_helper(depth,&JsonPageDefs_Wiki[0]); | |
| 1333 | +} | |
| 1314 | 1334 | |
| 1315 | 1335 | /* |
| 1316 | 1336 | ** INTERIM implementation of /json/wiki/list |
| 1317 | 1337 | */ |
| 1318 | 1338 | static cson_value * json_wiki_list(unsigned int depth){ |
| @@ -1341,10 +1361,121 @@ | ||
| 1341 | 1361 | listV = NULL; |
| 1342 | 1362 | end: |
| 1343 | 1363 | db_finalize(&q); |
| 1344 | 1364 | return listV; |
| 1345 | 1365 | } |
| 1366 | + | |
| 1367 | + | |
| 1368 | +static cson_value * json_timeline_ci(unsigned int depth); | |
| 1369 | +/* | |
| 1370 | +** Mapping of /json/timeline/XXX commands/paths to callbacks. | |
| 1371 | +*/ | |
| 1372 | +static const JsonPageDef JsonPageDefs_Timeline[] = { | |
| 1373 | +{"ci", json_timeline_ci, 0}, | |
| 1374 | +/* Last entry MUST have a NULL name. */ | |
| 1375 | +{NULL,NULL,0} | |
| 1376 | +}; | |
| 1377 | + | |
| 1378 | +/* | |
| 1379 | +** Implements the /json/timeline family of pages/commands. Far from | |
| 1380 | +** complete. | |
| 1381 | +** | |
| 1382 | +*/ | |
| 1383 | +static cson_value * json_page_timeline(unsigned int depth){ | |
| 1384 | + return json_page_dispatch_helper(depth,&JsonPageDefs_Timeline[0]); | |
| 1385 | +} | |
| 1386 | + | |
| 1387 | +static int json_getenv_int(char const * pKey, int dflt ){ | |
| 1388 | + cson_value const * v = json_getenv(pKey); | |
| 1389 | + if(!v){ | |
| 1390 | + return dflt; | |
| 1391 | + }else if( cson_value_is_number(v) ){ | |
| 1392 | + return (int)cson_value_get_integer(v); | |
| 1393 | + }else if( cson_value_is_string(v) ){ | |
| 1394 | + char const * sv = cson_string_cstr(cson_value_get_string(v)); | |
| 1395 | + assert( (NULL!=sv) && "This is quite unexpected." ); | |
| 1396 | + return sv ? atoi(sv) : dflt; | |
| 1397 | + }else if( cson_value_is_bool(v) ){ | |
| 1398 | + return cson_value_get_bool(v) ? 1 : 0; | |
| 1399 | + }else{ | |
| 1400 | + /* we should arguably treat JSON null as 0. */ | |
| 1401 | + return dflt; | |
| 1402 | + } | |
| 1403 | +} | |
| 1404 | + | |
| 1405 | +/* | |
| 1406 | +** /json/timeline/ci | |
| 1407 | +** | |
| 1408 | +** Far from complete. | |
| 1409 | +*/ | |
| 1410 | +static cson_value * json_timeline_ci(unsigned int depth){ | |
| 1411 | + cson_value * payV = NULL; | |
| 1412 | + cson_object * pay = NULL; | |
| 1413 | + cson_value * tmp = NULL; | |
| 1414 | + int limit = json_getenv_int("n",10); | |
| 1415 | + Stmt q; | |
| 1416 | + Blob sql = empty_blob; | |
| 1417 | + if( !g.perm.Read && !g.perm.RdTkt && !g.perm.RdWiki ){ | |
| 1418 | + g.json.resultCode = FSL_JSON_E_DENIED; | |
| 1419 | + return NULL; | |
| 1420 | + } | |
| 1421 | + if( limit < 0 ) limit = 10; | |
| 1422 | + timeline_temp_table(); | |
| 1423 | + | |
| 1424 | + blob_append(&sql, "INSERT OR IGNORE INTO timeline ", -1); | |
| 1425 | + blob_append(&sql, timeline_query_for_www(), -1); | |
| 1426 | + blob_append(&sql, "AND event.type IN('ci') ", -1); | |
| 1427 | + blob_append(&sql, "ORDER BY mtime DESC ", -1); | |
| 1428 | + if(limit){ | |
| 1429 | + blob_appendf(&sql,"LIMIT %d ",limit); | |
| 1430 | + } | |
| 1431 | + db_multi_exec(blob_buffer(&sql)); | |
| 1432 | + payV = cson_value_new_object(); | |
| 1433 | + pay = cson_value_get_object(payV); | |
| 1434 | +#define SET(K) cson_object_set(pay,K,tmp) | |
| 1435 | + | |
| 1436 | +#if 1 | |
| 1437 | + /* only for testing! */ | |
| 1438 | + tmp = cson_value_new_string(blob_buffer(&sql),strlen(blob_buffer(&sql))); | |
| 1439 | + SET("timelineSql"); | |
| 1440 | +#endif | |
| 1441 | + | |
| 1442 | + blob_reset(&sql); | |
| 1443 | + blob_append(&sql, "SELECT rid AS rid," | |
| 1444 | + " uuid AS uuid," | |
| 1445 | + " timestamp AS timestamp," | |
| 1446 | + " comment AS comment, " | |
| 1447 | + " user AS user," | |
| 1448 | + " isleaf AS isLeaf," | |
| 1449 | + " bgcolor AS bgColor," | |
| 1450 | + " etype AS eventType," | |
| 1451 | + " taglist AS tagList," /*FIXME: split this into | |
| 1452 | + a JSON array*/ | |
| 1453 | + " tagid AS tagId," | |
| 1454 | + " short AS shortText" | |
| 1455 | + /* ignoring sortby field */ | |
| 1456 | + " FROM timeline",-1); | |
| 1457 | + db_prepare(&q,blob_buffer(&sql)); | |
| 1458 | + tmp = NULL; | |
| 1459 | + cson_sqlite3_stmt_to_json(q.pStmt, &tmp, 1); | |
| 1460 | + db_finalize(&q); | |
| 1461 | + if(tmp){ | |
| 1462 | + cson_value * rows = cson_object_take(cson_value_get_object(tmp),"rows"); | |
| 1463 | + assert(NULL != rows); | |
| 1464 | + cson_value_free(tmp); | |
| 1465 | + tmp = rows; | |
| 1466 | + } | |
| 1467 | + SET("timeline"); | |
| 1468 | +#undef SET | |
| 1469 | + goto ok; | |
| 1470 | + error: | |
| 1471 | + cson_value_free(payV); | |
| 1472 | + payV = NULL; | |
| 1473 | + ok: | |
| 1474 | + return payV; | |
| 1475 | +} | |
| 1476 | + | |
| 1346 | 1477 | |
| 1347 | 1478 | /* |
| 1348 | 1479 | ** Mapping of names to JSON pages/commands. Each name is a subpath of |
| 1349 | 1480 | ** /json (in CGI mode) or a subcommand of the json command in CLI mode |
| 1350 | 1481 | */ |
| @@ -1358,11 +1489,11 @@ | ||
| 1358 | 1489 | {"login",json_page_login,1}, |
| 1359 | 1490 | {"logout",json_page_logout,1}, |
| 1360 | 1491 | {"stat",json_page_stat,0}, |
| 1361 | 1492 | {"tag", json_page_nyi,0}, |
| 1362 | 1493 | {"ticket", json_page_nyi,0}, |
| 1363 | -{"timeline", json_page_nyi,0}, | |
| 1494 | +{"timeline", json_page_timeline,0}, | |
| 1364 | 1495 | {"user", json_page_nyi,0}, |
| 1365 | 1496 | {"version",json_page_version,0}, |
| 1366 | 1497 | {"wiki",json_page_wiki,0}, |
| 1367 | 1498 | /* Last entry MUST have a NULL name. */ |
| 1368 | 1499 | {NULL,NULL,0} |
| 1369 | 1500 |
| --- src/json.c | |
| +++ src/json.c | |
| @@ -1289,30 +1289,50 @@ | |
| 1289 | /* Last entry MUST have a NULL name. */ |
| 1290 | {NULL,NULL,0} |
| 1291 | }; |
| 1292 | |
| 1293 | /* |
| 1294 | ** Implements the /json/wiki family of pages/commands. Far from |
| 1295 | ** complete. |
| 1296 | ** |
| 1297 | */ |
| 1298 | static cson_value * json_page_wiki(unsigned int depth){ |
| 1299 | JsonPageDef const * def; |
| 1300 | char const * cmd = json_command_arg(1+depth); |
| 1301 | if( ! cmd ){ |
| 1302 | g.json.resultCode = FSL_JSON_E_MISSING_ARGS; |
| 1303 | return NULL; |
| 1304 | } |
| 1305 | def = json_handler_for_name( cmd, &JsonPageDefs_Wiki[0] ); |
| 1306 | if(!def){ |
| 1307 | g.json.resultCode = FSL_JSON_E_UNKNOWN_COMMAND; |
| 1308 | return NULL; |
| 1309 | } |
| 1310 | else{ |
| 1311 | return (*def->func)(1+depth); |
| 1312 | } |
| 1313 | } |
| 1314 | |
| 1315 | /* |
| 1316 | ** INTERIM implementation of /json/wiki/list |
| 1317 | */ |
| 1318 | static cson_value * json_wiki_list(unsigned int depth){ |
| @@ -1341,10 +1361,121 @@ | |
| 1341 | listV = NULL; |
| 1342 | end: |
| 1343 | db_finalize(&q); |
| 1344 | return listV; |
| 1345 | } |
| 1346 | |
| 1347 | /* |
| 1348 | ** Mapping of names to JSON pages/commands. Each name is a subpath of |
| 1349 | ** /json (in CGI mode) or a subcommand of the json command in CLI mode |
| 1350 | */ |
| @@ -1358,11 +1489,11 @@ | |
| 1358 | {"login",json_page_login,1}, |
| 1359 | {"logout",json_page_logout,1}, |
| 1360 | {"stat",json_page_stat,0}, |
| 1361 | {"tag", json_page_nyi,0}, |
| 1362 | {"ticket", json_page_nyi,0}, |
| 1363 | {"timeline", json_page_nyi,0}, |
| 1364 | {"user", json_page_nyi,0}, |
| 1365 | {"version",json_page_version,0}, |
| 1366 | {"wiki",json_page_wiki,0}, |
| 1367 | /* Last entry MUST have a NULL name. */ |
| 1368 | {NULL,NULL,0} |
| 1369 |
| --- src/json.c | |
| +++ src/json.c | |
| @@ -1289,30 +1289,50 @@ | |
| 1289 | /* Last entry MUST have a NULL name. */ |
| 1290 | {NULL,NULL,0} |
| 1291 | }; |
| 1292 | |
| 1293 | /* |
| 1294 | ** A page/command dispatch helper for fossil_json_f() implementations. |
| 1295 | ** depth should be the depth parameter passed to the fossil_json_f(). |
| 1296 | ** pages must be an array of JsonPageDef commands which we can |
| 1297 | ** dispatch. The final item in the array MUST have a NULL name |
| 1298 | ** element. |
| 1299 | ** |
| 1300 | ** This function takes json_comand_arg(1+depth) and searches pages |
| 1301 | ** for a matching name. If found then that page's func() is called |
| 1302 | ** to fetch the payload, which is returned to the caller. |
| 1303 | ** |
| 1304 | ** On error, g.json.resultCode is set to one of the FossilJsonCodes |
| 1305 | ** values. |
| 1306 | */ |
| 1307 | static cson_value * json_page_dispatch_helper(unsigned int depth, |
| 1308 | JsonPageDef const * pages){ |
| 1309 | JsonPageDef const * def; |
| 1310 | char const * cmd = json_command_arg(1+depth); |
| 1311 | assert( NULL != pages ); |
| 1312 | if( ! cmd ){ |
| 1313 | g.json.resultCode = FSL_JSON_E_MISSING_ARGS; |
| 1314 | return NULL; |
| 1315 | } |
| 1316 | def = json_handler_for_name( cmd, pages ); |
| 1317 | if(!def){ |
| 1318 | g.json.resultCode = FSL_JSON_E_UNKNOWN_COMMAND; |
| 1319 | return NULL; |
| 1320 | } |
| 1321 | else{ |
| 1322 | return (*def->func)(1+depth); |
| 1323 | } |
| 1324 | } |
| 1325 | |
| 1326 | /* |
| 1327 | ** Implements the /json/wiki family of pages/commands. Far from |
| 1328 | ** complete. |
| 1329 | ** |
| 1330 | */ |
| 1331 | static cson_value * json_page_wiki(unsigned int depth){ |
| 1332 | return json_page_dispatch_helper(depth,&JsonPageDefs_Wiki[0]); |
| 1333 | } |
| 1334 | |
| 1335 | /* |
| 1336 | ** INTERIM implementation of /json/wiki/list |
| 1337 | */ |
| 1338 | static cson_value * json_wiki_list(unsigned int depth){ |
| @@ -1341,10 +1361,121 @@ | |
| 1361 | listV = NULL; |
| 1362 | end: |
| 1363 | db_finalize(&q); |
| 1364 | return listV; |
| 1365 | } |
| 1366 | |
| 1367 | |
| 1368 | static cson_value * json_timeline_ci(unsigned int depth); |
| 1369 | /* |
| 1370 | ** Mapping of /json/timeline/XXX commands/paths to callbacks. |
| 1371 | */ |
| 1372 | static const JsonPageDef JsonPageDefs_Timeline[] = { |
| 1373 | {"ci", json_timeline_ci, 0}, |
| 1374 | /* Last entry MUST have a NULL name. */ |
| 1375 | {NULL,NULL,0} |
| 1376 | }; |
| 1377 | |
| 1378 | /* |
| 1379 | ** Implements the /json/timeline family of pages/commands. Far from |
| 1380 | ** complete. |
| 1381 | ** |
| 1382 | */ |
| 1383 | static cson_value * json_page_timeline(unsigned int depth){ |
| 1384 | return json_page_dispatch_helper(depth,&JsonPageDefs_Timeline[0]); |
| 1385 | } |
| 1386 | |
| 1387 | static int json_getenv_int(char const * pKey, int dflt ){ |
| 1388 | cson_value const * v = json_getenv(pKey); |
| 1389 | if(!v){ |
| 1390 | return dflt; |
| 1391 | }else if( cson_value_is_number(v) ){ |
| 1392 | return (int)cson_value_get_integer(v); |
| 1393 | }else if( cson_value_is_string(v) ){ |
| 1394 | char const * sv = cson_string_cstr(cson_value_get_string(v)); |
| 1395 | assert( (NULL!=sv) && "This is quite unexpected." ); |
| 1396 | return sv ? atoi(sv) : dflt; |
| 1397 | }else if( cson_value_is_bool(v) ){ |
| 1398 | return cson_value_get_bool(v) ? 1 : 0; |
| 1399 | }else{ |
| 1400 | /* we should arguably treat JSON null as 0. */ |
| 1401 | return dflt; |
| 1402 | } |
| 1403 | } |
| 1404 | |
| 1405 | /* |
| 1406 | ** /json/timeline/ci |
| 1407 | ** |
| 1408 | ** Far from complete. |
| 1409 | */ |
| 1410 | static cson_value * json_timeline_ci(unsigned int depth){ |
| 1411 | cson_value * payV = NULL; |
| 1412 | cson_object * pay = NULL; |
| 1413 | cson_value * tmp = NULL; |
| 1414 | int limit = json_getenv_int("n",10); |
| 1415 | Stmt q; |
| 1416 | Blob sql = empty_blob; |
| 1417 | if( !g.perm.Read && !g.perm.RdTkt && !g.perm.RdWiki ){ |
| 1418 | g.json.resultCode = FSL_JSON_E_DENIED; |
| 1419 | return NULL; |
| 1420 | } |
| 1421 | if( limit < 0 ) limit = 10; |
| 1422 | timeline_temp_table(); |
| 1423 | |
| 1424 | blob_append(&sql, "INSERT OR IGNORE INTO timeline ", -1); |
| 1425 | blob_append(&sql, timeline_query_for_www(), -1); |
| 1426 | blob_append(&sql, "AND event.type IN('ci') ", -1); |
| 1427 | blob_append(&sql, "ORDER BY mtime DESC ", -1); |
| 1428 | if(limit){ |
| 1429 | blob_appendf(&sql,"LIMIT %d ",limit); |
| 1430 | } |
| 1431 | db_multi_exec(blob_buffer(&sql)); |
| 1432 | payV = cson_value_new_object(); |
| 1433 | pay = cson_value_get_object(payV); |
| 1434 | #define SET(K) cson_object_set(pay,K,tmp) |
| 1435 | |
| 1436 | #if 1 |
| 1437 | /* only for testing! */ |
| 1438 | tmp = cson_value_new_string(blob_buffer(&sql),strlen(blob_buffer(&sql))); |
| 1439 | SET("timelineSql"); |
| 1440 | #endif |
| 1441 | |
| 1442 | blob_reset(&sql); |
| 1443 | blob_append(&sql, "SELECT rid AS rid," |
| 1444 | " uuid AS uuid," |
| 1445 | " timestamp AS timestamp," |
| 1446 | " comment AS comment, " |
| 1447 | " user AS user," |
| 1448 | " isleaf AS isLeaf," |
| 1449 | " bgcolor AS bgColor," |
| 1450 | " etype AS eventType," |
| 1451 | " taglist AS tagList," /*FIXME: split this into |
| 1452 | a JSON array*/ |
| 1453 | " tagid AS tagId," |
| 1454 | " short AS shortText" |
| 1455 | /* ignoring sortby field */ |
| 1456 | " FROM timeline",-1); |
| 1457 | db_prepare(&q,blob_buffer(&sql)); |
| 1458 | tmp = NULL; |
| 1459 | cson_sqlite3_stmt_to_json(q.pStmt, &tmp, 1); |
| 1460 | db_finalize(&q); |
| 1461 | if(tmp){ |
| 1462 | cson_value * rows = cson_object_take(cson_value_get_object(tmp),"rows"); |
| 1463 | assert(NULL != rows); |
| 1464 | cson_value_free(tmp); |
| 1465 | tmp = rows; |
| 1466 | } |
| 1467 | SET("timeline"); |
| 1468 | #undef SET |
| 1469 | goto ok; |
| 1470 | error: |
| 1471 | cson_value_free(payV); |
| 1472 | payV = NULL; |
| 1473 | ok: |
| 1474 | return payV; |
| 1475 | } |
| 1476 | |
| 1477 | |
| 1478 | /* |
| 1479 | ** Mapping of names to JSON pages/commands. Each name is a subpath of |
| 1480 | ** /json (in CGI mode) or a subcommand of the json command in CLI mode |
| 1481 | */ |
| @@ -1358,11 +1489,11 @@ | |
| 1489 | {"login",json_page_login,1}, |
| 1490 | {"logout",json_page_logout,1}, |
| 1491 | {"stat",json_page_stat,0}, |
| 1492 | {"tag", json_page_nyi,0}, |
| 1493 | {"ticket", json_page_nyi,0}, |
| 1494 | {"timeline", json_page_timeline,0}, |
| 1495 | {"user", json_page_nyi,0}, |
| 1496 | {"version",json_page_version,0}, |
| 1497 | {"wiki",json_page_wiki,0}, |
| 1498 | /* Last entry MUST have a NULL name. */ |
| 1499 | {NULL,NULL,0} |
| 1500 |
+12
-12
| --- src/timeline.c | ||
| +++ src/timeline.c | ||
| @@ -738,11 +738,11 @@ | ||
| 738 | 738 | } |
| 739 | 739 | |
| 740 | 740 | /* |
| 741 | 741 | ** Create a temporary table suitable for storing timeline data. |
| 742 | 742 | */ |
| 743 | -static void timeline_temp_table(void){ | |
| 743 | +void timeline_temp_table(void){ | |
| 744 | 744 | static const char zSql[] = |
| 745 | 745 | @ CREATE TEMP TABLE IF NOT EXISTS timeline( |
| 746 | 746 | @ rid INTEGER PRIMARY KEY, |
| 747 | 747 | @ uuid TEXT, |
| 748 | 748 | @ timestamp TEXT, |
| @@ -766,24 +766,24 @@ | ||
| 766 | 766 | */ |
| 767 | 767 | const char *timeline_query_for_www(void){ |
| 768 | 768 | static char *zBase = 0; |
| 769 | 769 | static const char zBaseSql[] = |
| 770 | 770 | @ SELECT |
| 771 | - @ blob.rid, | |
| 772 | - @ uuid, | |
| 771 | + @ blob.rid AS blobRid, | |
| 772 | + @ uuid AS uuid, | |
| 773 | 773 | @ datetime(event.mtime,'localtime') AS timestamp, |
| 774 | - @ coalesce(ecomment, comment), | |
| 775 | - @ coalesce(euser, user), | |
| 776 | - @ blob.rid IN leaf, | |
| 777 | - @ bgcolor, | |
| 778 | - @ event.type, | |
| 774 | + @ coalesce(ecomment, comment) AS comment, | |
| 775 | + @ coalesce(euser, user) AS user, | |
| 776 | + @ blob.rid IN leaf AS leaf, | |
| 777 | + @ bgcolor AS bgColor, | |
| 778 | + @ event.type AS eventType, | |
| 779 | 779 | @ (SELECT group_concat(substr(tagname,5), ', ') FROM tag, tagxref |
| 780 | 780 | @ WHERE tagname GLOB 'sym-*' AND tag.tagid=tagxref.tagid |
| 781 | - @ AND tagxref.rid=blob.rid AND tagxref.tagtype>0), | |
| 782 | - @ tagid, | |
| 783 | - @ brief, | |
| 784 | - @ event.mtime | |
| 781 | + @ AND tagxref.rid=blob.rid AND tagxref.tagtype>0) AS tags, | |
| 782 | + @ tagid AS tagid, | |
| 783 | + @ brief AS brief, | |
| 784 | + @ event.mtime AS mtime | |
| 785 | 785 | @ FROM event JOIN blob |
| 786 | 786 | @ WHERE blob.rid=event.objid |
| 787 | 787 | ; |
| 788 | 788 | if( zBase==0 ){ |
| 789 | 789 | zBase = mprintf(zBaseSql, TAG_BRANCH, TAG_BRANCH); |
| 790 | 790 |
| --- src/timeline.c | |
| +++ src/timeline.c | |
| @@ -738,11 +738,11 @@ | |
| 738 | } |
| 739 | |
| 740 | /* |
| 741 | ** Create a temporary table suitable for storing timeline data. |
| 742 | */ |
| 743 | static void timeline_temp_table(void){ |
| 744 | static const char zSql[] = |
| 745 | @ CREATE TEMP TABLE IF NOT EXISTS timeline( |
| 746 | @ rid INTEGER PRIMARY KEY, |
| 747 | @ uuid TEXT, |
| 748 | @ timestamp TEXT, |
| @@ -766,24 +766,24 @@ | |
| 766 | */ |
| 767 | const char *timeline_query_for_www(void){ |
| 768 | static char *zBase = 0; |
| 769 | static const char zBaseSql[] = |
| 770 | @ SELECT |
| 771 | @ blob.rid, |
| 772 | @ uuid, |
| 773 | @ datetime(event.mtime,'localtime') AS timestamp, |
| 774 | @ coalesce(ecomment, comment), |
| 775 | @ coalesce(euser, user), |
| 776 | @ blob.rid IN leaf, |
| 777 | @ bgcolor, |
| 778 | @ event.type, |
| 779 | @ (SELECT group_concat(substr(tagname,5), ', ') FROM tag, tagxref |
| 780 | @ WHERE tagname GLOB 'sym-*' AND tag.tagid=tagxref.tagid |
| 781 | @ AND tagxref.rid=blob.rid AND tagxref.tagtype>0), |
| 782 | @ tagid, |
| 783 | @ brief, |
| 784 | @ event.mtime |
| 785 | @ FROM event JOIN blob |
| 786 | @ WHERE blob.rid=event.objid |
| 787 | ; |
| 788 | if( zBase==0 ){ |
| 789 | zBase = mprintf(zBaseSql, TAG_BRANCH, TAG_BRANCH); |
| 790 |
| --- src/timeline.c | |
| +++ src/timeline.c | |
| @@ -738,11 +738,11 @@ | |
| 738 | } |
| 739 | |
| 740 | /* |
| 741 | ** Create a temporary table suitable for storing timeline data. |
| 742 | */ |
| 743 | void timeline_temp_table(void){ |
| 744 | static const char zSql[] = |
| 745 | @ CREATE TEMP TABLE IF NOT EXISTS timeline( |
| 746 | @ rid INTEGER PRIMARY KEY, |
| 747 | @ uuid TEXT, |
| 748 | @ timestamp TEXT, |
| @@ -766,24 +766,24 @@ | |
| 766 | */ |
| 767 | const char *timeline_query_for_www(void){ |
| 768 | static char *zBase = 0; |
| 769 | static const char zBaseSql[] = |
| 770 | @ SELECT |
| 771 | @ blob.rid AS blobRid, |
| 772 | @ uuid AS uuid, |
| 773 | @ datetime(event.mtime,'localtime') AS timestamp, |
| 774 | @ coalesce(ecomment, comment) AS comment, |
| 775 | @ coalesce(euser, user) AS user, |
| 776 | @ blob.rid IN leaf AS leaf, |
| 777 | @ bgcolor AS bgColor, |
| 778 | @ event.type AS eventType, |
| 779 | @ (SELECT group_concat(substr(tagname,5), ', ') FROM tag, tagxref |
| 780 | @ WHERE tagname GLOB 'sym-*' AND tag.tagid=tagxref.tagid |
| 781 | @ AND tagxref.rid=blob.rid AND tagxref.tagtype>0) AS tags, |
| 782 | @ tagid AS tagid, |
| 783 | @ brief AS brief, |
| 784 | @ event.mtime AS mtime |
| 785 | @ FROM event JOIN blob |
| 786 | @ WHERE blob.rid=event.objid |
| 787 | ; |
| 788 | if( zBase==0 ){ |
| 789 | zBase = mprintf(zBaseSql, TAG_BRANCH, TAG_BRANCH); |
| 790 |