Fossil

Check-in [64f141fce0]
Login

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

Overview
Comment: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].
Downloads: Tarball | ZIP archive
Timelines: family | ancestors | descendants | both | trunk
Files: files | file ages | folders
SHA3-256: 64f141fce02f83497f54d12da457969bd85d11d82e8b5b63ad04fecd60a12d54
User & Date: drh 2023-03-27 13:41:25.164
Context
2023-03-28
00:02
Post-sleep edit pass on the new material in §3.2 of the containers doc. check-in: d21fb2678a user: wyoung tags: trunk
2023-03-27
13:41
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]. check-in: 64f141fce0 user: drh tags: trunk
07:24
Refined the Tcl and Python examples in the new §3.2 of the container doc. check-in: 9baa4423f6 user: wyoung tags: trunk
Changes
Unified Diff Ignore Whitespace Patch
Changes to src/name.c.
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
/*
** Find the RID for a check-in that is the most recent check-in with
** tag zTag that occurs on or prior to rDate.
**
** See also the performance note on most_recent_event_with_tag() which
** applies to this routine too.
*/
int most_recent_checkin_with_tag_before_date(const char *zTag, double rLimit){
  Stmt s;
  int rid = 0;
  if( strncmp(zTag, "tag:", 4)==0 ) zTag += 4;
  db_prepare(&s,
    "SELECT objid FROM ("
      /* Q1:  Begin by looking for the tag in the 30 most recent events */
      "SELECT objid"







|







249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
/*
** Find the RID for a check-in that is the most recent check-in with
** tag zTag that occurs on or prior to rDate.
**
** See also the performance note on most_recent_event_with_tag() which
** applies to this routine too.
*/
int last_checkin_with_tag_before_date(const char *zTag, double rLimit){
  Stmt s;
  int rid = 0;
  if( strncmp(zTag, "tag:", 4)==0 ) zTag += 4;
  db_prepare(&s,
    "SELECT objid FROM ("
      /* Q1:  Begin by looking for the tag in the 30 most recent events */
      "SELECT objid"
285
286
287
288
289
290
291















































292
293
294
295
296
297
298
    ") LIMIT 1;",
    zTag, zTag
  );
  db_bind_double(&s, ":datelimit", rLimit);
  if( db_step(&s)==SQLITE_ROW ){
    rid = db_column_int(&s,0);
  }















































  db_finalize(&s);
  return rid;
}

/*
** Return true if character "c" is a character that might have been
** accidentally appended to the end of a URL.







>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>







285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
    ") LIMIT 1;",
    zTag, zTag
  );
  db_bind_double(&s, ":datelimit", rLimit);
  if( db_step(&s)==SQLITE_ROW ){
    rid = db_column_int(&s,0);
  }
  db_finalize(&s);
  return rid;
}

/*
** Find the RID of the first check-in (chronologically) after rStart that
** has tag zTag.
**
** See also the performance note on most_recent_event_with_tag() which
** applies to this routine too.
*/
int first_checkin_with_tag_after_date(const char *zTag, double rStart){
  Stmt s;
  int rid = 0;
  if( strncmp(zTag, "tag:", 4)==0 ) zTag += 4;
  db_prepare(&s,
    "SELECT objid FROM ("
      /* Q1:  Begin by looking for the tag in the 30 most recent events */
      "SELECT objid"
       " FROM (SELECT * FROM event WHERE mtime>=:startdate"
             " ORDER BY mtime LIMIT 30) AS ex"
      " WHERE type='ci'"
        " AND EXISTS(SELECT 1 FROM tagxref, tag"
                     " WHERE tag.tagname='sym-%q'"
                       " AND tagxref.tagid=tag.tagid"
                       " AND tagxref.tagtype>0"
                       " AND tagxref.rid=ex.objid)"
      " ORDER BY mtime LIMIT 1"
    ") UNION ALL SELECT * FROM ("
      /* Q2: If the tag is not found in the 30 most recent events, then using
      ** the tagxref table to index for the tag */
      "SELECT event.objid"
       " FROM tag, tagxref, event"
      " WHERE tag.tagname='sym-%q'"
        " AND tagxref.tagid=tag.tagid"
        " AND tagxref.tagtype>0"
        " AND event.objid=tagxref.rid"
        " AND event.type='ci'"
        " AND event.mtime>=:startdate"
      " ORDER BY event.mtime LIMIT 1"
    ") LIMIT 1;",
    zTag, zTag
  );
  db_bind_double(&s, ":startdate", rStart);
  if( db_step(&s)==SQLITE_ROW ){
    rid = db_column_int(&s,0);
  }
  db_finalize(&s);
  return rid;
}

/*
** Return true if character "c" is a character that might have been
** accidentally appended to the end of a URL.
Changes to src/timeline.c.
1564
1565
1566
1567
1568
1569
1570

1571
1572
1573
1574
1575
1576
1577
**    sel2=TIMEORTAG  Like sel1= but use the secondary highlight.
**    n=COUNT         Maximum number of events. "all" for no limit
**    n1=COUNT        Same as "n" but doesn't set the display-preference cookie
**                       Use "n1=COUNT" for a one-time display change
**    p=CHECKIN       Parents and ancestors of CHECKIN
**                       bt=PRIOR   ... going back to PRIOR
**    d=CHECKIN       Children and descendants of CHECKIN

**    dp=CHECKIN      Same as 'd=CHECKIN&p=CHECKIN'
**    df=CHECKIN      Same as 'd=CHECKIN&n1=all&nd'.  Mnemonic: "Derived From"
**    bt=CHECKIN      In conjunction with p=CX, this means show all
**                       ancestors of CX going back to the time of CHECKIN.
**                       All qualifying check-ins are shown unless there
**                       is also an n= or n1= query parameter.
**    t=TAG           Show only check-ins with the given TAG







>







1564
1565
1566
1567
1568
1569
1570
1571
1572
1573
1574
1575
1576
1577
1578
**    sel2=TIMEORTAG  Like sel1= but use the secondary highlight.
**    n=COUNT         Maximum number of events. "all" for no limit
**    n1=COUNT        Same as "n" but doesn't set the display-preference cookie
**                       Use "n1=COUNT" for a one-time display change
**    p=CHECKIN       Parents and ancestors of CHECKIN
**                       bt=PRIOR   ... going back to PRIOR
**    d=CHECKIN       Children and descendants of CHECKIN
**                       ft=DESCENDANT   ... going forward to DESCENDANT
**    dp=CHECKIN      Same as 'd=CHECKIN&p=CHECKIN'
**    df=CHECKIN      Same as 'd=CHECKIN&n1=all&nd'.  Mnemonic: "Derived From"
**    bt=CHECKIN      In conjunction with p=CX, this means show all
**                       ancestors of CX going back to the time of CHECKIN.
**                       All qualifying check-ins are shown unless there
**                       is also an n= or n1= query parameter.
**    t=TAG           Show only check-ins with the given TAG
2087
2088
2089
2090
2091
2092
2093

2094

2095
2096
2097
2098
2099
2100
2101
2102
2103
2104
2105
2106
2107
2108
2109
2110
































2111
2112
2113
2114
2115
2116
2117
2118
2119
2120
2121
2122
2123
2124
2125
2126
2127
2128
2129
2130
2131
2132

2133
2134
2135
2136
2137
2138
2139
    addFileGlobDescription(zChng, &desc);
  }else if( (p_rid || d_rid) && g.perm.Read && zTagSql==0 ){
    /* If p= or d= is present, ignore all other parameters other than n= */
    char *zUuid;
    const char *zCiName;
    int np = 0, nd;
    const char *zBackTo = 0;

    int ridBackTo = 0;


    tmFlags |= TIMELINE_XMERGE | TIMELINE_FILLGAPS;
    if( p_rid && d_rid ){
      if( p_rid!=d_rid ) p_rid = d_rid;
      if( !haveParameterN ) nEntry = 10;
    }
    db_multi_exec(
       "CREATE TEMP TABLE IF NOT EXISTS ok(rid INTEGER PRIMARY KEY)"
    );
    zUuid = db_text("", "SELECT uuid FROM blob WHERE rid=%d",
                         p_rid ? p_rid : d_rid);
    zCiName = pd_rid ? P("pd") : p_rid ? P("p") : P("d");
    if( zCiName==0 ) zCiName = zUuid;
    blob_append_sql(&sql, " AND event.objid IN ok");
    nd = 0;
    if( d_rid ){
































      compute_descendants(d_rid, nEntry==0 ? 0 : nEntry+1);
      nd = db_int(0, "SELECT count(*)-1 FROM ok");
      if( nd>=0 ) db_multi_exec("%s", blob_sql_text(&sql));
      if( nd>0 || p_rid==0 ){
        blob_appendf(&desc, "%d descendant%s", nd,(1==nd)?"":"s");
      }
      if( useDividers && !selectedRid ) selectedRid = d_rid;
      db_multi_exec("DELETE FROM ok");
    }
    if( p_rid ){
      ridBackTo = 0;
      zBackTo = P("bt");
      if( zBackTo ){
        double rDateLimit = db_double(0.0,
           "SELECT mtime FROM event WHERE objid=%d", p_rid);
        ridBackTo =
          most_recent_checkin_with_tag_before_date(zBackTo, rDateLimit);
        if( ridBackTo==0 ){
          ridBackTo = name_to_typed_rid(zBackTo,"ci");
        }
      }
      if( ridBackTo && !haveParameterN ) nEntry = 0;

      compute_ancestors(p_rid, nEntry==0 ? 0 : nEntry+1, 0, ridBackTo);
      np = db_int(0, "SELECT count(*)-1 FROM ok");
      if( np>0 || nd==0 ){
        if( nd>0 ) blob_appendf(&desc, " and ");
        blob_appendf(&desc, "%d ancestor%s", np, (1==np)?"":"s");
        db_multi_exec("%s", blob_sql_text(&sql));
      }







>

>
















>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
|









<




<
|



<
|
>







2088
2089
2090
2091
2092
2093
2094
2095
2096
2097
2098
2099
2100
2101
2102
2103
2104
2105
2106
2107
2108
2109
2110
2111
2112
2113
2114
2115
2116
2117
2118
2119
2120
2121
2122
2123
2124
2125
2126
2127
2128
2129
2130
2131
2132
2133
2134
2135
2136
2137
2138
2139
2140
2141
2142
2143
2144
2145
2146
2147
2148
2149
2150
2151
2152
2153
2154
2155

2156
2157
2158
2159

2160
2161
2162
2163

2164
2165
2166
2167
2168
2169
2170
2171
2172
    addFileGlobDescription(zChng, &desc);
  }else if( (p_rid || d_rid) && g.perm.Read && zTagSql==0 ){
    /* If p= or d= is present, ignore all other parameters other than n= */
    char *zUuid;
    const char *zCiName;
    int np = 0, nd;
    const char *zBackTo = 0;
    const char *zFwdTo = 0;
    int ridBackTo = 0;
    int ridFwdTo = 0;

    tmFlags |= TIMELINE_XMERGE | TIMELINE_FILLGAPS;
    if( p_rid && d_rid ){
      if( p_rid!=d_rid ) p_rid = d_rid;
      if( !haveParameterN ) nEntry = 10;
    }
    db_multi_exec(
       "CREATE TEMP TABLE IF NOT EXISTS ok(rid INTEGER PRIMARY KEY)"
    );
    zUuid = db_text("", "SELECT uuid FROM blob WHERE rid=%d",
                         p_rid ? p_rid : d_rid);
    zCiName = pd_rid ? P("pd") : p_rid ? P("p") : P("d");
    if( zCiName==0 ) zCiName = zUuid;
    blob_append_sql(&sql, " AND event.objid IN ok");
    nd = 0;
    if( d_rid ){
      Stmt s;
      double rStopTime = 9e99;
      zFwdTo = P("ft");
      if( zFwdTo ){
        double rStartDate = db_double(0.0,
           "SELECT mtime FROM event WHERE objid=%d", d_rid);
        ridFwdTo = first_checkin_with_tag_after_date(zFwdTo, rStartDate);
        if( ridFwdTo==0 ){
          ridFwdTo = name_to_typed_rid(zBackTo,"ci");
        }
        if( ridFwdTo ){
          if( !haveParameterN ) nEntry = 0;
          rStopTime = db_double(9e99,
            "SELECT mtime FROM event WHERE objid=%d", ridFwdTo);
        }
      }
      db_prepare(&s,
        "WITH RECURSIVE"
        "  dx(rid,mtime) AS ("
        "     SELECT %d, 0"
        "     UNION"
        "     SELECT plink.cid, plink.mtime FROM dx, plink"
        "      WHERE plink.pid=dx.rid"
        "        AND (:stop>=8e99 OR plink.mtime<=:stop)"
        "      ORDER BY 2"
        "  )"
        "INSERT OR IGNORE INTO ok SELECT rid FROM dx LIMIT %d",
        d_rid, nEntry<=0 ? -1 : nEntry+1
      );
      db_bind_double(&s, ":stop", rStopTime);
      db_step(&s);
      db_finalize(&s);
      /* compute_descendants(d_rid, nEntry==0 ? 0 : nEntry+1); */
      nd = db_int(0, "SELECT count(*)-1 FROM ok");
      if( nd>=0 ) db_multi_exec("%s", blob_sql_text(&sql));
      if( nd>0 || p_rid==0 ){
        blob_appendf(&desc, "%d descendant%s", nd,(1==nd)?"":"s");
      }
      if( useDividers && !selectedRid ) selectedRid = d_rid;
      db_multi_exec("DELETE FROM ok");
    }
    if( p_rid ){

      zBackTo = P("bt");
      if( zBackTo ){
        double rDateLimit = db_double(0.0,
           "SELECT mtime FROM event WHERE objid=%d", p_rid);

        ridBackTo = last_checkin_with_tag_before_date(zBackTo, rDateLimit);
        if( ridBackTo==0 ){
          ridBackTo = name_to_typed_rid(zBackTo,"ci");
        }

        if( ridBackTo && !haveParameterN ) nEntry = 0;
      }
      compute_ancestors(p_rid, nEntry==0 ? 0 : nEntry+1, 0, ridBackTo);
      np = db_int(0, "SELECT count(*)-1 FROM ok");
      if( np>0 || nd==0 ){
        if( nd>0 ) blob_appendf(&desc, " and ");
        blob_appendf(&desc, "%d ancestor%s", np, (1==np)?"":"s");
        db_multi_exec("%s", blob_sql_text(&sql));
      }
2148
2149
2150
2151
2152
2153
2154















2155
2156
2157
2158
2159
2160
2161
        blob_appendf(&desc, 
                    "Check-in %z%h</a> only (%z%h</a> is not an ancestor)",
                     href("%R/info?name=%h",zCiName), zCiName,
                     href("%R/info?name=%h",zBackTo), zBackTo);
      }else{
        blob_appendf(&desc, " back to %z%h</a>",
                     href("%R/info?name=%h",zBackTo), zBackTo);















      }
    }
    if( d_rid ){
      if( p_rid ){
        /* If both p= and d= are set, we don't have the uuid of d yet. */
        zUuid = db_text("", "SELECT uuid FROM blob WHERE rid=%d", d_rid);
      }







>
>
>
>
>
>
>
>
>
>
>
>
>
>
>







2181
2182
2183
2184
2185
2186
2187
2188
2189
2190
2191
2192
2193
2194
2195
2196
2197
2198
2199
2200
2201
2202
2203
2204
2205
2206
2207
2208
2209
        blob_appendf(&desc, 
                    "Check-in %z%h</a> only (%z%h</a> is not an ancestor)",
                     href("%R/info?name=%h",zCiName), zCiName,
                     href("%R/info?name=%h",zBackTo), zBackTo);
      }else{
        blob_appendf(&desc, " back to %z%h</a>",
                     href("%R/info?name=%h",zBackTo), zBackTo);
        if( ridFwdTo && zFwdTo ){
          blob_appendf(&desc, " and up to %z%h</a>",
                     href("%R/info?name=%h",zFwdTo), zFwdTo);
        }
      }
    }else if( ridFwdTo ){
      if( nd==0 ){
        blob_reset(&desc);
        blob_appendf(&desc, 
                    "Check-in %z%h</a> only (%z%h</a> is not an descendant)",
                     href("%R/info?name=%h",zCiName), zCiName,
                     href("%R/info?name=%h",zFwdTo), zFwdTo);
      }else{
        blob_appendf(&desc, " up to %z%h</a>",
                     href("%R/info?name=%h",zFwdTo), zFwdTo);
      }
    }
    if( d_rid ){
      if( p_rid ){
        /* If both p= and d= are set, we don't have the uuid of d yet. */
        zUuid = db_text("", "SELECT uuid FROM blob WHERE rid=%d", d_rid);
      }