|
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 |
} |