Check-in [09c4adeb6f]
Not logged in
Overview

SHA1 Hash:09c4adeb6f9009bd4eb9893d1d4f8f676810c74b
Date: 2007-09-22 23:41:29
User: drh
Comment:Rework the tag system so that propagation to children is a property of each tag and does not depend on the tag name beginning with "br". Older tag artifacts might not work. The database will need to be rebuilt after upgrading to this version.
Timelines: ancestors | descendants | both | trunk
Other Links: files | ZIP archive | manifest

Tags And Properties
Changes
[hide diffs]

Modified src/info.c from [51260a23a7] to [ce5dc953e1].

@@ -245,11 +245,11 @@
 static void showTags(int rid){
   Stmt q;
   int cnt = 0;
   db_prepare(&q,
     "SELECT tag.tagid, tagname, srcid, blob.uuid, value,"
-    "       datetime(tagxref.mtime,'localtime'), addflag"
+    "       datetime(tagxref.mtime,'localtime'), tagtype"
     "  FROM tagxref JOIN tag ON tagxref.tagid=tag.tagid"
     "       LEFT JOIN blob ON blob.rid=tagxref.srcid"
     " WHERE tagxref.rid=%d"
     " ORDER BY tagname", rid
   );
@@ -257,21 +257,21 @@
     const char *zTagname = db_column_text(&q, 1);
     int srcid = db_column_int(&q, 2);
     const char *zUuid = db_column_text(&q, 3);
     const char *zValue = db_column_text(&q, 4);
     const char *zDate = db_column_text(&q, 5);
-    int addFlag = db_column_int(&q, 6);
+    int tagtype = db_column_int(&q, 6);
     cnt++;
     if( cnt==1 ){
       @ <h2>Tags And Properties</h2>
       @ <ul>
     }
     @ <li>
     @ <b>%h(zTagname)</b>
     if( zValue ){
       @ = %h(zValue)<i>
-    }else if( !addFlag ){
+    }else if( tagtype==0 ){
       @ <i>Cancelled
     }else{
       @ <i>
     }
     if( srcid==0 ){

Modified src/manifest.c from [d1a0570f09] to [1d87ff81d3].

@@ -20,11 +20,13 @@
 **   http://www.hwaci.com/drh/
 **
 *******************************************************************************
 **
 ** This file contains code used to cross link control files and
-** manifests.
+** manifests.  The file is named "manifest.c" because it was
+** original only used to parse manifests.  Then later clusters
+** and control files were added.
 */
 #include "config.h"
 #include "manifest.h"
 #include <assert.h>
 
@@ -41,10 +43,11 @@
 */
 struct Manifest {
   Blob content;         /* The original content blob */
   int type;             /* Type of file */
   char *zComment;       /* Decoded comment */
+  char zUuid[UUID_SIZE+1];  /* Self UUID */
   double rDate;         /* Time in the "D" line */
   char *zUser;          /* Name of the user */
   char *zRepoCksum;     /* MD5 checksum of the baseline content */
   int nFile;            /* Number of F lines */
   int nFileAlloc;       /* Slots allocated in aFile[] */
@@ -96,14 +99,18 @@
 */
 int manifest_parse(Manifest *p, Blob *pContent){
   int seenHeader = 0;
   int i;
   Blob line, token, a1, a2, a3;
+  Blob selfuuid;
   char zPrevLine[10];
 
   memset(p, 0, sizeof(*p));
   memcpy(&p->content, pContent, sizeof(p->content));
+  sha1sum_blob(&p->content, &selfuuid);
+  memcpy(p->zUuid, blob_buffer(&selfuuid), UUID_SIZE);
+  p->zUuid[UUID_SIZE] = 0;
   blob_zero(pContent);
   pContent = &p->content;
 
   blob_zero(&a1);
   blob_zero(&a2);
@@ -226,16 +233,22 @@
         }
         break;
       }
 
       /*
-      **    T (+|-)<tagname> <uuid> ?<value>?
+      **    T (+|*|-)<tagname> <uuid> ?<value>?
       **
       ** Create or cancel a tag or property.  The tagname is fossil-encoded.
-      ** The first character of the name must be either "+" to create or
-      ** "-" to cancel. The tag is applied to <uuid>.  If <value> is
-      ** provided then the tag is really a property with the given value.
+      ** The first character of the name must be either "+" to create a
+      ** singleton tag, "*" to create a propagating tag, or "-" to create
+      ** anti-tag that undoes a prior "+" or blocks propagation of of
+      ** a "*".
+      **
+      ** The tag is applied to <uuid>.  If <uuid> is "*" then the tag is
+      ** applied to the current manifest.  If <value> is provided then
+      ** the tag is really a property with the given value.
+      **
       ** Tags are not allowed in clusters.  Multiple T lines are allowed.
       */
       case 'T': {
         char *zName, *zUuid, *zValue;
         md5sum_step_text(blob_buffer(&line), blob_size(&line));
@@ -247,14 +260,19 @@
           zValue = 0;
         }else{
           zValue = blob_terminate(&a3);
           defossilize(zValue);
         }
-        if( blob_size(&a2)!=UUID_SIZE ) goto manifest_syntax_error;
-        if( !validate16(zUuid, UUID_SIZE) ) goto manifest_syntax_error;
+        if( blob_size(&a2)==UUID_SIZE && validate16(zUuid, UUID_SIZE) ){
+          /* A valid uuid */
+        }else if( blob_size(&a2)==1 && zUuid[0]=='*' ){
+          zUuid = p->zUuid;
+        }else{
+          goto manifest_syntax_error;
+        }
         defossilize(zName);
-        if( zName[0]!='-' && zName[0]!='+' ){
+        if( zName[0]!='-' && zName[0]!='+' && zName[0]!='*' ){
           goto manifest_syntax_error;
         }
         if( validate16(&zName[1], strlen(&zName[1])) ){
           /* Do not allow tags whose names look like UUIDs */
           goto manifest_syntax_error;
@@ -532,14 +550,20 @@
     }
   }
   if( m.type==CFTYPE_CONTROL || m.type==CFTYPE_MANIFEST ){
     for(i=0; i<m.nTag; i++){
       int tid;
+      int type;
       tid = uuid_to_rid(m.aTag[i].zUuid, 1);
-      tag_insert(&m.aTag[i].zName[1], m.aTag[i].zName[0]=='+',
-                 m.aTag[i].zValue, rid, m.rDate, tid);
+      switch( m.aTag[i].zName[0] ){
+        case '+':  type = 1; break;
+        case '*':  type = 2; break;
+        case '-':  type = 0; break;
+      }
+      tag_insert(&m.aTag[i].zName[1], type, m.aTag[i].zValue,
+                 rid, m.rDate, tid);
     }
   }
   db_end_transaction(0);
   manifest_clear(&m);
   return 1;
 }

Modified src/name.c from [36506ea789] to [6d9bb622dd].

@@ -51,11 +51,11 @@
 
     db_prepare(&q,
       "SELECT (SELECT uuid FROM blob WHERE rid=objid)"
       "  FROM tagxref JOIN event ON rid=objid"
       " WHERE tagid=(SELECT tagid FROM tag WHERE tagname=%B)"
-      "   AND addflag"
+      "   AND tagtype>0"
       "   AND value IS NULL"
       " ORDER BY event.mtime DESC",
       pName
     );
     blob_zero(&uuid);

Modified src/schema.c from [051f5076cf] to [e222adb420].

@@ -204,25 +204,24 @@
 @ -- Tags that begin with "br" automatically propagate to direct
 @ -- children, but not to merge children.
 @ --
 @ CREATE TABLE tag(
 @   tagid INTEGER PRIMARY KEY,       -- Numeric tag ID
-@   tagname TEXT UNIQUE              -- Tag name.  Prefixed by 'v' or 'b'
+@   tagname TEXT UNIQUE              -- Tag name.
 @ );
 @ INSERT INTO tag VALUES(1, 'bgcolor');         -- TAG_BGCOLOR
-@ INSERT INTO tag VALUES(2, 'br-bgcolor');      -- TAG_BR_BGCOLOR
-@ INSERT INTO tag VALUES(3, 'comment');         -- TAG_COMMENT
-@ INSERT INTO tag VALUES(4, 'user');            -- TAG_USER
+@ INSERT INTO tag VALUES(2, 'comment');         -- TAG_COMMENT
+@ INSERT INTO tag VALUES(3, 'user');            -- TAG_USER
 @
 @ -- Assignments of tags to baselines.  Note that we allow tags to
 @ -- have values assigned to them.  So we are not really dealing with
 @ -- tags here.  These are really properties.  But we are going to
 @ -- keep calling them tags because in many cases the value is ignored.
 @ --
 @ CREATE TABLE tagxref(
 @   tagid INTEGER REFERENCES tag,   -- The tag that added or removed
-@   addFlag BOOLEAN,                -- True to add the tag, False to remove
+@   tagtype INTEGER,                -- 0:cancel  1:single  2:branch
 @   srcid INTEGER REFERENCES blob,  -- Origin of the tag. 0 for propagated tags
 @   value TEXT,                     -- Value of the tag.  Might be NULL.
 @   mtime TIMESTAMP,                -- Time of addition or removal
 @   rid INTEGER REFERENCE blob,     -- Baseline that tag added/removed from
 @   UNIQUE(rid, tagid)
@@ -233,13 +232,12 @@
 /*
 ** Predefined tagid values
 */
 #if INTERFACE
 # define TAG_BGCOLOR    1
-# define TAG_BR_BGCOLOR 2
-# define TAG_COMMENT    3
-# define TAG_USER       4
+# define TAG_COMMENT    2
+# define TAG_USER       3
 #endif
 
 /*
 ** The schema for the locate FOSSIL database file found at the root
 ** of very check-out.  This database contains the complete state of

Modified src/tag.c from [c028c6b677] to [c707ca1de0].

@@ -31,46 +31,49 @@
 ** Propagate the tag given by tagid to the children of pid.
 **
 ** This routine assumes that tagid is a tag that should be
 ** propagated and that the tag is already present in pid.
 **
-** If addFlag is true then the tag is added.  If false, then the
-** tag is removed.
+** If tagtype is 2 then the tag is being propagated from an
+** ancestor node.  If tagtype is 0 it means a branch tag is
+** being cancelled.
 */
 void tag_propagate(
   int pid,             /* Propagate the tag to children of this node */
   int tagid,           /* Tag to propagate */
-  int addFlag,         /* True to add the tag. False to delete it. */
+  int tagType,         /* 2 for a propagating tag.  0 for an antitag */
   const char *zValue,  /* Value of the tag.  Might be NULL */
   double mtime         /* Timestamp on the tag */
 ){
   PQueue queue;
   Stmt s, ins, eventupdate;
+
+  assert( tagType==0 || tagType==2 );
   pqueue_init(&queue);
   pqueue_insert(&queue, pid, 0.0);
   db_prepare(&s,
      "SELECT cid, plink.mtime,"
      "       coalesce(srcid=0 AND tagxref.mtime<:mtime, %d) AS doit"
      "  FROM plink LEFT JOIN tagxref ON cid=rid AND tagid=%d"
      " WHERE pid=:pid AND isprim",
-     addFlag, tagid
+     tagType!=0, tagid
   );
   db_bind_double(&s, ":mtime", mtime);
-  if( addFlag ){
+  if( tagType==2 ){
     db_prepare(&ins,
-       "REPLACE INTO tagxref(tagid, addFlag, srcid, value, mtime, rid)"
-       "VALUES(%d,1,0,%Q,:mtime,:rid)",
+       "REPLACE INTO tagxref(tagid, tagtype, srcid, value, mtime, rid)"
+       "VALUES(%d,2,0,%Q,:mtime,:rid)",
        tagid, zValue
     );
     db_bind_double(&ins, ":mtime", mtime);
   }else{
     zValue = 0;
     db_prepare(&ins,
        "DELETE FROM tagxref WHERE tagid=%d AND rid=:rid", tagid
     );
   }
-  if( tagid==TAG_BR_BGCOLOR ){
+  if( tagid==TAG_BGCOLOR ){
     db_prepare(&eventupdate,
       "UPDATE event SET brbgcolor=%Q WHERE objid=:rid", zValue
     );
   }
   while( (pid = pqueue_extract(&queue))!=0 ){
@@ -82,11 +85,11 @@
         double mtime = db_column_double(&s, 1);
         pqueue_insert(&queue, cid, mtime);
         db_bind_int(&ins, ":rid", cid);
         db_step(&ins);
         db_reset(&ins);
-        if( tagid==TAG_BR_BGCOLOR ){
+        if( tagid==TAG_BGCOLOR ){
           db_bind_int(&eventupdate, ":rid", cid);
           db_step(&eventupdate);
           db_reset(&eventupdate);
         }
       }
@@ -94,11 +97,11 @@
     db_reset(&s);
   }
   pqueue_clear(&queue);
   db_finalize(&ins);
   db_finalize(&s);
-  if( tagid==TAG_BR_BGCOLOR ){
+  if( tagid==TAG_BGCOLOR ){
     db_finalize(&eventupdate);
   }
 }
 
 /*
@@ -105,21 +108,21 @@
 ** Propagate all propagatable tags in pid to its children.
 */
 void tag_propagate_all(int pid){
   Stmt q;
   db_prepare(&q,
-     "SELECT tagid, addflag, mtime, value FROM tagxref"
+     "SELECT tagid, tagtype, mtime, value FROM tagxref"
      " WHERE rid=%d"
-     "   AND (SELECT tagname FROM tag WHERE tagid=tagxref.tagid) LIKE 'br%'",
+     "   AND (tagtype=0 OR tagtype=2)",
      pid
   );
   while( db_step(&q)==SQLITE_ROW ){
     int tagid = db_column_int(&q, 0);
-    int addflag = db_column_int(&q, 1);
+    int tagtype = db_column_int(&q, 1);
     double mtime = db_column_double(&q, 2);
     const char *zValue = db_column_text(&q, 3);
-    tag_propagate(pid, tagid, addflag, zValue, mtime);
+    tag_propagate(pid, tagid, tagtype, zValue, mtime);
   }
   db_finalize(&q);
 }
 
 /*
@@ -139,106 +142,93 @@
 /*
 ** Insert a tag into the database.
 */
 void tag_insert(
   const char *zTag,        /* Name of the tag (w/o the "+" or "-" prefix */
-  int addFlag,             /* True to add.  False to remove */
+  int tagtype,             /* 0:cancel  1:singleton  2:propagated */
   const char *zValue,      /* Value if the tag is really a property */
   int srcId,               /* Artifact that contains this tag */
   double mtime,            /* Timestamp.  Use default if <=0.0 */
   int rid                  /* Artifact to which the tag is to attached */
 ){
   Stmt s;
+  const char *zCol;
   int tagid = tag_findid(zTag, 1);
   if( mtime<=0.0 ){
     mtime = db_double(0.0, "SELECT julianday('now')");
   }
   db_prepare(&s,
-    "REPLACE INTO tagxref(tagid,addFlag,srcId,value,mtime,rid)"
+    "REPLACE INTO tagxref(tagid,tagtype,srcId,value,mtime,rid)"
     " VALUES(%d,%d,%d,%Q,:mtime,%d)",
-    tagid, addFlag, srcId, zValue, rid
+    tagid, tagtype, srcId, zValue, rid
   );
   db_bind_double(&s, ":mtime", mtime);
   db_step(&s);
   db_finalize(&s);
-  if( addFlag==0 ){
+  if( tagtype==0 ){
     zValue = 0;
   }
+  zCol = 0;
   switch( tagid ){
     case TAG_BGCOLOR: {
-      db_multi_exec("UPDATE event SET bgcolor=%Q WHERE objid=%d", zValue, rid);
-      break;
-    }
-    case TAG_BR_BGCOLOR: {
-      db_multi_exec("UPDATE event SET brbgcolor=%Q WHERE objid=%d", zValue,rid);
+      if( tagtype==1 ){
+        zCol = "bgcolor";
+      }else{
+        zCol = "brbgcolor";
+      }
       break;
     }
     case TAG_COMMENT: {
-      db_multi_exec("UPDATE event SET ecomment=%Q WHERE objid=%d", zValue, rid);
+      zCol = "ecomment";
       break;
     }
     case TAG_USER: {
-      db_multi_exec("UPDATE event SET euser=%Q WHERE objid=%d", zValue, rid);
+      zCol = "euser";
       break;
     }
   }
-  if( strncmp(zTag, "br", 2)==0 ){
-    tag_propagate(rid, tagid, addFlag, zValue, mtime);
+  if( zCol ){
+    db_multi_exec("UPDATE event SET %s=%Q WHERE objid=%d", zCol, zValue, rid);
+  }
+  if( tagtype==0 || tagtype==2 ){
+    tag_propagate(rid, tagid, tagtype, zValue, mtime);
   }
 }
 
 
 /*
-** COMMAND: test-addtag
-** %fossil test-addtag TAGNAME UUID ?VALUE?
+** COMMAND: test-tag
+** %fossil test-tag (+|*|-)TAGNAME UUID ?VALUE?
 **
-** Add a tag to the rebuildable tables of the local repository.
+** Add a tag or anti-tag to the rebuildable tables of the local repository.
 ** No tag artifact is created so the new tag is erased the next
 ** time the repository is rebuilt.  This routine is for testing
 ** use only.
 */
-void addtag_cmd(void){
+void testtag_cmd(void){
   const char *zTag;
   const char *zValue;
   int rid;
+  int tagtype;
   db_must_be_within_tree();
   if( g.argc!=4 && g.argc!=5 ){
     usage("TAGNAME UUID ?VALUE?");
   }
   zTag = g.argv[2];
+  switch( zTag[0] ){
+    case '+':  tagtype = 1;  break;
+    case '*':  tagtype = 2;  break;
+    case '-':  tagtype = 0;  break;
+    default:   fossil_fatal("tag should begin with '+', '*', or '-'");
+  }
   rid = name_to_rid(g.argv[3]);
   if( rid==0 ){
     fossil_fatal("no such object: %s", g.argv[3]);
   }
   zValue = g.argc==5 ? g.argv[4] : 0;
   db_begin_transaction();
-  tag_insert(zTag, 1, zValue, -1, 0.0, rid);
-  db_end_transaction(0);
-}
-/*
-** COMMAND: test-deltag
-** %fossil test-deltag TAGNAME UUID
-**
-** Cancel a tag to the rebuildable tables of the local repository.
-** No tag artifact is created so the cancellation is undone the next
-** time the repository is rebuilt.  This routine is for testing
-** use only.
-*/
-void deltag_cmd(void){
-  const char *zTag;
-  int rid;
-  db_must_be_within_tree();
-  if( g.argc!=4 ){
-    usage("TAGNAME UUID");
-  }
-  zTag = g.argv[2];
-  rid = name_to_rid(g.argv[3]);
-  if( rid==0 ){
-    fossil_fatal("no such object: %s", g.argv[3]);
-  }
-  db_begin_transaction();
-  tag_insert(zTag, 0, 0, -1, 0.0, rid);
+  tag_insert(zTag, tagtype, zValue, -1, 0.0, rid);
   db_end_transaction(0);
 }
 
 /*
 ** Add a control record to the repository that either creates
@@ -246,19 +236,21 @@
 */
 static void tag_add_artifact(
   const char *zTagname,       /* The tag to add or cancel */
   const char *zObjName,       /* Name of object attached to */
   const char *zValue,         /* Value for the tag.  Might be NULL */
-  int addFlag                 /* True to add.  false to cancel */
+  int tagtype                 /* 0:cancel 1:singleton 2:propagated */
 ){
   int rid;
   int nrid;
   char *zDate;
   Blob uuid;
   Blob ctrl;
   Blob cksum;
+  static const char zTagtype[] = { '-', '+', '*' };
 
+  assert( tagtype>=0 && tagtype<=2 );
   user_select();
   rid = name_to_rid(zObjName);
   blob_zero(&uuid);
   db_blob(&uuid, "SELECT uuid FROM blob WHERE rid=%d", rid);
   blob_zero(&ctrl);
@@ -268,12 +260,12 @@
                  zTagname);
   }
   zDate = db_text(0, "SELECT datetime('now')");
   zDate[10] = 'T';
   blob_appendf(&ctrl, "D %s\n", zDate);
-  blob_appendf(&ctrl, "T %c%F %b", addFlag ? '+' : '-', zTagname, &uuid);
-  if( addFlag && zValue && zValue[0] ){
+  blob_appendf(&ctrl, "T %c%F %b", zTagtype[tagtype], zTagname, &uuid);
+  if( tagtype && zValue && zValue[0] ){
     blob_appendf(&ctrl, " %F\n", zValue);
   }else{
     blob_appendf(&ctrl, "\n");
   }
   blob_appendf(&ctrl, "U %F\n", g.zLogin);
@@ -292,10 +284,15 @@
 ** Run various subcommands to control tags and properties
 **
 **     %fossil tag add TAGNAME UUID ?VALUE?
 **
 **         Add a new tag or property to UUID.
+**
+**     %fossil tag branch TAGNAME UUID ?VALUE?
+**
+**         Add a new tag or property to UUID and make that
+**         tag propagate to all direct children.
 **
 **     %fossil tag delete TAGNAME UUID
 **
 **         Delete the tag TAGNAME from UUID
 **
@@ -326,10 +323,19 @@
     }
     zValue = g.argc==6 ? g.argv[5] : 0;
     tag_add_artifact(g.argv[3], g.argv[4], zValue, 1);
   }else
 
+  if( strncmp(g.argv[2],"branch",n)==0 ){
+    char *zValue;
+    if( g.argc!=5 && g.argc!=6 ){
+      usage("tag branch TAGNAME UUID ?VALUE?");
+    }
+    zValue = g.argc==6 ? g.argv[5] : 0;
+    tag_add_artifact(g.argv[3], g.argv[4], zValue, 2);
+  }else
+
   if( strncmp(g.argv[2],"delete",n)==0 ){
     if( g.argc!=5 ){
       usage("tag delete TAGNAME UUID");
     }
     tag_add_artifact(g.argv[3], g.argv[4], 0, 0);
@@ -357,11 +363,11 @@
       db_prepare(&q,
         "SELECT tagname"
         "  FROM tag"
         " WHERE EXISTS(SELECT 1 FROM tagxref"
         "               WHERE tagid=tag.tagid"
-        "                 AND addflag)"
+        "                 AND tagtype>0)"
         " ORDER BY tagname"
       );
       while( db_step(&q)==SQLITE_ROW ){
         printf("%s\n", db_column_text(&q, 0));
       }
@@ -370,11 +376,11 @@
       int rid = name_to_rid(g.argv[3]);
       db_prepare(&q,
         "SELECT tagname, value"
         "  FROM tagxref, tag"
         " WHERE tagxref.rid=%d AND tagxref.tagid=tag.tagid"
-        "   AND addflag"
+        "   AND tagtype>0"
         " ORDER BY tagname",
         rid
       );
       while( db_step(&q)==SQLITE_ROW ){
         const char *zName = db_column_text(&q, 0);
@@ -394,7 +400,7 @@
     goto tag_cmd_usage;
   }
   return;
 
 tag_cmd_usage:
-  usage("add|delete|find|list ...");
+  usage("add|branch|delete|find|list ...");
 }