Fossil SCM

Add the ft=TAG query parameter to /timeline which, in combination with d=Y shows all descendants of Y up to TAG. Implementation of enhancement request [eadfb8c2ffa9fe03].

drh 2023-03-27 13:41 trunk
Commit 64f141fce02f83497f54d12da457969bd85d11d82e8b5b63ad04fecd60a12d54
2 files changed +48 -1 +53 -5
+48 -1
--- src/name.c
+++ src/name.c
@@ -251,11 +251,11 @@
251251
** tag zTag that occurs on or prior to rDate.
252252
**
253253
** See also the performance note on most_recent_event_with_tag() which
254254
** applies to this routine too.
255255
*/
256
-int most_recent_checkin_with_tag_before_date(const char *zTag, double rLimit){
256
+int last_checkin_with_tag_before_date(const char *zTag, double rLimit){
257257
Stmt s;
258258
int rid = 0;
259259
if( strncmp(zTag, "tag:", 4)==0 ) zTag += 4;
260260
db_prepare(&s,
261261
"SELECT objid FROM ("
@@ -287,10 +287,57 @@
287287
);
288288
db_bind_double(&s, ":datelimit", rLimit);
289289
if( db_step(&s)==SQLITE_ROW ){
290290
rid = db_column_int(&s,0);
291291
}
292
+ db_finalize(&s);
293
+ return rid;
294
+}
295
+
296
+/*
297
+** Find the RID of the first check-in (chronologically) after rStart that
298
+** has tag zTag.
299
+**
300
+** See also the performance note on most_recent_event_with_tag() which
301
+** applies to this routine too.
302
+*/
303
+int first_checkin_with_tag_after_date(const char *zTag, double rStart){
304
+ Stmt s;
305
+ int rid = 0;
306
+ if( strncmp(zTag, "tag:", 4)==0 ) zTag += 4;
307
+ db_prepare(&s,
308
+ "SELECT objid FROM ("
309
+ /* Q1: Begin by looking for the tag in the 30 most recent events */
310
+ "SELECT objid"
311
+ " FROM (SELECT * FROM event WHERE mtime>=:startdate"
312
+ " ORDER BY mtime LIMIT 30) AS ex"
313
+ " WHERE type='ci'"
314
+ " AND EXISTS(SELECT 1 FROM tagxref, tag"
315
+ " WHERE tag.tagname='sym-%q'"
316
+ " AND tagxref.tagid=tag.tagid"
317
+ " AND tagxref.tagtype>0"
318
+ " AND tagxref.rid=ex.objid)"
319
+ " ORDER BY mtime LIMIT 1"
320
+ ") UNION ALL SELECT * FROM ("
321
+ /* Q2: If the tag is not found in the 30 most recent events, then using
322
+ ** the tagxref table to index for the tag */
323
+ "SELECT event.objid"
324
+ " FROM tag, tagxref, event"
325
+ " WHERE tag.tagname='sym-%q'"
326
+ " AND tagxref.tagid=tag.tagid"
327
+ " AND tagxref.tagtype>0"
328
+ " AND event.objid=tagxref.rid"
329
+ " AND event.type='ci'"
330
+ " AND event.mtime>=:startdate"
331
+ " ORDER BY event.mtime LIMIT 1"
332
+ ") LIMIT 1;",
333
+ zTag, zTag
334
+ );
335
+ db_bind_double(&s, ":startdate", rStart);
336
+ if( db_step(&s)==SQLITE_ROW ){
337
+ rid = db_column_int(&s,0);
338
+ }
292339
db_finalize(&s);
293340
return rid;
294341
}
295342
296343
/*
297344
--- src/name.c
+++ src/name.c
@@ -251,11 +251,11 @@
251 ** tag zTag that occurs on or prior to rDate.
252 **
253 ** See also the performance note on most_recent_event_with_tag() which
254 ** applies to this routine too.
255 */
256 int most_recent_checkin_with_tag_before_date(const char *zTag, double rLimit){
257 Stmt s;
258 int rid = 0;
259 if( strncmp(zTag, "tag:", 4)==0 ) zTag += 4;
260 db_prepare(&s,
261 "SELECT objid FROM ("
@@ -287,10 +287,57 @@
287 );
288 db_bind_double(&s, ":datelimit", rLimit);
289 if( db_step(&s)==SQLITE_ROW ){
290 rid = db_column_int(&s,0);
291 }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
292 db_finalize(&s);
293 return rid;
294 }
295
296 /*
297
--- src/name.c
+++ src/name.c
@@ -251,11 +251,11 @@
251 ** tag zTag that occurs on or prior to rDate.
252 **
253 ** See also the performance note on most_recent_event_with_tag() which
254 ** applies to this routine too.
255 */
256 int last_checkin_with_tag_before_date(const char *zTag, double rLimit){
257 Stmt s;
258 int rid = 0;
259 if( strncmp(zTag, "tag:", 4)==0 ) zTag += 4;
260 db_prepare(&s,
261 "SELECT objid FROM ("
@@ -287,10 +287,57 @@
287 );
288 db_bind_double(&s, ":datelimit", rLimit);
289 if( db_step(&s)==SQLITE_ROW ){
290 rid = db_column_int(&s,0);
291 }
292 db_finalize(&s);
293 return rid;
294 }
295
296 /*
297 ** Find the RID of the first check-in (chronologically) after rStart that
298 ** has tag zTag.
299 **
300 ** See also the performance note on most_recent_event_with_tag() which
301 ** applies to this routine too.
302 */
303 int first_checkin_with_tag_after_date(const char *zTag, double rStart){
304 Stmt s;
305 int rid = 0;
306 if( strncmp(zTag, "tag:", 4)==0 ) zTag += 4;
307 db_prepare(&s,
308 "SELECT objid FROM ("
309 /* Q1: Begin by looking for the tag in the 30 most recent events */
310 "SELECT objid"
311 " FROM (SELECT * FROM event WHERE mtime>=:startdate"
312 " ORDER BY mtime LIMIT 30) AS ex"
313 " WHERE type='ci'"
314 " AND EXISTS(SELECT 1 FROM tagxref, tag"
315 " WHERE tag.tagname='sym-%q'"
316 " AND tagxref.tagid=tag.tagid"
317 " AND tagxref.tagtype>0"
318 " AND tagxref.rid=ex.objid)"
319 " ORDER BY mtime LIMIT 1"
320 ") UNION ALL SELECT * FROM ("
321 /* Q2: If the tag is not found in the 30 most recent events, then using
322 ** the tagxref table to index for the tag */
323 "SELECT event.objid"
324 " FROM tag, tagxref, event"
325 " WHERE tag.tagname='sym-%q'"
326 " AND tagxref.tagid=tag.tagid"
327 " AND tagxref.tagtype>0"
328 " AND event.objid=tagxref.rid"
329 " AND event.type='ci'"
330 " AND event.mtime>=:startdate"
331 " ORDER BY event.mtime LIMIT 1"
332 ") LIMIT 1;",
333 zTag, zTag
334 );
335 db_bind_double(&s, ":startdate", rStart);
336 if( db_step(&s)==SQLITE_ROW ){
337 rid = db_column_int(&s,0);
338 }
339 db_finalize(&s);
340 return rid;
341 }
342
343 /*
344
+53 -5
--- src/timeline.c
+++ src/timeline.c
@@ -1566,10 +1566,11 @@
15661566
** n1=COUNT Same as "n" but doesn't set the display-preference cookie
15671567
** Use "n1=COUNT" for a one-time display change
15681568
** p=CHECKIN Parents and ancestors of CHECKIN
15691569
** bt=PRIOR ... going back to PRIOR
15701570
** d=CHECKIN Children and descendants of CHECKIN
1571
+** ft=DESCENDANT ... going forward to DESCENDANT
15711572
** dp=CHECKIN Same as 'd=CHECKIN&p=CHECKIN'
15721573
** df=CHECKIN Same as 'd=CHECKIN&n1=all&nd'. Mnemonic: "Derived From"
15731574
** bt=CHECKIN In conjunction with p=CX, this means show all
15741575
** ancestors of CX going back to the time of CHECKIN.
15751576
** All qualifying check-ins are shown unless there
@@ -2089,11 +2090,13 @@
20892090
/* If p= or d= is present, ignore all other parameters other than n= */
20902091
char *zUuid;
20912092
const char *zCiName;
20922093
int np = 0, nd;
20932094
const char *zBackTo = 0;
2095
+ const char *zFwdTo = 0;
20942096
int ridBackTo = 0;
2097
+ int ridFwdTo = 0;
20952098
20962099
tmFlags |= TIMELINE_XMERGE | TIMELINE_FILLGAPS;
20972100
if( p_rid && d_rid ){
20982101
if( p_rid!=d_rid ) p_rid = d_rid;
20992102
if( !haveParameterN ) nEntry = 10;
@@ -2106,32 +2109,62 @@
21062109
zCiName = pd_rid ? P("pd") : p_rid ? P("p") : P("d");
21072110
if( zCiName==0 ) zCiName = zUuid;
21082111
blob_append_sql(&sql, " AND event.objid IN ok");
21092112
nd = 0;
21102113
if( d_rid ){
2111
- compute_descendants(d_rid, nEntry==0 ? 0 : nEntry+1);
2114
+ Stmt s;
2115
+ double rStopTime = 9e99;
2116
+ zFwdTo = P("ft");
2117
+ if( zFwdTo ){
2118
+ double rStartDate = db_double(0.0,
2119
+ "SELECT mtime FROM event WHERE objid=%d", d_rid);
2120
+ ridFwdTo = first_checkin_with_tag_after_date(zFwdTo, rStartDate);
2121
+ if( ridFwdTo==0 ){
2122
+ ridFwdTo = name_to_typed_rid(zBackTo,"ci");
2123
+ }
2124
+ if( ridFwdTo ){
2125
+ if( !haveParameterN ) nEntry = 0;
2126
+ rStopTime = db_double(9e99,
2127
+ "SELECT mtime FROM event WHERE objid=%d", ridFwdTo);
2128
+ }
2129
+ }
2130
+ db_prepare(&s,
2131
+ "WITH RECURSIVE"
2132
+ " dx(rid,mtime) AS ("
2133
+ " SELECT %d, 0"
2134
+ " UNION"
2135
+ " SELECT plink.cid, plink.mtime FROM dx, plink"
2136
+ " WHERE plink.pid=dx.rid"
2137
+ " AND (:stop>=8e99 OR plink.mtime<=:stop)"
2138
+ " ORDER BY 2"
2139
+ " )"
2140
+ "INSERT OR IGNORE INTO ok SELECT rid FROM dx LIMIT %d",
2141
+ d_rid, nEntry<=0 ? -1 : nEntry+1
2142
+ );
2143
+ db_bind_double(&s, ":stop", rStopTime);
2144
+ db_step(&s);
2145
+ db_finalize(&s);
2146
+ /* compute_descendants(d_rid, nEntry==0 ? 0 : nEntry+1); */
21122147
nd = db_int(0, "SELECT count(*)-1 FROM ok");
21132148
if( nd>=0 ) db_multi_exec("%s", blob_sql_text(&sql));
21142149
if( nd>0 || p_rid==0 ){
21152150
blob_appendf(&desc, "%d descendant%s", nd,(1==nd)?"":"s");
21162151
}
21172152
if( useDividers && !selectedRid ) selectedRid = d_rid;
21182153
db_multi_exec("DELETE FROM ok");
21192154
}
21202155
if( p_rid ){
2121
- ridBackTo = 0;
21222156
zBackTo = P("bt");
21232157
if( zBackTo ){
21242158
double rDateLimit = db_double(0.0,
21252159
"SELECT mtime FROM event WHERE objid=%d", p_rid);
2126
- ridBackTo =
2127
- most_recent_checkin_with_tag_before_date(zBackTo, rDateLimit);
2160
+ ridBackTo = last_checkin_with_tag_before_date(zBackTo, rDateLimit);
21282161
if( ridBackTo==0 ){
21292162
ridBackTo = name_to_typed_rid(zBackTo,"ci");
21302163
}
2164
+ if( ridBackTo && !haveParameterN ) nEntry = 0;
21312165
}
2132
- if( ridBackTo && !haveParameterN ) nEntry = 0;
21332166
compute_ancestors(p_rid, nEntry==0 ? 0 : nEntry+1, 0, ridBackTo);
21342167
np = db_int(0, "SELECT count(*)-1 FROM ok");
21352168
if( np>0 || nd==0 ){
21362169
if( nd>0 ) blob_appendf(&desc, " and ");
21372170
blob_appendf(&desc, "%d ancestor%s", np, (1==np)?"":"s");
@@ -2150,10 +2183,25 @@
21502183
href("%R/info?name=%h",zCiName), zCiName,
21512184
href("%R/info?name=%h",zBackTo), zBackTo);
21522185
}else{
21532186
blob_appendf(&desc, " back to %z%h</a>",
21542187
href("%R/info?name=%h",zBackTo), zBackTo);
2188
+ if( ridFwdTo && zFwdTo ){
2189
+ blob_appendf(&desc, " and up to %z%h</a>",
2190
+ href("%R/info?name=%h",zFwdTo), zFwdTo);
2191
+ }
2192
+ }
2193
+ }else if( ridFwdTo ){
2194
+ if( nd==0 ){
2195
+ blob_reset(&desc);
2196
+ blob_appendf(&desc,
2197
+ "Check-in %z%h</a> only (%z%h</a> is not an descendant)",
2198
+ href("%R/info?name=%h",zCiName), zCiName,
2199
+ href("%R/info?name=%h",zFwdTo), zFwdTo);
2200
+ }else{
2201
+ blob_appendf(&desc, " up to %z%h</a>",
2202
+ href("%R/info?name=%h",zFwdTo), zFwdTo);
21552203
}
21562204
}
21572205
if( d_rid ){
21582206
if( p_rid ){
21592207
/* If both p= and d= are set, we don't have the uuid of d yet. */
21602208
--- src/timeline.c
+++ src/timeline.c
@@ -1566,10 +1566,11 @@
1566 ** n1=COUNT Same as "n" but doesn't set the display-preference cookie
1567 ** Use "n1=COUNT" for a one-time display change
1568 ** p=CHECKIN Parents and ancestors of CHECKIN
1569 ** bt=PRIOR ... going back to PRIOR
1570 ** d=CHECKIN Children and descendants of CHECKIN
 
1571 ** dp=CHECKIN Same as 'd=CHECKIN&p=CHECKIN'
1572 ** df=CHECKIN Same as 'd=CHECKIN&n1=all&nd'. Mnemonic: "Derived From"
1573 ** bt=CHECKIN In conjunction with p=CX, this means show all
1574 ** ancestors of CX going back to the time of CHECKIN.
1575 ** All qualifying check-ins are shown unless there
@@ -2089,11 +2090,13 @@
2089 /* If p= or d= is present, ignore all other parameters other than n= */
2090 char *zUuid;
2091 const char *zCiName;
2092 int np = 0, nd;
2093 const char *zBackTo = 0;
 
2094 int ridBackTo = 0;
 
2095
2096 tmFlags |= TIMELINE_XMERGE | TIMELINE_FILLGAPS;
2097 if( p_rid && d_rid ){
2098 if( p_rid!=d_rid ) p_rid = d_rid;
2099 if( !haveParameterN ) nEntry = 10;
@@ -2106,32 +2109,62 @@
2106 zCiName = pd_rid ? P("pd") : p_rid ? P("p") : P("d");
2107 if( zCiName==0 ) zCiName = zUuid;
2108 blob_append_sql(&sql, " AND event.objid IN ok");
2109 nd = 0;
2110 if( d_rid ){
2111 compute_descendants(d_rid, nEntry==0 ? 0 : nEntry+1);
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
2112 nd = db_int(0, "SELECT count(*)-1 FROM ok");
2113 if( nd>=0 ) db_multi_exec("%s", blob_sql_text(&sql));
2114 if( nd>0 || p_rid==0 ){
2115 blob_appendf(&desc, "%d descendant%s", nd,(1==nd)?"":"s");
2116 }
2117 if( useDividers && !selectedRid ) selectedRid = d_rid;
2118 db_multi_exec("DELETE FROM ok");
2119 }
2120 if( p_rid ){
2121 ridBackTo = 0;
2122 zBackTo = P("bt");
2123 if( zBackTo ){
2124 double rDateLimit = db_double(0.0,
2125 "SELECT mtime FROM event WHERE objid=%d", p_rid);
2126 ridBackTo =
2127 most_recent_checkin_with_tag_before_date(zBackTo, rDateLimit);
2128 if( ridBackTo==0 ){
2129 ridBackTo = name_to_typed_rid(zBackTo,"ci");
2130 }
 
2131 }
2132 if( ridBackTo && !haveParameterN ) nEntry = 0;
2133 compute_ancestors(p_rid, nEntry==0 ? 0 : nEntry+1, 0, ridBackTo);
2134 np = db_int(0, "SELECT count(*)-1 FROM ok");
2135 if( np>0 || nd==0 ){
2136 if( nd>0 ) blob_appendf(&desc, " and ");
2137 blob_appendf(&desc, "%d ancestor%s", np, (1==np)?"":"s");
@@ -2150,10 +2183,25 @@
2150 href("%R/info?name=%h",zCiName), zCiName,
2151 href("%R/info?name=%h",zBackTo), zBackTo);
2152 }else{
2153 blob_appendf(&desc, " back to %z%h</a>",
2154 href("%R/info?name=%h",zBackTo), zBackTo);
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
2155 }
2156 }
2157 if( d_rid ){
2158 if( p_rid ){
2159 /* If both p= and d= are set, we don't have the uuid of d yet. */
2160
--- src/timeline.c
+++ src/timeline.c
@@ -1566,10 +1566,11 @@
1566 ** n1=COUNT Same as "n" but doesn't set the display-preference cookie
1567 ** Use "n1=COUNT" for a one-time display change
1568 ** p=CHECKIN Parents and ancestors of CHECKIN
1569 ** bt=PRIOR ... going back to PRIOR
1570 ** d=CHECKIN Children and descendants of CHECKIN
1571 ** ft=DESCENDANT ... going forward to DESCENDANT
1572 ** dp=CHECKIN Same as 'd=CHECKIN&p=CHECKIN'
1573 ** df=CHECKIN Same as 'd=CHECKIN&n1=all&nd'. Mnemonic: "Derived From"
1574 ** bt=CHECKIN In conjunction with p=CX, this means show all
1575 ** ancestors of CX going back to the time of CHECKIN.
1576 ** All qualifying check-ins are shown unless there
@@ -2089,11 +2090,13 @@
2090 /* If p= or d= is present, ignore all other parameters other than n= */
2091 char *zUuid;
2092 const char *zCiName;
2093 int np = 0, nd;
2094 const char *zBackTo = 0;
2095 const char *zFwdTo = 0;
2096 int ridBackTo = 0;
2097 int ridFwdTo = 0;
2098
2099 tmFlags |= TIMELINE_XMERGE | TIMELINE_FILLGAPS;
2100 if( p_rid && d_rid ){
2101 if( p_rid!=d_rid ) p_rid = d_rid;
2102 if( !haveParameterN ) nEntry = 10;
@@ -2106,32 +2109,62 @@
2109 zCiName = pd_rid ? P("pd") : p_rid ? P("p") : P("d");
2110 if( zCiName==0 ) zCiName = zUuid;
2111 blob_append_sql(&sql, " AND event.objid IN ok");
2112 nd = 0;
2113 if( d_rid ){
2114 Stmt s;
2115 double rStopTime = 9e99;
2116 zFwdTo = P("ft");
2117 if( zFwdTo ){
2118 double rStartDate = db_double(0.0,
2119 "SELECT mtime FROM event WHERE objid=%d", d_rid);
2120 ridFwdTo = first_checkin_with_tag_after_date(zFwdTo, rStartDate);
2121 if( ridFwdTo==0 ){
2122 ridFwdTo = name_to_typed_rid(zBackTo,"ci");
2123 }
2124 if( ridFwdTo ){
2125 if( !haveParameterN ) nEntry = 0;
2126 rStopTime = db_double(9e99,
2127 "SELECT mtime FROM event WHERE objid=%d", ridFwdTo);
2128 }
2129 }
2130 db_prepare(&s,
2131 "WITH RECURSIVE"
2132 " dx(rid,mtime) AS ("
2133 " SELECT %d, 0"
2134 " UNION"
2135 " SELECT plink.cid, plink.mtime FROM dx, plink"
2136 " WHERE plink.pid=dx.rid"
2137 " AND (:stop>=8e99 OR plink.mtime<=:stop)"
2138 " ORDER BY 2"
2139 " )"
2140 "INSERT OR IGNORE INTO ok SELECT rid FROM dx LIMIT %d",
2141 d_rid, nEntry<=0 ? -1 : nEntry+1
2142 );
2143 db_bind_double(&s, ":stop", rStopTime);
2144 db_step(&s);
2145 db_finalize(&s);
2146 /* compute_descendants(d_rid, nEntry==0 ? 0 : nEntry+1); */
2147 nd = db_int(0, "SELECT count(*)-1 FROM ok");
2148 if( nd>=0 ) db_multi_exec("%s", blob_sql_text(&sql));
2149 if( nd>0 || p_rid==0 ){
2150 blob_appendf(&desc, "%d descendant%s", nd,(1==nd)?"":"s");
2151 }
2152 if( useDividers && !selectedRid ) selectedRid = d_rid;
2153 db_multi_exec("DELETE FROM ok");
2154 }
2155 if( p_rid ){
 
2156 zBackTo = P("bt");
2157 if( zBackTo ){
2158 double rDateLimit = db_double(0.0,
2159 "SELECT mtime FROM event WHERE objid=%d", p_rid);
2160 ridBackTo = last_checkin_with_tag_before_date(zBackTo, rDateLimit);
 
2161 if( ridBackTo==0 ){
2162 ridBackTo = name_to_typed_rid(zBackTo,"ci");
2163 }
2164 if( ridBackTo && !haveParameterN ) nEntry = 0;
2165 }
 
2166 compute_ancestors(p_rid, nEntry==0 ? 0 : nEntry+1, 0, ridBackTo);
2167 np = db_int(0, "SELECT count(*)-1 FROM ok");
2168 if( np>0 || nd==0 ){
2169 if( nd>0 ) blob_appendf(&desc, " and ");
2170 blob_appendf(&desc, "%d ancestor%s", np, (1==np)?"":"s");
@@ -2150,10 +2183,25 @@
2183 href("%R/info?name=%h",zCiName), zCiName,
2184 href("%R/info?name=%h",zBackTo), zBackTo);
2185 }else{
2186 blob_appendf(&desc, " back to %z%h</a>",
2187 href("%R/info?name=%h",zBackTo), zBackTo);
2188 if( ridFwdTo && zFwdTo ){
2189 blob_appendf(&desc, " and up to %z%h</a>",
2190 href("%R/info?name=%h",zFwdTo), zFwdTo);
2191 }
2192 }
2193 }else if( ridFwdTo ){
2194 if( nd==0 ){
2195 blob_reset(&desc);
2196 blob_appendf(&desc,
2197 "Check-in %z%h</a> only (%z%h</a> is not an descendant)",
2198 href("%R/info?name=%h",zCiName), zCiName,
2199 href("%R/info?name=%h",zFwdTo), zFwdTo);
2200 }else{
2201 blob_appendf(&desc, " up to %z%h</a>",
2202 href("%R/info?name=%h",zFwdTo), zFwdTo);
2203 }
2204 }
2205 if( d_rid ){
2206 if( p_rid ){
2207 /* If both p= and d= are set, we don't have the uuid of d yet. */
2208

Keyboard Shortcuts

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