Fossil SCM

fossil-scm / src / leaf.c
Source Blame History 284 lines
5ac4e15… drh 1 /*
c19f34c… drh 2 ** Copyright (c) 2011 D. Richard Hipp
5ac4e15… drh 3 **
5ac4e15… drh 4 ** This program is free software; you can redistribute it and/or
5ac4e15… drh 5 ** modify it under the terms of the Simplified BSD License (also
5ac4e15… drh 6 ** known as the "2-Clause License" or "FreeBSD License".)
5ac4e15… drh 7
5ac4e15… drh 8 ** This program is distributed in the hope that it will be useful,
5ac4e15… drh 9 ** but without any warranty; without even the implied warranty of
5ac4e15… drh 10 ** merchantability or fitness for a particular purpose.
5ac4e15… drh 11 **
5ac4e15… drh 12 ** Author contact information:
5ac4e15… drh 13 ** [email protected]
5ac4e15… drh 14 ** http://www.hwaci.com/drh/
5ac4e15… drh 15 **
5ac4e15… drh 16 *******************************************************************************
5ac4e15… drh 17 **
5ac4e15… drh 18 ** This file contains code used to manage the "leaf" table of the
5ac4e15… drh 19 ** repository.
5ac4e15… drh 20 **
c49030f… drh 21 ** The LEAF table contains the rids for all leaves in the check-in DAG.
c49030f… drh 22 ** A leaf is a check-in that has no children in the same branch.
5ac4e15… drh 23 */
5ac4e15… drh 24 #include "config.h"
5ac4e15… drh 25 #include "leaf.h"
5ac4e15… drh 26 #include <assert.h>
5ac4e15… drh 27
5ac4e15… drh 28
5ac4e15… drh 29 /*
e17fc71… drh 30 ** Return true if the check-in with RID=rid is a leaf.
e17fc71… drh 31 **
080ab8c… jan.nijtmans 32 ** A leaf has no children in the same branch.
e17fc71… drh 33 */
e17fc71… drh 34 int is_a_leaf(int rid){
e17fc71… drh 35 int rc;
080ab8c… jan.nijtmans 36 static const char zSql[] =
e17fc71… drh 37 @ SELECT 1 FROM plink
e17fc71… drh 38 @ WHERE pid=%d
e17fc71… drh 39 @ AND coalesce((SELECT value FROM tagxref
e17fc71… drh 40 @ WHERE tagid=%d AND rid=plink.pid), 'trunk')
e17fc71… drh 41 @ =coalesce((SELECT value FROM tagxref
e17fc71… drh 42 @ WHERE tagid=%d AND rid=plink.cid), 'trunk')
e17fc71… drh 43 ;
49b0ff1… drh 44 rc = db_int(0, zSql /*works-like:"%d,%d,%d"*/,
49b0ff1… drh 45 rid, TAG_BRANCH, TAG_BRANCH);
e17fc71… drh 46 return rc==0;
e17fc71… drh 47 }
e17fc71… drh 48
e17fc71… drh 49 /*
e17fc71… drh 50 ** Count the number of primary non-branch children for the given check-in.
e17fc71… drh 51 **
e17fc71… drh 52 ** A primary child is one where the parent is the primary parent, not
e17fc71… drh 53 ** a merge parent. A "leaf" is a node that has zero children of any
e17fc71… drh 54 ** kind. This routine counts only primary children.
e17fc71… drh 55 **
e17fc71… drh 56 ** A non-branch child is one which is on the same branch as the parent.
e17fc71… drh 57 */
e17fc71… drh 58 int count_nonbranch_children(int pid){
e17fc71… drh 59 int nNonBranch = 0;
e17fc71… drh 60 static Stmt q;
080ab8c… jan.nijtmans 61 static const char zSql[] =
e17fc71… drh 62 @ SELECT count(*) FROM plink
e17fc71… drh 63 @ WHERE pid=:pid AND isprim
e17fc71… drh 64 @ AND coalesce((SELECT value FROM tagxref
e17fc71… drh 65 @ WHERE tagid=%d AND rid=plink.pid), 'trunk')
e17fc71… drh 66 @ =coalesce((SELECT value FROM tagxref
e17fc71… drh 67 @ WHERE tagid=%d AND rid=plink.cid), 'trunk')
e17fc71… drh 68 ;
49b0ff1… drh 69 db_static_prepare(&q, zSql /*works-like: "%d,%d"*/, TAG_BRANCH, TAG_BRANCH);
e17fc71… drh 70 db_bind_int(&q, ":pid", pid);
e17fc71… drh 71 if( db_step(&q)==SQLITE_ROW ){
e17fc71… drh 72 nNonBranch = db_column_int(&q, 0);
e17fc71… drh 73 }
e17fc71… drh 74 db_reset(&q);
e17fc71… drh 75 return nNonBranch;
e17fc71… drh 76 }
e17fc71… drh 77
e17fc71… drh 78
e17fc71… drh 79 /*
080ab8c… jan.nijtmans 80 ** Recompute the entire LEAF table.
5ac4e15… drh 81 **
5ac4e15… drh 82 ** This can be expensive (5 seconds or so) for a really large repository.
5ac4e15… drh 83 ** So it is only done for things like a rebuild.
5ac4e15… drh 84 */
5ac4e15… drh 85 void leaf_rebuild(void){
5ac4e15… drh 86 db_multi_exec(
5ac4e15… drh 87 "DELETE FROM leaf;"
5ac4e15… drh 88 "INSERT OR IGNORE INTO leaf"
5ac4e15… drh 89 " SELECT cid FROM plink"
5ac4e15… drh 90 " EXCEPT"
5ac4e15… drh 91 " SELECT pid FROM plink"
5ac4e15… drh 92 " WHERE coalesce((SELECT value FROM tagxref"
5ac4e15… drh 93 " WHERE tagid=%d AND rid=plink.pid),'trunk')"
5ac4e15… drh 94 " == coalesce((SELECT value FROM tagxref"
e17fc71… drh 95 " WHERE tagid=%d AND rid=plink.cid),'trunk')",
5ac4e15… drh 96 TAG_BRANCH, TAG_BRANCH
5ac4e15… drh 97 );
5ac4e15… drh 98 }
5ac4e15… drh 99
5ac4e15… drh 100 /*
6ec2c2e… mistachkin 101 ** A bag of check-ins whose leaf status needs to be checked.
5ac4e15… drh 102 */
5ac4e15… drh 103 static Bag needToCheck;
5ac4e15… drh 104
5ac4e15… drh 105 /*
c49030f… drh 106 ** Check to see if check-in "rid" is a leaf and either add it to the LEAF
5ac4e15… drh 107 ** table if it is, or remove it if it is not.
5ac4e15… drh 108 */
5ac4e15… drh 109 void leaf_check(int rid){
5ac4e15… drh 110 static Stmt checkIfLeaf;
5ac4e15… drh 111 static Stmt addLeaf;
5ac4e15… drh 112 static Stmt removeLeaf;
5ac4e15… drh 113 int rc;
5ac4e15… drh 114
5ac4e15… drh 115 db_static_prepare(&checkIfLeaf,
5ac4e15… drh 116 "SELECT 1 FROM plink"
e17fc71… drh 117 " WHERE pid=:rid"
5ac4e15… drh 118 " AND coalesce((SELECT value FROM tagxref"
5ac4e15… drh 119 " WHERE tagid=%d AND rid=:rid),'trunk')"
5ac4e15… drh 120 " == coalesce((SELECT value FROM tagxref"
5ac4e15… drh 121 " WHERE tagid=%d AND rid=plink.cid),'trunk');",
5ac4e15… drh 122 TAG_BRANCH, TAG_BRANCH
5ac4e15… drh 123 );
5ac4e15… drh 124 db_bind_int(&checkIfLeaf, ":rid", rid);
5ac4e15… drh 125 rc = db_step(&checkIfLeaf);
5ac4e15… drh 126 db_reset(&checkIfLeaf);
5ac4e15… drh 127 if( rc==SQLITE_ROW ){
5ac4e15… drh 128 db_static_prepare(&removeLeaf, "DELETE FROM leaf WHERE rid=:rid");
5ac4e15… drh 129 db_bind_int(&removeLeaf, ":rid", rid);
5ac4e15… drh 130 db_step(&removeLeaf);
5ac4e15… drh 131 db_reset(&removeLeaf);
5ac4e15… drh 132 }else{
5ac4e15… drh 133 db_static_prepare(&addLeaf, "INSERT OR IGNORE INTO leaf VALUES(:rid)");
5ac4e15… drh 134 db_bind_int(&addLeaf, ":rid", rid);
5ac4e15… drh 135 db_step(&addLeaf);
5ac4e15… drh 136 db_reset(&addLeaf);
5ac4e15… drh 137 }
5ac4e15… drh 138 }
5ac4e15… drh 139
5ac4e15… drh 140 /*
5ac4e15… drh 141 ** Return an SQL expression (stored in memory obtained from fossil_malloc())
5ac4e15… drh 142 ** that is true if the SQL variable named "zVar" contains the rid with
5ac4e15… drh 143 ** a CLOSED tag. In other words, return true if the leaf is closed.
5ac4e15… drh 144 **
5ac4e15… drh 145 ** The result can be prefaced with a NOT operator to get all leaves that
5ac4e15… drh 146 ** are open.
5ac4e15… drh 147 */
5ac4e15… drh 148 char *leaf_is_closed_sql(const char *zVar){
5ac4e15… drh 149 return mprintf(
5ac4e15… drh 150 "EXISTS(SELECT 1 FROM tagxref AS tx"
5ac4e15… drh 151 " WHERE tx.rid=%s"
5ac4e15… drh 152 " AND tx.tagid=%d"
5ac4e15… drh 153 " AND tx.tagtype>0)",
5ac4e15… drh 154 zVar, TAG_CLOSED
5ac4e15… drh 155 );
5ac4e15… drh 156 }
5ac4e15… drh 157
5ac4e15… drh 158 /*
1243bf3… stephan 159 ** Returns true if vid refers to a closed leaf, else false. vid is
1243bf3… stephan 160 ** assumed to refer to a manifest, but this function does not verify
1243bf3… stephan 161 ** that.
1243bf3… stephan 162 */
1243bf3… stephan 163 int leaf_is_closed(int vid){
1243bf3… stephan 164 return db_exists("SELECT 1 FROM tagxref"
1243bf3… stephan 165 " WHERE tagid=%d AND rid=%d AND tagtype>0",
1243bf3… stephan 166 TAG_CLOSED, vid);
1243bf3… stephan 167 }
1243bf3… stephan 168
1243bf3… stephan 169 /*
5ac4e15… drh 170 ** Schedule a leaf check for "rid" and its parents.
5ac4e15… drh 171 */
5ac4e15… drh 172 void leaf_eventually_check(int rid){
5ac4e15… drh 173 static Stmt parentsOf;
5ac4e15… drh 174
080ab8c… jan.nijtmans 175 db_static_prepare(&parentsOf,
aa6abc5… drh 176 "SELECT pid FROM plink WHERE cid=:rid AND pid>0"
aa6abc5… drh 177 );
5ac4e15… drh 178 db_bind_int(&parentsOf, ":rid", rid);
5ac4e15… drh 179 bag_insert(&needToCheck, rid);
5ac4e15… drh 180 while( db_step(&parentsOf)==SQLITE_ROW ){
5ac4e15… drh 181 bag_insert(&needToCheck, db_column_int(&parentsOf, 0));
5ac4e15… drh 182 }
5ac4e15… drh 183 db_reset(&parentsOf);
5ac4e15… drh 184 }
5ac4e15… drh 185
5ac4e15… drh 186 /*
5ac4e15… drh 187 ** Do all pending leaf checks.
5ac4e15… drh 188 */
5ac4e15… drh 189 void leaf_do_pending_checks(void){
5ac4e15… drh 190 int rid;
5ac4e15… drh 191 for(rid=bag_first(&needToCheck); rid; rid=bag_next(&needToCheck,rid)){
5ac4e15… drh 192 leaf_check(rid);
5ac4e15… drh 193 }
5ac4e15… drh 194 bag_clear(&needToCheck);
f78cba5… drh 195 }
f78cba5… drh 196
f78cba5… drh 197 /*
f78cba5… drh 198 ** If check-in rid is an open-leaf and there exists another
f78cba5… drh 199 ** open leaf on the same branch, then return 1.
f78cba5… drh 200 **
f78cba5… drh 201 ** If check-in rid is not an open leaf, or if it is the only open leaf
f78cba5… drh 202 ** on its branch, then return 0.
f78cba5… drh 203 */
f78cba5… drh 204 int leaf_ambiguity(int rid){
f78cba5… drh 205 int rc; /* Result */
f78cba5… drh 206 char zVal[30];
f78cba5… drh 207 if( !is_a_leaf(rid) ) return 0;
f78cba5… drh 208 sqlite3_snprintf(sizeof(zVal), zVal, "%d", rid);
f78cba5… drh 209 rc = db_exists(
fc3d9f5… jan.nijtmans 210 "SELECT 1 FROM leaf"
f78cba5… drh 211 " WHERE NOT %z"
f78cba5… drh 212 " AND rid<>%d"
f78cba5… drh 213 " AND (SELECT value FROM tagxref WHERE tagid=%d AND rid=leaf.rid)="
f78cba5… drh 214 " (SELECT value FROM tagxref WHERE tagid=%d AND rid=%d)"
f78cba5… drh 215 " AND NOT %z",
f78cba5… drh 216 leaf_is_closed_sql(zVal), rid, TAG_BRANCH, TAG_BRANCH, rid,
f78cba5… drh 217 leaf_is_closed_sql("leaf.rid"));
f78cba5… drh 218 return rc;
f78cba5… drh 219 }
f78cba5… drh 220
f78cba5… drh 221 /*
f78cba5… drh 222 ** If check-in rid is an open-leaf and there exists another open leaf
f78cba5… drh 223 ** on the same branch, then print a detailed warning showing all open
f78cba5… drh 224 ** leaves on that branch.
f78cba5… drh 225 */
f78cba5… drh 226 int leaf_ambiguity_warning(int rid, int currentCkout){
f78cba5… drh 227 char *zBr;
f78cba5… drh 228 Stmt q;
f78cba5… drh 229 int n = 0;
f78cba5… drh 230 Blob msg;
3a6dd83… drh 231 const char *zMainBranch;
f78cba5… drh 232 if( leaf_ambiguity(rid)==0 ) return 0;
ca0d66b… danield 233 zMainBranch = db_main_branch();
f78cba5… drh 234 zBr = db_text(0, "SELECT value FROM tagxref WHERE tagid=%d AND rid=%d",
f78cba5… drh 235 TAG_BRANCH, rid);
3a6dd83… drh 236 if( zBr==0 ) zBr = fossil_strdup(zMainBranch);
f78cba5… drh 237 blob_init(&msg, 0, 0);
f78cba5… drh 238 blob_appendf(&msg, "WARNING: multiple open leaf check-ins on %s:", zBr);
f78cba5… drh 239 db_prepare(&q,
f78cba5… drh 240 "SELECT"
f78cba5… drh 241 " (SELECT uuid FROM blob WHERE rid=leaf.rid),"
ea63a2d… drh 242 " (SELECT datetime(mtime,toLocal()) FROM event WHERE objid=leaf.rid),"
f78cba5… drh 243 " leaf.rid"
f78cba5… drh 244 " FROM leaf"
f78cba5… drh 245 " WHERE (SELECT value FROM tagxref WHERE tagid=%d AND rid=leaf.rid)=%Q"
f78cba5… drh 246 " AND NOT %z"
f78cba5… drh 247 " ORDER BY 2 DESC",
ea63a2d… drh 248 TAG_BRANCH, zBr, leaf_is_closed_sql("leaf.rid")
f78cba5… drh 249 );
f78cba5… drh 250 while( db_step(&q)==SQLITE_ROW ){
f78cba5… drh 251 blob_appendf(&msg, "\n (%d) %s [%S]%s",
f78cba5… drh 252 ++n, db_column_text(&q,1), db_column_text(&q,0),
f78cba5… drh 253 db_column_int(&q,2)==currentCkout ? " (current)" : "");
f78cba5… drh 254 }
f78cba5… drh 255 db_finalize(&q);
f78cba5… drh 256 fossil_warning("%s",blob_str(&msg));
f78cba5… drh 257 blob_reset(&msg);
f78cba5… drh 258 return 1;
f78cba5… drh 259 }
f78cba5… drh 260
f78cba5… drh 261 /*
f78cba5… drh 262 ** COMMAND: test-leaf-ambiguity
f78cba5… drh 263 **
f78cba5… drh 264 ** Usage: %fossil NAME ...
f78cba5… drh 265 **
f78cba5… drh 266 ** Resolve each name on the command line and call leaf_ambiguity_warning()
f78cba5… drh 267 ** for each resulting RID.
f78cba5… drh 268 */
f78cba5… drh 269 void leaf_ambiguity_warning_test(void){
f78cba5… drh 270 int i;
f78cba5… drh 271 int rid;
f78cba5… drh 272 int rc;
f78cba5… drh 273 db_find_and_open_repository(0,0);
f78cba5… drh 274 verify_all_options();
f78cba5… drh 275 for(i=2; i<g.argc; i++){
f78cba5… drh 276 char *zUuid;
f78cba5… drh 277 rid = name_to_typed_rid(g.argv[i], "ci");
f78cba5… drh 278 zUuid = db_text(0, "SELECT uuid FROM blob WHERE rid=%d", rid);
f78cba5… drh 279 fossil_print("%s (rid=%d) %S ", g.argv[i], rid, zUuid ? zUuid : "(none)");
f78cba5… drh 280 fossil_free(zUuid);
f78cba5… drh 281 rc = leaf_ambiguity_warning(rid, rid);
f78cba5… drh 282 if( rc==0 ) fossil_print(" ok\n");
f78cba5… drh 283 }
5ac4e15… drh 284 }

Keyboard Shortcuts

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