Many hyperlinks are disabled.
Use anonymous login
to enable hyperlinks.
Overview
| Comment: | merged from trunk |
|---|---|
| Downloads: | Tarball | ZIP archive |
| Timelines: | family | ancestors | descendants | both | wolfgangFormat2CSS_2 |
| Files: | files | file ages | folders |
| SHA1: |
def52067be777e33e62be11c6b998828 |
| User & Date: | wolfgang 2010-10-05 09:43:46.000 |
Context
|
2010-10-06
| ||
| 09:54 | merge from trunk ... (check-in: 454658c0b0 user: wolfgang tags: wolfgangFormat2CSS_2) | |
|
2010-10-05
| ||
| 09:43 | merged from trunk ... (check-in: def52067be user: wolfgang tags: wolfgangFormat2CSS_2) | |
| 03:55 | Update to the latest SQLite 3.7.3 beta. ... (check-in: 12a79e5b93 user: drh tags: trunk, release) | |
|
2010-10-04
| ||
| 11:48 | merged from trunk ... (check-in: fafcb6c780 user: Ratte tags: wolfgangFormat2CSS_2) | |
Changes
Changes to src/blob.c.
| ︙ | ︙ | |||
70 71 72 73 74 75 76 | #define blob_is_reset(x) #endif /* ** We find that the built-in isspace() function does not work for ** some international character sets. So here is a substitute. */ | | | 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 |
#define blob_is_reset(x)
#endif
/*
** We find that the built-in isspace() function does not work for
** some international character sets. So here is a substitute.
*/
int blob_isspace(char c){
return c==' ' || (c<='\r' && c>='\t');
}
/*
** COMMAND: test-isspace
*/
void isspace_cmd(void){
|
| ︙ | ︙ |
Changes to src/content.c.
| ︙ | ︙ | |||
17 18 19 20 21 22 23 | ** ** Procedures store and retrieve records from the repository */ #include "config.h" #include "content.h" #include <assert.h> | < < < < < < < < < < < > | > | | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 |
**
** Procedures store and retrieve records from the repository
*/
#include "config.h"
#include "content.h"
#include <assert.h>
/*
** The artifact retrival cache
*/
static struct {
i64 szTotal; /* Total size of all entries in the cache */
int n; /* Current number of eache entries */
int nAlloc; /* Number of slots allocated in a[] */
int nextAge; /* Age counter for implementing LRU */
int skipCnt; /* Used to limit entries expelled from cache */
struct cacheLine { /* One instance of this for each cache entry */
int rid; /* Artifact id */
int age; /* Age. Newer is larger */
Blob content; /* Content of the artifact */
} *a; /* The positive cache */
Bag inCache; /* Set of artifacts currently in cache */
/*
** The missing artifact cache.
**
** Artifacts whose record ID are in missingCache cannot be retrieved
** either because they are phantoms or because they are a delta that
** depends on a phantom. Artifacts whose content we are certain is
** available are in availableCache. If an artifact is in neither cache
** then its current availablity is unknown.
*/
Bag missing; /* Cache of artifacts that are incomplete */
Bag available; /* Cache of artifacts that are complete */
} contentCache;
/*
** Remove the oldest element from the content cache
*/
static void content_cache_expire_oldest(void){
int i;
int mnAge = contentCache.nextAge;
int mn = -1;
for(i=0; i<contentCache.n; i++){
if( contentCache.a[i].age<mnAge ){
mnAge = contentCache.a[i].age;
mn = i;
}
}
if( mn>=0 ){
bag_remove(&contentCache.inCache, contentCache.a[mn].rid);
contentCache.szTotal -= blob_size(&contentCache.a[mn].content);
blob_reset(&contentCache.a[mn].content);
contentCache.n--;
contentCache.a[mn] = contentCache.a[contentCache.n];
}
}
/*
** Add an entry to the content cache
*/
void content_cache_insert(int rid, Blob *pBlob){
struct cacheLine *p;
if( contentCache.n>500 || contentCache.szTotal>50000000 ){
content_cache_expire_oldest();
}
if( contentCache.n>=contentCache.nAlloc ){
contentCache.nAlloc = contentCache.nAlloc*2 + 10;
contentCache.a = realloc(contentCache.a,
contentCache.nAlloc*sizeof(contentCache.a[0]));
if( contentCache.a==0 ) fossil_panic("out of memory");
}
p = &contentCache.a[contentCache.n++];
p->rid = rid;
p->age = contentCache.nextAge++;
contentCache.szTotal += blob_size(pBlob);
p->content = *pBlob;
blob_zero(pBlob);
bag_insert(&contentCache.inCache, rid);
}
/*
** Clear the content cache.
*/
void content_clear_cache(void){
int i;
for(i=0; i<contentCache.n; i++){
blob_reset(&contentCache.a[i].content);
}
bag_clear(&contentCache.missing);
bag_clear(&contentCache.available);
bag_clear(&contentCache.inCache);
contentCache.n = 0;
contentCache.szTotal = 0;
}
/*
** Return the srcid associated with rid. Or return 0 if rid is
** original content and not a delta.
*/
static int findSrcid(int rid){
|
| ︙ | ︙ | |||
93 94 95 96 97 98 99 |
/*
** Check to see if content is available for artifact "rid". Return
** true if it is. Return false if rid is a phantom or depends on
** a phantom.
*/
int content_is_available(int rid){
int srcid;
| > > | | | | | | | | | | | | | | | | < < < > | | < | 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 |
/*
** Check to see if content is available for artifact "rid". Return
** true if it is. Return false if rid is a phantom or depends on
** a phantom.
*/
int content_is_available(int rid){
int srcid;
int depth = 0; /* Limit to recursion depth */
while( depth++ < 10000000 ){
if( bag_find(&contentCache.missing, rid) ){
return 0;
}
if( bag_find(&contentCache.available, rid) ){
return 1;
}
if( db_int(-1, "SELECT size FROM blob WHERE rid=%d", rid)<0 ){
bag_insert(&contentCache.missing, rid);
return 0;
}
srcid = findSrcid(rid);
if( srcid==0 ){
bag_insert(&contentCache.available, rid);
return 1;
}
rid = srcid;
}
fossil_panic("delta-loop in repository");
return 0;
}
/*
** Mark artifact rid as being available now. Update the cache to
** show that everything that was formerly unavailable because rid
** was missing is now available.
*/
|
| ︙ | ︙ | |||
161 162 163 164 165 166 167 |
blob_uncompress(pBlob, pBlob);
rc = 1;
}
db_reset(&q);
return rc;
}
| < < < | | < < > | | < | | < < < < < | | | | | | > > > > > | | > > | | | > | < < < < | > | | < > > | < > | > | < > > | < > | < < | < < | < < | < < < < < < < < < < < | | < < < < < < < | > < | < | < < | 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 |
blob_uncompress(pBlob, pBlob);
rc = 1;
}
db_reset(&q);
return rc;
}
/*
** Extract the content for ID rid and put it into the
** uninitialized blob. Return 1 on success. If the record
** is a phantom, zero pBlob and return 0.
*/
int content_get(int rid, Blob *pBlob){
int rc;
int i;
int nextRid;
assert( g.repositoryOpen );
blob_zero(pBlob);
if( rid==0 ) return 0;
/* Early out if we know the content is not available */
if( bag_find(&contentCache.missing, rid) ){
return 0;
}
/* Look for the artifact in the cache first */
if( bag_find(&contentCache.inCache, rid) ){
for(i=0; i<contentCache.n; i++){
if( contentCache.a[i].rid==rid ){
blob_copy(pBlob, &contentCache.a[i].content);
contentCache.a[i].age = contentCache.nextAge++;
return 1;
}
}
}
nextRid = findSrcid(rid);
if( nextRid==0 ){
rc = content_of_blob(rid, pBlob);
}else{
int n = 1;
int nAlloc = 10;
int *a = 0;
int mx;
Blob delta, next;
a = malloc( sizeof(a[0])*nAlloc );
if( a==0 ) fossil_panic("out of memory");
a[0] = rid;
a[1] = nextRid;
n = 1;
while( !bag_find(&contentCache.inCache, nextRid)
&& (nextRid = findSrcid(nextRid))>0 ){
n++;
if( n>=nAlloc ){
nAlloc = nAlloc*2 + 10;
a = realloc(a, nAlloc*sizeof(a[0]));
if( a==0 ) fossil_panic("out of memory");
}
a[n] = nextRid;
}
mx = n;
rc = content_get(a[n], pBlob);
n--;
while( rc && n>=0 ){
rc = content_of_blob(a[n], &delta);
if( rc ){
blob_delta_apply(pBlob, &delta, &next);
blob_reset(&delta);
if( (mx-n)%8==0 ){
content_cache_insert(a[n+1], pBlob);
}else{
blob_reset(pBlob);
}
*pBlob = next;
}
n--;
}
free(a);
if( !rc ) blob_reset(pBlob);
}
if( rc==0 ){
bag_insert(&contentCache.missing, rid);
}else{
bag_insert(&contentCache.available, rid);
}
return rc;
|
| ︙ | ︙ | |||
318 319 320 321 322 323 324 325 326 327 |
blob_write_to_file(&content, zFile);
}
/*
** When a record is converted from a phantom to a real record,
** if that record has other records that are derived by delta,
** then call manifest_crosslink() on those other records.
*/
void after_dephantomize(int rid, int linkFlag){
Stmt q;
| > > > | > | < < < | < > > > > > > | | | | > > > > | | | > | > > | < < < < > | 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 |
blob_write_to_file(&content, zFile);
}
/*
** When a record is converted from a phantom to a real record,
** if that record has other records that are derived by delta,
** then call manifest_crosslink() on those other records.
**
** Tail recursion is used to minimize stack depth.
*/
void after_dephantomize(int rid, int linkFlag){
Stmt q;
int nChildAlloc = 0;
int *aChild = 0;
while( rid ){
int nChildUsed = 0;
int i;
if( linkFlag ){
Blob content;
content_get(rid, &content);
manifest_crosslink(rid, &content);
blob_reset(&content);
}
db_prepare(&q, "SELECT rid FROM delta WHERE srcid=%d", rid);
while( db_step(&q)==SQLITE_ROW ){
int child = db_column_int(&q, 0);
if( nChildUsed>=nChildAlloc ){
nChildAlloc = nChildAlloc*2 + 10;
aChild = realloc(aChild, nChildAlloc*sizeof(aChild));
if( aChild==0 ) fossil_panic("out of memory");
}
aChild[nChildUsed++] = child;
}
db_finalize(&q);
for(i=1; i<nChildUsed; i++){
after_dephantomize(aChild[i], 1);
}
rid = nChildUsed>0 ? aChild[0] : 0;
linkFlag = 1;
}
free(aChild);
}
/*
** Write content into the database. Return the record ID. If the
** content is already in the database, just return the record ID.
**
** If srcId is specified, then pBlob is delta content from
|
| ︙ | ︙ |
Added src/event.c.
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 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 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 |
/*
** Copyright (c) 2010 D. Richard Hipp
**
** This program is free software; you can redistribute it and/or
** modify it under the terms of the Simplified BSD License (also
** known as the "2-Clause License" or "FreeBSD License".)
** This program is distributed in the hope that it will be useful,
** but without any warranty; without even the implied warranty of
** merchantability or fitness for a particular purpose.
**
** Author contact information:
** drh@hwaci.com
** http://www.hwaci.com/drh/
**
*******************************************************************************
**
** This file contains code to do formatting of event messages:
**
** Milestones
** Blog posts
** New articles
** Process checkpoints
** Announcements
*/
#include <assert.h>
#include <ctype.h>
#include "config.h"
#include "event.h"
/*
** Output a hyperlink to an event given its tagid.
*/
void hyperlink_to_event_tagid(int tagid){
char *zEventId;
char zShort[12];
zEventId = db_text(0, "SELECT substr(tagname, 7) FROM tag WHERE tagid=%d",
tagid);
sqlite3_snprintf(sizeof(zShort), zShort, "%.10s", zEventId);
if( g.okHistory ){
@ [<a href="%s(g.zTop)/event?name=%s(zEventId)">%s(zShort)</a>]
}else{
@ [%s(zShort)]
}
free(zEventId);
}
/*
** WEBPAGE: event
** URL: /event?name=EVENTID&detail=BOOLEAN&aid=ARTIFACTID
**
** Display an existing event identified by EVENTID
*/
void event_page(void){
int rid = 0; /* rid of the event artifact */
char *zUuid; /* UUID corresponding to rid */
const char *zEventId; /* Event identifier */
char *zETime; /* Time of the event */
char *zATime; /* Time the artifact was created */
int specRid; /* rid specified by aid= parameter */
int prevRid, nextRid; /* Previous or next edits of this event */
Manifest m; /* Parsed event artifact */
Blob content; /* Original event artifact content */
Blob fullbody; /* Complete content of the event body */
Blob title; /* Title extracted from the event body */
Blob tail; /* Event body that comes after the title */
Stmt q1; /* Query to search for the event */
int showDetail; /* True to show details */
/* wiki-read privilege is needed in order to read events.
*/
login_check_credentials();
if( !g.okRdWiki ){
login_needed();
return;
}
zEventId = P("name");
if( zEventId==0 ){ fossil_redirect_home(); return; }
zUuid = (char*)P("aid");
specRid = zUuid ? uuid_to_rid(zUuid, 0) : 0;
rid = nextRid = prevRid = 0;
db_prepare(&q1,
"SELECT rid FROM tagxref"
" WHERE tagid=(SELECT tagid FROM tag WHERE tagname='event-%q')"
" ORDER BY mtime DESC",
zEventId
);
while( db_step(&q1)==SQLITE_ROW ){
nextRid = rid;
rid = db_column_int(&q1, 0);
if( specRid==0 || specRid==rid ){
if( db_step(&q1)==SQLITE_ROW ){
prevRid = db_column_int(&q1, 0);
}
break;
}
}
db_finalize(&q1);
if( rid==0 || (specRid!=0 && specRid!=rid) ){
style_header("No Such Event");
@ Cannot locate specified event
style_footer();
return;
}
zUuid = db_text(0, "SELECT uuid FROM blob WHERE rid=%d", rid);
showDetail = atoi(PD("detail","0"));
/* Extract the event content.
*/
memset(&m, 0, sizeof(m));
blob_zero(&m.content);
content_get(rid, &content);
manifest_parse(&m, &content);
if( m.type!=CFTYPE_EVENT ){
fossil_panic("Object #%d is not an event", rid);
}
blob_init(&fullbody, m.zWiki, -1);
if( wiki_find_title(&fullbody, &title, &tail) ){
style_header(blob_str(&title));
}else{
style_header("Event %S", zEventId);
tail = fullbody;
}
if( g.okWrWiki && g.okWrite && nextRid==0 ){
style_submenu_element("Edit", "Edit", "%s/eventedit?name=%s",
g.zTop, zEventId);
}
zETime = db_text(0, "SELECT datetime(%.17g)", m.rEventDate);
style_submenu_element("Context", "Context", "%s/timeline?c=%T",
g.zTop, zETime);
if( g.okHistory ){
if( showDetail ){
style_submenu_element("Plain", "Plain", "%s/event?name=%s&aid=%s",
g.zTop, zEventId, zUuid);
if( nextRid ){
char *zNext;
zNext = db_text(0, "SELECT uuid FROM blob WHERE rid=%d", nextRid);
style_submenu_element("Next", "Next",
"%s/event?name=%s&aid=%s&detail=1",
g.zTop, zEventId, zNext);
free(zNext);
}
if( prevRid ){
char *zPrev;
zPrev = db_text(0, "SELECT uuid FROM blob WHERE rid=%d", prevRid);
style_submenu_element("Prev", "Prev",
"%s/event?name=%s&aid=%s&detail=1",
g.zTop, zEventId, zPrev);
free(zPrev);
}
}else{
style_submenu_element("Detail", "Detail",
"%s/event?name=%s&aid=%s&detail=1",
g.zTop, zEventId, zUuid);
}
}
if( showDetail && g.okHistory ){
int i;
const char *zClr = 0;
Blob comment;
zATime = db_text(0, "SELECT datetime(%.17g)", m.rDate);
@ <p>Event [<a href="%s(g.zTop)/artifact/%s(zUuid)">%S(zUuid)</a>] at
@ [<a href="%s(g.zTop)/timeline?c=%T(zETime)">%s(zETime)</a>]
@ entered by user <b>%h(m.zUser)</b> on
@ [<a href="%s(g.zTop)/timeline?c=%T(zATime)">%s(zATime)</a>]:</p>
@ <blockquote>
for(i=0; i<m.nTag; i++){
if( strcmp(m.aTag[i].zName,"+bgcolor")==0 ){
zClr = m.aTag[i].zValue;
}
}
if( zClr && zClr[0]==0 ) zClr = 0;
if( zClr ){
@ <div style="background-color: %h(zClr);">
}else{
@ <div>
}
blob_init(&comment, m.zComment, -1);
wiki_convert(&comment, 0, WIKI_INLINE);
blob_reset(&comment);
@ </div>
@ </blockquote><hr />
}
wiki_convert(&tail, 0, 0);
style_footer();
manifest_clear(&m);
}
/*
** WEBPAGE: eventedit
** URL: /eventedit?name=EVENTID
**
** Edit an event. If name is omitted, create a new event.
*/
void eventedit_page(void){
char *zTag;
int rid = 0;
Blob event;
const char *zEventId;
char *zHtmlPageName;
int n;
const char *z;
char *zBody = (char*)P("w");
char *zETime = (char*)P("t");
const char *zComment = P("c");
const char *zTags = P("g");
const char *zClr;
if( zBody ){
zBody = mprintf("%s", zBody);
}
login_check_credentials();
zEventId = P("name");
if( zEventId==0 ){
zEventId = db_text(0, "SELECT lower(hex(randomblob(20)))");
}else{
int nEventId = strlen(zEventId);
if( nEventId!=40 || !validate16(zEventId, 40) ){
fossil_redirect_home();
return;
}
}
zTag = mprintf("event-%s", zEventId);
rid = db_int(0,
"SELECT rid FROM tagxref"
" WHERE tagid=(SELECT tagid FROM tag WHERE tagname=%Q)"
" ORDER BY mtime DESC", zTag
);
free(zTag);
/* Need both check-in and wiki-write or wiki-create privileges in order
** to edit/create an event.
*/
if( !g.okWrite || (rid && !g.okWrWiki) || (!rid && !g.okNewWiki) ){
login_needed();
return;
}
/* Figure out the color */
if( rid ){
zClr = db_text("", "SELECT bgcolor FROM event WHERE objid=%d", rid);
}else{
zClr = "";
}
zClr = PD("clr",zClr);
if( strcmp(zClr,"##")==0 ) zClr = PD("cclr","");
/* If editing an existing event, extract the key fields to use as
** a starting point for the edit.
*/
if( rid && (zBody==0 || zETime==0 || zComment==0 || zTags==0) ){
Manifest m;
Blob content;
memset(&m, 0, sizeof(m));
blob_zero(&m.content);
content_get(rid, &content);
manifest_parse(&m, &content);
if( m.type==CFTYPE_EVENT ){
if( zBody==0 ) zBody = m.zWiki;
if( zETime==0 ){
zETime = db_text(0, "SELECT datetime(%.17g)", m.rEventDate);
}
if( zComment==0 ) zComment = m.zComment;
}
if( zTags==0 ){
zTags = db_text(0,
"SELECT group_concat(substr(tagname,5),', ')"
" FROM tagxref, tag"
" WHERE tagxref.rid=%d"
" AND tagxref.tagid=tag.tagid"
" AND tag.tagname GLOB 'sym-*'",
rid
);
}
}
zETime = db_text(0, "SELECT coalesce(datetime(%Q),datetime('now'))", zETime);
if( P("submit")!=0 && (zBody!=0 && zComment!=0) ){
char *zDate;
Blob cksum;
int nrid;
blob_zero(&event);
db_begin_transaction();
login_verify_csrf_secret();
blob_appendf(&event, "C %F\n", zComment);
zDate = db_text(0, "SELECT datetime('now')");
zDate[10] = 'T';
blob_appendf(&event, "D %s\n", zDate);
free(zDate);
zETime[10] = 'T';
blob_appendf(&event, "E %s %s\n", zETime, zEventId);
zETime[10] = ' ';
if( rid ){
char *zUuid = db_text(0, "SELECT uuid FROM blob WHERE rid=%d", rid);
blob_appendf(&event, "P %s\n", zUuid);
free(zUuid);
}
if( zClr && zClr[0] ){
blob_appendf(&event, "T +bgcolor * %F\n", zClr);
}
if( zTags && zTags[0] ){
Blob tags, one;
int i, j;
Stmt q;
char *zBlob;
/* Load the tags string into a blob */
blob_zero(&tags);
blob_append(&tags, zTags, -1);
/* Collapse all sequences of whitespace and "," characters into
** a single space character */
zBlob = blob_str(&tags);
for(i=j=0; zBlob[i]; i++, j++){
if( blob_isspace(zBlob[i]) || zBlob[i]==',' ){
while( blob_isspace(zBlob[i+1]) ){ i++; }
zBlob[j] = ' ';
}else{
zBlob[j] = zBlob[i];
}
}
blob_resize(&tags, j);
/* Parse out each tag and load it into a temporary table for sorting */
db_multi_exec("CREATE TEMP TABLE newtags(x);");
while( blob_token(&tags, &one) ){
db_multi_exec("INSERT INTO newtags VALUES(%B)", &one);
}
blob_reset(&tags);
/* Extract the tags in sorted order and make an entry in the
** artifact for each. */
db_prepare(&q, "SELECT x FROM newtags ORDER BY x");
while( db_step(&q)==SQLITE_ROW ){
blob_appendf(&event, "T +sym-%F *\n", db_column_text(&q, 0));
}
db_finalize(&q);
}
if( g.zLogin ){
blob_appendf(&event, "U %F\n", g.zLogin);
}
blob_appendf(&event, "W %d\n%s\n", strlen(zBody), zBody);
md5sum_blob(&event, &cksum);
blob_appendf(&event, "Z %b\n", &cksum);
blob_reset(&cksum);
nrid = content_put(&event, 0, 0);
db_multi_exec("INSERT OR IGNORE INTO unsent VALUES(%d)", nrid);
manifest_crosslink(nrid, &event);
blob_reset(&event);
content_deltify(rid, nrid, 0);
db_end_transaction(0);
cgi_redirectf("event?name=%T", zEventId);
}
if( P("cancel")!=0 ){
cgi_redirectf("event?name=%T", zEventId);
return;
}
if( zBody==0 ){
zBody = mprintf("<i>Event Text</i>");
}
zHtmlPageName = mprintf("Edit Event %S", zEventId);
style_header(zHtmlPageName);
if( P("preview")!=0 ){
Blob title, tail, com;
@ <p><b>Timeline comment preview:</b></p>
@ <blockquote>
@ <table border="0">
if( zClr && zClr[0] ){
@ <tr><td style="background-color: %h(zClr);">
}else{
@ <tr><td>
}
blob_zero(&com);
blob_append(&com, zComment, -1);
wiki_convert(&com, 0, WIKI_INLINE);
@ </td></tr></table>
@ </blockquote>
@ <p><b>Page content preview:</b><p>
@ <blockquote>
blob_zero(&event);
blob_append(&event, zBody, -1);
if( wiki_find_title(&event, &title, &tail) ){
@ <h2 align="center">%h(blob_str(&title))</h2>
wiki_convert(&tail, 0, 0);
}else{
wiki_convert(&event, 0, 0);
}
@ </blockquote><hr />
blob_reset(&event);
}
for(n=2, z=zBody; z[0]; z++){
if( z[0]=='\n' ) n++;
}
if( n<20 ) n = 20;
if( n>40 ) n = 40;
@ <form method="post" action="%s(g.zBaseURL)/eventedit"><div>
login_insert_csrf_secret();
@ <input type="hidden" name="name" value="%h(zEventId)" />
@ <table border="0" cellspacing="10">
@ <tr><td align="right" valign="top"><b>Event Time:</b></td>
@ <td valign="top">
@ <input type="text" name="t" size="25" value="%h(zETime)" />
@ </td></tr>
@ <tr><td align="right" valign="top"><b>Timeline Comment:</b></td>
@ <td valign="top">
@ <textarea name="c" class="eventedit" cols="80"
@ rows="3" wrap="virtual">%h(zComment)</textarea>
@ </td></tr>
@ <tr><td align="right" valign="top"><b>Background Color:</b></td>
@ <td valign="top">
render_color_chooser(0, zClr, 0, "clr", "cclr");
@ </td></tr>
@ <tr><td align="right" valign="top"><b>Tags:</b></td>
@ <td valign="top">
@ <input type="text" name="g" size="40" value="%h(zTags)" />
@ </td></tr>
@ <tr><td align="right" valign="top"><b>Page Content:</b></td>
@ <td valign="top">
@ <textarea name="w" class="eventedit" cols="80"
@ rows="%d(n)" wrap="virtual">%h(zBody)</textarea>
@ </td></tr>
@ <tr><td colspan="2">
@ <input type="submit" name="preview" value="Preview Your Changes" />
@ <input type="submit" name="submit" value="Apply These Changes" />
@ <input type="submit" name="cancel" value="Cancel" />
@ </td></tr></table>
@ </div></form>
style_footer();
}
|
Changes to src/info.c.
| ︙ | ︙ | |||
777 778 779 780 781 782 783 |
if( pDownloadName && blob_size(pDownloadName)==0 ){
blob_appendf(pDownloadName, "%s.wiki", zPagename);
}
}
db_finalize(&q);
if( nWiki==0 ){
db_prepare(&q,
| | > > > > | > | 777 778 779 780 781 782 783 784 785 786 787 788 789 790 791 792 793 794 795 796 797 798 799 800 801 802 803 804 805 806 807 808 809 810 811 812 813 814 815 816 817 818 819 820 |
if( pDownloadName && blob_size(pDownloadName)==0 ){
blob_appendf(pDownloadName, "%s.wiki", zPagename);
}
}
db_finalize(&q);
if( nWiki==0 ){
db_prepare(&q,
"SELECT datetime(mtime), user, comment, type, uuid, tagid"
" FROM event, blob"
" WHERE event.objid=%d"
" AND blob.rid=%d",
rid, rid
);
while( db_step(&q)==SQLITE_ROW ){
const char *zDate = db_column_text(&q, 0);
const char *zUser = db_column_text(&q, 1);
const char *zCom = db_column_text(&q, 2);
const char *zType = db_column_text(&q, 3);
const char *zUuid = db_column_text(&q, 4);
if( cnt>0 ){
@ Also
}
if( zType[0]=='w' ){
@ Wiki edit
}else if( zType[0]=='t' ){
@ Ticket change
}else if( zType[0]=='c' ){
@ Manifest of check-in
}else if( zType[0]=='e' ){
@ Instance of event
hyperlink_to_event_tagid(db_column_int(&q, 5));
}else{
@ Control file referencing
}
if( zType[0]!='e' ){
hyperlink_to_uuid(zUuid);
}
@ - %w(zCom) by
hyperlink_to_user(zUser,zDate," on");
hyperlink_to_date(zDate, ".");
if( pDownloadName && blob_size(pDownloadName)==0 ){
blob_appendf(pDownloadName, "%.10s.txt", zUuid);
}
cnt++;
|
| ︙ | ︙ | |||
1258 1259 1260 1261 1262 1263 1264 1265 1266 1267 1268 1269 1270 1271 |
if( db_exists("SELECT 1 FROM plink WHERE pid=%d", rid) ){
ci_page();
}else
{
artifact_page();
}
}
/*
** WEBPAGE: ci_edit
** URL: ci_edit?r=RID&c=NEWCOMMENT&u=NEWUSER
**
** Present a dialog for updating properties of a baseline:
**
| > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 1263 1264 1265 1266 1267 1268 1269 1270 1271 1272 1273 1274 1275 1276 1277 1278 1279 1280 1281 1282 1283 1284 1285 1286 1287 1288 1289 1290 1291 1292 1293 1294 1295 1296 1297 1298 1299 1300 1301 1302 1303 1304 1305 1306 1307 1308 1309 1310 1311 1312 1313 1314 1315 1316 1317 1318 1319 1320 1321 1322 1323 1324 1325 1326 1327 1328 1329 1330 1331 1332 1333 1334 1335 1336 1337 1338 1339 1340 1341 1342 1343 1344 1345 1346 1347 1348 1349 1350 1351 1352 1353 1354 1355 1356 |
if( db_exists("SELECT 1 FROM plink WHERE pid=%d", rid) ){
ci_page();
}else
{
artifact_page();
}
}
/*
** Generate HTML that will present the user with a selection of
** potential background colors for timeline entries.
*/
void render_color_chooser(
int fPropagate, /* Default value for propagation */
const char *zDefaultColor, /* The current default color */
const char *zIdPropagate, /* ID of form element checkbox. NULL for none */
const char *zId, /* The ID of the form element */
const char *zIdCustom /* ID of text box for custom color */
){
static const struct SampleColors {
const char *zCName;
const char *zColor;
} aColor[] = {
{ "(none)", "" },
{ "#f2dcdc", "#f2dcdc" },
{ "#f0ffc0", "#f0ffc0" },
{ "#bde5d6", "#bde5d6" },
{ "#c0ffc0", "#c0ffc0" },
{ "#c0fff0", "#c0fff0" },
{ "#c0f0ff", "#c0f0ff" },
{ "#d0c0ff", "#d0c0ff" },
{ "#ffc0ff", "#ffc0ff" },
{ "#ffc0d0", "#ffc0d0" },
{ "#fff0c0", "#fff0c0" },
{ "#c0c0c0", "#c0c0c0" },
{ "custom", "##" },
};
int nColor = sizeof(aColor)/sizeof(aColor[0])-1;
int stdClrFound = 0;
int i;
@ <table border="0" cellpadding="0" cellspacing="1">
if( zIdPropagate ){
@ <tr><td colspan="6" align="left">
if( fPropagate ){
@ <input type="checkbox" name="%s(zIdPropagate)" checked="checked" />
}else{
@ <input type="checkbox" name="%s(zIdPropagate)" />
}
@ Propagate color to descendants</td></tr>
}
@ <tr>
for(i=0; i<nColor; i++){
if( aColor[i].zColor[0] ){
@ <td style="background-color: %h(aColor[i].zColor);">
}else{
@ <td>
}
if( strcmp(zDefaultColor, aColor[i].zColor)==0 ){
@ <input type="radio" name="%s(zId)" value="%h(aColor[i].zColor)"
@ checked="checked" />
stdClrFound=1;
}else{
@ <input type="radio" name="%s(zId)" value="%h(aColor[i].zColor)" />
}
@ %h(aColor[i].zCName)</td>
if( (i%6)==5 && i+1<nColor ){
@ </tr><tr>
}
}
@ </tr><tr>
if (stdClrFound){
@ <td colspan="6">
@ <input type="radio" name="%s(zId)" value="%h(aColor[nColor].zColor)" />
}else{
@ <td style="background-color: %h(zDefaultColor);" colspan="6">
@ <input type="radio" name="%s(zId)" value="%h(aColor[nColor].zColor)"
@ checked="checked" />
}
@ %h(aColor[i].zCName)
@ <input type="text" name="%s(zIdCustom)"
@ id="%s(zIdCustom)" class="checkinUserColor"
@ value="%h(stdClrFound?"":zDefaultColor)" />
@ </td>
@ </tr>
@ </table>
}
/*
** WEBPAGE: ci_edit
** URL: ci_edit?r=RID&c=NEWCOMMENT&u=NEWUSER
**
** Present a dialog for updating properties of a baseline:
**
|
| ︙ | ︙ | |||
1288 1289 1290 1291 1292 1293 1294 | const char *zNewBrFlag; const char *zNewBranch; const char *zCloseFlag; int fPropagateColor; char *zUuid; Blob comment; Stmt q; | < < < < < < < < < < < < < < < < < < < < < | 1373 1374 1375 1376 1377 1378 1379 1380 1381 1382 1383 1384 1385 1386 |
const char *zNewBrFlag;
const char *zNewBranch;
const char *zCloseFlag;
int fPropagateColor;
char *zUuid;
Blob comment;
Stmt q;
login_check_credentials();
if( !g.okWrite ){ login_needed(); return; }
rid = name_to_rid(P("r"));
zUuid = db_text(0, "SELECT uuid FROM blob WHERE rid=%d", rid);
zComment = db_text(0, "SELECT coalesce(ecomment,comment)"
" FROM event WHERE objid=%d", rid);
|
| ︙ | ︙ | |||
1332 1333 1334 1335 1336 1337 1338 |
zDate = db_text(0, "SELECT datetime(mtime)"
" FROM event WHERE objid=%d", rid);
if( zDate==0 ) fossil_redirect_home();
zNewDate = PD("dt",zDate);
zColor = db_text("", "SELECT bgcolor"
" FROM event WHERE objid=%d", rid);
zNewColor = PD("clr",zColor);
| | | 1396 1397 1398 1399 1400 1401 1402 1403 1404 1405 1406 1407 1408 1409 1410 |
zDate = db_text(0, "SELECT datetime(mtime)"
" FROM event WHERE objid=%d", rid);
if( zDate==0 ) fossil_redirect_home();
zNewDate = PD("dt",zDate);
zColor = db_text("", "SELECT bgcolor"
" FROM event WHERE objid=%d", rid);
zNewColor = PD("clr",zColor);
if( strcmp(zNewColor,"##")==0 ){
zNewColor = P("clrcust");
}
fPropagateColor = P("pclr")!=0;
zNewTagFlag = P("newtag") ? " checked" : "";
zNewTag = PD("tagname","");
zNewBrFlag = P("newbr") ? " checked" : "";
zNewBranch = PD("brname","");
|
| ︙ | ︙ | |||
1499 1500 1501 1502 1503 1504 1505 | @ <tr><td align="right" valign="top"><b>Check-in Time:</b></td> @ <td valign="top"> @ <input type="text" name="dt" size="20" value="%h(zNewDate)" /> @ </td></tr> @ <tr><td align="right" valign="top"><b>Background Color:</b></td> @ <td valign="top"> | < < | < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < | 1563 1564 1565 1566 1567 1568 1569 1570 1571 1572 1573 1574 1575 1576 1577 | @ <tr><td align="right" valign="top"><b>Check-in Time:</b></td> @ <td valign="top"> @ <input type="text" name="dt" size="20" value="%h(zNewDate)" /> @ </td></tr> @ <tr><td align="right" valign="top"><b>Background Color:</b></td> @ <td valign="top"> render_color_chooser(fPropagateColor, zNewColor, "pclr", "clr", "clrcust"); @ </td></tr> @ <tr><td align="right" valign="top"><b>Tags:</b></td> @ <td valign="top"> @ <input type="checkbox" name="newtag"%s(zNewTagFlag) /> @ Add the following new tag name to this check-in: @ <input type="text" style="width:15;" name="tagname" value="%h(zNewTag)" /> |
| ︙ | ︙ |
Changes to src/main.mk.
| ︙ | ︙ | |||
33 34 35 36 37 38 39 40 41 42 43 44 45 46 | $(SRCDIR)/delta.c \ $(SRCDIR)/deltacmd.c \ $(SRCDIR)/descendants.c \ $(SRCDIR)/diff.c \ $(SRCDIR)/diffcmd.c \ $(SRCDIR)/doc.c \ $(SRCDIR)/encode.c \ $(SRCDIR)/file.c \ $(SRCDIR)/finfo.c \ $(SRCDIR)/graph.c \ $(SRCDIR)/http.c \ $(SRCDIR)/http_socket.c \ $(SRCDIR)/http_ssl.c \ $(SRCDIR)/http_transport.c \ | > | 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 | $(SRCDIR)/delta.c \ $(SRCDIR)/deltacmd.c \ $(SRCDIR)/descendants.c \ $(SRCDIR)/diff.c \ $(SRCDIR)/diffcmd.c \ $(SRCDIR)/doc.c \ $(SRCDIR)/encode.c \ $(SRCDIR)/event.c \ $(SRCDIR)/file.c \ $(SRCDIR)/finfo.c \ $(SRCDIR)/graph.c \ $(SRCDIR)/http.c \ $(SRCDIR)/http_socket.c \ $(SRCDIR)/http_ssl.c \ $(SRCDIR)/http_transport.c \ |
| ︙ | ︙ | |||
106 107 108 109 110 111 112 113 114 115 116 117 118 119 | delta_.c \ deltacmd_.c \ descendants_.c \ diff_.c \ diffcmd_.c \ doc_.c \ encode_.c \ file_.c \ finfo_.c \ graph_.c \ http_.c \ http_socket_.c \ http_ssl_.c \ http_transport_.c \ | > | 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 | delta_.c \ deltacmd_.c \ descendants_.c \ diff_.c \ diffcmd_.c \ doc_.c \ encode_.c \ event_.c \ file_.c \ finfo_.c \ graph_.c \ http_.c \ http_socket_.c \ http_ssl_.c \ http_transport_.c \ |
| ︙ | ︙ | |||
179 180 181 182 183 184 185 186 187 188 189 190 191 192 | $(OBJDIR)/delta.o \ $(OBJDIR)/deltacmd.o \ $(OBJDIR)/descendants.o \ $(OBJDIR)/diff.o \ $(OBJDIR)/diffcmd.o \ $(OBJDIR)/doc.o \ $(OBJDIR)/encode.o \ $(OBJDIR)/file.o \ $(OBJDIR)/finfo.o \ $(OBJDIR)/graph.o \ $(OBJDIR)/http.o \ $(OBJDIR)/http_socket.o \ $(OBJDIR)/http_ssl.o \ $(OBJDIR)/http_transport.o \ | > | 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 | $(OBJDIR)/delta.o \ $(OBJDIR)/deltacmd.o \ $(OBJDIR)/descendants.o \ $(OBJDIR)/diff.o \ $(OBJDIR)/diffcmd.o \ $(OBJDIR)/doc.o \ $(OBJDIR)/encode.o \ $(OBJDIR)/event.o \ $(OBJDIR)/file.o \ $(OBJDIR)/finfo.o \ $(OBJDIR)/graph.o \ $(OBJDIR)/http.o \ $(OBJDIR)/http_socket.o \ $(OBJDIR)/http_ssl.o \ $(OBJDIR)/http_transport.o \ |
| ︙ | ︙ | |||
271 272 273 274 275 276 277 | # $(SRCDIR)/../manifest: # noop clean: rm -f $(OBJDIR)/*.o *_.c $(APPNAME) VERSION.h rm -f translate makeheaders mkindex page_index.h headers | | | | 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 | # $(SRCDIR)/../manifest: # noop clean: rm -f $(OBJDIR)/*.o *_.c $(APPNAME) VERSION.h rm -f translate makeheaders mkindex page_index.h headers rm -f add.h allrepo.h attach.h bag.h blob.h branch.h browse.h captcha.h cgi.h checkin.h checkout.h clearsign.h clone.h comformat.h configure.h content.h db.h delta.h deltacmd.h descendants.h diff.h diffcmd.h doc.h encode.h event.h file.h finfo.h graph.h http.h http_socket.h http_ssl.h http_transport.h info.h login.h main.h manifest.h md5.h merge.h merge3.h name.h pivot.h popen.h pqueue.h printf.h rebuild.h report.h rss.h schema.h search.h setup.h sha1.h shun.h skins.h stat.h style.h sync.h tag.h th_main.h timeline.h tkt.h tktsetup.h undo.h update.h url.h user.h verify.h vfile.h wiki.h wikiformat.h winhttp.h xfer.h zip.h page_index.h: $(TRANS_SRC) mkindex ./mkindex $(TRANS_SRC) >$@ headers: page_index.h makeheaders VERSION.h ./makeheaders add_.c:add.h allrepo_.c:allrepo.h attach_.c:attach.h bag_.c:bag.h blob_.c:blob.h branch_.c:branch.h browse_.c:browse.h captcha_.c:captcha.h cgi_.c:cgi.h checkin_.c:checkin.h checkout_.c:checkout.h clearsign_.c:clearsign.h clone_.c:clone.h comformat_.c:comformat.h configure_.c:configure.h content_.c:content.h db_.c:db.h delta_.c:delta.h deltacmd_.c:deltacmd.h descendants_.c:descendants.h diff_.c:diff.h diffcmd_.c:diffcmd.h doc_.c:doc.h encode_.c:encode.h event_.c:event.h file_.c:file.h finfo_.c:finfo.h graph_.c:graph.h http_.c:http.h http_socket_.c:http_socket.h http_ssl_.c:http_ssl.h http_transport_.c:http_transport.h info_.c:info.h login_.c:login.h main_.c:main.h manifest_.c:manifest.h md5_.c:md5.h merge_.c:merge.h merge3_.c:merge3.h name_.c:name.h pivot_.c:pivot.h popen_.c:popen.h pqueue_.c:pqueue.h printf_.c:printf.h rebuild_.c:rebuild.h report_.c:report.h rss_.c:rss.h schema_.c:schema.h search_.c:search.h setup_.c:setup.h sha1_.c:sha1.h shun_.c:shun.h skins_.c:skins.h stat_.c:stat.h style_.c:style.h sync_.c:sync.h tag_.c:tag.h th_main_.c:th_main.h timeline_.c:timeline.h tkt_.c:tkt.h tktsetup_.c:tktsetup.h undo_.c:undo.h update_.c:update.h url_.c:url.h user_.c:user.h verify_.c:verify.h vfile_.c:vfile.h wiki_.c:wiki.h wikiformat_.c:wikiformat.h winhttp_.c:winhttp.h xfer_.c:xfer.h zip_.c:zip.h $(SRCDIR)/sqlite3.h $(SRCDIR)/th.h VERSION.h touch headers headers: Makefile Makefile: add_.c: $(SRCDIR)/add.c translate ./translate $(SRCDIR)/add.c >add_.c $(OBJDIR)/add.o: add_.c add.h $(SRCDIR)/config.h |
| ︙ | ︙ | |||
448 449 450 451 452 453 454 455 456 457 458 459 460 461 | encode_.c: $(SRCDIR)/encode.c translate ./translate $(SRCDIR)/encode.c >encode_.c $(OBJDIR)/encode.o: encode_.c encode.h $(SRCDIR)/config.h $(XTCC) -o $(OBJDIR)/encode.o -c encode_.c encode.h: headers file_.c: $(SRCDIR)/file.c translate ./translate $(SRCDIR)/file.c >file_.c $(OBJDIR)/file.o: file_.c file.h $(SRCDIR)/config.h $(XTCC) -o $(OBJDIR)/file.o -c file_.c file.h: headers | > > > > > > > | 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 | encode_.c: $(SRCDIR)/encode.c translate ./translate $(SRCDIR)/encode.c >encode_.c $(OBJDIR)/encode.o: encode_.c encode.h $(SRCDIR)/config.h $(XTCC) -o $(OBJDIR)/encode.o -c encode_.c encode.h: headers event_.c: $(SRCDIR)/event.c translate ./translate $(SRCDIR)/event.c >event_.c $(OBJDIR)/event.o: event_.c event.h $(SRCDIR)/config.h $(XTCC) -o $(OBJDIR)/event.o -c event_.c event.h: headers file_.c: $(SRCDIR)/file.c translate ./translate $(SRCDIR)/file.c >file_.c $(OBJDIR)/file.o: file_.c file.h $(SRCDIR)/config.h $(XTCC) -o $(OBJDIR)/file.o -c file_.c file.h: headers |
| ︙ | ︙ |
Changes to src/makemake.tcl.
| ︙ | ︙ | |||
27 28 29 30 31 32 33 34 35 36 37 38 39 40 | delta deltacmd descendants diff diffcmd doc encode file finfo graph http http_socket http_transport info | > | 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 | delta deltacmd descendants diff diffcmd doc encode event file finfo graph http http_socket http_transport info |
| ︙ | ︙ |
Changes to src/manifest.c.
| ︙ | ︙ | |||
30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 |
*/
#define CFTYPE_MANIFEST 1
#define CFTYPE_CLUSTER 2
#define CFTYPE_CONTROL 3
#define CFTYPE_WIKI 4
#define CFTYPE_TICKET 5
#define CFTYPE_ATTACHMENT 6
/*
** A parsed manifest or cluster.
*/
struct Manifest {
Blob content; /* The original content blob */
int type; /* Type of artifact. One of CFTYPE_xxxxx */
char *zComment; /* Decoded comment. The C card. */
double rDate; /* Date and time from D card. 0.0 if no D card. */
char *zUser; /* Name of the user from the U card. */
char *zRepoCksum; /* MD5 checksum of the baseline content. R card. */
char *zWiki; /* Text of the wiki page. W card. */
char *zWikiTitle; /* Name of the wiki page. L card. */
char *zTicketUuid; /* UUID for a ticket. K card. */
char *zAttachName; /* Filename of an attachment. A card. */
char *zAttachSrc; /* UUID of document being attached. A card. */
char *zAttachTarget; /* Ticket or wiki that attachment applies to. A card */
int nFile; /* Number of F cards */
int nFileAlloc; /* Slots allocated in aFile[] */
struct {
| > > > | 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 |
*/
#define CFTYPE_MANIFEST 1
#define CFTYPE_CLUSTER 2
#define CFTYPE_CONTROL 3
#define CFTYPE_WIKI 4
#define CFTYPE_TICKET 5
#define CFTYPE_ATTACHMENT 6
#define CFTYPE_EVENT 7
/*
** A parsed manifest or cluster.
*/
struct Manifest {
Blob content; /* The original content blob */
int type; /* Type of artifact. One of CFTYPE_xxxxx */
char *zComment; /* Decoded comment. The C card. */
double rDate; /* Date and time from D card. 0.0 if no D card. */
char *zUser; /* Name of the user from the U card. */
char *zRepoCksum; /* MD5 checksum of the baseline content. R card. */
char *zWiki; /* Text of the wiki page. W card. */
char *zWikiTitle; /* Name of the wiki page. L card. */
double rEventDate; /* Date of an event. E card. */
char *zEventId; /* UUID for an event. E card. */
char *zTicketUuid; /* UUID for a ticket. K card. */
char *zAttachName; /* Filename of an attachment. A card. */
char *zAttachSrc; /* UUID of document being attached. A card. */
char *zAttachTarget; /* Ticket or wiki that attachment applies to. A card */
int nFile; /* Number of F cards */
int nFileAlloc; /* Slots allocated in aFile[] */
struct {
|
| ︙ | ︙ | |||
228 229 230 231 232 233 234 235 236 237 238 239 240 241 |
if( p->rDate!=0.0 ) goto manifest_syntax_error;
if( blob_token(&line, &a1)==0 ) goto manifest_syntax_error;
if( blob_token(&line, &a2)!=0 ) goto manifest_syntax_error;
zDate = blob_terminate(&a1);
p->rDate = db_double(0.0, "SELECT julianday(%Q)", zDate);
break;
}
/*
** F <filename> <uuid> ?<permissions>? ?<old-name>?
**
** Identifies a file in a manifest. Multiple F lines are
** allowed in a manifest. F lines are not allowed in any
** other control file. The filename and old-name are fossil-encoded.
| > > > > > > > > > > > > > > > > > > > > > > > > > | 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 |
if( p->rDate!=0.0 ) goto manifest_syntax_error;
if( blob_token(&line, &a1)==0 ) goto manifest_syntax_error;
if( blob_token(&line, &a2)!=0 ) goto manifest_syntax_error;
zDate = blob_terminate(&a1);
p->rDate = db_double(0.0, "SELECT julianday(%Q)", zDate);
break;
}
/*
** E <timestamp> <uuid>
**
** An "event" card that contains the timestamp of the event in the
** format YYYY-MM-DDtHH:MM:SS and a unique identifier for the event.
** The event timestamp is distinct from the D timestamp. The D
** timestamp is when the artifact was created whereas the E timestamp
** is when the specific event is said to occur.
*/
case 'E': {
char *zEDate;
md5sum_step_text(blob_buffer(&line), blob_size(&line));
if( p->rEventDate!=0.0 ) goto manifest_syntax_error;
if( blob_token(&line, &a1)==0 ) goto manifest_syntax_error;
if( blob_token(&line, &a2)==0 ) goto manifest_syntax_error;
if( blob_token(&line, &a3)!=0 ) goto manifest_syntax_error;
zEDate = blob_terminate(&a1);
p->rEventDate = db_double(0.0, "SELECT julianday(%Q)", zEDate);
if( p->rEventDate<=0.0 ) goto manifest_syntax_error;
if( blob_size(&a2)!=UUID_SIZE ) goto manifest_syntax_error;
p->zEventId = blob_terminate(&a2);
if( !validate16(p->zEventId, UUID_SIZE) ) goto manifest_syntax_error;
break;
}
/*
** F <filename> <uuid> ?<permissions>? ?<old-name>?
**
** Identifies a file in a manifest. Multiple F lines are
** allowed in a manifest. F lines are not allowed in any
** other control file. The filename and old-name are fossil-encoded.
|
| ︙ | ︙ | |||
561 562 563 564 565 566 567 |
}
}
}
if( !seenHeader ) goto manifest_syntax_error;
if( p->nFile>0 || p->zRepoCksum!=0 ){
if( p->nCChild>0 ) goto manifest_syntax_error;
| | > > | > > > > > > > > > > > > > > | | < < | 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 |
}
}
}
if( !seenHeader ) goto manifest_syntax_error;
if( p->nFile>0 || p->zRepoCksum!=0 ){
if( p->nCChild>0 ) goto manifest_syntax_error;
if( p->rDate<=0.0 ) goto manifest_syntax_error;
if( p->nField>0 ) goto manifest_syntax_error;
if( p->zTicketUuid ) goto manifest_syntax_error;
if( p->zWiki ) goto manifest_syntax_error;
if( p->zWikiTitle ) goto manifest_syntax_error;
if( p->zEventId ) goto manifest_syntax_error;
if( p->zTicketUuid ) goto manifest_syntax_error;
if( p->zAttachName ) goto manifest_syntax_error;
p->type = CFTYPE_MANIFEST;
}else if( p->nCChild>0 ){
if( p->rDate>0.0 ) goto manifest_syntax_error;
if( p->zComment!=0 ) goto manifest_syntax_error;
if( p->zUser!=0 ) goto manifest_syntax_error;
if( p->nTag>0 ) goto manifest_syntax_error;
if( p->nParent>0 ) goto manifest_syntax_error;
if( p->nField>0 ) goto manifest_syntax_error;
if( p->zTicketUuid ) goto manifest_syntax_error;
if( p->zWiki ) goto manifest_syntax_error;
if( p->zWikiTitle ) goto manifest_syntax_error;
if( p->zEventId ) goto manifest_syntax_error;
if( p->zAttachName ) goto manifest_syntax_error;
if( !seenZ ) goto manifest_syntax_error;
p->type = CFTYPE_CLUSTER;
}else if( p->nField>0 ){
if( p->rDate<=0.0 ) goto manifest_syntax_error;
if( p->zWiki ) goto manifest_syntax_error;
if( p->zWikiTitle ) goto manifest_syntax_error;
if( p->zEventId ) goto manifest_syntax_error;
if( p->nCChild>0 ) goto manifest_syntax_error;
if( p->nTag>0 ) goto manifest_syntax_error;
if( p->zTicketUuid==0 ) goto manifest_syntax_error;
if( p->zUser==0 ) goto manifest_syntax_error;
if( p->zAttachName ) goto manifest_syntax_error;
if( !seenZ ) goto manifest_syntax_error;
p->type = CFTYPE_TICKET;
}else if( p->zEventId ){
if( p->rDate<=0.0 ) goto manifest_syntax_error;
if( p->nCChild>0 ) goto manifest_syntax_error;
if( p->zTicketUuid!=0 ) goto manifest_syntax_error;
if( p->zWikiTitle!=0 ) goto manifest_syntax_error;
if( p->zWiki==0 ) goto manifest_syntax_error;
if( p->zAttachName ) goto manifest_syntax_error;
for(i=0; i<p->nTag; i++){
if( p->aTag[i].zName[0]!='+' ) goto manifest_syntax_error;
if( p->aTag[i].zUuid!=0 ) goto manifest_syntax_error;
}
if( !seenZ ) goto manifest_syntax_error;
p->type = CFTYPE_EVENT;
}else if( p->zWiki!=0 ){
if( p->rDate<=0.0 ) goto manifest_syntax_error;
if( p->nCChild>0 ) goto manifest_syntax_error;
if( p->nTag>0 ) goto manifest_syntax_error;
if( p->zTicketUuid!=0 ) goto manifest_syntax_error;
if( p->zWikiTitle==0 ) goto manifest_syntax_error;
if( p->zAttachName ) goto manifest_syntax_error;
if( !seenZ ) goto manifest_syntax_error;
p->type = CFTYPE_WIKI;
}else if( p->nTag>0 ){
if( p->rDate<=0.0 ) goto manifest_syntax_error;
if( p->nParent>0 ) goto manifest_syntax_error;
if( p->zWikiTitle ) goto manifest_syntax_error;
if( p->zTicketUuid ) goto manifest_syntax_error;
if( p->zAttachName ) goto manifest_syntax_error;
if( !seenZ ) goto manifest_syntax_error;
p->type = CFTYPE_CONTROL;
}else if( p->zAttachName ){
if( p->nCChild>0 ) goto manifest_syntax_error;
if( p->rDate<=0.0 ) goto manifest_syntax_error;
if( p->zTicketUuid ) goto manifest_syntax_error;
if( p->zWikiTitle ) goto manifest_syntax_error;
if( !seenZ ) goto manifest_syntax_error;
p->type = CFTYPE_ATTACHMENT;
}else{
if( p->nCChild>0 ) goto manifest_syntax_error;
if( p->rDate<=0.0 ) goto manifest_syntax_error;
if( p->nParent>0 ) goto manifest_syntax_error;
if( p->nField>0 ) goto manifest_syntax_error;
if( p->zTicketUuid ) goto manifest_syntax_error;
if( p->zWikiTitle ) goto manifest_syntax_error;
if( p->zTicketUuid ) goto manifest_syntax_error;
p->type = CFTYPE_MANIFEST;
}
md5sum_init();
return 1;
manifest_syntax_error:
/*fprintf(stderr, "Manifest error on line %i\n", lineNo);fflush(stderr);*/
|
| ︙ | ︙ | |||
962 963 964 965 966 967 968 969 970 971 972 973 974 975 | ** any key: ** ** * Manifest ** * Control ** * Wiki Page ** * Ticket Change ** * Cluster ** ** If the input is a control artifact, then make appropriate entries ** in the auxiliary tables of the database in order to crosslink the ** artifact. ** ** If global variable g.xlinkClusterOnly is true, then ignore all ** control artifacts other than clusters. | > > | 1004 1005 1006 1007 1008 1009 1010 1011 1012 1013 1014 1015 1016 1017 1018 1019 | ** any key: ** ** * Manifest ** * Control ** * Wiki Page ** * Ticket Change ** * Cluster ** * Attachment ** * Event ** ** If the input is a control artifact, then make appropriate entries ** in the auxiliary tables of the database in order to crosslink the ** artifact. ** ** If global variable g.xlinkClusterOnly is true, then ignore all ** control artifacts other than clusters. |
| ︙ | ︙ | |||
1041 1042 1043 1044 1045 1046 1047 |
int mid;
mid = uuid_to_rid(m.azCChild[i], 1);
if( mid>0 ){
db_multi_exec("DELETE FROM unclustered WHERE rid=%d", mid);
}
}
}
| | > > > | 1085 1086 1087 1088 1089 1090 1091 1092 1093 1094 1095 1096 1097 1098 1099 1100 1101 1102 |
int mid;
mid = uuid_to_rid(m.azCChild[i], 1);
if( mid>0 ){
db_multi_exec("DELETE FROM unclustered WHERE rid=%d", mid);
}
}
}
if( m.type==CFTYPE_CONTROL
|| m.type==CFTYPE_MANIFEST
|| m.type==CFTYPE_EVENT
){
for(i=0; i<m.nTag; i++){
int tid;
int type;
if( m.aTag[i].zUuid ){
tid = uuid_to_rid(m.aTag[i].zUuid, 1);
}else{
tid = rid;
|
| ︙ | ︙ | |||
1107 1108 1109 1110 1111 1112 1113 1114 1115 1116 1117 1118 1119 1120 |
m.rDate, rid, m.zUser, zComment,
TAG_BGCOLOR, rid,
TAG_BGCOLOR, rid,
TAG_USER, rid,
TAG_COMMENT, rid
);
free(zComment);
}
if( m.type==CFTYPE_TICKET ){
char *zTag;
assert( manifest_crosslink_busy==1 );
zTag = mprintf("tkt-%s", m.zTicketUuid);
tag_insert(zTag, 1, 0, rid, m.rDate, rid);
| > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 1154 1155 1156 1157 1158 1159 1160 1161 1162 1163 1164 1165 1166 1167 1168 1169 1170 1171 1172 1173 1174 1175 1176 1177 1178 1179 1180 1181 1182 1183 1184 1185 1186 1187 1188 1189 1190 1191 1192 1193 1194 1195 1196 1197 1198 1199 1200 1201 1202 1203 1204 1205 1206 1207 1208 1209 1210 1211 1212 |
m.rDate, rid, m.zUser, zComment,
TAG_BGCOLOR, rid,
TAG_BGCOLOR, rid,
TAG_USER, rid,
TAG_COMMENT, rid
);
free(zComment);
}
if( m.type==CFTYPE_EVENT ){
char *zTag = mprintf("event-%s", m.zEventId);
int tagid = tag_findid(zTag, 1);
int prior, subsequent;
int nWiki;
char zLength[40];
while( isspace(m.zWiki[0]) ) m.zWiki++;
nWiki = strlen(m.zWiki);
sqlite3_snprintf(sizeof(zLength), zLength, "%d", nWiki);
tag_insert(zTag, 1, zLength, rid, m.rDate, rid);
free(zTag);
prior = db_int(0,
"SELECT rid FROM tagxref"
" WHERE tagid=%d AND mtime<%.17g"
" ORDER BY mtime DESC",
tagid, m.rDate
);
if( prior ){
content_deltify(prior, rid, 0);
db_multi_exec(
"DELETE FROM event"
" WHERE type='e'"
" AND tagid=%d"
" AND objid IN (SELECT rid FROM tagxref WHERE tagid=%d)",
tagid, tagid
);
}
subsequent = db_int(0,
"SELECT rid FROM tagxref"
" WHERE tagid=%d AND mtime>%.17g"
" ORDER BY mtime",
tagid, m.rDate
);
if( subsequent ){
content_deltify(rid, subsequent, 0);
}else{
db_multi_exec(
"REPLACE INTO event(type,mtime,objid,tagid,user,comment,bgcolor)"
"VALUES('e',%.17g,%d,%d,%Q,%Q,"
" (SELECT value FROM tagxref WHERE tagid=%d AND rid=%d));",
m.rEventDate, rid, tagid, m.zUser, m.zComment,
TAG_BGCOLOR, rid
);
}
}
if( m.type==CFTYPE_TICKET ){
char *zTag;
assert( manifest_crosslink_busy==1 );
zTag = mprintf("tkt-%s", m.zTicketUuid);
tag_insert(zTag, 1, 0, rid, m.rDate, rid);
|
| ︙ | ︙ |
Changes to src/printf.c.
| ︙ | ︙ | |||
42 43 44 45 46 47 48 |
NULL pointers replaced by SQL NULL. %Q */
#define etPOINTER 15 /* The %p conversion */
#define etHTMLIZE 16 /* Make text safe for HTML */
#define etHTTPIZE 17 /* Make text safe for HTTP. "/" encoded as %2f */
#define etURLIZE 18 /* Make text safe for HTTP. "/" not encoded */
#define etFOSSILIZE 19 /* The fossil header encoding format. */
#define etPATH 20 /* Path type */
| | | | | 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 |
NULL pointers replaced by SQL NULL. %Q */
#define etPOINTER 15 /* The %p conversion */
#define etHTMLIZE 16 /* Make text safe for HTML */
#define etHTTPIZE 17 /* Make text safe for HTTP. "/" encoded as %2f */
#define etURLIZE 18 /* Make text safe for HTTP. "/" not encoded */
#define etFOSSILIZE 19 /* The fossil header encoding format. */
#define etPATH 20 /* Path type */
#define etWIKISTR 21 /* Wiki text rendered from a char*: %w */
#define etWIKIBLOB 22 /* Wiki text rendered from a Blob*: %W */
#define etSTRINGID 23 /* String with length limit for a UUID prefix: %S */
/*
** An "etByte" is an 8-bit unsigned value.
*/
typedef unsigned char etByte;
|
| ︙ | ︙ |
Changes to src/rebuild.c.
| ︙ | ︙ | |||
119 120 121 122 123 124 125 |
static void rebuild_step(int rid, int size, Blob *pBase){
static Stmt q1;
Bag children;
Blob copy;
Blob *pUse;
int nChild, i, cid;
| > > | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | > | | | < < < < < < | | | | | | | | > | | > > > > > > > | | | | | > | 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 |
static void rebuild_step(int rid, int size, Blob *pBase){
static Stmt q1;
Bag children;
Blob copy;
Blob *pUse;
int nChild, i, cid;
while( rid>0 ){
/* Fix up the "blob.size" field if needed. */
if( size!=blob_size(pBase) ){
db_multi_exec(
"UPDATE blob SET size=%d WHERE rid=%d", blob_size(pBase), rid
);
}
/* Find all children of artifact rid */
db_static_prepare(&q1, "SELECT rid FROM delta WHERE srcid=:rid");
db_bind_int(&q1, ":rid", rid);
bag_init(&children);
while( db_step(&q1)==SQLITE_ROW ){
int cid = db_column_int(&q1, 0);
if( !bag_find(&bagDone, cid) ){
bag_insert(&children, cid);
}
}
nChild = bag_count(&children);
db_reset(&q1);
/* Crosslink the artifact */
if( nChild==0 ){
pUse = pBase;
}else{
blob_copy(©, pBase);
pUse = ©
}
if( zFNameFormat==0 ){
/* We are doing "fossil rebuild" */
manifest_crosslink(rid, pUse);
}else{
/* We are doing "fossil deconstruct" */
char *zUuid = db_text(0, "SELECT uuid FROM blob WHERE rid=%d", rid);
char *zFile = mprintf(zFNameFormat, zUuid, zUuid+prefixLength);
blob_write_to_file(pUse,zFile);
free(zFile);
free(zUuid);
}
blob_reset(pUse);
rebuild_step_done(rid);
/* Call all children recursively */
rid = 0;
for(cid=bag_first(&children), i=1; cid; cid=bag_next(&children, cid), i++){
Stmt q2;
int sz;
db_prepare(&q2, "SELECT content, size FROM blob WHERE rid=%d", cid);
if( db_step(&q2)==SQLITE_ROW && (sz = db_column_int(&q2,1))>=0 ){
Blob delta, next;
db_ephemeral_blob(&q2, 0, &delta);
blob_uncompress(&delta, &delta);
blob_delta_apply(pBase, &delta, &next);
blob_reset(&delta);
db_finalize(&q2);
if( i<nChild ){
rebuild_step(cid, sz, &next);
}else{
/* Tail recursion */
rid = cid;
size = sz;
blob_reset(pBase);
*pBase = next;
}
}else{
db_finalize(&q2);
blob_reset(pBase);
}
}
bag_clear(&children);
}
}
/*
** Check to see if the "sym-trunk" tag exists. If not, create it
** and attach it to the very first check-in.
*/
static void rebuild_tag_trunk(void){
|
| ︙ | ︙ |
Changes to src/sqlite3.c.
| ︙ | ︙ | |||
648 649 650 651 652 653 654 | ** ** See also: [sqlite3_libversion()], ** [sqlite3_libversion_number()], [sqlite3_sourceid()], ** [sqlite_version()] and [sqlite_source_id()]. */ #define SQLITE_VERSION "3.7.3" #define SQLITE_VERSION_NUMBER 3007003 | | | 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 | ** ** See also: [sqlite3_libversion()], ** [sqlite3_libversion_number()], [sqlite3_sourceid()], ** [sqlite_version()] and [sqlite_source_id()]. */ #define SQLITE_VERSION "3.7.3" #define SQLITE_VERSION_NUMBER 3007003 #define SQLITE_SOURCE_ID "2010-10-04 23:55:51 ece641eb8951c6314cedbdb3243f91cb199c3239" /* ** CAPI3REF: Run-Time Library Version Numbers ** KEYWORDS: sqlite3_version, sqlite3_sourceid ** ** These interfaces provide the same information as the [SQLITE_VERSION], ** [SQLITE_VERSION_NUMBER], and [SQLITE_SOURCE_ID] C preprocessor macros |
| ︙ | ︙ | |||
10568 10569 10570 10571 10572 10573 10574 | # define sqlite3Tolower(x) tolower((unsigned char)(x)) #endif /* ** Internal function prototypes */ SQLITE_PRIVATE int sqlite3StrICmp(const char *, const char *); | < | 10568 10569 10570 10571 10572 10573 10574 10575 10576 10577 10578 10579 10580 10581 | # define sqlite3Tolower(x) tolower((unsigned char)(x)) #endif /* ** Internal function prototypes */ SQLITE_PRIVATE int sqlite3StrICmp(const char *, const char *); SQLITE_PRIVATE int sqlite3Strlen30(const char*); #define sqlite3StrNICmp sqlite3_strnicmp SQLITE_PRIVATE int sqlite3MallocInit(void); SQLITE_PRIVATE void sqlite3MallocEnd(void); SQLITE_PRIVATE void *sqlite3Malloc(int); SQLITE_PRIVATE void *sqlite3MallocZero(int); |
| ︙ | ︙ | |||
10888 10889 10890 10891 10892 10893 10894 | SQLITE_PRIVATE void sqlite3Detach(Parse*, Expr*); SQLITE_PRIVATE int sqlite3FixInit(DbFixer*, Parse*, int, const char*, const Token*); SQLITE_PRIVATE int sqlite3FixSrcList(DbFixer*, SrcList*); SQLITE_PRIVATE int sqlite3FixSelect(DbFixer*, Select*); SQLITE_PRIVATE int sqlite3FixExpr(DbFixer*, Expr*); SQLITE_PRIVATE int sqlite3FixExprList(DbFixer*, ExprList*); SQLITE_PRIVATE int sqlite3FixTriggerStep(DbFixer*, TriggerStep*); | | < | 10887 10888 10889 10890 10891 10892 10893 10894 10895 10896 10897 10898 10899 10900 10901 10902 | SQLITE_PRIVATE void sqlite3Detach(Parse*, Expr*); SQLITE_PRIVATE int sqlite3FixInit(DbFixer*, Parse*, int, const char*, const Token*); SQLITE_PRIVATE int sqlite3FixSrcList(DbFixer*, SrcList*); SQLITE_PRIVATE int sqlite3FixSelect(DbFixer*, Select*); SQLITE_PRIVATE int sqlite3FixExpr(DbFixer*, Expr*); SQLITE_PRIVATE int sqlite3FixExprList(DbFixer*, ExprList*); SQLITE_PRIVATE int sqlite3FixTriggerStep(DbFixer*, TriggerStep*); SQLITE_PRIVATE int sqlite3AtoF(const char *z, double*, int, u8); SQLITE_PRIVATE int sqlite3GetInt32(const char *, int*); SQLITE_PRIVATE int sqlite3Utf16ByteLen(const void *pData, int nChar); SQLITE_PRIVATE int sqlite3Utf8CharLen(const char *pData, int nByte); SQLITE_PRIVATE int sqlite3Utf8Read(const u8*, const u8**); /* ** Routines to read and write variable-length integers. These used to ** be defined locally, but now we use the varint routines in the util.c |
| ︙ | ︙ | |||
10936 10937 10938 10939 10940 10941 10942 | SQLITE_PRIVATE const char *sqlite3IndexAffinityStr(Vdbe *, Index *); SQLITE_PRIVATE void sqlite3TableAffinityStr(Vdbe *, Table *); SQLITE_PRIVATE char sqlite3CompareAffinity(Expr *pExpr, char aff2); SQLITE_PRIVATE int sqlite3IndexAffinityOk(Expr *pExpr, char idx_affinity); SQLITE_PRIVATE char sqlite3ExprAffinity(Expr *pExpr); | | | 10934 10935 10936 10937 10938 10939 10940 10941 10942 10943 10944 10945 10946 10947 10948 | SQLITE_PRIVATE const char *sqlite3IndexAffinityStr(Vdbe *, Index *); SQLITE_PRIVATE void sqlite3TableAffinityStr(Vdbe *, Table *); SQLITE_PRIVATE char sqlite3CompareAffinity(Expr *pExpr, char aff2); SQLITE_PRIVATE int sqlite3IndexAffinityOk(Expr *pExpr, char idx_affinity); SQLITE_PRIVATE char sqlite3ExprAffinity(Expr *pExpr); SQLITE_PRIVATE int sqlite3Atoi64(const char*, i64*, int, u8); SQLITE_PRIVATE void sqlite3Error(sqlite3*, int, const char*,...); SQLITE_PRIVATE void *sqlite3HexToBlob(sqlite3*, const char *z, int n); SQLITE_PRIVATE int sqlite3TwoPartName(Parse *, Token *, Token *, Token **); SQLITE_PRIVATE const char *sqlite3ErrStr(int); SQLITE_PRIVATE int sqlite3ReadSchema(Parse *pParse); SQLITE_PRIVATE CollSeq *sqlite3FindCollSeq(sqlite3*,u8 enc, const char*,int); SQLITE_PRIVATE CollSeq *sqlite3LocateCollSeq(Parse *pParse, const char*zName); |
| ︙ | ︙ | |||
12635 12636 12637 12638 12639 12640 12641 |
cnt++;
}while( nextC );
end_getDigits:
va_end(ap);
return cnt;
}
| < < < < < < | 12633 12634 12635 12636 12637 12638 12639 12640 12641 12642 12643 12644 12645 12646 |
cnt++;
}while( nextC );
end_getDigits:
va_end(ap);
return cnt;
}
/*
** Parse a timezone extension on the end of a date-time.
** The extension is of the form:
**
** (+/-)HH:MM
**
** Or the "zulu" notation:
|
| ︙ | ︙ | |||
12842 12843 12844 12845 12846 12847 12848 |
** as there is a year and date.
*/
static int parseDateOrTime(
sqlite3_context *context,
const char *zDate,
DateTime *p
){
| | | < < | 12834 12835 12836 12837 12838 12839 12840 12841 12842 12843 12844 12845 12846 12847 12848 12849 12850 12851 12852 12853 12854 12855 12856 |
** as there is a year and date.
*/
static int parseDateOrTime(
sqlite3_context *context,
const char *zDate,
DateTime *p
){
double r;
if( parseYyyyMmDd(zDate,p)==0 ){
return 0;
}else if( parseHhMmSs(zDate, p)==0 ){
return 0;
}else if( sqlite3StrICmp(zDate,"now")==0){
setDateTimeToCurrent(context, p);
return 0;
}else if( sqlite3AtoF(zDate, &r, sqlite3Strlen30(zDate), SQLITE_UTF8) ){
p->iJD = (sqlite3_int64)(r*86400000.0 + 0.5);
p->validJD = 1;
return 0;
}
return 1;
}
|
| ︙ | ︙ | |||
13073 13074 13075 13076 13077 13078 13079 |
/*
** weekday N
**
** Move the date to the same time on the next occurrence of
** weekday N where 0==Sunday, 1==Monday, and so forth. If the
** date is already on the appropriate weekday, this is a no-op.
*/
| | > | | 13063 13064 13065 13066 13067 13068 13069 13070 13071 13072 13073 13074 13075 13076 13077 13078 13079 |
/*
** weekday N
**
** Move the date to the same time on the next occurrence of
** weekday N where 0==Sunday, 1==Monday, and so forth. If the
** date is already on the appropriate weekday, this is a no-op.
*/
if( strncmp(z, "weekday ", 8)==0
&& sqlite3AtoF(&z[8], &r, sqlite3Strlen30(&z[8]), SQLITE_UTF8)
&& (n=(int)r)==r && n>=0 && r<7 ){
sqlite3_int64 Z;
computeYMD_HMS(p);
p->validTZ = 0;
p->validJD = 0;
computeJD(p);
Z = ((p->iJD + 129600000)/86400000) % 7;
if( Z>n ) Z -= 7;
|
| ︙ | ︙ | |||
13129 13130 13131 13132 13133 13134 13135 |
case '4':
case '5':
case '6':
case '7':
case '8':
case '9': {
double rRounder;
| > > | | > | 13120 13121 13122 13123 13124 13125 13126 13127 13128 13129 13130 13131 13132 13133 13134 13135 13136 13137 13138 |
case '4':
case '5':
case '6':
case '7':
case '8':
case '9': {
double rRounder;
for(n=1; z[n] && z[n]!=':' && !sqlite3Isspace(z[n]); n++){}
if( !sqlite3AtoF(z, &r, n, SQLITE_UTF8) ){
rc = 1;
break;
}
if( z[n]==':' ){
/* A modifier of the form (+|-)HH:MM:SS.FFF adds (or subtracts) the
** specified number of hours, minutes, seconds, and fractional seconds
** to the time. The ".FFF" may be omitted. The ":SS.FFF" may be
** omitted.
*/
const char *z2 = z;
|
| ︙ | ︙ | |||
17500 17501 17502 17503 17504 17505 17506 |
if( n<0 ) return priorLimit;
if( n>0 ){
sqlite3MemoryAlarm(softHeapLimitEnforcer, 0, n);
}else{
sqlite3MemoryAlarm(0, 0, 0);
}
excess = sqlite3_memory_used() - n;
| | | 17494 17495 17496 17497 17498 17499 17500 17501 17502 17503 17504 17505 17506 17507 17508 |
if( n<0 ) return priorLimit;
if( n>0 ){
sqlite3MemoryAlarm(softHeapLimitEnforcer, 0, n);
}else{
sqlite3MemoryAlarm(0, 0, 0);
}
excess = sqlite3_memory_used() - n;
if( excess>0 ) sqlite3_release_memory((int)(excess & 0x7fffffff));
return priorLimit;
}
SQLITE_API void sqlite3_soft_heap_limit(int n){
if( n<0 ) n = 0;
sqlite3_soft_heap_limit64(n);
}
|
| ︙ | ︙ | |||
20094 20095 20096 20097 20098 20099 20100 |
a = (unsigned char *)zLeft;
b = (unsigned char *)zRight;
while( N-- > 0 && *a!=0 && UpperToLower[*a]==UpperToLower[*b]){ a++; b++; }
return N<0 ? 0 : UpperToLower[*a] - UpperToLower[*b];
}
/*
| < | > | > > | | < < | > > > > > | > > > > | > < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < | | | | | | > > > > > | > > | | > | | | > | > | | | | > | > > | | | | > > > > > > > | 20088 20089 20090 20091 20092 20093 20094 20095 20096 20097 20098 20099 20100 20101 20102 20103 20104 20105 20106 20107 20108 20109 20110 20111 20112 20113 20114 20115 20116 20117 20118 20119 20120 20121 20122 20123 20124 20125 20126 20127 20128 20129 20130 20131 20132 20133 20134 20135 20136 20137 20138 20139 20140 20141 20142 20143 20144 20145 20146 20147 20148 20149 20150 20151 20152 20153 20154 20155 20156 20157 20158 20159 20160 20161 20162 20163 20164 20165 20166 20167 20168 20169 20170 20171 20172 20173 20174 20175 20176 20177 20178 20179 20180 20181 20182 20183 20184 20185 20186 20187 20188 20189 20190 20191 20192 20193 20194 20195 20196 20197 20198 20199 20200 20201 20202 20203 20204 20205 20206 |
a = (unsigned char *)zLeft;
b = (unsigned char *)zRight;
while( N-- > 0 && *a!=0 && UpperToLower[*a]==UpperToLower[*b]){ a++; b++; }
return N<0 ? 0 : UpperToLower[*a] - UpperToLower[*b];
}
/*
** The string z[] is an text representation of a real number.
** Convert this string to a double and write it into *pResult.
**
** The string z[] is length bytes in length (bytes, not characters) and
** uses the encoding enc. The string is not necessarily zero-terminated.
**
** Return TRUE if the result is a valid real number (or integer) and FALSE
** if the string is empty or contains extraneous text. Valid numbers
** are in one of these formats:
**
** [+-]digits[E[+-]digits]
** [+-]digits.[digits][E[+-]digits]
** [+-].digits[E[+-]digits]
**
** Leading and trailing whitespace is ignored for the purpose of determining
** validity.
**
** If some prefix of the input string is a valid number, this routine
** returns FALSE but it still converts the prefix and writes the result
** into *pResult.
*/
SQLITE_PRIVATE int sqlite3AtoF(const char *z, double *pResult, int length, u8 enc){
#ifndef SQLITE_OMIT_FLOATING_POINT
int incr = (enc==SQLITE_UTF8?1:2);
const char *zEnd = z + length;
/* sign * significand * (10 ^ (esign * exponent)) */
int sign = 1; /* sign of significand */
i64 s = 0; /* significand */
int d = 0; /* adjust exponent for shifting decimal point */
int esign = 1; /* sign of exponent */
int e = 0; /* exponent */
int eValid = 1; /* True exponent is either not used or is well-formed */
double result;
int nDigits = 0;
*pResult = 0.0; /* Default return value, in case of an error */
if( enc==SQLITE_UTF16BE ) z++;
/* skip leading spaces */
while( z<zEnd && sqlite3Isspace(*z) ) z+=incr;
if( z>=zEnd ) return 0;
/* get sign of significand */
if( *z=='-' ){
sign = -1;
z+=incr;
}else if( *z=='+' ){
z+=incr;
}
/* skip leading zeroes */
while( z<zEnd && z[0]=='0' ) z+=incr, nDigits++;
/* copy max significant digits to significand */
while( z<zEnd && sqlite3Isdigit(*z) && s<((LARGEST_INT64-9)/10) ){
s = s*10 + (*z - '0');
z+=incr, nDigits++;
}
/* skip non-significant significand digits
** (increase exponent by d to shift decimal left) */
while( z<zEnd && sqlite3Isdigit(*z) ) z+=incr, nDigits++, d++;
if( z>=zEnd ) goto do_atof_calc;
/* if decimal point is present */
if( *z=='.' ){
z+=incr;
/* copy digits from after decimal to significand
** (decrease exponent by d to shift decimal right) */
while( z<zEnd && sqlite3Isdigit(*z) && s<((LARGEST_INT64-9)/10) ){
s = s*10 + (*z - '0');
z+=incr, nDigits++, d--;
}
/* skip non-significant digits */
while( z<zEnd && sqlite3Isdigit(*z) ) z+=incr, nDigits++;
}
if( z>=zEnd ) goto do_atof_calc;
/* if exponent is present */
if( *z=='e' || *z=='E' ){
z+=incr;
eValid = 0;
if( z>=zEnd ) goto do_atof_calc;
/* get sign of exponent */
if( *z=='-' ){
esign = -1;
z+=incr;
}else if( *z=='+' ){
z+=incr;
}
/* copy digits to exponent */
while( z<zEnd && sqlite3Isdigit(*z) ){
e = e*10 + (*z - '0');
z+=incr;
eValid = 1;
}
}
/* skip trailing spaces */
if( nDigits && eValid ){
while( z<zEnd && sqlite3Isspace(*z) ) z+=incr;
}
do_atof_calc:
/* adjust exponent by d, and update sign */
e = (e*esign) + d;
if( e<0 ) {
esign = -1;
e *= -1;
} else {
esign = 1;
|
| ︙ | ︙ | |||
20267 20268 20269 20270 20271 20272 20273 |
result = (double)s;
}
}
/* store the result */
*pResult = result;
| | | | > | | > | > | > > > | | | > > > > | | < < > > | | > | | > > > | > | < | < < > | | | | | | | | | < < | < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < | < < < < < < | 20251 20252 20253 20254 20255 20256 20257 20258 20259 20260 20261 20262 20263 20264 20265 20266 20267 20268 20269 20270 20271 20272 20273 20274 20275 20276 20277 20278 20279 20280 20281 20282 20283 20284 20285 20286 20287 20288 20289 20290 20291 20292 20293 20294 20295 20296 20297 20298 20299 20300 20301 20302 20303 20304 20305 20306 20307 20308 20309 20310 20311 20312 20313 20314 20315 20316 20317 20318 20319 20320 20321 20322 20323 20324 20325 20326 20327 20328 20329 20330 20331 20332 20333 20334 20335 20336 20337 20338 20339 20340 20341 20342 20343 20344 20345 20346 20347 20348 20349 20350 20351 20352 20353 20354 20355 20356 20357 20358 |
result = (double)s;
}
}
/* store the result */
*pResult = result;
/* return true if number and no extra non-whitespace chracters after */
return z>=zEnd && nDigits>0 && eValid;
#else
return !sqlite3Atoi64(z, pResult, length, enc);
#endif /* SQLITE_OMIT_FLOATING_POINT */
}
/*
** Compare the 19-character string zNum against the text representation
** value 2^63: 9223372036854775808. Return negative, zero, or positive
** if zNum is less than, equal to, or greater than the string.
** Note that zNum must contain exactly 19 characters.
**
** Unlike memcmp() this routine is guaranteed to return the difference
** in the values of the last digit if the only difference is in the
** last digit. So, for example,
**
** compare2pow63("9223372036854775800", 1)
**
** will return -8.
*/
static int compare2pow63(const char *zNum, int incr){
int c = 0;
int i;
/* 012345678901234567 */
const char *pow63 = "922337203685477580";
for(i=0; c==0 && i<18; i++){
c = (zNum[i*incr]-pow63[i])*10;
}
if( c==0 ){
c = zNum[18*incr] - '8';
testcase( c==(-1) );
testcase( c==0 );
testcase( c==(+1) );
}
return c;
}
/*
** Convert zNum to a 64-bit signed integer and write
** the value of the integer into *pNum.
** If zNum is exactly 9223372036854665808, return 2.
** This is a special case as the context will determine
** if it is too big (used as a negative).
** If zNum is not an integer or is an integer that
** is too large to be expressed with 64 bits,
** then return 1. Otherwise return 0.
**
** length is the number of bytes in the string (bytes, not characters).
** The string is not necessarily zero-terminated. The encoding is
** given by enc.
*/
SQLITE_PRIVATE int sqlite3Atoi64(const char *zNum, i64 *pNum, int length, u8 enc){
int incr = (enc==SQLITE_UTF8?1:2);
i64 v = 0;
int neg = 0; /* assume positive */
int i;
int c = 0;
const char *zStart;
const char *zEnd = zNum + length;
if( enc==SQLITE_UTF16BE ) zNum++;
while( zNum<zEnd && sqlite3Isspace(*zNum) ) zNum+=incr;
if( zNum>=zEnd ) goto do_atoi_calc;
if( *zNum=='-' ){
neg = 1;
zNum+=incr;
}else if( *zNum=='+' ){
zNum+=incr;
}
do_atoi_calc:
zStart = zNum;
while( zNum<zEnd && zNum[0]=='0' ){ zNum+=incr; } /* Skip leading zeros. */
for(i=0; &zNum[i]<zEnd && (c=zNum[i])>='0' && c<='9'; i+=incr){
v = v*10 + c - '0';
}
*pNum = neg ? -v : v;
testcase( i==18 );
testcase( i==19 );
testcase( i==20 );
if( (c!=0 && &zNum[i]<zEnd) || (i==0 && zStart==zNum) || i>19*incr ){
/* zNum is empty or contains non-numeric text or is longer
** than 19 digits (thus guaranteeing that it is too large) */
return 1;
}else if( i<19*incr ){
/* Less than 19 digits, so we know that it fits in 64 bits */
return 0;
}else{
/* 19-digit numbers must be no larger than 9223372036854775807 if positive
** or 9223372036854775808 if negative. Note that 9223372036854665808
** is 2^63. Return 1 if to large */
c=compare2pow63(zNum, incr);
if( c==0 && neg==0 ) return 2; /* too big, exactly 9223372036854665808 */
return c<neg ? 0 : 1;
}
}
/*
** If zNum represents an integer that will fit in 32-bits, then set
** *pValue to that integer and return true. Otherwise return false.
**
|
| ︙ | ︙ | |||
54147 54148 54149 54150 54151 54152 54153 |
flags = pMem->flags;
if( flags & MEM_Int ){
return pMem->u.i;
}else if( flags & MEM_Real ){
return doubleToInt64(pMem->r);
}else if( flags & (MEM_Str|MEM_Blob) ){
i64 value;
| < < < < < | > | | 54103 54104 54105 54106 54107 54108 54109 54110 54111 54112 54113 54114 54115 54116 54117 54118 54119 |
flags = pMem->flags;
if( flags & MEM_Int ){
return pMem->u.i;
}else if( flags & MEM_Real ){
return doubleToInt64(pMem->r);
}else if( flags & (MEM_Str|MEM_Blob) ){
i64 value;
assert( pMem->z || pMem->n==0 );
testcase( pMem->z==0 );
sqlite3Atoi64(pMem->z, &value, pMem->n, pMem->enc);
return value;
}else{
return 0;
}
}
/*
|
| ︙ | ︙ | |||
54183 54184 54185 54186 54187 54188 54189 |
pMem->flags |= MEM_Str;
if( sqlite3VdbeChangeEncoding(pMem, SQLITE_UTF8)
|| sqlite3VdbeMemNulTerminate(pMem) ){
/* (double)0 In case of SQLITE_OMIT_FLOATING_POINT... */
return (double)0;
}
assert( pMem->z );
| | | 54135 54136 54137 54138 54139 54140 54141 54142 54143 54144 54145 54146 54147 54148 54149 |
pMem->flags |= MEM_Str;
if( sqlite3VdbeChangeEncoding(pMem, SQLITE_UTF8)
|| sqlite3VdbeMemNulTerminate(pMem) ){
/* (double)0 In case of SQLITE_OMIT_FLOATING_POINT... */
return (double)0;
}
assert( pMem->z );
sqlite3AtoF(pMem->z, &val, pMem->n, SQLITE_UTF8);
return val;
}else{
/* (double)0 In case of SQLITE_OMIT_FLOATING_POINT... */
return (double)0;
}
}
|
| ︙ | ︙ | |||
54256 54257 54258 54259 54260 54261 54262 |
** Invalidate any prior representations.
**
** Every effort is made to force the conversion, even if the input
** is a string that does not look completely like a number. Convert
** as much of the string as we can and ignore the rest.
*/
SQLITE_PRIVATE int sqlite3VdbeMemNumerify(Mem *pMem){
| < | | | < < < < | | | | | | | > > > | 54208 54209 54210 54211 54212 54213 54214 54215 54216 54217 54218 54219 54220 54221 54222 54223 54224 54225 54226 54227 54228 54229 54230 54231 54232 54233 54234 |
** Invalidate any prior representations.
**
** Every effort is made to force the conversion, even if the input
** is a string that does not look completely like a number. Convert
** as much of the string as we can and ignore the rest.
*/
SQLITE_PRIVATE int sqlite3VdbeMemNumerify(Mem *pMem){
if( (pMem->flags & (MEM_Int|MEM_Real|MEM_Null))==0 ){
assert( (pMem->flags & (MEM_Blob|MEM_Str))!=0 );
assert( pMem->db==0 || sqlite3_mutex_held(pMem->db->mutex) );
if( 0==sqlite3Atoi64(pMem->z, &pMem->u.i, pMem->n, pMem->enc) ){
MemSetTypeFlag(pMem, MEM_Int);
}else{
pMem->r = sqlite3VdbeRealValue(pMem);
MemSetTypeFlag(pMem, MEM_Real);
sqlite3VdbeIntegerAffinity(pMem);
}
}
assert( (pMem->flags & (MEM_Int|MEM_Real|MEM_Null))!=0 );
pMem->flags &= ~(MEM_Str|MEM_Blob);
return SQLITE_OK;
}
/*
** Delete any previous value and set the value stored in *pMem to NULL.
*/
SQLITE_PRIVATE void sqlite3VdbeMemSetNull(Mem *pMem){
|
| ︙ | ︙ | |||
54813 54814 54815 54816 54817 54818 54819 54820 54821 54822 54823 54824 54825 54826 54827 54828 54829 54830 54831 54832 54833 54834 54835 54836 54837 54838 54839 54840 |
u8 enc, /* Encoding to use */
u8 affinity, /* Affinity to use */
sqlite3_value **ppVal /* Write the new value here */
){
int op;
char *zVal = 0;
sqlite3_value *pVal = 0;
if( !pExpr ){
*ppVal = 0;
return SQLITE_OK;
}
op = pExpr->op;
/* op can only be TK_REGISTER if we have compiled with SQLITE_ENABLE_STAT2.
** The ifdef here is to enable us to achieve 100% branch test coverage even
** when SQLITE_ENABLE_STAT2 is omitted.
*/
#ifdef SQLITE_ENABLE_STAT2
if( op==TK_REGISTER ) op = pExpr->op2;
#else
if( NEVER(op==TK_REGISTER) ) op = pExpr->op2;
#endif
if( op==TK_STRING || op==TK_FLOAT || op==TK_INTEGER ){
pVal = sqlite3ValueNew(db);
if( pVal==0 ) goto no_mem;
if( ExprHasProperty(pExpr, EP_IntValue) ){
| > > > > > > > > > > > > > | | > > > > | 54763 54764 54765 54766 54767 54768 54769 54770 54771 54772 54773 54774 54775 54776 54777 54778 54779 54780 54781 54782 54783 54784 54785 54786 54787 54788 54789 54790 54791 54792 54793 54794 54795 54796 54797 54798 54799 54800 54801 54802 54803 54804 54805 54806 54807 54808 54809 54810 54811 54812 54813 54814 54815 54816 54817 54818 54819 54820 54821 54822 54823 54824 54825 54826 54827 54828 54829 54830 54831 54832 54833 54834 |
u8 enc, /* Encoding to use */
u8 affinity, /* Affinity to use */
sqlite3_value **ppVal /* Write the new value here */
){
int op;
char *zVal = 0;
sqlite3_value *pVal = 0;
int negInt = 1;
const char *zNeg = "";
if( !pExpr ){
*ppVal = 0;
return SQLITE_OK;
}
op = pExpr->op;
/* op can only be TK_REGISTER if we have compiled with SQLITE_ENABLE_STAT2.
** The ifdef here is to enable us to achieve 100% branch test coverage even
** when SQLITE_ENABLE_STAT2 is omitted.
*/
#ifdef SQLITE_ENABLE_STAT2
if( op==TK_REGISTER ) op = pExpr->op2;
#else
if( NEVER(op==TK_REGISTER) ) op = pExpr->op2;
#endif
/* Handle negative integers in a single step. This is needed in the
** case when the value is -9223372036854775808.
*/
if( op==TK_UMINUS
&& (pExpr->pLeft->op==TK_INTEGER || pExpr->pLeft->op==TK_FLOAT) ){
pExpr = pExpr->pLeft;
op = pExpr->op;
negInt = -1;
zNeg = "-";
}
if( op==TK_STRING || op==TK_FLOAT || op==TK_INTEGER ){
pVal = sqlite3ValueNew(db);
if( pVal==0 ) goto no_mem;
if( ExprHasProperty(pExpr, EP_IntValue) ){
sqlite3VdbeMemSetInt64(pVal, (i64)pExpr->u.iValue*negInt);
}else{
zVal = sqlite3MPrintf(db, "%s%s", zNeg, pExpr->u.zToken);
if( zVal==0 ) goto no_mem;
sqlite3ValueSetStr(pVal, -1, zVal, SQLITE_UTF8, SQLITE_DYNAMIC);
if( op==TK_FLOAT ) pVal->type = SQLITE_FLOAT;
}
if( (op==TK_INTEGER || op==TK_FLOAT ) && affinity==SQLITE_AFF_NONE ){
sqlite3ValueApplyAffinity(pVal, SQLITE_AFF_NUMERIC, SQLITE_UTF8);
}else{
sqlite3ValueApplyAffinity(pVal, affinity, SQLITE_UTF8);
}
if( pVal->flags & (MEM_Int|MEM_Real) ) pVal->flags &= ~MEM_Str;
if( enc!=SQLITE_UTF8 ){
sqlite3VdbeChangeEncoding(pVal, enc);
}
}else if( op==TK_UMINUS ) {
/* This branch happens for multiple negative signs. Ex: -(-5) */
if( SQLITE_OK==sqlite3ValueFromExpr(db,pExpr->pLeft,enc,affinity,&pVal) ){
sqlite3VdbeMemNumerify(pVal);
pVal->u.i = -1 * pVal->u.i;
/* (double)-1 In case of SQLITE_OMIT_FLOATING_POINT... */
pVal->r = (double)-1 * pVal->r;
sqlite3ValueApplyAffinity(pVal, affinity, enc);
}
}
#ifndef SQLITE_OMIT_BLOB_LITERAL
else if( op==TK_BLOB ){
int nVal;
assert( pExpr->u.zToken[0]=='x' || pExpr->u.zToken[0]=='X' );
assert( pExpr->u.zToken[1]=='\'' );
|
| ︙ | ︙ | |||
59768 59769 59770 59771 59772 59773 59774 |
** Try to convert a value into a numeric representation if we can
** do so without loss of information. In other words, if the string
** looks like a number, convert it into a number. If it does not
** look like a number, leave it alone.
*/
static void applyNumericAffinity(Mem *pRec){
if( (pRec->flags & (MEM_Real|MEM_Int))==0 ){
| > | < | < | < < < < < < < | | | | | | < < < < < < | 59735 59736 59737 59738 59739 59740 59741 59742 59743 59744 59745 59746 59747 59748 59749 59750 59751 59752 59753 59754 59755 59756 59757 59758 59759 |
** Try to convert a value into a numeric representation if we can
** do so without loss of information. In other words, if the string
** looks like a number, convert it into a number. If it does not
** look like a number, leave it alone.
*/
static void applyNumericAffinity(Mem *pRec){
if( (pRec->flags & (MEM_Real|MEM_Int))==0 ){
double rValue;
i64 iValue;
u8 enc = pRec->enc;
if( (pRec->flags&MEM_Str)==0 ) return;
if( sqlite3AtoF(pRec->z, &rValue, pRec->n, enc)==0 ) return;
if( 0==sqlite3Atoi64(pRec->z, &iValue, pRec->n, enc) ){
pRec->u.i = iValue;
pRec->flags |= MEM_Int;
}else{
pRec->r = rValue;
pRec->flags |= MEM_Real;
}
}
}
/*
** Processing is determine by the affinity parameter:
**
|
| ︙ | ︙ | |||
61578 61579 61580 61581 61582 61583 61584 |
** This opcode is used when extracting information from a column that
** has REAL affinity. Such column values may still be stored as
** integers, for space efficiency, but after extraction we want them
** to have only a real value.
*/
case OP_RealAffinity: { /* in1 */
pIn1 = &aMem[pOp->p1];
| < | 61531 61532 61533 61534 61535 61536 61537 61538 61539 61540 61541 61542 61543 61544 |
** This opcode is used when extracting information from a column that
** has REAL affinity. Such column values may still be stored as
** integers, for space efficiency, but after extraction we want them
** to have only a real value.
*/
case OP_RealAffinity: { /* in1 */
pIn1 = &aMem[pOp->p1];
if( pIn1->flags & MEM_Int ){
sqlite3VdbeMemRealify(pIn1);
}
break;
}
#endif
|
| ︙ | ︙ | |||
61621 61622 61623 61624 61625 61626 61627 |
** Strings are simply reinterpreted as blobs with no change
** to the underlying data.
**
** A NULL value is not changed by this routine. It remains NULL.
*/
case OP_ToBlob: { /* same as TK_TO_BLOB, in1 */
pIn1 = &aMem[pOp->p1];
| < | 61573 61574 61575 61576 61577 61578 61579 61580 61581 61582 61583 61584 61585 61586 |
** Strings are simply reinterpreted as blobs with no change
** to the underlying data.
**
** A NULL value is not changed by this routine. It remains NULL.
*/
case OP_ToBlob: { /* same as TK_TO_BLOB, in1 */
pIn1 = &aMem[pOp->p1];
if( pIn1->flags & MEM_Null ) break;
if( (pIn1->flags & MEM_Blob)==0 ){
applyAffinity(pIn1, SQLITE_AFF_TEXT, encoding);
assert( pIn1->flags & MEM_Str || db->mallocFailed );
MemSetTypeFlag(pIn1, MEM_Blob);
}else{
pIn1->flags &= ~(MEM_TypeMask&~MEM_Blob);
|
| ︙ | ︙ | |||
61646 61647 61648 61649 61650 61651 61652 |
** equivalent of atoi() or atof() and store 0 if no such conversion
** is possible.
**
** A NULL value is not changed by this routine. It remains NULL.
*/
case OP_ToNumeric: { /* same as TK_TO_NUMERIC, in1 */
pIn1 = &aMem[pOp->p1];
| < < | < < | 61597 61598 61599 61600 61601 61602 61603 61604 61605 61606 61607 61608 61609 61610 61611 61612 61613 61614 61615 61616 61617 61618 61619 61620 61621 61622 61623 61624 61625 61626 |
** equivalent of atoi() or atof() and store 0 if no such conversion
** is possible.
**
** A NULL value is not changed by this routine. It remains NULL.
*/
case OP_ToNumeric: { /* same as TK_TO_NUMERIC, in1 */
pIn1 = &aMem[pOp->p1];
sqlite3VdbeMemNumerify(pIn1);
break;
}
#endif /* SQLITE_OMIT_CAST */
/* Opcode: ToInt P1 * * * *
**
** Force the value in register P1 to be an integer. If
** The value is currently a real number, drop its fractional part.
** If the value is text or blob, try to convert it to an integer using the
** equivalent of atoi() and store 0 if no such conversion is possible.
**
** A NULL value is not changed by this routine. It remains NULL.
*/
case OP_ToInt: { /* same as TK_TO_INT, in1 */
pIn1 = &aMem[pOp->p1];
if( (pIn1->flags & MEM_Null)==0 ){
sqlite3VdbeMemIntegerify(pIn1);
}
break;
}
#if !defined(SQLITE_OMIT_CAST) && !defined(SQLITE_OMIT_FLOATING_POINT)
|
| ︙ | ︙ | |||
61779 61780 61781 61782 61783 61784 61785 | char affinity; /* Affinity to use for comparison */ u16 flags1; /* Copy of initial value of pIn1->flags */ u16 flags3; /* Copy of initial value of pIn3->flags */ #endif /* local variables moved into u.ai */ pIn1 = &aMem[pOp->p1]; pIn3 = &aMem[pOp->p3]; | < < | 61726 61727 61728 61729 61730 61731 61732 61733 61734 61735 61736 61737 61738 61739 |
char affinity; /* Affinity to use for comparison */
u16 flags1; /* Copy of initial value of pIn1->flags */
u16 flags3; /* Copy of initial value of pIn3->flags */
#endif /* local variables moved into u.ai */
pIn1 = &aMem[pOp->p1];
pIn3 = &aMem[pOp->p3];
u.ai.flags1 = pIn1->flags;
u.ai.flags3 = pIn3->flags;
if( (pIn1->flags | pIn3->flags)&MEM_Null ){
/* One or both operands are NULL */
if( pOp->p5 & SQLITE_NULLEQ ){
/* If SQLITE_NULLEQ is set (which will only happen if the operator is
** OP_Eq or OP_Ne) then take the jump or not depending on whether
|
| ︙ | ︙ | |||
62413 62414 62415 62416 62417 62418 62419 |
u.an.zAffinity = pOp->p4.z;
assert( u.an.zAffinity!=0 );
assert( u.an.zAffinity[pOp->p2]==0 );
pIn1 = &aMem[pOp->p1];
while( (u.an.cAff = *(u.an.zAffinity++))!=0 ){
assert( pIn1 <= &p->aMem[p->nMem] );
assert( memIsValid(pIn1) );
| < | 62358 62359 62360 62361 62362 62363 62364 62365 62366 62367 62368 62369 62370 62371 |
u.an.zAffinity = pOp->p4.z;
assert( u.an.zAffinity!=0 );
assert( u.an.zAffinity[pOp->p2]==0 );
pIn1 = &aMem[pOp->p1];
while( (u.an.cAff = *(u.an.zAffinity++))!=0 ){
assert( pIn1 <= &p->aMem[p->nMem] );
assert( memIsValid(pIn1) );
ExpandBlob(pIn1);
applyAffinity(pIn1, u.an.cAff, encoding);
pIn1++;
}
break;
}
|
| ︙ | ︙ | |||
62493 62494 62495 62496 62497 62498 62499 |
/* Loop through the elements that will make up the record to figure
** out how much space is required for the new record.
*/
for(u.ao.pRec=u.ao.pData0; u.ao.pRec<=u.ao.pLast; u.ao.pRec++){
assert( memIsValid(u.ao.pRec) );
if( u.ao.zAffinity ){
| < | 62437 62438 62439 62440 62441 62442 62443 62444 62445 62446 62447 62448 62449 62450 |
/* Loop through the elements that will make up the record to figure
** out how much space is required for the new record.
*/
for(u.ao.pRec=u.ao.pData0; u.ao.pRec<=u.ao.pLast; u.ao.pRec++){
assert( memIsValid(u.ao.pRec) );
if( u.ao.zAffinity ){
applyAffinity(u.ao.pRec, u.ao.zAffinity[u.ao.pRec-u.ao.pData0], encoding);
}
if( u.ao.pRec->flags&MEM_Zero && u.ao.pRec->n>0 ){
sqlite3VdbeMemExpandBlob(u.ao.pRec);
}
u.ao.serial_type = sqlite3VdbeSerialType(u.ao.pRec, u.ao.file_format);
u.ao.len = sqlite3VdbeSerialTypeLen(u.ao.serial_type);
|
| ︙ | ︙ | |||
68878 68879 68880 68881 68882 68883 68884 |
/* Wildcard of the form "?". Assign the next variable number */
assert( z[0]=='?' );
pExpr->iColumn = (ynVar)(++pParse->nVar);
}else if( z[0]=='?' ){
/* Wildcard of the form "?nnn". Convert "nnn" to an integer and
** use it as the variable number */
i64 i;
| | | 68821 68822 68823 68824 68825 68826 68827 68828 68829 68830 68831 68832 68833 68834 68835 |
/* Wildcard of the form "?". Assign the next variable number */
assert( z[0]=='?' );
pExpr->iColumn = (ynVar)(++pParse->nVar);
}else if( z[0]=='?' ){
/* Wildcard of the form "?nnn". Convert "nnn" to an integer and
** use it as the variable number */
i64 i;
int bOk = 0==sqlite3Atoi64(&z[1], &i, sqlite3Strlen30(&z[1]), SQLITE_UTF8);
pExpr->iColumn = (ynVar)i;
testcase( i==0 );
testcase( i==1 );
testcase( i==db->aLimit[SQLITE_LIMIT_VARIABLE_NUMBER]-1 );
testcase( i==db->aLimit[SQLITE_LIMIT_VARIABLE_NUMBER] );
if( bOk==0 || i<1 || i>db->aLimit[SQLITE_LIMIT_VARIABLE_NUMBER] ){
sqlite3ErrorMsg(pParse, "variable number must be between ?1 and ?%d",
|
| ︙ | ︙ | |||
70241 70242 70243 70244 70245 70246 70247 |
** z[n] character is guaranteed to be something that does not look
** like the continuation of the number.
*/
static void codeReal(Vdbe *v, const char *z, int negateFlag, int iMem){
if( ALWAYS(z!=0) ){
double value;
char *zV;
| | | < < > > > | < < | | 70184 70185 70186 70187 70188 70189 70190 70191 70192 70193 70194 70195 70196 70197 70198 70199 70200 70201 70202 70203 70204 70205 70206 70207 70208 70209 70210 70211 70212 70213 70214 70215 70216 70217 70218 70219 70220 70221 70222 70223 70224 70225 70226 70227 70228 |
** z[n] character is guaranteed to be something that does not look
** like the continuation of the number.
*/
static void codeReal(Vdbe *v, const char *z, int negateFlag, int iMem){
if( ALWAYS(z!=0) ){
double value;
char *zV;
sqlite3AtoF(z, &value, sqlite3Strlen30(z), SQLITE_UTF8);
assert( !sqlite3IsNaN(value) ); /* The new AtoF never returns NaN */
if( negateFlag ) value = -value;
zV = dup8bytes(v, (char*)&value);
sqlite3VdbeAddOp4(v, OP_Real, 0, iMem, 0, zV, P4_REAL);
}
}
#endif
/*
** Generate an instruction that will put the integer describe by
** text z[0..n-1] into register iMem.
**
** Expr.u.zToken is always UTF8 and zero-terminated.
*/
static void codeInteger(Parse *pParse, Expr *pExpr, int negFlag, int iMem){
Vdbe *v = pParse->pVdbe;
if( pExpr->flags & EP_IntValue ){
int i = pExpr->u.iValue;
if( negFlag ) i = -i;
sqlite3VdbeAddOp2(v, OP_Integer, i, iMem);
}else{
int c;
i64 value;
const char *z = pExpr->u.zToken;
assert( z!=0 );
c = sqlite3Atoi64(z, &value, sqlite3Strlen30(z), SQLITE_UTF8);
if( c==0 || (c==2 && negFlag) ){
char *zV;
if( negFlag ){ value = -value; }
zV = dup8bytes(v, (char*)&value);
sqlite3VdbeAddOp4(v, OP_Int64, 0, iMem, 0, zV, P4_INT64);
}else{
#ifdef SQLITE_OMIT_FLOATING_POINT
sqlite3ErrorMsg(pParse, "oversized integer: %s%s", negFlag ? "-" : "", z);
#else
codeReal(v, z, negFlag, iMem);
|
| ︙ | ︙ | |||
79412 79413 79414 79415 79416 79417 79418 |
r = -(double)((sqlite_int64)((-r)+0.5));
}else{
zBuf = sqlite3_mprintf("%.*f",n,r);
if( zBuf==0 ){
sqlite3_result_error_nomem(context);
return;
}
| | | 79354 79355 79356 79357 79358 79359 79360 79361 79362 79363 79364 79365 79366 79367 79368 |
r = -(double)((sqlite_int64)((-r)+0.5));
}else{
zBuf = sqlite3_mprintf("%.*f",n,r);
if( zBuf==0 ){
sqlite3_result_error_nomem(context);
return;
}
sqlite3AtoF(zBuf, &r, sqlite3Strlen30(zBuf), SQLITE_UTF8);
sqlite3_free(zBuf);
}
sqlite3_result_double(context, r);
}
#endif
/*
|
| ︙ | ︙ | |||
85470 85471 85472 85473 85474 85475 85476 |
**
** Get or set the size limit on rollback journal files.
*/
if( sqlite3StrICmp(zLeft,"journal_size_limit")==0 ){
Pager *pPager = sqlite3BtreePager(pDb->pBt);
i64 iLimit = -2;
if( zRight ){
| | | 85412 85413 85414 85415 85416 85417 85418 85419 85420 85421 85422 85423 85424 85425 85426 |
**
** Get or set the size limit on rollback journal files.
*/
if( sqlite3StrICmp(zLeft,"journal_size_limit")==0 ){
Pager *pPager = sqlite3BtreePager(pDb->pBt);
i64 iLimit = -2;
if( zRight ){
sqlite3Atoi64(zRight, &iLimit, 1000000, SQLITE_UTF8);
if( iLimit<-1 ) iLimit = -1;
}
iLimit = sqlite3PagerJournalSizeLimit(pPager, iLimit);
returnSingleInt(pParse, "journal_size_limit", iLimit);
}else
#endif /* SQLITE_OMIT_PAGER_PRAGMAS */
|
| ︙ | ︙ | |||
96378 96379 96380 96381 96382 96383 96384 | #define TRACE_IDX_OUTPUTS(A) #endif /* ** Required because bestIndex() is called by bestOrClauseIndex() */ static void bestIndex( | | > | > | 96320 96321 96322 96323 96324 96325 96326 96327 96328 96329 96330 96331 96332 96333 96334 96335 96336 96337 96338 96339 96340 96341 96342 96343 96344 96345 96346 96347 96348 96349 |
#define TRACE_IDX_OUTPUTS(A)
#endif
/*
** Required because bestIndex() is called by bestOrClauseIndex()
*/
static void bestIndex(
Parse*, WhereClause*, struct SrcList_item*,
Bitmask, Bitmask, ExprList*, WhereCost*);
/*
** This routine attempts to find an scanning strategy that can be used
** to optimize an 'OR' expression that is part of a WHERE clause.
**
** The table associated with FROM clause term pSrc may be either a
** regular B-Tree table or a virtual table.
*/
static void bestOrClauseIndex(
Parse *pParse, /* The parsing context */
WhereClause *pWC, /* The WHERE clause */
struct SrcList_item *pSrc, /* The FROM clause term to search */
Bitmask notReady, /* Mask of cursors not available for indexing */
Bitmask notValid, /* Cursors not available for any purpose */
ExprList *pOrderBy, /* The ORDER BY clause */
WhereCost *pCost /* Lowest cost query plan */
){
#ifndef SQLITE_OMIT_OR_OPTIMIZATION
const int iCur = pSrc->iCursor; /* The cursor of the table to be accessed */
const Bitmask maskSrc = getMask(pWC->pMaskSet, iCur); /* Bitmask for pSrc */
WhereTerm * const pWCEnd = &pWC->a[pWC->nTerm]; /* End of pWC->a[] */
|
| ︙ | ︙ | |||
96427 96428 96429 96430 96431 96432 96433 |
for(pOrTerm=pOrWC->a; pOrTerm<pOrWCEnd; pOrTerm++){
WhereCost sTermCost;
WHERETRACE(("... Multi-index OR testing for term %d of %d....\n",
(pOrTerm - pOrWC->a), (pTerm - pWC->a)
));
if( pOrTerm->eOperator==WO_AND ){
WhereClause *pAndWC = &pOrTerm->u.pAndInfo->wc;
| | | | 96371 96372 96373 96374 96375 96376 96377 96378 96379 96380 96381 96382 96383 96384 96385 96386 96387 96388 96389 96390 96391 96392 96393 |
for(pOrTerm=pOrWC->a; pOrTerm<pOrWCEnd; pOrTerm++){
WhereCost sTermCost;
WHERETRACE(("... Multi-index OR testing for term %d of %d....\n",
(pOrTerm - pOrWC->a), (pTerm - pWC->a)
));
if( pOrTerm->eOperator==WO_AND ){
WhereClause *pAndWC = &pOrTerm->u.pAndInfo->wc;
bestIndex(pParse, pAndWC, pSrc, notReady, notValid, 0, &sTermCost);
}else if( pOrTerm->leftCursor==iCur ){
WhereClause tempWC;
tempWC.pParse = pWC->pParse;
tempWC.pMaskSet = pWC->pMaskSet;
tempWC.op = TK_AND;
tempWC.a = pOrTerm;
tempWC.nTerm = 1;
bestIndex(pParse, &tempWC, pSrc, notReady, notValid, 0, &sTermCost);
}else{
continue;
}
rTotal += sTermCost.rCost;
nRow += sTermCost.nRow;
used |= sTermCost.used;
if( rTotal>=pCost->rCost ) break;
|
| ︙ | ︙ | |||
96882 96883 96884 96885 96886 96887 96888 | ** routine takes care of freeing the sqlite3_index_info structure after ** everybody has finished with it. */ static void bestVirtualIndex( Parse *pParse, /* The parsing context */ WhereClause *pWC, /* The WHERE clause */ struct SrcList_item *pSrc, /* The FROM clause term to search */ | | > | 96826 96827 96828 96829 96830 96831 96832 96833 96834 96835 96836 96837 96838 96839 96840 96841 |
** routine takes care of freeing the sqlite3_index_info structure after
** everybody has finished with it.
*/
static void bestVirtualIndex(
Parse *pParse, /* The parsing context */
WhereClause *pWC, /* The WHERE clause */
struct SrcList_item *pSrc, /* The FROM clause term to search */
Bitmask notReady, /* Mask of cursors not available for index */
Bitmask notValid, /* Cursors not valid for any purpose */
ExprList *pOrderBy, /* The order by clause */
WhereCost *pCost, /* Lowest cost query plan */
sqlite3_index_info **ppIdxInfo /* Index information passed to xBestIndex */
){
Table *pTab = pSrc->pTab;
sqlite3_index_info *pIdxInfo;
struct sqlite3_index_constraint *pIdxCons;
|
| ︙ | ︙ | |||
97012 97013 97014 97015 97016 97017 97018 | } pCost->plan.nEq = 0; pIdxInfo->nOrderBy = nOrderBy; /* Try to find a more efficient access pattern by using multiple indexes ** to optimize an OR expression within the WHERE clause. */ | | | 96957 96958 96959 96960 96961 96962 96963 96964 96965 96966 96967 96968 96969 96970 96971 | } pCost->plan.nEq = 0; pIdxInfo->nOrderBy = nOrderBy; /* Try to find a more efficient access pattern by using multiple indexes ** to optimize an OR expression within the WHERE clause. */ bestOrClauseIndex(pParse, pWC, pSrc, notReady, notValid, pOrderBy, pCost); } #endif /* SQLITE_OMIT_VIRTUALTABLE */ /* ** Argument pIdx is a pointer to an index structure that has an array of ** SQLITE_INDEX_SAMPLES evenly spaced samples of the first indexed column ** stored in Index.aSample. The domain of values stored in said column |
| ︙ | ︙ | |||
97293 97294 97295 97296 97297 97298 97299 | ** selected plan may still take advantage of the tables built-in rowid ** index. */ static void bestBtreeIndex( Parse *pParse, /* The parsing context */ WhereClause *pWC, /* The WHERE clause */ struct SrcList_item *pSrc, /* The FROM clause term to search */ | | > | 97238 97239 97240 97241 97242 97243 97244 97245 97246 97247 97248 97249 97250 97251 97252 97253 |
** selected plan may still take advantage of the tables built-in rowid
** index.
*/
static void bestBtreeIndex(
Parse *pParse, /* The parsing context */
WhereClause *pWC, /* The WHERE clause */
struct SrcList_item *pSrc, /* The FROM clause term to search */
Bitmask notReady, /* Mask of cursors not available for indexing */
Bitmask notValid, /* Cursors not available for any purpose */
ExprList *pOrderBy, /* The ORDER BY clause */
WhereCost *pCost /* Lowest cost query plan */
){
int iCur = pSrc->iCursor; /* The cursor of the table to be accessed */
Index *pProbe; /* An index we are evaluating */
Index *pIdx; /* Copy of pProbe, or zero for IPK index */
int eqTermMask; /* Current mask of valid equality operators */
|
| ︙ | ︙ | |||
97555 97556 97557 97558 97559 97560 97561 |
/* If there are additional constraints on this table that cannot
** be used with the current index, but which might lower the number
** of output rows, adjust the nRow value accordingly. This only
** matters if the current index is the least costly, so do not bother
** with this step if we already know this index will not be chosen.
** Also, never reduce the output row count below 2 using this step.
**
| | < < | < > | > > | | | | | 97501 97502 97503 97504 97505 97506 97507 97508 97509 97510 97511 97512 97513 97514 97515 97516 97517 97518 97519 97520 97521 97522 97523 97524 97525 97526 97527 97528 97529 97530 97531 97532 97533 |
/* If there are additional constraints on this table that cannot
** be used with the current index, but which might lower the number
** of output rows, adjust the nRow value accordingly. This only
** matters if the current index is the least costly, so do not bother
** with this step if we already know this index will not be chosen.
** Also, never reduce the output row count below 2 using this step.
**
** It is critical that the notValid mask be used here instead of
** the notReady mask. When computing an "optimal" index, the notReady
** mask will only have one bit set - the bit for the current table.
** The notValid mask, on the other hand, always has all bits set for
** tables that are not in outer loops. If notReady is used here instead
** of notValid, then a optimal index that depends on inner joins loops
** might be selected even when there exists an optimal index that has
** no such dependency.
*/
if( nRow>2 && cost<=pCost->rCost ){
int k; /* Loop counter */
int nSkipEq = nEq; /* Number of == constraints to skip */
int nSkipRange = nBound; /* Number of < constraints to skip */
Bitmask thisTab; /* Bitmap for pSrc */
thisTab = getMask(pWC->pMaskSet, iCur);
for(pTerm=pWC->a, k=pWC->nTerm; nRow>2 && k; k--, pTerm++){
if( pTerm->wtFlags & TERM_VIRTUAL ) continue;
if( (pTerm->prereqAll & notValid)!=thisTab ) continue;
if( pTerm->eOperator & (WO_EQ|WO_IN|WO_ISNULL) ){
if( nSkipEq ){
/* Ignore the first nEq equality matches since the index
** has already accounted for these */
nSkipEq--;
}else{
/* Assume each additional equality match reduces the result
|
| ︙ | ︙ | |||
97655 97656 97657 97658 97659 97660 97661 |
);
WHERETRACE(("best index is: %s\n",
((pCost->plan.wsFlags & WHERE_NOT_FULLSCAN)==0 ? "none" :
pCost->plan.u.pIdx ? pCost->plan.u.pIdx->zName : "ipk")
));
| | | > | | | 97601 97602 97603 97604 97605 97606 97607 97608 97609 97610 97611 97612 97613 97614 97615 97616 97617 97618 97619 97620 97621 97622 97623 97624 97625 97626 97627 97628 97629 97630 97631 97632 97633 97634 97635 97636 97637 97638 97639 97640 97641 97642 97643 97644 97645 97646 |
);
WHERETRACE(("best index is: %s\n",
((pCost->plan.wsFlags & WHERE_NOT_FULLSCAN)==0 ? "none" :
pCost->plan.u.pIdx ? pCost->plan.u.pIdx->zName : "ipk")
));
bestOrClauseIndex(pParse, pWC, pSrc, notReady, notValid, pOrderBy, pCost);
bestAutomaticIndex(pParse, pWC, pSrc, notReady, pCost);
pCost->plan.wsFlags |= eqTermMask;
}
/*
** Find the query plan for accessing table pSrc->pTab. Write the
** best query plan and its cost into the WhereCost object supplied
** as the last parameter. This function may calculate the cost of
** both real and virtual table scans.
*/
static void bestIndex(
Parse *pParse, /* The parsing context */
WhereClause *pWC, /* The WHERE clause */
struct SrcList_item *pSrc, /* The FROM clause term to search */
Bitmask notReady, /* Mask of cursors not available for indexing */
Bitmask notValid, /* Cursors not available for any purpose */
ExprList *pOrderBy, /* The ORDER BY clause */
WhereCost *pCost /* Lowest cost query plan */
){
#ifndef SQLITE_OMIT_VIRTUALTABLE
if( IsVirtual(pSrc->pTab) ){
sqlite3_index_info *p = 0;
bestVirtualIndex(pParse, pWC, pSrc, notReady, notValid, pOrderBy, pCost,&p);
if( p->needToFreeIdxStr ){
sqlite3_free(p->idxStr);
}
sqlite3DbFree(pParse->db, p);
}else
#endif
{
bestBtreeIndex(pParse, pWC, pSrc, notReady, notValid, pOrderBy, pCost);
}
}
/*
** Disable a term in the WHERE clause. Except, do not disable the term
** if it controls a LEFT OUTER JOIN and it did not originate in the ON
** or USING clause of that join.
|
| ︙ | ︙ | |||
98900 98901 98902 98903 98904 98905 98906 98907 98908 |
** were used as the innermost nested loop. In other words, a table
** is chosen such that the cost of running that table cannot be reduced
** by waiting for other tables to run first. This "optimal" test works
** by first assuming that the FROM clause is on the inner loop and finding
** its query plan, then checking to see if that query plan uses any
** other FROM clause terms that are notReady. If no notReady terms are
** used then the "optimal" query plan works.
**
** The second loop iteration is only performed if no optimal scan
| > > > > > > | | | | 98847 98848 98849 98850 98851 98852 98853 98854 98855 98856 98857 98858 98859 98860 98861 98862 98863 98864 98865 98866 98867 98868 98869 98870 98871 98872 98873 98874 98875 98876 98877 98878 98879 98880 98881 98882 98883 98884 98885 98886 98887 98888 98889 98890 |
** were used as the innermost nested loop. In other words, a table
** is chosen such that the cost of running that table cannot be reduced
** by waiting for other tables to run first. This "optimal" test works
** by first assuming that the FROM clause is on the inner loop and finding
** its query plan, then checking to see if that query plan uses any
** other FROM clause terms that are notReady. If no notReady terms are
** used then the "optimal" query plan works.
**
** Note that the WhereCost.nRow parameter for an optimal scan might
** not be as small as it would be if the table really were the innermost
** join. The nRow value can be reduced by WHERE clause constraints
** that do not use indices. But this nRow reduction only happens if the
** table really is the innermost join.
**
** The second loop iteration is only performed if no optimal scan
** strategies were found by the first iteration. This second iteration
** is used to search for the lowest cost scan overall.
**
** Previous versions of SQLite performed only the second iteration -
** the next outermost loop was always that with the lowest overall
** cost. However, this meant that SQLite could select the wrong plan
** for scripts such as the following:
**
** CREATE TABLE t1(a, b);
** CREATE TABLE t2(c, d);
** SELECT * FROM t2, t1 WHERE t2.rowid = t1.a;
**
** The best strategy is to iterate through table t1 first. However it
** is not possible to determine this with a simple greedy algorithm.
** Since the cost of a linear scan through table t2 is the same
** as the cost of a linear scan through table t1, a simple greedy
** algorithm may choose to use t2 for the outer loop, which is a much
** costlier approach.
*/
nUnconstrained = 0;
notIndexed = 0;
for(isOptimal=(iFrom<nTabList-1); isOptimal>=0 && bestJ<0; isOptimal--){
Bitmask mask; /* Mask of tables not yet ready */
for(j=iFrom, pTabItem=&pTabList->a[j]; j<nTabList; j++, pTabItem++){
int doNotReorder; /* True if this table should not be reordered */
WhereCost sCost; /* Cost information from best[Virtual]Index() */
ExprList *pOrderBy; /* ORDER BY clause for index to optimize */
doNotReorder = (pTabItem->jointype & (JT_LEFT|JT_CROSS))!=0;
|
| ︙ | ︙ | |||
98945 98946 98947 98948 98949 98950 98951 |
pOrderBy = ((i==0 && ppOrderBy )?*ppOrderBy:0);
if( pTabItem->pIndex==0 ) nUnconstrained++;
assert( pTabItem->pTab );
#ifndef SQLITE_OMIT_VIRTUALTABLE
if( IsVirtual(pTabItem->pTab) ){
sqlite3_index_info **pp = &pWInfo->a[j].pIdxInfo;
| | > | > | 98898 98899 98900 98901 98902 98903 98904 98905 98906 98907 98908 98909 98910 98911 98912 98913 98914 98915 98916 98917 98918 |
pOrderBy = ((i==0 && ppOrderBy )?*ppOrderBy:0);
if( pTabItem->pIndex==0 ) nUnconstrained++;
assert( pTabItem->pTab );
#ifndef SQLITE_OMIT_VIRTUALTABLE
if( IsVirtual(pTabItem->pTab) ){
sqlite3_index_info **pp = &pWInfo->a[j].pIdxInfo;
bestVirtualIndex(pParse, pWC, pTabItem, mask, notReady, pOrderBy,
&sCost, pp);
}else
#endif
{
bestBtreeIndex(pParse, pWC, pTabItem, mask, notReady, pOrderBy,
&sCost);
}
assert( isOptimal || (sCost.used¬Ready)==0 );
/* If an INDEXED BY clause is present, then the plan must use that
** index if it uses any index at all */
assert( pTabItem->pIndex==0
|| (sCost.plan.wsFlags & WHERE_NOT_FULLSCAN)==0
|
| ︙ | ︙ |
Changes to src/sqlite3.h.
| ︙ | ︙ | |||
105 106 107 108 109 110 111 | ** ** See also: [sqlite3_libversion()], ** [sqlite3_libversion_number()], [sqlite3_sourceid()], ** [sqlite_version()] and [sqlite_source_id()]. */ #define SQLITE_VERSION "3.7.3" #define SQLITE_VERSION_NUMBER 3007003 | | | 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 | ** ** See also: [sqlite3_libversion()], ** [sqlite3_libversion_number()], [sqlite3_sourceid()], ** [sqlite_version()] and [sqlite_source_id()]. */ #define SQLITE_VERSION "3.7.3" #define SQLITE_VERSION_NUMBER 3007003 #define SQLITE_SOURCE_ID "2010-10-04 23:55:51 ece641eb8951c6314cedbdb3243f91cb199c3239" /* ** CAPI3REF: Run-Time Library Version Numbers ** KEYWORDS: sqlite3_version, sqlite3_sourceid ** ** These interfaces provide the same information as the [SQLITE_VERSION], ** [SQLITE_VERSION_NUMBER], and [SQLITE_SOURCE_ID] C preprocessor macros |
| ︙ | ︙ |
Changes to src/style.c.
| ︙ | ︙ | |||
197 198 199 200 201 202 203 | @ href="$baseurl/timeline.rss" /> @ <link rel="stylesheet" href="$baseurl/style.css?default" type="text/css" @ media="screen" /> @ </head> @ <body> @ <div class="header"> @ <div class="logo"> | | | 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 |
@ href="$baseurl/timeline.rss" />
@ <link rel="stylesheet" href="$baseurl/style.css?default" type="text/css"
@ media="screen" />
@ </head>
@ <body>
@ <div class="header">
@ <div class="logo">
@ <img src="$baseurl/logo" alt="logo" />
@ </div>
@ <div class="title"><small>$<project_name></small><br />$<title></div>
@ <div class="status"><th1>
@ if {[info exists login]} {
@ puts "Logged in as $login"
@ } else {
@ puts "Not logged in"
|
| ︙ | ︙ |
Changes to src/timeline.c.
| ︙ | ︙ | |||
163 164 165 166 167 168 169 |
** 2. Date/Time
** 3. Comment string
** 4. User
** 5. True if is a leaf
** 6. background color
** 7. type ("ci", "w", "t")
** 8. list of symbolic tags.
| | | 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 |
** 2. Date/Time
** 3. Comment string
** 4. User
** 5. True if is a leaf
** 6. background color
** 7. type ("ci", "w", "t")
** 8. list of symbolic tags.
** 9. tagid for ticket or wiki or event
** 10. Short comment to user for repeated tickets and wiki
*/
void www_print_timeline(
Stmt *pQuery, /* Query to implement the timeline */
int tmFlags, /* Flags controlling display behavior */
void (*xExtra)(int) /* Routine to call on each line of display */
){
|
| ︙ | ︙ | |||
286 287 288 289 290 291 292 293 294 295 296 297 298 299 |
" WHERE rid=%d AND tagid=%d AND tagtype>0",
rid, TAG_CLOSED) ){
@ <span class="timelineLeaf">Closed-Leaf:</span>
}else{
@ <span class="timelineLeaf">Leaf:</span>
}
}
}else if( (tmFlags & TIMELINE_ARTID)!=0 ){
hyperlink_to_uuid(zUuid);
}
db_column_blob(pQuery, commentColumn, &comment);
if( mxWikiLen>0 && blob_size(&comment)>mxWikiLen ){
Blob truncated;
blob_zero(&truncated);
| > > | 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 |
" WHERE rid=%d AND tagid=%d AND tagtype>0",
rid, TAG_CLOSED) ){
@ <span class="timelineLeaf">Closed-Leaf:</span>
}else{
@ <span class="timelineLeaf">Leaf:</span>
}
}
}else if( zType[0]=='e' && tagid ){
hyperlink_to_event_tagid(tagid);
}else if( (tmFlags & TIMELINE_ARTID)!=0 ){
hyperlink_to_uuid(zUuid);
}
db_column_blob(pQuery, commentColumn, &comment);
if( mxWikiLen>0 && blob_size(&comment)>mxWikiLen ){
Blob truncated;
blob_zero(&truncated);
|
| ︙ | ︙ | |||
637 638 639 640 641 642 643 | ** c=TIMESTAMP "circa" this date. ** n=COUNT number of events in output ** p=RID artifact RID and up to COUNT parents and ancestors ** d=RID artifact RID and up to COUNT descendants ** t=TAGID show only check-ins with the given tagid ** r=TAGID show check-ins related to tagid ** u=USER only if belonging to this user | | | 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 | ** c=TIMESTAMP "circa" this date. ** n=COUNT number of events in output ** p=RID artifact RID and up to COUNT parents and ancestors ** d=RID artifact RID and up to COUNT descendants ** t=TAGID show only check-ins with the given tagid ** r=TAGID show check-ins related to tagid ** u=USER only if belonging to this user ** y=TYPE 'ci', 'w', 't', 'e' ** s=TEXT string search (comment and brief) ** ng Suppress the graph if present ** ** p= and d= can appear individually or together. If either p= or d= ** appear, then u=, y=, a=, and b= are ignored. ** ** If a= and b= appear, only a= is used. If neither appear, the most |
| ︙ | ︙ | |||
747 748 749 750 751 752 753 |
blob_appendf(&desc, " of <a href='%s/info/%s'>[%.10s]</a>",
g.zBaseURL, zUuid, zUuid);
}else{
blob_appendf(&desc, " of check-in [%.10s]", zUuid);
}
}else{
int n;
| | | | > | > > | 749 750 751 752 753 754 755 756 757 758 759 760 761 762 763 764 765 766 767 768 769 770 771 772 773 774 775 776 777 778 779 780 781 782 783 784 785 786 787 788 789 790 791 792 793 794 795 796 797 798 799 800 801 802 803 804 805 806 807 808 809 810 811 812 813 814 815 816 817 818 819 820 821 822 823 824 825 826 827 828 |
blob_appendf(&desc, " of <a href='%s/info/%s'>[%.10s]</a>",
g.zBaseURL, zUuid, zUuid);
}else{
blob_appendf(&desc, " of check-in [%.10s]", zUuid);
}
}else{
int n;
const char *zEType = "timeline item";
char *zDate;
char *zNEntry = mprintf("%d", nEntry);
url_initialize(&url, "timeline");
url_add_parameter(&url, "n", zNEntry);
if( tagid>0 ){
if( zType[0]!='e' ) zType = "ci";
blob_appendf(&sql,
"AND (EXISTS(SELECT 1 FROM tagxref"
" WHERE tagid=%d AND tagtype>0 AND rid=blob.rid)", tagid);
if( zBrName && zType[0]=='c' ){
/* The next two blob_appendf() calls add SQL that causes checkins that
** are not part of the branch which are parents or childen of the branch
** to be included in the report. This related check-ins are useful
** in helping to visualize what has happened on a quiescent branch
** that is infrequently merged with a much more activate branch.
*/
url_add_parameter(&url, "r", zBrName);
blob_appendf(&sql,
" OR EXISTS(SELECT 1 FROM plink JOIN tagxref ON rid=cid"
" WHERE tagid=%d AND tagtype>0 AND pid=blob.rid)", tagid);
blob_appendf(&sql,
" OR EXISTS(SELECT 1 FROM plink JOIN tagxref ON rid=pid"
" WHERE tagid=%d AND tagtype>0 AND cid=blob.rid)", tagid);
}else{
url_add_parameter(&url, "t", zTagName);
}
blob_appendf(&sql, ")");
}
if( (zType[0]=='w' && !g.okRdWiki)
|| (zType[0]=='t' && !g.okRdTkt)
|| (zType[0]=='e' && !g.okRdWiki)
|| (zType[0]=='c' && !g.okRead)
){
zType = "all";
}
if( zType[0]=='a' ){
if( !g.okRead || !g.okRdWiki || !g.okRdTkt ){
char cSep = '(';
blob_appendf(&sql, " AND event.type IN ");
if( g.okRead ){
blob_appendf(&sql, "%c'ci'", cSep);
cSep = ',';
}
if( g.okRdWiki ){
blob_appendf(&sql, "%c'w','e'", cSep);
cSep = ',';
}
if( g.okRdTkt ){
blob_appendf(&sql, "%c't'", cSep);
cSep = ',';
}
blob_appendf(&sql, ")");
}
}else{ /* zType!="all" */
blob_appendf(&sql, " AND event.type=%Q", zType);
url_add_parameter(&url, "y", zType);
if( zType[0]=='c' ){
zEType = "checkin";
}else if( zType[0]=='w' ){
zEType = "wiki edit";
}else if( zType[0]=='t' ){
zEType = "ticket change";
}else if( zType[0]=='e' ){
zEType = "event";
}
}
if( zUser ){
blob_appendf(&sql, " AND event.user=%Q", zUser);
url_add_parameter(&url, "u", zUser);
}
if ( zSearch ){
|
| ︙ | ︙ | |||
924 925 926 927 928 929 930 |
}
if( zType[0]!='c' && g.okRead ){
timeline_submenu(&url, "Checkins Only", "y", "ci", 0);
}
if( zType[0]!='t' && g.okRdTkt ){
timeline_submenu(&url, "Tickets Only", "y", "t", 0);
}
| > > | > | | | 929 930 931 932 933 934 935 936 937 938 939 940 941 942 943 944 945 946 947 948 949 950 951 |
}
if( zType[0]!='c' && g.okRead ){
timeline_submenu(&url, "Checkins Only", "y", "ci", 0);
}
if( zType[0]!='t' && g.okRdTkt ){
timeline_submenu(&url, "Tickets Only", "y", "t", 0);
}
if( zType[0]!='e' && g.okRdWiki ){
timeline_submenu(&url, "Events Only", "y", "e", 0);
}
}
if( nEntry>20 ){
timeline_submenu(&url, "20 Entries", "n", "20", 0);
}
if( nEntry<200 ){
timeline_submenu(&url, "200 Entries", "n", "200", 0);
}
}
}
blob_zero(&sql);
db_prepare(&q, "SELECT * FROM timeline ORDER BY timestamp DESC /*scan*/");
@ <h2>%b(&desc)</h2>
blob_reset(&desc);
|
| ︙ | ︙ |
Changes to src/wiki.c.
| ︙ | ︙ | |||
104 105 106 107 108 109 110 |
}
style_header("Home");
@ <p>This is a stub home-page for the project.
@ To fill in this page, first go to
@ <a href="%s(g.zBaseURL)/setup_config">setup/config</a>
@ and establish a "Project Name". Then create a
@ wiki page with that name. The content of that wiki page
| | | 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 |
}
style_header("Home");
@ <p>This is a stub home-page for the project.
@ To fill in this page, first go to
@ <a href="%s(g.zBaseURL)/setup_config">setup/config</a>
@ and establish a "Project Name". Then create a
@ wiki page with that name. The content of that wiki page
@ will be displayed in place of this message.</p>
style_footer();
}
/*
** Return true if the given pagename is the name of the sandbox
*/
static int is_sandbox(const char *zPagename){
|
| ︙ | ︙ | |||
151 152 153 154 155 156 157 158 159 160 161 162 163 164 |
@ pages. </li>
@ <li> <a href="%s(g.zBaseURL)/wiki_rules">Formatting rules</a> for
@ wiki.</li>
@ <li> Use the <a href="%s(g.zBaseURL)/wiki?name=Sandbox">Sandbox</a>
@ to experiment.</li>
if( g.okNewWiki ){
@ <li> Create a <a href="%s(g.zBaseURL)/wikinew">new wiki page</a>.</li>
}
@ <li> <a href="%s(g.zBaseURL)/wcontent">List of All Wiki Pages</a>
@ available on this server.</li>
@ <li> <form method="get" action="%s(g.zBaseURL)/wfind"><div>
@ Search wiki titles: <input type="text" name="title"/>
@ <input type="submit" /></div></form>
@ </li>
| > > > | 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 |
@ pages. </li>
@ <li> <a href="%s(g.zBaseURL)/wiki_rules">Formatting rules</a> for
@ wiki.</li>
@ <li> Use the <a href="%s(g.zBaseURL)/wiki?name=Sandbox">Sandbox</a>
@ to experiment.</li>
if( g.okNewWiki ){
@ <li> Create a <a href="%s(g.zBaseURL)/wikinew">new wiki page</a>.</li>
if( g.okWrite ){
@ <li> Create a <a href="%s(g.zTop)/eventedit">new event</a>.</li>
}
}
@ <li> <a href="%s(g.zBaseURL)/wcontent">List of All Wiki Pages</a>
@ available on this server.</li>
@ <li> <form method="get" action="%s(g.zBaseURL)/wfind"><div>
@ Search wiki titles: <input type="text" name="title"/>
@ <input type="submit" /></div></form>
@ </li>
|
| ︙ | ︙ |
Changes to src/xfer.c.
| ︙ | ︙ | |||
117 118 119 120 121 122 123 |
blob_zero(&hash);
blob_extract(pXfer->pIn, n, &content);
if( uuid_is_shunned(blob_str(&pXfer->aToken[1])) ){
/* Ignore files that have been shunned */
return;
}
if( pXfer->nToken==4 ){
| | | > > | 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 |
blob_zero(&hash);
blob_extract(pXfer->pIn, n, &content);
if( uuid_is_shunned(blob_str(&pXfer->aToken[1])) ){
/* Ignore files that have been shunned */
return;
}
if( pXfer->nToken==4 ){
Blob src, next;
srcid = rid_from_uuid(&pXfer->aToken[2], 1);
if( content_get(srcid, &src)==0 ){
rid = content_put(&content, blob_str(&pXfer->aToken[1]), srcid);
pXfer->nDanglingFile++;
db_multi_exec("DELETE FROM phantom WHERE rid=%d", rid);
content_make_public(rid);
return;
}
pXfer->nDeltaRcvd++;
blob_delta_apply(&src, &content, &next);
blob_reset(&src);
blob_reset(&content);
content = next;
}else{
pXfer->nFileRcvd++;
}
sha1sum_blob(&content, &hash);
if( !blob_eq_str(&pXfer->aToken[1], blob_str(&hash), -1) ){
blob_appendf(&pXfer->err, "content does not match sha1 hash");
}
|
| ︙ | ︙ |
Changes to www/branching.wiki.
| ︙ | ︙ | |||
153 154 155 156 157 158 159 160 161 162 163 164 165 166 | concerned, there is no difference. The distinction is in the intent. In figure 2, the fact that check-in 2 has multiple children is an accident that stems from concurrent development. In figure 4, giving check-in 2 multiple children is a deliberate act. So, to a good approximation, we define forking to be by accident and branching to be by intent. Apart from that, they are the same. <h2>Tags And Properties</h2> Tags and properties are used in fossil to help express the intent, and thus to distinguish between forks and branches. Figure 5 shows the same scenario as figure 4 but with tags and properties added: <center><table border=1 cellpadding=10 hspace=10 vspace=10> | > | 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 | concerned, there is no difference. The distinction is in the intent. In figure 2, the fact that check-in 2 has multiple children is an accident that stems from concurrent development. In figure 4, giving check-in 2 multiple children is a deliberate act. So, to a good approximation, we define forking to be by accident and branching to be by intent. Apart from that, they are the same. <a name="tags"></a> <h2>Tags And Properties</h2> Tags and properties are used in fossil to help express the intent, and thus to distinguish between forks and branches. Figure 5 shows the same scenario as figure 4 but with tags and properties added: <center><table border=1 cellpadding=10 hspace=10 vspace=10> |
| ︙ | ︙ |
Added www/event.wiki.
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 |
<title>Events</title>
<h2>What Is An "Event"?</h2>
In Fossil, and "event" is a special kind of [./wikitheory.wiki | wiki page]
that is associated with a point in time rather than having a page name.
Each event causes a single entry to appear on the [/timeline | Timeline Page].
Clicking on the hyperlink of the timeline entry cause a jump to the wiki
content for the event. The wiki content, the timeline entry text, the
time of the event, and the timeline background color can all be edited.
As with check-ins, wiki, and tickets, all events automatically synchronize
to other repositories. Hence, events can be viewed, created, and edited
off-line. And the complete edit history for events is maintained
for auditing purposes.
Possible uses for events include:
* <b>Milestones</b>. Project milestones, such as releases or beta-test
cycles, can be recorded as events. The timeline entry for the event
can be something simple like "Version 1.2.3" perhaps with a bright
color background to draw attention to the entry and the wiki content
can contain release notes, for example.
* <b>Blog Entries</b>. Blog entries from developers describing the current
state of a project, or rational for various design decisions, or
roadmaps for future development, can be entered as events.
* <b>Process Checkpoints</b>. For projects that have a formal process,
events can be used to record the completion or the initiation of
various process steps. For example, an event can be used to record
the successful completion of a long-running test, perhaps with
performance results and details of where the test was run and who
ran it recorded in the wiki content.
* <b>News Articles</b>. Significant occurrences in the lifecycle of
a project can be recorded as news articles using events. Perhaps the
domain name of the canonical website for a project changes, or new
server hardware is obtained. Such happenings are appropriate for
reporting as news.
* <b>Announcements</b>. Changes to the composition of the development
team or acquisition of new project sponsors can be communicated as
announcements which can be implemented as events.
No project is required to use events. But events can help many projects
stay better organized and provide a better historical record of the
development progress.
<h2>Viewing Events</h2>
Because events are considered a special kind of wiki,
users must have permission to read wiki in order read events.
Enable the "j" permission under the /Setup/Users menu in order
to give specific users or user classes the ability to view wiki
and events.
Events show up on the timeline. Click on the hyperlink beside the
event title to see the details of the event.
<h2>Creating And Editing Events</h2>
There is a hyperlink under the /Wiki menu that can be used to create
new events. And there is a submenu hyperlink on event displays for
editing existing events.
Users must have check-in privileges (permission "i") in order to
create or edit events. In addition, users must have create-wiki
privilege (permission "f") to create new events and edit-wiki
privilege (permission "k") in order to edit existing events.
If the first non-whitespace text of the event wiki content is
<title>...</title> then that markup is omitted from
the body of the wiki pages and is instead displayed as the page
title.
|
Changes to www/fileformat.wiki.
| ︙ | ︙ | |||
41 42 43 44 45 46 47 48 49 | <ul> <li> [#manifest | Manifests] </li> <li> [#cluster | Clusters] </li> <li> [#ctrl | Control Artifacts] </li> <li> [#wikichng | Wiki Pages] </li> <li> [#tktchng | Ticket Changes] </li> <li> [#attachment | Attachments] </li> </ul> | > | > > > | 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 | <ul> <li> [#manifest | Manifests] </li> <li> [#cluster | Clusters] </li> <li> [#ctrl | Control Artifacts] </li> <li> [#wikichng | Wiki Pages] </li> <li> [#tktchng | Ticket Changes] </li> <li> [#attachment | Attachments] </li> <li> [#event | Events] </li> </ul> These seven artifact types are described in the sequel. In the current implementation (as of 2009-01-25) the artifacts that make up a fossil repository are stored in in as delta- and zlib-compressed blobs in an <a href="http://www.sqlite.org/">SQLite</a> database. This is an implementation detail and might change in a future release. For the purpose of this article "file format" means the format of the artifacts, not how the artifacts are stored on disk. It is the artifact format that is intended to be enduring. The specifics of how artifacts are stored on disk, though stable, is not intended to live as long as the artifact format. All of the artifacts can be extracted from a Fossil repository using the "fossil deconstruct" command. <a name="manifest"></a> <h2>1.0 The Manifest</h2> A manifest defines a check-in or version of the project source tree. The manifest contains a list of artifacts for each file in the project and the corresponding filenames, as |
| ︙ | ︙ | |||
163 164 165 166 167 168 169 | the manifest itself) in strict sorted lexicographical order, take the pathname of the file relative to the root of the repository, append a single space (ASCII 0x20), the size of the file in ASCII decimal, a single newline character (ASCII 0x0A), and the complete text of the file. Compute the MD5 checksum of the result. | | > | | | 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 | the manifest itself) in strict sorted lexicographical order, take the pathname of the file relative to the root of the repository, append a single space (ASCII 0x20), the size of the file in ASCII decimal, a single newline character (ASCII 0x0A), and the complete text of the file. Compute the MD5 checksum of the result. A manifest might contain one or more T-cards used to set [./branching.wiki#tags | tags or properties] on the check-in. The format of the T-card is the same as described in <i>Control Artifacts</i> section below, except that the second argument is the single characcter "<b>*</b>" instead of an artifact ID. The <b>*</b> in place of the artifact ID indicates that the tag or property applies to the current artifact. It is not possible to encode the current artifact ID as part of an artifact, since the act of inserting the artifact ID would change the artifact ID, hence a <b>*</b> is used to represent "self". T-cards are typically added to manifests in order to set the <b>branch</b> property and a symbolic name when the check-in is intended to start a new branch. Each manifest has a single U-card. The argument to the U-card is the login of the user who created the manifest. The login name is encoded using the same character escapes as is used for the check-in comment argument to the C-card. A manifest has an optional Z-card as its last line. The argument to the Z-card is a 32-character lowercase hexadecimal MD5 hash of all prior lines of the manifest up to and including the newline character that immediately precedes the "Z". The Z-card is just a sanity check to prove that the manifest is well-formed and consistent. A sample manifest from Fossil itself can be seen |
| ︙ | ︙ | |||
260 261 262 263 264 265 266 | one or more T cards. No other cards or other text is allowed in a control artifact. Control artifacts might be PGP clearsigned. The D card and the Z card of a control artifact are the same as in a manifest. | > | | 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 | one or more T cards. No other cards or other text is allowed in a control artifact. Control artifacts might be PGP clearsigned. The D card and the Z card of a control artifact are the same as in a manifest. The T card represents a [./branching.wiki#tags | tag or property] that is applied to some other artifact. The T card has two or three values. The second argument is the 40 character lowercase artifact ID of the artifact to which the tag is to be applied. The first value is the tag name. The first character of the tag is either "+", "-", or "*". A "+" means the tag should be added to the artifact. The "-" means the tag should be removed. The "*" character means the tag should be added to the artifact |
| ︙ | ︙ | |||
322 323 324 325 326 327 328 | The W card is used to specify the text of the wiki page. The argument to the W card is an integer which is the number of bytes of text in the wiki page. That text follows the newline character that terminates the W card. The wiki text is always followed by one extra newline. An example wiki artifact can be seen | | | 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 | The W card is used to specify the text of the wiki page. The argument to the W card is an integer which is the number of bytes of text in the wiki page. That text follows the newline character that terminates the W card. The wiki text is always followed by one extra newline. An example wiki artifact can be seen [/artifact?name=7b2f5fd0e0&txt=1 | here]. <a name="tktchng"></a> <h2>5.0 Ticket Changes</h2> A ticket-change artifact represents a change to a trouble ticket. The following cards are allowed on a ticket change artifact: |
| ︙ | ︙ | |||
373 374 375 376 377 378 379 | An example ticket-change artifact can be seen [/artifact/91f1ec6af053 | here]. <a name="attachment"></a> <h2>6.0 Attachments</h2> An attachment artifact associates some other artifact that is the | | | | | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | | > > | > > > > > > > > > > > > > > > > > > > > > > > | 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 | An example ticket-change artifact can be seen [/artifact/91f1ec6af053 | here]. <a name="attachment"></a> <h2>6.0 Attachments</h2> An attachment artifact associates some other artifact that is the attachment (the source artifact) with a ticket or wiki page or event to which the attachment is connected (the target artifact). The following cards are allowed on an attachment artifact: <blockquote> <b>A</b> <i>filename target</i> ?<i>source</i>?<br /> <b>C</b> <i>comment</i><br /> <b>D</b> <i>time-and-date-stamp</i><br /> <b>U</b> <i>user-name</i><br /> <b>Z</b> <i>checksum</i> </blockquote> The A card specifies a filename for the attachment in its first argument. The second argument to the A card is the name of the wiki page or ticket or event to which the attachment is connected. The third argument is either missing or else it is the 40-character artifact ID of the attachment itself. A missing third argument means that the attachment should be deleted. The C card is an optional comment describing what the attachment is about. The C card is optional, but there can only be one. A single D card is required to give the date and time when the attachment was applied. A single U card gives the name of the user to added the attachment. If an attachment is added anonymously, then the U card may be omitted. The Z card is the usual checksum over the rest of the attachment artifact. <a name="event"></a> <h2>7.0 Events</h2> An event artifact associates a timeline comment and a page of text (similar to a wiki page) with a point in time. Events can be used to record project milestones, release notes, blog entries, process checkpoints, or news articles. The following cards are allowed on an event artifact: <blockquote> <b>C</b> <i>comment</i><br> <b>D</b> <i>time-and-date-stamp</i><br /> <b>E</b> <i>event-time</i> <i>event-id</i><br /> <b>P</b> <i>parent-artifact-id</i>+<br /> <b>T</b> <b>+</b><i>tag-name</i> <b>*</b> <i>value</i><br /> <b>U</b> <i>user-name</i><br /> <b>W</b> <i>size</i> <b>\n</b> <i>text</i> <b>\n</b><br /> <b>Z</b> <i>checksum</i> </blockquote> The C card contains text that is displayed on the timeline for the event. Exactly one C card is required on an event artifact. A single D card is required to give the date and time when the event artifact was created. This is different from the time at which the event occurs. A single E card gives the time of the event (the point on the timeline where the event is displayed) and a unique identifier for the event. When there are multiple artifacts with the same event-id, the one with the most recent D card is the only one used. The event-id must be a 40-character lower-case hexadecimal string. The option P card specifies a prior event with the same event-id from which the current event is an edit. The P card is a hint to the system that it might be space efficient to store one event as a delta of the other. An event might contain one or more T-cards used to set [./branching.wiki#tags | tags or properties] on the event. The format of the T-card is the same as described in [#ctrl | Control Artifacts] section above, except that the second argument is the single characcter "<b>*</b>" instead of an artifact ID and the name is always prefaced by "<b>+</b>". The <b>*</b> in place of the artifact ID indicates that the tag or property applies to the current artifact. It is not possible to encode the current artifact ID as part of an artifact, since the act of inserting the artifact ID would change the artifact ID, hence a <b>*</b> is used to represent "self". The "<b>+</b>" on the name means that tags can only be add and they can only be non-propagating tags. A an event, T cards are normally used to set the background display color for timelines. The optional U card gives name of the user who entered the event. A single W card provides wiki text for the document associated with the event. The format of the W card is exactly the same as for a [#wikichng | wiki artifact]. The Z card is the usual checksum over the rest of the attachment artifact. <a name="summary"></a> <h2>8.0 Card Summary</h2> The following table summaries the various kinds of cards that appear on Fossil artifacts: <table border=1 width="100%"> <tr> <th rowspan=2 valign=bottom>Card Format</th> <th colspan=7>Used By</th> </tr> <tr> <th>Manifest</th> <th>Cluster</th> <th>Control</th> <th>Wiki</th> <th>Ticket</th> <th>Attachment</th> <th>Event</th> </tr> <tr> <td><b>A</b> <i>filename target source</i></td> <td> </td> <td> </td> <td> </td> <td> </td> <td> </td> <td align=center><b>X</b></td> <td> </td> </tr> <tr> <td><b>C</b> <i>comment-text</i></td> <td align=center><b>X</b></td> <td> </td> <td> </td> <td> </td> <td> </td> <td align=center><b>X</b></td> <td align=center><b>X</b></td> </tr> <tr> <td><b>D</b> <i>date-time-stamp</i></td> <td align=center><b>X</b></td> <td align=center> </td> <td align=center><b>X</b></td> <td align=center><b>X</b></td> <td align=center><b>X</b></td> <td align=center><b>X</b></td> <td align=center><b>X</b></td> </tr> <tr> <td><b>E</b> <i>event-time event-id</i></td> <td align=center> </td> <td align=center> </td> <td align=center> </td> <td align=center> </td> <td align=center> </td> <td align=center> </td> <td align=center><b>X</b></td> </tr> <tr> <td><b>F</b> <i>filename uuid permissions oldname</i></td> <td align=center><b>X</b></td> <td align=center> </td> <td align=center> </td> <td align=center> </td> <td align=center> </td> <td align=center> </td> <td align=center> </td> </tr> <tr> <td><b>J</b> <i>name value</i></td> <td align=center> </td> <td align=center> </td> <td align=center> </td> <td align=center> </td> <td align=center><b>X</b></td> <td align=center> </td> <td align=center> </td> </tr> <tr> <td><b>K</b> <i>ticket-uuid</i></td> <td align=center> </td> <td align=center> </td> <td align=center> </td> <td align=center> </td> <td align=center><b>X</b></td> <td align=center> </td> <td align=center> </td> </tr> <tr> <td><b>L</b> <i>wiki-title</i></td> <td align=center> </td> <td align=center> </td> <td align=center> </td> <td align=center><b>X</b></td> <td align=center> </td> <td align=center> </td> <td align=center> </td> </tr> <tr> <td><b>M</b> <i>uuid</i></td> <td align=center> </td> <td align=center><b>X</b></td> <td align=center> </td> <td align=center> </td> <td align=center> </td> <td align=center> </td> <td align=center> </td> </tr> <tr> <td><b>P</b> <i>uuid ...</i></td> <td align=center><b>X</b></td> <td align=center> </td> <td align=center> </td> <td align=center><b>X</b></td> <td align=center> </td> <td align=center> </td> <td align=center> </td> </tr> <tr> <td><b>R</b> <i>md5sum</i></td> <td align=center><b>X</b></td> <td align=center> </td> <td align=center> </td> <td align=center> </td> <td align=center> </td> <td align=center> </td> <td align=center> </td> <tr> <td><b>T</b> (<b>+</b>|<b>*</b>|<b>-</b>)<i>tagname uuid value</i></td> <td align=center><b>X</b></td> <td align=center> </td> <td align=center><b>X</b></td> <td align=center> </td> <td align=center> </td> <td align=center> </td> <td align=center><b>X</b></td> </tr> <tr> <td><b>U</b> <i>username</i></td> <td align=center><b>X</b></td> <td align=center> </td> <td align=center><b>X</b></td> <td align=center><b>X</b></td> <td align=center><b>X</b></td> <td align=center><b>X</b></td> <td align=center><b>X</b></td> </tr> <tr> <td><b>W</b> <i>size</i></td> <td align=center> </td> <td align=center> </td> <td align=center> </td> <td align=center><b>X</b></td> <td align=center> </td> <td align=center> </td> <td align=center><b>X</b></td> </tr> <tr> <td><b>Z</b> <i>md5sum</i></td> <td align=center><b>X</b></td> <td align=center><b>X</b></td> <td align=center><b>X</b></td> <td align=center><b>X</b></td> <td align=center><b>X</b></td> <td align=center><b>X</b></td> <td align=center><b>X</b></td> </tr> </table> |
Changes to www/index.wiki.
| ︙ | ︙ | |||
38 39 40 41 42 43 44 |
There are plenty of open-source version control systems available on the
internet these days. What makes Fossil worthy of attention?
1. <b>Bug Tracking And Wiki</b> -
In addition to doing [./concepts.wiki | distributed version control]
like Git and Mercurial,
| | | > | 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 |
There are plenty of open-source version control systems available on the
internet these days. What makes Fossil worthy of attention?
1. <b>Bug Tracking And Wiki</b> -
In addition to doing [./concepts.wiki | distributed version control]
like Git and Mercurial,
Fossil also supports [./bugtheory.wiki | distributed bug tracking],
[./wikitheory.wiki | distributed wiki], and a
[./event.wiki | distributed blog] mechanism all in a single
integrated package.
2. <b>Web Interface</b> -
Fossil has a built-in and easy-to-use [./webui.wiki | web interface]
that simplifies project tracking and promotes situational awareness.
Simply type "fossil ui" from within any check-out and Fossil
automatically opens your web browser in a page that gives detailed
|
| ︙ | ︙ | |||
109 110 111 112 113 114 115 116 117 118 119 120 121 122 |
designed to be readable, searchable, and extensible by people
not yet born.
* A tutorial on [./branching.wiki | branching], what it means and how
to do it using fossil.
* The [./selfcheck.wiki | automatic self-check] mechanism
helps insure project integrity.
* Fossil contains a [./wikitheory.wiki | built-in wiki].
* There is a
[http://lists.fossil-scm.org:8080/cgi-bin/mailman/listinfo/fossil-users | mailing list] (with publicly readable
[http://www.mail-archive.com/fossil-users@lists.fossil-scm.org | archives]
available for discussing fossil issues.
* [./stats.wiki | Performance statistics] taken from real-world projects
hosted on fossil.
* How to [./shunning.wiki | delete content] from a fossil repository.
| > > | 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 |
designed to be readable, searchable, and extensible by people
not yet born.
* A tutorial on [./branching.wiki | branching], what it means and how
to do it using fossil.
* The [./selfcheck.wiki | automatic self-check] mechanism
helps insure project integrity.
* Fossil contains a [./wikitheory.wiki | built-in wiki].
* An [./event.wiki | Event] is a special kind of wiki page associated
with a point in time rather than a name.
* There is a
[http://lists.fossil-scm.org:8080/cgi-bin/mailman/listinfo/fossil-users | mailing list] (with publicly readable
[http://www.mail-archive.com/fossil-users@lists.fossil-scm.org | archives]
available for discussing fossil issues.
* [./stats.wiki | Performance statistics] taken from real-world projects
hosted on fossil.
* How to [./shunning.wiki | delete content] from a fossil repository.
|
| ︙ | ︙ |
Changes to www/qandc.wiki.
| ︙ | ︙ | |||
61 62 63 64 65 66 67 | fossil repository <a href="http://www.sqlite.org/docsrc/">here</a>, for example. Other projects are also adopting fossil. But fossil does not yet have the massive user base of git or mercurial. </blockquote> <b>Fossil looks like the bug tracker that would be in your | | | 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 | fossil repository <a href="http://www.sqlite.org/docsrc/">here</a>, for example. Other projects are also adopting fossil. But fossil does not yet have the massive user base of git or mercurial. </blockquote> <b>Fossil looks like the bug tracker that would be in your Linksys Router's administration screen.</b> <blockquote> <p>I take a pragmatic approach to software: form follows function. To me, it is more important to have a reliable, fast, efficient, enduring, and simple DVCS than one that looks pretty.</p> <p>On the other hand, if you have patches that improve the appearance |
| ︙ | ︙ |
Changes to www/wikitheory.wiki.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
<h1>Wiki In [./index.wiki | Fossil]</h1>
Fossil uses [/wiki_rules | wiki markup] for many things:
* Stand-alone wiki pages.
* Description and comments in [./bugtheory.wiki | bug reports].
* Check-in comments.
* [./embeddeddoc.wiki | Embedded documentation] files whose
name ends in "wiki".
The [/wiki_rules | formatting rules] for fossil wiki
are designed to be simple and intuitive. The idea is that wiki provides
paragraph breaks, numbered and bulleted lists, and hyperlinking for
simple documents together with a safe subset of HTML for more complex
formatting tasks.
| > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
<h1>Wiki In [./index.wiki | Fossil]</h1>
Fossil uses [/wiki_rules | wiki markup] for many things:
* Stand-alone wiki pages.
* Description and comments in [./bugtheory.wiki | bug reports].
* Check-in comments.
* [./embeddeddoc.wiki | Embedded documentation] files whose
name ends in "wiki".
* [./event.wiki | Event descriptions].
The [/wiki_rules | formatting rules] for fossil wiki
are designed to be simple and intuitive. The idea is that wiki provides
paragraph breaks, numbered and bulleted lists, and hyperlinking for
simple documents together with a safe subset of HTML for more complex
formatting tasks.
|
| ︙ | ︙ |