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