Fossil SCM
merged from trunk
Commit
def52067be777e33e62be11c6b998828a729eaf4
Parent
fafcb6c780557d8…
22 files changed
+1
-1
+160
-142
+2
+89
-66
+12
-2
+1
+99
-7
+3
-3
+72
-66
+223
-268
+1
-1
+1
-1
+1
-1
+16
-8
+4
-1
+4
-2
+1
+35
+106
-13
+5
-2
+1
-1
+1
~
src/blob.c
~
src/content.c
~
src/event.c
~
src/info.c
~
src/main.mk
~
src/makemake.tcl
~
src/manifest.c
~
src/printf.c
~
src/rebuild.c
~
src/sqlite3.c
~
src/sqlite3.h
~
src/style.c
~
src/style.c
~
src/timeline.c
~
src/wiki.c
~
src/xfer.c
~
www/branching.wiki
~
www/event.wiki
~
www/fileformat.wiki
~
www/index.wiki
~
www/qandc.wiki
~
www/wikitheory.wiki
+1
-1
| --- src/blob.c | ||
| +++ src/blob.c | ||
| @@ -72,11 +72,11 @@ | ||
| 72 | 72 | |
| 73 | 73 | /* |
| 74 | 74 | ** We find that the built-in isspace() function does not work for |
| 75 | 75 | ** some international character sets. So here is a substitute. |
| 76 | 76 | */ |
| 77 | -static int blob_isspace(char c){ | |
| 77 | +int blob_isspace(char c){ | |
| 78 | 78 | return c==' ' || (c<='\r' && c>='\t'); |
| 79 | 79 | } |
| 80 | 80 | |
| 81 | 81 | /* |
| 82 | 82 | ** COMMAND: test-isspace |
| 83 | 83 |
| --- src/blob.c | |
| +++ src/blob.c | |
| @@ -72,11 +72,11 @@ | |
| 72 | |
| 73 | /* |
| 74 | ** We find that the built-in isspace() function does not work for |
| 75 | ** some international character sets. So here is a substitute. |
| 76 | */ |
| 77 | static int blob_isspace(char c){ |
| 78 | return c==' ' || (c<='\r' && c>='\t'); |
| 79 | } |
| 80 | |
| 81 | /* |
| 82 | ** COMMAND: test-isspace |
| 83 |
| --- src/blob.c | |
| +++ src/blob.c | |
| @@ -72,11 +72,11 @@ | |
| 72 | |
| 73 | /* |
| 74 | ** We find that the built-in isspace() function does not work for |
| 75 | ** some international character sets. So here is a substitute. |
| 76 | */ |
| 77 | int blob_isspace(char c){ |
| 78 | return c==' ' || (c<='\r' && c>='\t'); |
| 79 | } |
| 80 | |
| 81 | /* |
| 82 | ** COMMAND: test-isspace |
| 83 |
+160
-142
| --- src/content.c | ||
| +++ src/content.c | ||
| @@ -19,33 +19,25 @@ | ||
| 19 | 19 | */ |
| 20 | 20 | #include "config.h" |
| 21 | 21 | #include "content.h" |
| 22 | 22 | #include <assert.h> |
| 23 | 23 | |
| 24 | -/* | |
| 25 | -** Macros for debugging | |
| 26 | -*/ | |
| 27 | -#if 0 | |
| 28 | -# define CONTENT_TRACE(X) printf X; | |
| 29 | -#else | |
| 30 | -# define CONTENT_TRACE(X) | |
| 31 | -#endif | |
| 32 | - | |
| 33 | 24 | /* |
| 34 | 25 | ** The artifact retrival cache |
| 35 | 26 | */ |
| 36 | -#define MX_CACHE_CNT 50 /* Maximum number of positive cache entries */ | |
| 37 | -#define EXPELL_INTERVAL 5 /* How often to expell from a full cache */ | |
| 38 | 27 | static struct { |
| 39 | - int n; /* Current number of positive cache entries */ | |
| 28 | + i64 szTotal; /* Total size of all entries in the cache */ | |
| 29 | + int n; /* Current number of eache entries */ | |
| 30 | + int nAlloc; /* Number of slots allocated in a[] */ | |
| 40 | 31 | int nextAge; /* Age counter for implementing LRU */ |
| 41 | 32 | int skipCnt; /* Used to limit entries expelled from cache */ |
| 42 | - struct { /* One instance of this for each cache entry */ | |
| 33 | + struct cacheLine { /* One instance of this for each cache entry */ | |
| 43 | 34 | int rid; /* Artifact id */ |
| 44 | 35 | int age; /* Age. Newer is larger */ |
| 45 | 36 | Blob content; /* Content of the artifact */ |
| 46 | - } a[MX_CACHE_CNT]; /* The positive cache */ | |
| 37 | + } *a; /* The positive cache */ | |
| 38 | + Bag inCache; /* Set of artifacts currently in cache */ | |
| 47 | 39 | |
| 48 | 40 | /* |
| 49 | 41 | ** The missing artifact cache. |
| 50 | 42 | ** |
| 51 | 43 | ** Artifacts whose record ID are in missingCache cannot be retrieved |
| @@ -56,10 +48,54 @@ | ||
| 56 | 48 | */ |
| 57 | 49 | Bag missing; /* Cache of artifacts that are incomplete */ |
| 58 | 50 | Bag available; /* Cache of artifacts that are complete */ |
| 59 | 51 | } contentCache; |
| 60 | 52 | |
| 53 | +/* | |
| 54 | +** Remove the oldest element from the content cache | |
| 55 | +*/ | |
| 56 | +static void content_cache_expire_oldest(void){ | |
| 57 | + int i; | |
| 58 | + int mnAge = contentCache.nextAge; | |
| 59 | + int mn = -1; | |
| 60 | + for(i=0; i<contentCache.n; i++){ | |
| 61 | + if( contentCache.a[i].age<mnAge ){ | |
| 62 | + mnAge = contentCache.a[i].age; | |
| 63 | + mn = i; | |
| 64 | + } | |
| 65 | + } | |
| 66 | + if( mn>=0 ){ | |
| 67 | + bag_remove(&contentCache.inCache, contentCache.a[mn].rid); | |
| 68 | + contentCache.szTotal -= blob_size(&contentCache.a[mn].content); | |
| 69 | + blob_reset(&contentCache.a[mn].content); | |
| 70 | + contentCache.n--; | |
| 71 | + contentCache.a[mn] = contentCache.a[contentCache.n]; | |
| 72 | + } | |
| 73 | +} | |
| 74 | + | |
| 75 | +/* | |
| 76 | +** Add an entry to the content cache | |
| 77 | +*/ | |
| 78 | +void content_cache_insert(int rid, Blob *pBlob){ | |
| 79 | + struct cacheLine *p; | |
| 80 | + if( contentCache.n>500 || contentCache.szTotal>50000000 ){ | |
| 81 | + content_cache_expire_oldest(); | |
| 82 | + } | |
| 83 | + if( contentCache.n>=contentCache.nAlloc ){ | |
| 84 | + contentCache.nAlloc = contentCache.nAlloc*2 + 10; | |
| 85 | + contentCache.a = realloc(contentCache.a, | |
| 86 | + contentCache.nAlloc*sizeof(contentCache.a[0])); | |
| 87 | + if( contentCache.a==0 ) fossil_panic("out of memory"); | |
| 88 | + } | |
| 89 | + p = &contentCache.a[contentCache.n++]; | |
| 90 | + p->rid = rid; | |
| 91 | + p->age = contentCache.nextAge++; | |
| 92 | + contentCache.szTotal += blob_size(pBlob); | |
| 93 | + p->content = *pBlob; | |
| 94 | + blob_zero(pBlob); | |
| 95 | + bag_insert(&contentCache.inCache, rid); | |
| 96 | +} | |
| 61 | 97 | |
| 62 | 98 | /* |
| 63 | 99 | ** Clear the content cache. |
| 64 | 100 | */ |
| 65 | 101 | void content_clear_cache(void){ |
| @@ -67,11 +103,13 @@ | ||
| 67 | 103 | for(i=0; i<contentCache.n; i++){ |
| 68 | 104 | blob_reset(&contentCache.a[i].content); |
| 69 | 105 | } |
| 70 | 106 | bag_clear(&contentCache.missing); |
| 71 | 107 | bag_clear(&contentCache.available); |
| 108 | + bag_clear(&contentCache.inCache); | |
| 72 | 109 | contentCache.n = 0; |
| 110 | + contentCache.szTotal = 0; | |
| 73 | 111 | } |
| 74 | 112 | |
| 75 | 113 | /* |
| 76 | 114 | ** Return the srcid associated with rid. Or return 0 if rid is |
| 77 | 115 | ** original content and not a delta. |
| @@ -95,32 +133,31 @@ | ||
| 95 | 133 | ** true if it is. Return false if rid is a phantom or depends on |
| 96 | 134 | ** a phantom. |
| 97 | 135 | */ |
| 98 | 136 | int content_is_available(int rid){ |
| 99 | 137 | int srcid; |
| 100 | - if( bag_find(&contentCache.missing, rid) ){ | |
| 101 | - return 0; | |
| 102 | - } | |
| 103 | - if( bag_find(&contentCache.available, rid) ){ | |
| 104 | - return 1; | |
| 105 | - } | |
| 106 | - if( db_int(-1, "SELECT size FROM blob WHERE rid=%d", rid)<0 ){ | |
| 107 | - bag_insert(&contentCache.missing, rid); | |
| 108 | - return 0; | |
| 109 | - } | |
| 110 | - srcid = findSrcid(rid); | |
| 111 | - if( srcid==0 ){ | |
| 112 | - bag_insert(&contentCache.available, rid); | |
| 113 | - return 1; | |
| 114 | - } | |
| 115 | - if( content_is_available(srcid) ){ | |
| 116 | - bag_insert(&contentCache.available, rid); | |
| 117 | - return 1; | |
| 118 | - }else{ | |
| 119 | - bag_insert(&contentCache.missing, rid); | |
| 120 | - return 0; | |
| 121 | - } | |
| 138 | + int depth = 0; /* Limit to recursion depth */ | |
| 139 | + while( depth++ < 10000000 ){ | |
| 140 | + if( bag_find(&contentCache.missing, rid) ){ | |
| 141 | + return 0; | |
| 142 | + } | |
| 143 | + if( bag_find(&contentCache.available, rid) ){ | |
| 144 | + return 1; | |
| 145 | + } | |
| 146 | + if( db_int(-1, "SELECT size FROM blob WHERE rid=%d", rid)<0 ){ | |
| 147 | + bag_insert(&contentCache.missing, rid); | |
| 148 | + return 0; | |
| 149 | + } | |
| 150 | + srcid = findSrcid(rid); | |
| 151 | + if( srcid==0 ){ | |
| 152 | + bag_insert(&contentCache.available, rid); | |
| 153 | + return 1; | |
| 154 | + } | |
| 155 | + rid = srcid; | |
| 156 | + } | |
| 157 | + fossil_panic("delta-loop in repository"); | |
| 158 | + return 0; | |
| 122 | 159 | } |
| 123 | 160 | |
| 124 | 161 | /* |
| 125 | 162 | ** Mark artifact rid as being available now. Update the cache to |
| 126 | 163 | ** show that everything that was formerly unavailable because rid |
| @@ -163,113 +200,84 @@ | ||
| 163 | 200 | } |
| 164 | 201 | db_reset(&q); |
| 165 | 202 | return rc; |
| 166 | 203 | } |
| 167 | 204 | |
| 168 | - | |
| 169 | 205 | /* |
| 170 | 206 | ** Extract the content for ID rid and put it into the |
| 171 | 207 | ** uninitialized blob. Return 1 on success. If the record |
| 172 | 208 | ** is a phantom, zero pBlob and return 0. |
| 173 | 209 | */ |
| 174 | 210 | int content_get(int rid, Blob *pBlob){ |
| 175 | - Blob src; | |
| 176 | - int srcid; | |
| 177 | - int rc = 0; | |
| 211 | + int rc; | |
| 178 | 212 | int i; |
| 179 | - static Bag inProcess; | |
| 213 | + int nextRid; | |
| 180 | 214 | |
| 181 | 215 | assert( g.repositoryOpen ); |
| 182 | 216 | blob_zero(pBlob); |
| 183 | 217 | if( rid==0 ) return 0; |
| 184 | 218 | |
| 185 | 219 | /* Early out if we know the content is not available */ |
| 186 | 220 | if( bag_find(&contentCache.missing, rid) ){ |
| 187 | - CONTENT_TRACE(("%*smiss from cache: %d\n", | |
| 188 | - bag_count(&inProcess), "", rid)) | |
| 189 | 221 | return 0; |
| 190 | 222 | } |
| 191 | 223 | |
| 192 | 224 | /* Look for the artifact in the cache first */ |
| 193 | - for(i=0; i<contentCache.n; i++){ | |
| 194 | - if( contentCache.a[i].rid==rid ){ | |
| 195 | - *pBlob = contentCache.a[i].content; | |
| 196 | - blob_zero(&contentCache.a[i].content); | |
| 197 | - contentCache.n--; | |
| 198 | - if( i<contentCache.n ){ | |
| 199 | - contentCache.a[i] = contentCache.a[contentCache.n]; | |
| 200 | - } | |
| 201 | - CONTENT_TRACE(("%*scache: %d\n", | |
| 202 | - bag_count(&inProcess), "", rid)) | |
| 203 | - return 1; | |
| 204 | - } | |
| 205 | - } | |
| 206 | - | |
| 207 | - /* See if we need to apply a delta to find this artifact */ | |
| 208 | - srcid = findSrcid(rid); | |
| 209 | - CONTENT_TRACE(("%*ssearching for %d. Need %d.\n", | |
| 210 | - bag_count(&inProcess), "", rid, srcid)) | |
| 211 | - | |
| 212 | - | |
| 213 | - if( srcid ){ | |
| 214 | - /* Yes, a delta is required */ | |
| 215 | - if( bag_find(&inProcess, srcid) ){ | |
| 216 | - db_multi_exec( | |
| 217 | - "UPDATE blob SET content=NULL, size=-1 WHERE rid=%d;" | |
| 218 | - "DELETE FROM delta WHERE rid=%d;" | |
| 219 | - "INSERT OR IGNORE INTO phantom VALUES(%d);", | |
| 220 | - srcid, srcid, srcid | |
| 221 | - ); | |
| 222 | - blob_zero(pBlob); | |
| 223 | - return 0; | |
| 224 | - } | |
| 225 | - bag_insert(&inProcess, srcid); | |
| 226 | - | |
| 227 | - if( content_get(srcid, &src) ){ | |
| 228 | - Blob delta; | |
| 229 | - if( content_of_blob(rid, &delta) ){ | |
| 230 | - blob_init(pBlob,0,0); | |
| 231 | - blob_delta_apply(&src, &delta, pBlob); | |
| 225 | + if( bag_find(&contentCache.inCache, rid) ){ | |
| 226 | + for(i=0; i<contentCache.n; i++){ | |
| 227 | + if( contentCache.a[i].rid==rid ){ | |
| 228 | + blob_copy(pBlob, &contentCache.a[i].content); | |
| 229 | + contentCache.a[i].age = contentCache.nextAge++; | |
| 230 | + return 1; | |
| 231 | + } | |
| 232 | + } | |
| 233 | + } | |
| 234 | + | |
| 235 | + nextRid = findSrcid(rid); | |
| 236 | + if( nextRid==0 ){ | |
| 237 | + rc = content_of_blob(rid, pBlob); | |
| 238 | + }else{ | |
| 239 | + int n = 1; | |
| 240 | + int nAlloc = 10; | |
| 241 | + int *a = 0; | |
| 242 | + int mx; | |
| 243 | + Blob delta, next; | |
| 244 | + | |
| 245 | + a = malloc( sizeof(a[0])*nAlloc ); | |
| 246 | + if( a==0 ) fossil_panic("out of memory"); | |
| 247 | + a[0] = rid; | |
| 248 | + a[1] = nextRid; | |
| 249 | + n = 1; | |
| 250 | + while( !bag_find(&contentCache.inCache, nextRid) | |
| 251 | + && (nextRid = findSrcid(nextRid))>0 ){ | |
| 252 | + n++; | |
| 253 | + if( n>=nAlloc ){ | |
| 254 | + nAlloc = nAlloc*2 + 10; | |
| 255 | + a = realloc(a, nAlloc*sizeof(a[0])); | |
| 256 | + if( a==0 ) fossil_panic("out of memory"); | |
| 257 | + } | |
| 258 | + a[n] = nextRid; | |
| 259 | + } | |
| 260 | + mx = n; | |
| 261 | + rc = content_get(a[n], pBlob); | |
| 262 | + n--; | |
| 263 | + while( rc && n>=0 ){ | |
| 264 | + rc = content_of_blob(a[n], &delta); | |
| 265 | + if( rc ){ | |
| 266 | + blob_delta_apply(pBlob, &delta, &next); | |
| 232 | 267 | blob_reset(&delta); |
| 233 | - rc = 1; | |
| 234 | - } | |
| 235 | - | |
| 236 | - /* Save the srcid artifact in the cache */ | |
| 237 | - if( contentCache.n<MX_CACHE_CNT ){ | |
| 238 | - i = contentCache.n++; | |
| 239 | - }else if( ((contentCache.skipCnt++)%EXPELL_INTERVAL)!=0 ){ | |
| 240 | - i = -1; | |
| 241 | - }else{ | |
| 242 | - int j, best; | |
| 243 | - best = contentCache.nextAge+1; | |
| 244 | - i = -1; | |
| 245 | - for(j=0; j<contentCache.n; j++){ | |
| 246 | - if( contentCache.a[j].age<best ){ | |
| 247 | - i = j; | |
| 248 | - best = contentCache.a[j].age; | |
| 249 | - } | |
| 250 | - } | |
| 251 | - CONTENT_TRACE(("%*sexpell %d from cache\n", | |
| 252 | - bag_count(&inProcess), "", contentCache.a[i].rid)) | |
| 253 | - blob_reset(&contentCache.a[i].content); | |
| 254 | - } | |
| 255 | - if( i>=0 ){ | |
| 256 | - contentCache.a[i].content = src; | |
| 257 | - contentCache.a[i].age = contentCache.nextAge++; | |
| 258 | - contentCache.a[i].rid = srcid; | |
| 259 | - CONTENT_TRACE(("%*sadd %d to cache\n", | |
| 260 | - bag_count(&inProcess), "", srcid)) | |
| 261 | - }else{ | |
| 262 | - blob_reset(&src); | |
| 263 | - } | |
| 264 | - } | |
| 265 | - bag_remove(&inProcess, srcid); | |
| 266 | - }else{ | |
| 267 | - /* No delta required. Read content directly from the database */ | |
| 268 | - if( content_of_blob(rid, pBlob) ){ | |
| 269 | - rc = 1; | |
| 270 | - } | |
| 268 | + if( (mx-n)%8==0 ){ | |
| 269 | + content_cache_insert(a[n+1], pBlob); | |
| 270 | + }else{ | |
| 271 | + blob_reset(pBlob); | |
| 272 | + } | |
| 273 | + *pBlob = next; | |
| 274 | + } | |
| 275 | + n--; | |
| 276 | + } | |
| 277 | + free(a); | |
| 278 | + if( !rc ) blob_reset(pBlob); | |
| 271 | 279 | } |
| 272 | 280 | if( rc==0 ){ |
| 273 | 281 | bag_insert(&contentCache.missing, rid); |
| 274 | 282 | }else{ |
| 275 | 283 | bag_insert(&contentCache.available, rid); |
| @@ -320,35 +328,45 @@ | ||
| 320 | 328 | |
| 321 | 329 | /* |
| 322 | 330 | ** When a record is converted from a phantom to a real record, |
| 323 | 331 | ** if that record has other records that are derived by delta, |
| 324 | 332 | ** then call manifest_crosslink() on those other records. |
| 333 | +** | |
| 334 | +** Tail recursion is used to minimize stack depth. | |
| 325 | 335 | */ |
| 326 | 336 | void after_dephantomize(int rid, int linkFlag){ |
| 327 | 337 | Stmt q; |
| 328 | - int prevTid = 0; | |
| 329 | - | |
| 330 | - /* The prevTid variable is used to delay invoking this routine | |
| 331 | - ** recursively, if possible, until after the query has finalized, | |
| 332 | - ** in order to avoid having an excessive number of prepared statements. | |
| 333 | - ** This is most effective in the common case where the query returns | |
| 334 | - ** just one row. | |
| 335 | - */ | |
| 336 | - db_prepare(&q, "SELECT rid FROM delta WHERE srcid=%d", rid); | |
| 337 | - while( db_step(&q)==SQLITE_ROW ){ | |
| 338 | - int tid = db_column_int(&q, 0); | |
| 339 | - if( prevTid ) after_dephantomize(prevTid, 1); | |
| 340 | - prevTid = tid; | |
| 341 | - } | |
| 342 | - db_finalize(&q); | |
| 343 | - if( prevTid ) after_dephantomize(prevTid, 1); | |
| 344 | - if( linkFlag ){ | |
| 345 | - Blob content; | |
| 346 | - content_get(rid, &content); | |
| 347 | - manifest_crosslink(rid, &content); | |
| 348 | - blob_reset(&content); | |
| 349 | - } | |
| 338 | + int nChildAlloc = 0; | |
| 339 | + int *aChild = 0; | |
| 340 | + | |
| 341 | + while( rid ){ | |
| 342 | + int nChildUsed = 0; | |
| 343 | + int i; | |
| 344 | + if( linkFlag ){ | |
| 345 | + Blob content; | |
| 346 | + content_get(rid, &content); | |
| 347 | + manifest_crosslink(rid, &content); | |
| 348 | + blob_reset(&content); | |
| 349 | + } | |
| 350 | + db_prepare(&q, "SELECT rid FROM delta WHERE srcid=%d", rid); | |
| 351 | + while( db_step(&q)==SQLITE_ROW ){ | |
| 352 | + int child = db_column_int(&q, 0); | |
| 353 | + if( nChildUsed>=nChildAlloc ){ | |
| 354 | + nChildAlloc = nChildAlloc*2 + 10; | |
| 355 | + aChild = realloc(aChild, nChildAlloc*sizeof(aChild)); | |
| 356 | + if( aChild==0 ) fossil_panic("out of memory"); | |
| 357 | + } | |
| 358 | + aChild[nChildUsed++] = child; | |
| 359 | + } | |
| 360 | + db_finalize(&q); | |
| 361 | + for(i=1; i<nChildUsed; i++){ | |
| 362 | + after_dephantomize(aChild[i], 1); | |
| 363 | + } | |
| 364 | + rid = nChildUsed>0 ? aChild[0] : 0; | |
| 365 | + linkFlag = 1; | |
| 366 | + } | |
| 367 | + free(aChild); | |
| 350 | 368 | } |
| 351 | 369 | |
| 352 | 370 | /* |
| 353 | 371 | ** Write content into the database. Return the record ID. If the |
| 354 | 372 | ** content is already in the database, just return the record ID. |
| 355 | 373 | |
| 356 | 374 | ADDED src/event.c |
| --- src/content.c | |
| +++ src/content.c | |
| @@ -19,33 +19,25 @@ | |
| 19 | */ |
| 20 | #include "config.h" |
| 21 | #include "content.h" |
| 22 | #include <assert.h> |
| 23 | |
| 24 | /* |
| 25 | ** Macros for debugging |
| 26 | */ |
| 27 | #if 0 |
| 28 | # define CONTENT_TRACE(X) printf X; |
| 29 | #else |
| 30 | # define CONTENT_TRACE(X) |
| 31 | #endif |
| 32 | |
| 33 | /* |
| 34 | ** The artifact retrival cache |
| 35 | */ |
| 36 | #define MX_CACHE_CNT 50 /* Maximum number of positive cache entries */ |
| 37 | #define EXPELL_INTERVAL 5 /* How often to expell from a full cache */ |
| 38 | static struct { |
| 39 | int n; /* Current number of positive cache entries */ |
| 40 | int nextAge; /* Age counter for implementing LRU */ |
| 41 | int skipCnt; /* Used to limit entries expelled from cache */ |
| 42 | struct { /* One instance of this for each cache entry */ |
| 43 | int rid; /* Artifact id */ |
| 44 | int age; /* Age. Newer is larger */ |
| 45 | Blob content; /* Content of the artifact */ |
| 46 | } a[MX_CACHE_CNT]; /* The positive cache */ |
| 47 | |
| 48 | /* |
| 49 | ** The missing artifact cache. |
| 50 | ** |
| 51 | ** Artifacts whose record ID are in missingCache cannot be retrieved |
| @@ -56,10 +48,54 @@ | |
| 56 | */ |
| 57 | Bag missing; /* Cache of artifacts that are incomplete */ |
| 58 | Bag available; /* Cache of artifacts that are complete */ |
| 59 | } contentCache; |
| 60 | |
| 61 | |
| 62 | /* |
| 63 | ** Clear the content cache. |
| 64 | */ |
| 65 | void content_clear_cache(void){ |
| @@ -67,11 +103,13 @@ | |
| 67 | for(i=0; i<contentCache.n; i++){ |
| 68 | blob_reset(&contentCache.a[i].content); |
| 69 | } |
| 70 | bag_clear(&contentCache.missing); |
| 71 | bag_clear(&contentCache.available); |
| 72 | contentCache.n = 0; |
| 73 | } |
| 74 | |
| 75 | /* |
| 76 | ** Return the srcid associated with rid. Or return 0 if rid is |
| 77 | ** original content and not a delta. |
| @@ -95,32 +133,31 @@ | |
| 95 | ** true if it is. Return false if rid is a phantom or depends on |
| 96 | ** a phantom. |
| 97 | */ |
| 98 | int content_is_available(int rid){ |
| 99 | int srcid; |
| 100 | if( bag_find(&contentCache.missing, rid) ){ |
| 101 | return 0; |
| 102 | } |
| 103 | if( bag_find(&contentCache.available, rid) ){ |
| 104 | return 1; |
| 105 | } |
| 106 | if( db_int(-1, "SELECT size FROM blob WHERE rid=%d", rid)<0 ){ |
| 107 | bag_insert(&contentCache.missing, rid); |
| 108 | return 0; |
| 109 | } |
| 110 | srcid = findSrcid(rid); |
| 111 | if( srcid==0 ){ |
| 112 | bag_insert(&contentCache.available, rid); |
| 113 | return 1; |
| 114 | } |
| 115 | if( content_is_available(srcid) ){ |
| 116 | bag_insert(&contentCache.available, rid); |
| 117 | return 1; |
| 118 | }else{ |
| 119 | bag_insert(&contentCache.missing, rid); |
| 120 | return 0; |
| 121 | } |
| 122 | } |
| 123 | |
| 124 | /* |
| 125 | ** Mark artifact rid as being available now. Update the cache to |
| 126 | ** show that everything that was formerly unavailable because rid |
| @@ -163,113 +200,84 @@ | |
| 163 | } |
| 164 | db_reset(&q); |
| 165 | return rc; |
| 166 | } |
| 167 | |
| 168 | |
| 169 | /* |
| 170 | ** Extract the content for ID rid and put it into the |
| 171 | ** uninitialized blob. Return 1 on success. If the record |
| 172 | ** is a phantom, zero pBlob and return 0. |
| 173 | */ |
| 174 | int content_get(int rid, Blob *pBlob){ |
| 175 | Blob src; |
| 176 | int srcid; |
| 177 | int rc = 0; |
| 178 | int i; |
| 179 | static Bag inProcess; |
| 180 | |
| 181 | assert( g.repositoryOpen ); |
| 182 | blob_zero(pBlob); |
| 183 | if( rid==0 ) return 0; |
| 184 | |
| 185 | /* Early out if we know the content is not available */ |
| 186 | if( bag_find(&contentCache.missing, rid) ){ |
| 187 | CONTENT_TRACE(("%*smiss from cache: %d\n", |
| 188 | bag_count(&inProcess), "", rid)) |
| 189 | return 0; |
| 190 | } |
| 191 | |
| 192 | /* Look for the artifact in the cache first */ |
| 193 | for(i=0; i<contentCache.n; i++){ |
| 194 | if( contentCache.a[i].rid==rid ){ |
| 195 | *pBlob = contentCache.a[i].content; |
| 196 | blob_zero(&contentCache.a[i].content); |
| 197 | contentCache.n--; |
| 198 | if( i<contentCache.n ){ |
| 199 | contentCache.a[i] = contentCache.a[contentCache.n]; |
| 200 | } |
| 201 | CONTENT_TRACE(("%*scache: %d\n", |
| 202 | bag_count(&inProcess), "", rid)) |
| 203 | return 1; |
| 204 | } |
| 205 | } |
| 206 | |
| 207 | /* See if we need to apply a delta to find this artifact */ |
| 208 | srcid = findSrcid(rid); |
| 209 | CONTENT_TRACE(("%*ssearching for %d. Need %d.\n", |
| 210 | bag_count(&inProcess), "", rid, srcid)) |
| 211 | |
| 212 | |
| 213 | if( srcid ){ |
| 214 | /* Yes, a delta is required */ |
| 215 | if( bag_find(&inProcess, srcid) ){ |
| 216 | db_multi_exec( |
| 217 | "UPDATE blob SET content=NULL, size=-1 WHERE rid=%d;" |
| 218 | "DELETE FROM delta WHERE rid=%d;" |
| 219 | "INSERT OR IGNORE INTO phantom VALUES(%d);", |
| 220 | srcid, srcid, srcid |
| 221 | ); |
| 222 | blob_zero(pBlob); |
| 223 | return 0; |
| 224 | } |
| 225 | bag_insert(&inProcess, srcid); |
| 226 | |
| 227 | if( content_get(srcid, &src) ){ |
| 228 | Blob delta; |
| 229 | if( content_of_blob(rid, &delta) ){ |
| 230 | blob_init(pBlob,0,0); |
| 231 | blob_delta_apply(&src, &delta, pBlob); |
| 232 | blob_reset(&delta); |
| 233 | rc = 1; |
| 234 | } |
| 235 | |
| 236 | /* Save the srcid artifact in the cache */ |
| 237 | if( contentCache.n<MX_CACHE_CNT ){ |
| 238 | i = contentCache.n++; |
| 239 | }else if( ((contentCache.skipCnt++)%EXPELL_INTERVAL)!=0 ){ |
| 240 | i = -1; |
| 241 | }else{ |
| 242 | int j, best; |
| 243 | best = contentCache.nextAge+1; |
| 244 | i = -1; |
| 245 | for(j=0; j<contentCache.n; j++){ |
| 246 | if( contentCache.a[j].age<best ){ |
| 247 | i = j; |
| 248 | best = contentCache.a[j].age; |
| 249 | } |
| 250 | } |
| 251 | CONTENT_TRACE(("%*sexpell %d from cache\n", |
| 252 | bag_count(&inProcess), "", contentCache.a[i].rid)) |
| 253 | blob_reset(&contentCache.a[i].content); |
| 254 | } |
| 255 | if( i>=0 ){ |
| 256 | contentCache.a[i].content = src; |
| 257 | contentCache.a[i].age = contentCache.nextAge++; |
| 258 | contentCache.a[i].rid = srcid; |
| 259 | CONTENT_TRACE(("%*sadd %d to cache\n", |
| 260 | bag_count(&inProcess), "", srcid)) |
| 261 | }else{ |
| 262 | blob_reset(&src); |
| 263 | } |
| 264 | } |
| 265 | bag_remove(&inProcess, srcid); |
| 266 | }else{ |
| 267 | /* No delta required. Read content directly from the database */ |
| 268 | if( content_of_blob(rid, pBlob) ){ |
| 269 | rc = 1; |
| 270 | } |
| 271 | } |
| 272 | if( rc==0 ){ |
| 273 | bag_insert(&contentCache.missing, rid); |
| 274 | }else{ |
| 275 | bag_insert(&contentCache.available, rid); |
| @@ -320,35 +328,45 @@ | |
| 320 | |
| 321 | /* |
| 322 | ** When a record is converted from a phantom to a real record, |
| 323 | ** if that record has other records that are derived by delta, |
| 324 | ** then call manifest_crosslink() on those other records. |
| 325 | */ |
| 326 | void after_dephantomize(int rid, int linkFlag){ |
| 327 | Stmt q; |
| 328 | int prevTid = 0; |
| 329 | |
| 330 | /* The prevTid variable is used to delay invoking this routine |
| 331 | ** recursively, if possible, until after the query has finalized, |
| 332 | ** in order to avoid having an excessive number of prepared statements. |
| 333 | ** This is most effective in the common case where the query returns |
| 334 | ** just one row. |
| 335 | */ |
| 336 | db_prepare(&q, "SELECT rid FROM delta WHERE srcid=%d", rid); |
| 337 | while( db_step(&q)==SQLITE_ROW ){ |
| 338 | int tid = db_column_int(&q, 0); |
| 339 | if( prevTid ) after_dephantomize(prevTid, 1); |
| 340 | prevTid = tid; |
| 341 | } |
| 342 | db_finalize(&q); |
| 343 | if( prevTid ) after_dephantomize(prevTid, 1); |
| 344 | if( linkFlag ){ |
| 345 | Blob content; |
| 346 | content_get(rid, &content); |
| 347 | manifest_crosslink(rid, &content); |
| 348 | blob_reset(&content); |
| 349 | } |
| 350 | } |
| 351 | |
| 352 | /* |
| 353 | ** Write content into the database. Return the record ID. If the |
| 354 | ** content is already in the database, just return the record ID. |
| 355 | |
| 356 | DDED src/event.c |
| --- src/content.c | |
| +++ src/content.c | |
| @@ -19,33 +19,25 @@ | |
| 19 | */ |
| 20 | #include "config.h" |
| 21 | #include "content.h" |
| 22 | #include <assert.h> |
| 23 | |
| 24 | /* |
| 25 | ** The artifact retrival cache |
| 26 | */ |
| 27 | static struct { |
| 28 | i64 szTotal; /* Total size of all entries in the cache */ |
| 29 | int n; /* Current number of eache entries */ |
| 30 | int nAlloc; /* Number of slots allocated in a[] */ |
| 31 | int nextAge; /* Age counter for implementing LRU */ |
| 32 | int skipCnt; /* Used to limit entries expelled from cache */ |
| 33 | struct cacheLine { /* One instance of this for each cache entry */ |
| 34 | int rid; /* Artifact id */ |
| 35 | int age; /* Age. Newer is larger */ |
| 36 | Blob content; /* Content of the artifact */ |
| 37 | } *a; /* The positive cache */ |
| 38 | Bag inCache; /* Set of artifacts currently in cache */ |
| 39 | |
| 40 | /* |
| 41 | ** The missing artifact cache. |
| 42 | ** |
| 43 | ** Artifacts whose record ID are in missingCache cannot be retrieved |
| @@ -56,10 +48,54 @@ | |
| 48 | */ |
| 49 | Bag missing; /* Cache of artifacts that are incomplete */ |
| 50 | Bag available; /* Cache of artifacts that are complete */ |
| 51 | } contentCache; |
| 52 | |
| 53 | /* |
| 54 | ** Remove the oldest element from the content cache |
| 55 | */ |
| 56 | static void content_cache_expire_oldest(void){ |
| 57 | int i; |
| 58 | int mnAge = contentCache.nextAge; |
| 59 | int mn = -1; |
| 60 | for(i=0; i<contentCache.n; i++){ |
| 61 | if( contentCache.a[i].age<mnAge ){ |
| 62 | mnAge = contentCache.a[i].age; |
| 63 | mn = i; |
| 64 | } |
| 65 | } |
| 66 | if( mn>=0 ){ |
| 67 | bag_remove(&contentCache.inCache, contentCache.a[mn].rid); |
| 68 | contentCache.szTotal -= blob_size(&contentCache.a[mn].content); |
| 69 | blob_reset(&contentCache.a[mn].content); |
| 70 | contentCache.n--; |
| 71 | contentCache.a[mn] = contentCache.a[contentCache.n]; |
| 72 | } |
| 73 | } |
| 74 | |
| 75 | /* |
| 76 | ** Add an entry to the content cache |
| 77 | */ |
| 78 | void content_cache_insert(int rid, Blob *pBlob){ |
| 79 | struct cacheLine *p; |
| 80 | if( contentCache.n>500 || contentCache.szTotal>50000000 ){ |
| 81 | content_cache_expire_oldest(); |
| 82 | } |
| 83 | if( contentCache.n>=contentCache.nAlloc ){ |
| 84 | contentCache.nAlloc = contentCache.nAlloc*2 + 10; |
| 85 | contentCache.a = realloc(contentCache.a, |
| 86 | contentCache.nAlloc*sizeof(contentCache.a[0])); |
| 87 | if( contentCache.a==0 ) fossil_panic("out of memory"); |
| 88 | } |
| 89 | p = &contentCache.a[contentCache.n++]; |
| 90 | p->rid = rid; |
| 91 | p->age = contentCache.nextAge++; |
| 92 | contentCache.szTotal += blob_size(pBlob); |
| 93 | p->content = *pBlob; |
| 94 | blob_zero(pBlob); |
| 95 | bag_insert(&contentCache.inCache, rid); |
| 96 | } |
| 97 | |
| 98 | /* |
| 99 | ** Clear the content cache. |
| 100 | */ |
| 101 | void content_clear_cache(void){ |
| @@ -67,11 +103,13 @@ | |
| 103 | for(i=0; i<contentCache.n; i++){ |
| 104 | blob_reset(&contentCache.a[i].content); |
| 105 | } |
| 106 | bag_clear(&contentCache.missing); |
| 107 | bag_clear(&contentCache.available); |
| 108 | bag_clear(&contentCache.inCache); |
| 109 | contentCache.n = 0; |
| 110 | contentCache.szTotal = 0; |
| 111 | } |
| 112 | |
| 113 | /* |
| 114 | ** Return the srcid associated with rid. Or return 0 if rid is |
| 115 | ** original content and not a delta. |
| @@ -95,32 +133,31 @@ | |
| 133 | ** true if it is. Return false if rid is a phantom or depends on |
| 134 | ** a phantom. |
| 135 | */ |
| 136 | int content_is_available(int rid){ |
| 137 | int srcid; |
| 138 | int depth = 0; /* Limit to recursion depth */ |
| 139 | while( depth++ < 10000000 ){ |
| 140 | if( bag_find(&contentCache.missing, rid) ){ |
| 141 | return 0; |
| 142 | } |
| 143 | if( bag_find(&contentCache.available, rid) ){ |
| 144 | return 1; |
| 145 | } |
| 146 | if( db_int(-1, "SELECT size FROM blob WHERE rid=%d", rid)<0 ){ |
| 147 | bag_insert(&contentCache.missing, rid); |
| 148 | return 0; |
| 149 | } |
| 150 | srcid = findSrcid(rid); |
| 151 | if( srcid==0 ){ |
| 152 | bag_insert(&contentCache.available, rid); |
| 153 | return 1; |
| 154 | } |
| 155 | rid = srcid; |
| 156 | } |
| 157 | fossil_panic("delta-loop in repository"); |
| 158 | return 0; |
| 159 | } |
| 160 | |
| 161 | /* |
| 162 | ** Mark artifact rid as being available now. Update the cache to |
| 163 | ** show that everything that was formerly unavailable because rid |
| @@ -163,113 +200,84 @@ | |
| 200 | } |
| 201 | db_reset(&q); |
| 202 | return rc; |
| 203 | } |
| 204 | |
| 205 | /* |
| 206 | ** Extract the content for ID rid and put it into the |
| 207 | ** uninitialized blob. Return 1 on success. If the record |
| 208 | ** is a phantom, zero pBlob and return 0. |
| 209 | */ |
| 210 | int content_get(int rid, Blob *pBlob){ |
| 211 | int rc; |
| 212 | int i; |
| 213 | int nextRid; |
| 214 | |
| 215 | assert( g.repositoryOpen ); |
| 216 | blob_zero(pBlob); |
| 217 | if( rid==0 ) return 0; |
| 218 | |
| 219 | /* Early out if we know the content is not available */ |
| 220 | if( bag_find(&contentCache.missing, rid) ){ |
| 221 | return 0; |
| 222 | } |
| 223 | |
| 224 | /* Look for the artifact in the cache first */ |
| 225 | if( bag_find(&contentCache.inCache, rid) ){ |
| 226 | for(i=0; i<contentCache.n; i++){ |
| 227 | if( contentCache.a[i].rid==rid ){ |
| 228 | blob_copy(pBlob, &contentCache.a[i].content); |
| 229 | contentCache.a[i].age = contentCache.nextAge++; |
| 230 | return 1; |
| 231 | } |
| 232 | } |
| 233 | } |
| 234 | |
| 235 | nextRid = findSrcid(rid); |
| 236 | if( nextRid==0 ){ |
| 237 | rc = content_of_blob(rid, pBlob); |
| 238 | }else{ |
| 239 | int n = 1; |
| 240 | int nAlloc = 10; |
| 241 | int *a = 0; |
| 242 | int mx; |
| 243 | Blob delta, next; |
| 244 | |
| 245 | a = malloc( sizeof(a[0])*nAlloc ); |
| 246 | if( a==0 ) fossil_panic("out of memory"); |
| 247 | a[0] = rid; |
| 248 | a[1] = nextRid; |
| 249 | n = 1; |
| 250 | while( !bag_find(&contentCache.inCache, nextRid) |
| 251 | && (nextRid = findSrcid(nextRid))>0 ){ |
| 252 | n++; |
| 253 | if( n>=nAlloc ){ |
| 254 | nAlloc = nAlloc*2 + 10; |
| 255 | a = realloc(a, nAlloc*sizeof(a[0])); |
| 256 | if( a==0 ) fossil_panic("out of memory"); |
| 257 | } |
| 258 | a[n] = nextRid; |
| 259 | } |
| 260 | mx = n; |
| 261 | rc = content_get(a[n], pBlob); |
| 262 | n--; |
| 263 | while( rc && n>=0 ){ |
| 264 | rc = content_of_blob(a[n], &delta); |
| 265 | if( rc ){ |
| 266 | blob_delta_apply(pBlob, &delta, &next); |
| 267 | blob_reset(&delta); |
| 268 | if( (mx-n)%8==0 ){ |
| 269 | content_cache_insert(a[n+1], pBlob); |
| 270 | }else{ |
| 271 | blob_reset(pBlob); |
| 272 | } |
| 273 | *pBlob = next; |
| 274 | } |
| 275 | n--; |
| 276 | } |
| 277 | free(a); |
| 278 | if( !rc ) blob_reset(pBlob); |
| 279 | } |
| 280 | if( rc==0 ){ |
| 281 | bag_insert(&contentCache.missing, rid); |
| 282 | }else{ |
| 283 | bag_insert(&contentCache.available, rid); |
| @@ -320,35 +328,45 @@ | |
| 328 | |
| 329 | /* |
| 330 | ** When a record is converted from a phantom to a real record, |
| 331 | ** if that record has other records that are derived by delta, |
| 332 | ** then call manifest_crosslink() on those other records. |
| 333 | ** |
| 334 | ** Tail recursion is used to minimize stack depth. |
| 335 | */ |
| 336 | void after_dephantomize(int rid, int linkFlag){ |
| 337 | Stmt q; |
| 338 | int nChildAlloc = 0; |
| 339 | int *aChild = 0; |
| 340 | |
| 341 | while( rid ){ |
| 342 | int nChildUsed = 0; |
| 343 | int i; |
| 344 | if( linkFlag ){ |
| 345 | Blob content; |
| 346 | content_get(rid, &content); |
| 347 | manifest_crosslink(rid, &content); |
| 348 | blob_reset(&content); |
| 349 | } |
| 350 | db_prepare(&q, "SELECT rid FROM delta WHERE srcid=%d", rid); |
| 351 | while( db_step(&q)==SQLITE_ROW ){ |
| 352 | int child = db_column_int(&q, 0); |
| 353 | if( nChildUsed>=nChildAlloc ){ |
| 354 | nChildAlloc = nChildAlloc*2 + 10; |
| 355 | aChild = realloc(aChild, nChildAlloc*sizeof(aChild)); |
| 356 | if( aChild==0 ) fossil_panic("out of memory"); |
| 357 | } |
| 358 | aChild[nChildUsed++] = child; |
| 359 | } |
| 360 | db_finalize(&q); |
| 361 | for(i=1; i<nChildUsed; i++){ |
| 362 | after_dephantomize(aChild[i], 1); |
| 363 | } |
| 364 | rid = nChildUsed>0 ? aChild[0] : 0; |
| 365 | linkFlag = 1; |
| 366 | } |
| 367 | free(aChild); |
| 368 | } |
| 369 | |
| 370 | /* |
| 371 | ** Write content into the database. Return the record ID. If the |
| 372 | ** content is already in the database, just return the record ID. |
| 373 | |
| 374 | DDED src/event.c |
+2
| --- a/src/event.c | ||
| +++ b/src/event.c | ||
| @@ -0,0 +1,2 @@ | ||
| 1 | +mParsedblobblobmParsed?name=EVENTID&de='event-%q(); | |
| 2 | +} |
| --- a/src/event.c | |
| +++ b/src/event.c | |
| @@ -0,0 +1,2 @@ | |
| --- a/src/event.c | |
| +++ b/src/event.c | |
| @@ -0,0 +1,2 @@ | |
| 1 | mParsedblobblobmParsed?name=EVENTID&de='event-%q(); |
| 2 | } |
+89
-66
| --- src/info.c | ||
| +++ src/info.c | ||
| @@ -779,11 +779,11 @@ | ||
| 779 | 779 | } |
| 780 | 780 | } |
| 781 | 781 | db_finalize(&q); |
| 782 | 782 | if( nWiki==0 ){ |
| 783 | 783 | db_prepare(&q, |
| 784 | - "SELECT datetime(mtime), user, comment, type, uuid" | |
| 784 | + "SELECT datetime(mtime), user, comment, type, uuid, tagid" | |
| 785 | 785 | " FROM event, blob" |
| 786 | 786 | " WHERE event.objid=%d" |
| 787 | 787 | " AND blob.rid=%d", |
| 788 | 788 | rid, rid |
| 789 | 789 | ); |
| @@ -800,14 +800,19 @@ | ||
| 800 | 800 | @ Wiki edit |
| 801 | 801 | }else if( zType[0]=='t' ){ |
| 802 | 802 | @ Ticket change |
| 803 | 803 | }else if( zType[0]=='c' ){ |
| 804 | 804 | @ Manifest of check-in |
| 805 | + }else if( zType[0]=='e' ){ | |
| 806 | + @ Instance of event | |
| 807 | + hyperlink_to_event_tagid(db_column_int(&q, 5)); | |
| 805 | 808 | }else{ |
| 806 | 809 | @ Control file referencing |
| 807 | 810 | } |
| 808 | - hyperlink_to_uuid(zUuid); | |
| 811 | + if( zType[0]!='e' ){ | |
| 812 | + hyperlink_to_uuid(zUuid); | |
| 813 | + } | |
| 809 | 814 | @ - %w(zCom) by |
| 810 | 815 | hyperlink_to_user(zUser,zDate," on"); |
| 811 | 816 | hyperlink_to_date(zDate, "."); |
| 812 | 817 | if( pDownloadName && blob_size(pDownloadName)==0 ){ |
| 813 | 818 | blob_appendf(pDownloadName, "%.10s.txt", zUuid); |
| @@ -1260,10 +1265,90 @@ | ||
| 1260 | 1265 | }else |
| 1261 | 1266 | { |
| 1262 | 1267 | artifact_page(); |
| 1263 | 1268 | } |
| 1264 | 1269 | } |
| 1270 | + | |
| 1271 | +/* | |
| 1272 | +** Generate HTML that will present the user with a selection of | |
| 1273 | +** potential background colors for timeline entries. | |
| 1274 | +*/ | |
| 1275 | +void render_color_chooser( | |
| 1276 | + int fPropagate, /* Default value for propagation */ | |
| 1277 | + const char *zDefaultColor, /* The current default color */ | |
| 1278 | + const char *zIdPropagate, /* ID of form element checkbox. NULL for none */ | |
| 1279 | + const char *zId, /* The ID of the form element */ | |
| 1280 | + const char *zIdCustom /* ID of text box for custom color */ | |
| 1281 | +){ | |
| 1282 | + static const struct SampleColors { | |
| 1283 | + const char *zCName; | |
| 1284 | + const char *zColor; | |
| 1285 | + } aColor[] = { | |
| 1286 | + { "(none)", "" }, | |
| 1287 | + { "#f2dcdc", "#f2dcdc" }, | |
| 1288 | + { "#f0ffc0", "#f0ffc0" }, | |
| 1289 | + { "#bde5d6", "#bde5d6" }, | |
| 1290 | + { "#c0ffc0", "#c0ffc0" }, | |
| 1291 | + { "#c0fff0", "#c0fff0" }, | |
| 1292 | + { "#c0f0ff", "#c0f0ff" }, | |
| 1293 | + { "#d0c0ff", "#d0c0ff" }, | |
| 1294 | + { "#ffc0ff", "#ffc0ff" }, | |
| 1295 | + { "#ffc0d0", "#ffc0d0" }, | |
| 1296 | + { "#fff0c0", "#fff0c0" }, | |
| 1297 | + { "#c0c0c0", "#c0c0c0" }, | |
| 1298 | + { "custom", "##" }, | |
| 1299 | + }; | |
| 1300 | + int nColor = sizeof(aColor)/sizeof(aColor[0])-1; | |
| 1301 | + int stdClrFound = 0; | |
| 1302 | + int i; | |
| 1303 | + | |
| 1304 | + @ <table border="0" cellpadding="0" cellspacing="1"> | |
| 1305 | + if( zIdPropagate ){ | |
| 1306 | + @ <tr><td colspan="6" align="left"> | |
| 1307 | + if( fPropagate ){ | |
| 1308 | + @ <input type="checkbox" name="%s(zIdPropagate)" checked="checked" /> | |
| 1309 | + }else{ | |
| 1310 | + @ <input type="checkbox" name="%s(zIdPropagate)" /> | |
| 1311 | + } | |
| 1312 | + @ Propagate color to descendants</td></tr> | |
| 1313 | + } | |
| 1314 | + @ <tr> | |
| 1315 | + for(i=0; i<nColor; i++){ | |
| 1316 | + if( aColor[i].zColor[0] ){ | |
| 1317 | + @ <td style="background-color: %h(aColor[i].zColor);"> | |
| 1318 | + }else{ | |
| 1319 | + @ <td> | |
| 1320 | + } | |
| 1321 | + if( strcmp(zDefaultColor, aColor[i].zColor)==0 ){ | |
| 1322 | + @ <input type="radio" name="%s(zId)" value="%h(aColor[i].zColor)" | |
| 1323 | + @ checked="checked" /> | |
| 1324 | + stdClrFound=1; | |
| 1325 | + }else{ | |
| 1326 | + @ <input type="radio" name="%s(zId)" value="%h(aColor[i].zColor)" /> | |
| 1327 | + } | |
| 1328 | + @ %h(aColor[i].zCName)</td> | |
| 1329 | + if( (i%6)==5 && i+1<nColor ){ | |
| 1330 | + @ </tr><tr> | |
| 1331 | + } | |
| 1332 | + } | |
| 1333 | + @ </tr><tr> | |
| 1334 | + if (stdClrFound){ | |
| 1335 | + @ <td colspan="6"> | |
| 1336 | + @ <input type="radio" name="%s(zId)" value="%h(aColor[nColor].zColor)" /> | |
| 1337 | + }else{ | |
| 1338 | + @ <td style="background-color: %h(zDefaultColor);" colspan="6"> | |
| 1339 | + @ <input type="radio" name="%s(zId)" value="%h(aColor[nColor].zColor)" | |
| 1340 | + @ checked="checked" /> | |
| 1341 | + } | |
| 1342 | + @ %h(aColor[i].zCName) | |
| 1343 | + @ <input type="text" name="%s(zIdCustom)" | |
| 1344 | + @ id="%s(zIdCustom)" class="checkinUserColor" | |
| 1345 | + @ value="%h(stdClrFound?"":zDefaultColor)" /> | |
| 1346 | + @ </td> | |
| 1347 | + @ </tr> | |
| 1348 | + @ </table> | |
| 1349 | +} | |
| 1265 | 1350 | |
| 1266 | 1351 | /* |
| 1267 | 1352 | ** WEBPAGE: ci_edit |
| 1268 | 1353 | ** URL: ci_edit?r=RID&c=NEWCOMMENT&u=NEWUSER |
| 1269 | 1354 | ** |
| @@ -1290,31 +1375,10 @@ | ||
| 1290 | 1375 | const char *zCloseFlag; |
| 1291 | 1376 | int fPropagateColor; |
| 1292 | 1377 | char *zUuid; |
| 1293 | 1378 | Blob comment; |
| 1294 | 1379 | Stmt q; |
| 1295 | - static const struct SampleColors { | |
| 1296 | - const char *zCName; | |
| 1297 | - const char *zColor; | |
| 1298 | - } aColor[] = { | |
| 1299 | - { "(none)", "" }, | |
| 1300 | - { "#f2dcdc", "#f2dcdc" }, | |
| 1301 | - { "#f0ffc0", "#f0ffc0" }, | |
| 1302 | - { "#bde5d6", "#bde5d6" }, | |
| 1303 | - { "#c0ffc0", "#c0ffc0" }, | |
| 1304 | - { "#c0fff0", "#c0fff0" }, | |
| 1305 | - { "#c0f0ff", "#c0f0ff" }, | |
| 1306 | - { "#d0c0ff", "#d0c0ff" }, | |
| 1307 | - { "#ffc0ff", "#ffc0ff" }, | |
| 1308 | - { "#ffc0d0", "#ffc0d0" }, | |
| 1309 | - { "#fff0c0", "#fff0c0" }, | |
| 1310 | - { "#c0c0c0", "#c0c0c0" }, | |
| 1311 | - { "custom", "##" }, | |
| 1312 | - }; | |
| 1313 | - int nColor = sizeof(aColor)/sizeof(aColor[0])-1; | |
| 1314 | - int stdClrFound; | |
| 1315 | - int i; | |
| 1316 | 1380 | |
| 1317 | 1381 | login_check_credentials(); |
| 1318 | 1382 | if( !g.okWrite ){ login_needed(); return; } |
| 1319 | 1383 | rid = name_to_rid(P("r")); |
| 1320 | 1384 | zUuid = db_text(0, "SELECT uuid FROM blob WHERE rid=%d", rid); |
| @@ -1334,11 +1398,11 @@ | ||
| 1334 | 1398 | if( zDate==0 ) fossil_redirect_home(); |
| 1335 | 1399 | zNewDate = PD("dt",zDate); |
| 1336 | 1400 | zColor = db_text("", "SELECT bgcolor" |
| 1337 | 1401 | " FROM event WHERE objid=%d", rid); |
| 1338 | 1402 | zNewColor = PD("clr",zColor); |
| 1339 | - if( strcmp(zNewColor,aColor[nColor].zColor)==0 ){ | |
| 1403 | + if( strcmp(zNewColor,"##")==0 ){ | |
| 1340 | 1404 | zNewColor = P("clrcust"); |
| 1341 | 1405 | } |
| 1342 | 1406 | fPropagateColor = P("pclr")!=0; |
| 1343 | 1407 | zNewTagFlag = P("newtag") ? " checked" : ""; |
| 1344 | 1408 | zNewTag = PD("tagname",""); |
| @@ -1501,52 +1565,11 @@ | ||
| 1501 | 1565 | @ <input type="text" name="dt" size="20" value="%h(zNewDate)" /> |
| 1502 | 1566 | @ </td></tr> |
| 1503 | 1567 | |
| 1504 | 1568 | @ <tr><td align="right" valign="top"><b>Background Color:</b></td> |
| 1505 | 1569 | @ <td valign="top"> |
| 1506 | - @ <table border="0" cellpadding="0" cellspacing="1"> | |
| 1507 | - @ <tr><td colspan="6" align="left"> | |
| 1508 | - if( fPropagateColor ){ | |
| 1509 | - @ <input type="checkbox" name="pclr" checked="checked" /> | |
| 1510 | - }else{ | |
| 1511 | - @ <input type="checkbox" name="pclr" /> | |
| 1512 | - } | |
| 1513 | - @ Propagate color to descendants</td></tr> | |
| 1514 | - @ <tr> | |
| 1515 | - for(i=0,stdClrFound=0; i<nColor; i++){ | |
| 1516 | - if( aColor[i].zColor[0] ){ | |
| 1517 | - @ <td style="background-color: %h(aColor[i].zColor);"> | |
| 1518 | - }else{ | |
| 1519 | - @ <td> | |
| 1520 | - } | |
| 1521 | - if( strcmp(zNewColor, aColor[i].zColor)==0 ){ | |
| 1522 | - @ <input type="radio" name="clr" value="%h(aColor[i].zColor)" | |
| 1523 | - @ checked="checked" /> | |
| 1524 | - stdClrFound=1; | |
| 1525 | - }else{ | |
| 1526 | - @ <input type="radio" name="clr" value="%h(aColor[i].zColor)" /> | |
| 1527 | - } | |
| 1528 | - @ %h(aColor[i].zCName)</td> | |
| 1529 | - if( (i%6)==5 && i+1<nColor ){ | |
| 1530 | - @ </tr><tr> | |
| 1531 | - } | |
| 1532 | - } | |
| 1533 | - @ </tr><tr> | |
| 1534 | - if (stdClrFound){ | |
| 1535 | - @ <td colspan="6"> | |
| 1536 | - @ <input type="radio" name="clr" value="%h(aColor[nColor].zColor)" /> | |
| 1537 | - }else{ | |
| 1538 | - @ <td style="background-color: %h(zNewColor);" colspan="6"> | |
| 1539 | - @ <input type="radio" name="clr" value="%h(aColor[nColor].zColor)" | |
| 1540 | - @ checked="checked" /> | |
| 1541 | - } | |
| 1542 | - @ %h(aColor[i].zCName) | |
| 1543 | - @ <input type="text" name="clrcust" id="clrcust" class="checkinUserColor" | |
| 1544 | - @ value="%h(stdClrFound?"":zNewColor)" /> | |
| 1545 | - @ </td> | |
| 1546 | - @ </tr> | |
| 1547 | - @ </table> | |
| 1570 | + render_color_chooser(fPropagateColor, zNewColor, "pclr", "clr", "clrcust"); | |
| 1548 | 1571 | @ </td></tr> |
| 1549 | 1572 | |
| 1550 | 1573 | @ <tr><td align="right" valign="top"><b>Tags:</b></td> |
| 1551 | 1574 | @ <td valign="top"> |
| 1552 | 1575 | @ <input type="checkbox" name="newtag"%s(zNewTagFlag) /> |
| 1553 | 1576 |
| --- src/info.c | |
| +++ src/info.c | |
| @@ -779,11 +779,11 @@ | |
| 779 | } |
| 780 | } |
| 781 | db_finalize(&q); |
| 782 | if( nWiki==0 ){ |
| 783 | db_prepare(&q, |
| 784 | "SELECT datetime(mtime), user, comment, type, uuid" |
| 785 | " FROM event, blob" |
| 786 | " WHERE event.objid=%d" |
| 787 | " AND blob.rid=%d", |
| 788 | rid, rid |
| 789 | ); |
| @@ -800,14 +800,19 @@ | |
| 800 | @ Wiki edit |
| 801 | }else if( zType[0]=='t' ){ |
| 802 | @ Ticket change |
| 803 | }else if( zType[0]=='c' ){ |
| 804 | @ Manifest of check-in |
| 805 | }else{ |
| 806 | @ Control file referencing |
| 807 | } |
| 808 | hyperlink_to_uuid(zUuid); |
| 809 | @ - %w(zCom) by |
| 810 | hyperlink_to_user(zUser,zDate," on"); |
| 811 | hyperlink_to_date(zDate, "."); |
| 812 | if( pDownloadName && blob_size(pDownloadName)==0 ){ |
| 813 | blob_appendf(pDownloadName, "%.10s.txt", zUuid); |
| @@ -1260,10 +1265,90 @@ | |
| 1260 | }else |
| 1261 | { |
| 1262 | artifact_page(); |
| 1263 | } |
| 1264 | } |
| 1265 | |
| 1266 | /* |
| 1267 | ** WEBPAGE: ci_edit |
| 1268 | ** URL: ci_edit?r=RID&c=NEWCOMMENT&u=NEWUSER |
| 1269 | ** |
| @@ -1290,31 +1375,10 @@ | |
| 1290 | const char *zCloseFlag; |
| 1291 | int fPropagateColor; |
| 1292 | char *zUuid; |
| 1293 | Blob comment; |
| 1294 | Stmt q; |
| 1295 | static const struct SampleColors { |
| 1296 | const char *zCName; |
| 1297 | const char *zColor; |
| 1298 | } aColor[] = { |
| 1299 | { "(none)", "" }, |
| 1300 | { "#f2dcdc", "#f2dcdc" }, |
| 1301 | { "#f0ffc0", "#f0ffc0" }, |
| 1302 | { "#bde5d6", "#bde5d6" }, |
| 1303 | { "#c0ffc0", "#c0ffc0" }, |
| 1304 | { "#c0fff0", "#c0fff0" }, |
| 1305 | { "#c0f0ff", "#c0f0ff" }, |
| 1306 | { "#d0c0ff", "#d0c0ff" }, |
| 1307 | { "#ffc0ff", "#ffc0ff" }, |
| 1308 | { "#ffc0d0", "#ffc0d0" }, |
| 1309 | { "#fff0c0", "#fff0c0" }, |
| 1310 | { "#c0c0c0", "#c0c0c0" }, |
| 1311 | { "custom", "##" }, |
| 1312 | }; |
| 1313 | int nColor = sizeof(aColor)/sizeof(aColor[0])-1; |
| 1314 | int stdClrFound; |
| 1315 | int i; |
| 1316 | |
| 1317 | login_check_credentials(); |
| 1318 | if( !g.okWrite ){ login_needed(); return; } |
| 1319 | rid = name_to_rid(P("r")); |
| 1320 | zUuid = db_text(0, "SELECT uuid FROM blob WHERE rid=%d", rid); |
| @@ -1334,11 +1398,11 @@ | |
| 1334 | if( zDate==0 ) fossil_redirect_home(); |
| 1335 | zNewDate = PD("dt",zDate); |
| 1336 | zColor = db_text("", "SELECT bgcolor" |
| 1337 | " FROM event WHERE objid=%d", rid); |
| 1338 | zNewColor = PD("clr",zColor); |
| 1339 | if( strcmp(zNewColor,aColor[nColor].zColor)==0 ){ |
| 1340 | zNewColor = P("clrcust"); |
| 1341 | } |
| 1342 | fPropagateColor = P("pclr")!=0; |
| 1343 | zNewTagFlag = P("newtag") ? " checked" : ""; |
| 1344 | zNewTag = PD("tagname",""); |
| @@ -1501,52 +1565,11 @@ | |
| 1501 | @ <input type="text" name="dt" size="20" value="%h(zNewDate)" /> |
| 1502 | @ </td></tr> |
| 1503 | |
| 1504 | @ <tr><td align="right" valign="top"><b>Background Color:</b></td> |
| 1505 | @ <td valign="top"> |
| 1506 | @ <table border="0" cellpadding="0" cellspacing="1"> |
| 1507 | @ <tr><td colspan="6" align="left"> |
| 1508 | if( fPropagateColor ){ |
| 1509 | @ <input type="checkbox" name="pclr" checked="checked" /> |
| 1510 | }else{ |
| 1511 | @ <input type="checkbox" name="pclr" /> |
| 1512 | } |
| 1513 | @ Propagate color to descendants</td></tr> |
| 1514 | @ <tr> |
| 1515 | for(i=0,stdClrFound=0; i<nColor; i++){ |
| 1516 | if( aColor[i].zColor[0] ){ |
| 1517 | @ <td style="background-color: %h(aColor[i].zColor);"> |
| 1518 | }else{ |
| 1519 | @ <td> |
| 1520 | } |
| 1521 | if( strcmp(zNewColor, aColor[i].zColor)==0 ){ |
| 1522 | @ <input type="radio" name="clr" value="%h(aColor[i].zColor)" |
| 1523 | @ checked="checked" /> |
| 1524 | stdClrFound=1; |
| 1525 | }else{ |
| 1526 | @ <input type="radio" name="clr" value="%h(aColor[i].zColor)" /> |
| 1527 | } |
| 1528 | @ %h(aColor[i].zCName)</td> |
| 1529 | if( (i%6)==5 && i+1<nColor ){ |
| 1530 | @ </tr><tr> |
| 1531 | } |
| 1532 | } |
| 1533 | @ </tr><tr> |
| 1534 | if (stdClrFound){ |
| 1535 | @ <td colspan="6"> |
| 1536 | @ <input type="radio" name="clr" value="%h(aColor[nColor].zColor)" /> |
| 1537 | }else{ |
| 1538 | @ <td style="background-color: %h(zNewColor);" colspan="6"> |
| 1539 | @ <input type="radio" name="clr" value="%h(aColor[nColor].zColor)" |
| 1540 | @ checked="checked" /> |
| 1541 | } |
| 1542 | @ %h(aColor[i].zCName) |
| 1543 | @ <input type="text" name="clrcust" id="clrcust" class="checkinUserColor" |
| 1544 | @ value="%h(stdClrFound?"":zNewColor)" /> |
| 1545 | @ </td> |
| 1546 | @ </tr> |
| 1547 | @ </table> |
| 1548 | @ </td></tr> |
| 1549 | |
| 1550 | @ <tr><td align="right" valign="top"><b>Tags:</b></td> |
| 1551 | @ <td valign="top"> |
| 1552 | @ <input type="checkbox" name="newtag"%s(zNewTagFlag) /> |
| 1553 |
| --- src/info.c | |
| +++ src/info.c | |
| @@ -779,11 +779,11 @@ | |
| 779 | } |
| 780 | } |
| 781 | db_finalize(&q); |
| 782 | if( nWiki==0 ){ |
| 783 | db_prepare(&q, |
| 784 | "SELECT datetime(mtime), user, comment, type, uuid, tagid" |
| 785 | " FROM event, blob" |
| 786 | " WHERE event.objid=%d" |
| 787 | " AND blob.rid=%d", |
| 788 | rid, rid |
| 789 | ); |
| @@ -800,14 +800,19 @@ | |
| 800 | @ Wiki edit |
| 801 | }else if( zType[0]=='t' ){ |
| 802 | @ Ticket change |
| 803 | }else if( zType[0]=='c' ){ |
| 804 | @ Manifest of check-in |
| 805 | }else if( zType[0]=='e' ){ |
| 806 | @ Instance of event |
| 807 | hyperlink_to_event_tagid(db_column_int(&q, 5)); |
| 808 | }else{ |
| 809 | @ Control file referencing |
| 810 | } |
| 811 | if( zType[0]!='e' ){ |
| 812 | hyperlink_to_uuid(zUuid); |
| 813 | } |
| 814 | @ - %w(zCom) by |
| 815 | hyperlink_to_user(zUser,zDate," on"); |
| 816 | hyperlink_to_date(zDate, "."); |
| 817 | if( pDownloadName && blob_size(pDownloadName)==0 ){ |
| 818 | blob_appendf(pDownloadName, "%.10s.txt", zUuid); |
| @@ -1260,10 +1265,90 @@ | |
| 1265 | }else |
| 1266 | { |
| 1267 | artifact_page(); |
| 1268 | } |
| 1269 | } |
| 1270 | |
| 1271 | /* |
| 1272 | ** Generate HTML that will present the user with a selection of |
| 1273 | ** potential background colors for timeline entries. |
| 1274 | */ |
| 1275 | void render_color_chooser( |
| 1276 | int fPropagate, /* Default value for propagation */ |
| 1277 | const char *zDefaultColor, /* The current default color */ |
| 1278 | const char *zIdPropagate, /* ID of form element checkbox. NULL for none */ |
| 1279 | const char *zId, /* The ID of the form element */ |
| 1280 | const char *zIdCustom /* ID of text box for custom color */ |
| 1281 | ){ |
| 1282 | static const struct SampleColors { |
| 1283 | const char *zCName; |
| 1284 | const char *zColor; |
| 1285 | } aColor[] = { |
| 1286 | { "(none)", "" }, |
| 1287 | { "#f2dcdc", "#f2dcdc" }, |
| 1288 | { "#f0ffc0", "#f0ffc0" }, |
| 1289 | { "#bde5d6", "#bde5d6" }, |
| 1290 | { "#c0ffc0", "#c0ffc0" }, |
| 1291 | { "#c0fff0", "#c0fff0" }, |
| 1292 | { "#c0f0ff", "#c0f0ff" }, |
| 1293 | { "#d0c0ff", "#d0c0ff" }, |
| 1294 | { "#ffc0ff", "#ffc0ff" }, |
| 1295 | { "#ffc0d0", "#ffc0d0" }, |
| 1296 | { "#fff0c0", "#fff0c0" }, |
| 1297 | { "#c0c0c0", "#c0c0c0" }, |
| 1298 | { "custom", "##" }, |
| 1299 | }; |
| 1300 | int nColor = sizeof(aColor)/sizeof(aColor[0])-1; |
| 1301 | int stdClrFound = 0; |
| 1302 | int i; |
| 1303 | |
| 1304 | @ <table border="0" cellpadding="0" cellspacing="1"> |
| 1305 | if( zIdPropagate ){ |
| 1306 | @ <tr><td colspan="6" align="left"> |
| 1307 | if( fPropagate ){ |
| 1308 | @ <input type="checkbox" name="%s(zIdPropagate)" checked="checked" /> |
| 1309 | }else{ |
| 1310 | @ <input type="checkbox" name="%s(zIdPropagate)" /> |
| 1311 | } |
| 1312 | @ Propagate color to descendants</td></tr> |
| 1313 | } |
| 1314 | @ <tr> |
| 1315 | for(i=0; i<nColor; i++){ |
| 1316 | if( aColor[i].zColor[0] ){ |
| 1317 | @ <td style="background-color: %h(aColor[i].zColor);"> |
| 1318 | }else{ |
| 1319 | @ <td> |
| 1320 | } |
| 1321 | if( strcmp(zDefaultColor, aColor[i].zColor)==0 ){ |
| 1322 | @ <input type="radio" name="%s(zId)" value="%h(aColor[i].zColor)" |
| 1323 | @ checked="checked" /> |
| 1324 | stdClrFound=1; |
| 1325 | }else{ |
| 1326 | @ <input type="radio" name="%s(zId)" value="%h(aColor[i].zColor)" /> |
| 1327 | } |
| 1328 | @ %h(aColor[i].zCName)</td> |
| 1329 | if( (i%6)==5 && i+1<nColor ){ |
| 1330 | @ </tr><tr> |
| 1331 | } |
| 1332 | } |
| 1333 | @ </tr><tr> |
| 1334 | if (stdClrFound){ |
| 1335 | @ <td colspan="6"> |
| 1336 | @ <input type="radio" name="%s(zId)" value="%h(aColor[nColor].zColor)" /> |
| 1337 | }else{ |
| 1338 | @ <td style="background-color: %h(zDefaultColor);" colspan="6"> |
| 1339 | @ <input type="radio" name="%s(zId)" value="%h(aColor[nColor].zColor)" |
| 1340 | @ checked="checked" /> |
| 1341 | } |
| 1342 | @ %h(aColor[i].zCName) |
| 1343 | @ <input type="text" name="%s(zIdCustom)" |
| 1344 | @ id="%s(zIdCustom)" class="checkinUserColor" |
| 1345 | @ value="%h(stdClrFound?"":zDefaultColor)" /> |
| 1346 | @ </td> |
| 1347 | @ </tr> |
| 1348 | @ </table> |
| 1349 | } |
| 1350 | |
| 1351 | /* |
| 1352 | ** WEBPAGE: ci_edit |
| 1353 | ** URL: ci_edit?r=RID&c=NEWCOMMENT&u=NEWUSER |
| 1354 | ** |
| @@ -1290,31 +1375,10 @@ | |
| 1375 | const char *zCloseFlag; |
| 1376 | int fPropagateColor; |
| 1377 | char *zUuid; |
| 1378 | Blob comment; |
| 1379 | Stmt q; |
| 1380 | |
| 1381 | login_check_credentials(); |
| 1382 | if( !g.okWrite ){ login_needed(); return; } |
| 1383 | rid = name_to_rid(P("r")); |
| 1384 | zUuid = db_text(0, "SELECT uuid FROM blob WHERE rid=%d", rid); |
| @@ -1334,11 +1398,11 @@ | |
| 1398 | if( zDate==0 ) fossil_redirect_home(); |
| 1399 | zNewDate = PD("dt",zDate); |
| 1400 | zColor = db_text("", "SELECT bgcolor" |
| 1401 | " FROM event WHERE objid=%d", rid); |
| 1402 | zNewColor = PD("clr",zColor); |
| 1403 | if( strcmp(zNewColor,"##")==0 ){ |
| 1404 | zNewColor = P("clrcust"); |
| 1405 | } |
| 1406 | fPropagateColor = P("pclr")!=0; |
| 1407 | zNewTagFlag = P("newtag") ? " checked" : ""; |
| 1408 | zNewTag = PD("tagname",""); |
| @@ -1501,52 +1565,11 @@ | |
| 1565 | @ <input type="text" name="dt" size="20" value="%h(zNewDate)" /> |
| 1566 | @ </td></tr> |
| 1567 | |
| 1568 | @ <tr><td align="right" valign="top"><b>Background Color:</b></td> |
| 1569 | @ <td valign="top"> |
| 1570 | render_color_chooser(fPropagateColor, zNewColor, "pclr", "clr", "clrcust"); |
| 1571 | @ </td></tr> |
| 1572 | |
| 1573 | @ <tr><td align="right" valign="top"><b>Tags:</b></td> |
| 1574 | @ <td valign="top"> |
| 1575 | @ <input type="checkbox" name="newtag"%s(zNewTagFlag) /> |
| 1576 |
+12
-2
| --- src/main.mk | ||
| +++ src/main.mk | ||
| @@ -35,10 +35,11 @@ | ||
| 35 | 35 | $(SRCDIR)/descendants.c \ |
| 36 | 36 | $(SRCDIR)/diff.c \ |
| 37 | 37 | $(SRCDIR)/diffcmd.c \ |
| 38 | 38 | $(SRCDIR)/doc.c \ |
| 39 | 39 | $(SRCDIR)/encode.c \ |
| 40 | + $(SRCDIR)/event.c \ | |
| 40 | 41 | $(SRCDIR)/file.c \ |
| 41 | 42 | $(SRCDIR)/finfo.c \ |
| 42 | 43 | $(SRCDIR)/graph.c \ |
| 43 | 44 | $(SRCDIR)/http.c \ |
| 44 | 45 | $(SRCDIR)/http_socket.c \ |
| @@ -108,10 +109,11 @@ | ||
| 108 | 109 | descendants_.c \ |
| 109 | 110 | diff_.c \ |
| 110 | 111 | diffcmd_.c \ |
| 111 | 112 | doc_.c \ |
| 112 | 113 | encode_.c \ |
| 114 | + event_.c \ | |
| 113 | 115 | file_.c \ |
| 114 | 116 | finfo_.c \ |
| 115 | 117 | graph_.c \ |
| 116 | 118 | http_.c \ |
| 117 | 119 | http_socket_.c \ |
| @@ -181,10 +183,11 @@ | ||
| 181 | 183 | $(OBJDIR)/descendants.o \ |
| 182 | 184 | $(OBJDIR)/diff.o \ |
| 183 | 185 | $(OBJDIR)/diffcmd.o \ |
| 184 | 186 | $(OBJDIR)/doc.o \ |
| 185 | 187 | $(OBJDIR)/encode.o \ |
| 188 | + $(OBJDIR)/event.o \ | |
| 186 | 189 | $(OBJDIR)/file.o \ |
| 187 | 190 | $(OBJDIR)/finfo.o \ |
| 188 | 191 | $(OBJDIR)/graph.o \ |
| 189 | 192 | $(OBJDIR)/http.o \ |
| 190 | 193 | $(OBJDIR)/http_socket.o \ |
| @@ -273,16 +276,16 @@ | ||
| 273 | 276 | # noop |
| 274 | 277 | |
| 275 | 278 | clean: |
| 276 | 279 | rm -f $(OBJDIR)/*.o *_.c $(APPNAME) VERSION.h |
| 277 | 280 | rm -f translate makeheaders mkindex page_index.h headers |
| 278 | - 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 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 | |
| 281 | + 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 | |
| 279 | 282 | |
| 280 | 283 | page_index.h: $(TRANS_SRC) mkindex |
| 281 | 284 | ./mkindex $(TRANS_SRC) >$@ |
| 282 | 285 | headers: page_index.h makeheaders VERSION.h |
| 283 | - ./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 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 | |
| 286 | + ./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 | |
| 284 | 287 | touch headers |
| 285 | 288 | headers: Makefile |
| 286 | 289 | Makefile: |
| 287 | 290 | add_.c: $(SRCDIR)/add.c translate |
| 288 | 291 | ./translate $(SRCDIR)/add.c >add_.c |
| @@ -450,10 +453,17 @@ | ||
| 450 | 453 | |
| 451 | 454 | $(OBJDIR)/encode.o: encode_.c encode.h $(SRCDIR)/config.h |
| 452 | 455 | $(XTCC) -o $(OBJDIR)/encode.o -c encode_.c |
| 453 | 456 | |
| 454 | 457 | encode.h: headers |
| 458 | +event_.c: $(SRCDIR)/event.c translate | |
| 459 | + ./translate $(SRCDIR)/event.c >event_.c | |
| 460 | + | |
| 461 | +$(OBJDIR)/event.o: event_.c event.h $(SRCDIR)/config.h | |
| 462 | + $(XTCC) -o $(OBJDIR)/event.o -c event_.c | |
| 463 | + | |
| 464 | +event.h: headers | |
| 455 | 465 | file_.c: $(SRCDIR)/file.c translate |
| 456 | 466 | ./translate $(SRCDIR)/file.c >file_.c |
| 457 | 467 | |
| 458 | 468 | $(OBJDIR)/file.o: file_.c file.h $(SRCDIR)/config.h |
| 459 | 469 | $(XTCC) -o $(OBJDIR)/file.o -c file_.c |
| 460 | 470 |
| --- src/main.mk | |
| +++ src/main.mk | |
| @@ -35,10 +35,11 @@ | |
| 35 | $(SRCDIR)/descendants.c \ |
| 36 | $(SRCDIR)/diff.c \ |
| 37 | $(SRCDIR)/diffcmd.c \ |
| 38 | $(SRCDIR)/doc.c \ |
| 39 | $(SRCDIR)/encode.c \ |
| 40 | $(SRCDIR)/file.c \ |
| 41 | $(SRCDIR)/finfo.c \ |
| 42 | $(SRCDIR)/graph.c \ |
| 43 | $(SRCDIR)/http.c \ |
| 44 | $(SRCDIR)/http_socket.c \ |
| @@ -108,10 +109,11 @@ | |
| 108 | descendants_.c \ |
| 109 | diff_.c \ |
| 110 | diffcmd_.c \ |
| 111 | doc_.c \ |
| 112 | encode_.c \ |
| 113 | file_.c \ |
| 114 | finfo_.c \ |
| 115 | graph_.c \ |
| 116 | http_.c \ |
| 117 | http_socket_.c \ |
| @@ -181,10 +183,11 @@ | |
| 181 | $(OBJDIR)/descendants.o \ |
| 182 | $(OBJDIR)/diff.o \ |
| 183 | $(OBJDIR)/diffcmd.o \ |
| 184 | $(OBJDIR)/doc.o \ |
| 185 | $(OBJDIR)/encode.o \ |
| 186 | $(OBJDIR)/file.o \ |
| 187 | $(OBJDIR)/finfo.o \ |
| 188 | $(OBJDIR)/graph.o \ |
| 189 | $(OBJDIR)/http.o \ |
| 190 | $(OBJDIR)/http_socket.o \ |
| @@ -273,16 +276,16 @@ | |
| 273 | # noop |
| 274 | |
| 275 | clean: |
| 276 | rm -f $(OBJDIR)/*.o *_.c $(APPNAME) VERSION.h |
| 277 | rm -f translate makeheaders mkindex page_index.h headers |
| 278 | 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 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 |
| 279 | |
| 280 | page_index.h: $(TRANS_SRC) mkindex |
| 281 | ./mkindex $(TRANS_SRC) >$@ |
| 282 | headers: page_index.h makeheaders VERSION.h |
| 283 | ./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 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 |
| 284 | touch headers |
| 285 | headers: Makefile |
| 286 | Makefile: |
| 287 | add_.c: $(SRCDIR)/add.c translate |
| 288 | ./translate $(SRCDIR)/add.c >add_.c |
| @@ -450,10 +453,17 @@ | |
| 450 | |
| 451 | $(OBJDIR)/encode.o: encode_.c encode.h $(SRCDIR)/config.h |
| 452 | $(XTCC) -o $(OBJDIR)/encode.o -c encode_.c |
| 453 | |
| 454 | encode.h: headers |
| 455 | file_.c: $(SRCDIR)/file.c translate |
| 456 | ./translate $(SRCDIR)/file.c >file_.c |
| 457 | |
| 458 | $(OBJDIR)/file.o: file_.c file.h $(SRCDIR)/config.h |
| 459 | $(XTCC) -o $(OBJDIR)/file.o -c file_.c |
| 460 |
| --- src/main.mk | |
| +++ src/main.mk | |
| @@ -35,10 +35,11 @@ | |
| 35 | $(SRCDIR)/descendants.c \ |
| 36 | $(SRCDIR)/diff.c \ |
| 37 | $(SRCDIR)/diffcmd.c \ |
| 38 | $(SRCDIR)/doc.c \ |
| 39 | $(SRCDIR)/encode.c \ |
| 40 | $(SRCDIR)/event.c \ |
| 41 | $(SRCDIR)/file.c \ |
| 42 | $(SRCDIR)/finfo.c \ |
| 43 | $(SRCDIR)/graph.c \ |
| 44 | $(SRCDIR)/http.c \ |
| 45 | $(SRCDIR)/http_socket.c \ |
| @@ -108,10 +109,11 @@ | |
| 109 | descendants_.c \ |
| 110 | diff_.c \ |
| 111 | diffcmd_.c \ |
| 112 | doc_.c \ |
| 113 | encode_.c \ |
| 114 | event_.c \ |
| 115 | file_.c \ |
| 116 | finfo_.c \ |
| 117 | graph_.c \ |
| 118 | http_.c \ |
| 119 | http_socket_.c \ |
| @@ -181,10 +183,11 @@ | |
| 183 | $(OBJDIR)/descendants.o \ |
| 184 | $(OBJDIR)/diff.o \ |
| 185 | $(OBJDIR)/diffcmd.o \ |
| 186 | $(OBJDIR)/doc.o \ |
| 187 | $(OBJDIR)/encode.o \ |
| 188 | $(OBJDIR)/event.o \ |
| 189 | $(OBJDIR)/file.o \ |
| 190 | $(OBJDIR)/finfo.o \ |
| 191 | $(OBJDIR)/graph.o \ |
| 192 | $(OBJDIR)/http.o \ |
| 193 | $(OBJDIR)/http_socket.o \ |
| @@ -273,16 +276,16 @@ | |
| 276 | # noop |
| 277 | |
| 278 | clean: |
| 279 | rm -f $(OBJDIR)/*.o *_.c $(APPNAME) VERSION.h |
| 280 | rm -f translate makeheaders mkindex page_index.h headers |
| 281 | 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 |
| 282 | |
| 283 | page_index.h: $(TRANS_SRC) mkindex |
| 284 | ./mkindex $(TRANS_SRC) >$@ |
| 285 | headers: page_index.h makeheaders VERSION.h |
| 286 | ./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 |
| 287 | touch headers |
| 288 | headers: Makefile |
| 289 | Makefile: |
| 290 | add_.c: $(SRCDIR)/add.c translate |
| 291 | ./translate $(SRCDIR)/add.c >add_.c |
| @@ -450,10 +453,17 @@ | |
| 453 | |
| 454 | $(OBJDIR)/encode.o: encode_.c encode.h $(SRCDIR)/config.h |
| 455 | $(XTCC) -o $(OBJDIR)/encode.o -c encode_.c |
| 456 | |
| 457 | encode.h: headers |
| 458 | event_.c: $(SRCDIR)/event.c translate |
| 459 | ./translate $(SRCDIR)/event.c >event_.c |
| 460 | |
| 461 | $(OBJDIR)/event.o: event_.c event.h $(SRCDIR)/config.h |
| 462 | $(XTCC) -o $(OBJDIR)/event.o -c event_.c |
| 463 | |
| 464 | event.h: headers |
| 465 | file_.c: $(SRCDIR)/file.c translate |
| 466 | ./translate $(SRCDIR)/file.c >file_.c |
| 467 | |
| 468 | $(OBJDIR)/file.o: file_.c file.h $(SRCDIR)/config.h |
| 469 | $(XTCC) -o $(OBJDIR)/file.o -c file_.c |
| 470 |
+1
| --- src/makemake.tcl | ||
| +++ src/makemake.tcl | ||
| @@ -29,10 +29,11 @@ | ||
| 29 | 29 | descendants |
| 30 | 30 | diff |
| 31 | 31 | diffcmd |
| 32 | 32 | doc |
| 33 | 33 | encode |
| 34 | + event | |
| 34 | 35 | file |
| 35 | 36 | finfo |
| 36 | 37 | graph |
| 37 | 38 | http |
| 38 | 39 | http_socket |
| 39 | 40 |
| --- src/makemake.tcl | |
| +++ src/makemake.tcl | |
| @@ -29,10 +29,11 @@ | |
| 29 | descendants |
| 30 | diff |
| 31 | diffcmd |
| 32 | doc |
| 33 | encode |
| 34 | file |
| 35 | finfo |
| 36 | graph |
| 37 | http |
| 38 | http_socket |
| 39 |
| --- src/makemake.tcl | |
| +++ src/makemake.tcl | |
| @@ -29,10 +29,11 @@ | |
| 29 | descendants |
| 30 | diff |
| 31 | diffcmd |
| 32 | doc |
| 33 | encode |
| 34 | event |
| 35 | file |
| 36 | finfo |
| 37 | graph |
| 38 | http |
| 39 | http_socket |
| 40 |
+99
-7
| --- src/manifest.c | ||
| +++ src/manifest.c | ||
| @@ -32,10 +32,11 @@ | ||
| 32 | 32 | #define CFTYPE_CLUSTER 2 |
| 33 | 33 | #define CFTYPE_CONTROL 3 |
| 34 | 34 | #define CFTYPE_WIKI 4 |
| 35 | 35 | #define CFTYPE_TICKET 5 |
| 36 | 36 | #define CFTYPE_ATTACHMENT 6 |
| 37 | +#define CFTYPE_EVENT 7 | |
| 37 | 38 | |
| 38 | 39 | /* |
| 39 | 40 | ** A parsed manifest or cluster. |
| 40 | 41 | */ |
| 41 | 42 | struct Manifest { |
| @@ -45,10 +46,12 @@ | ||
| 45 | 46 | double rDate; /* Date and time from D card. 0.0 if no D card. */ |
| 46 | 47 | char *zUser; /* Name of the user from the U card. */ |
| 47 | 48 | char *zRepoCksum; /* MD5 checksum of the baseline content. R card. */ |
| 48 | 49 | char *zWiki; /* Text of the wiki page. W card. */ |
| 49 | 50 | char *zWikiTitle; /* Name of the wiki page. L card. */ |
| 51 | + double rEventDate; /* Date of an event. E card. */ | |
| 52 | + char *zEventId; /* UUID for an event. E card. */ | |
| 50 | 53 | char *zTicketUuid; /* UUID for a ticket. K card. */ |
| 51 | 54 | char *zAttachName; /* Filename of an attachment. A card. */ |
| 52 | 55 | char *zAttachSrc; /* UUID of document being attached. A card. */ |
| 53 | 56 | char *zAttachTarget; /* Ticket or wiki that attachment applies to. A card */ |
| 54 | 57 | int nFile; /* Number of F cards */ |
| @@ -230,10 +233,35 @@ | ||
| 230 | 233 | if( blob_token(&line, &a2)!=0 ) goto manifest_syntax_error; |
| 231 | 234 | zDate = blob_terminate(&a1); |
| 232 | 235 | p->rDate = db_double(0.0, "SELECT julianday(%Q)", zDate); |
| 233 | 236 | break; |
| 234 | 237 | } |
| 238 | + | |
| 239 | + /* | |
| 240 | + ** E <timestamp> <uuid> | |
| 241 | + ** | |
| 242 | + ** An "event" card that contains the timestamp of the event in the | |
| 243 | + ** format YYYY-MM-DDtHH:MM:SS and a unique identifier for the event. | |
| 244 | + ** The event timestamp is distinct from the D timestamp. The D | |
| 245 | + ** timestamp is when the artifact was created whereas the E timestamp | |
| 246 | + ** is when the specific event is said to occur. | |
| 247 | + */ | |
| 248 | + case 'E': { | |
| 249 | + char *zEDate; | |
| 250 | + md5sum_step_text(blob_buffer(&line), blob_size(&line)); | |
| 251 | + if( p->rEventDate!=0.0 ) goto manifest_syntax_error; | |
| 252 | + if( blob_token(&line, &a1)==0 ) goto manifest_syntax_error; | |
| 253 | + if( blob_token(&line, &a2)==0 ) goto manifest_syntax_error; | |
| 254 | + if( blob_token(&line, &a3)!=0 ) goto manifest_syntax_error; | |
| 255 | + zEDate = blob_terminate(&a1); | |
| 256 | + p->rEventDate = db_double(0.0, "SELECT julianday(%Q)", zEDate); | |
| 257 | + if( p->rEventDate<=0.0 ) goto manifest_syntax_error; | |
| 258 | + if( blob_size(&a2)!=UUID_SIZE ) goto manifest_syntax_error; | |
| 259 | + p->zEventId = blob_terminate(&a2); | |
| 260 | + if( !validate16(p->zEventId, UUID_SIZE) ) goto manifest_syntax_error; | |
| 261 | + break; | |
| 262 | + } | |
| 235 | 263 | |
| 236 | 264 | /* |
| 237 | 265 | ** F <filename> <uuid> ?<permissions>? ?<old-name>? |
| 238 | 266 | ** |
| 239 | 267 | ** Identifies a file in a manifest. Multiple F lines are |
| @@ -563,15 +591,16 @@ | ||
| 563 | 591 | } |
| 564 | 592 | if( !seenHeader ) goto manifest_syntax_error; |
| 565 | 593 | |
| 566 | 594 | if( p->nFile>0 || p->zRepoCksum!=0 ){ |
| 567 | 595 | if( p->nCChild>0 ) goto manifest_syntax_error; |
| 568 | - if( p->rDate==0.0 ) goto manifest_syntax_error; | |
| 596 | + if( p->rDate<=0.0 ) goto manifest_syntax_error; | |
| 569 | 597 | if( p->nField>0 ) goto manifest_syntax_error; |
| 570 | 598 | if( p->zTicketUuid ) goto manifest_syntax_error; |
| 571 | 599 | if( p->zWiki ) goto manifest_syntax_error; |
| 572 | 600 | if( p->zWikiTitle ) goto manifest_syntax_error; |
| 601 | + if( p->zEventId ) goto manifest_syntax_error; | |
| 573 | 602 | if( p->zTicketUuid ) goto manifest_syntax_error; |
| 574 | 603 | if( p->zAttachName ) goto manifest_syntax_error; |
| 575 | 604 | p->type = CFTYPE_MANIFEST; |
| 576 | 605 | }else if( p->nCChild>0 ){ |
| 577 | 606 | if( p->rDate>0.0 ) goto manifest_syntax_error; |
| @@ -581,26 +610,41 @@ | ||
| 581 | 610 | if( p->nParent>0 ) goto manifest_syntax_error; |
| 582 | 611 | if( p->nField>0 ) goto manifest_syntax_error; |
| 583 | 612 | if( p->zTicketUuid ) goto manifest_syntax_error; |
| 584 | 613 | if( p->zWiki ) goto manifest_syntax_error; |
| 585 | 614 | if( p->zWikiTitle ) goto manifest_syntax_error; |
| 615 | + if( p->zEventId ) goto manifest_syntax_error; | |
| 586 | 616 | if( p->zAttachName ) goto manifest_syntax_error; |
| 587 | 617 | if( !seenZ ) goto manifest_syntax_error; |
| 588 | 618 | p->type = CFTYPE_CLUSTER; |
| 589 | 619 | }else if( p->nField>0 ){ |
| 590 | - if( p->rDate==0.0 ) goto manifest_syntax_error; | |
| 620 | + if( p->rDate<=0.0 ) goto manifest_syntax_error; | |
| 591 | 621 | if( p->zWiki ) goto manifest_syntax_error; |
| 592 | 622 | if( p->zWikiTitle ) goto manifest_syntax_error; |
| 623 | + if( p->zEventId ) goto manifest_syntax_error; | |
| 593 | 624 | if( p->nCChild>0 ) goto manifest_syntax_error; |
| 594 | 625 | if( p->nTag>0 ) goto manifest_syntax_error; |
| 595 | 626 | if( p->zTicketUuid==0 ) goto manifest_syntax_error; |
| 596 | 627 | if( p->zUser==0 ) goto manifest_syntax_error; |
| 597 | 628 | if( p->zAttachName ) goto manifest_syntax_error; |
| 598 | 629 | if( !seenZ ) goto manifest_syntax_error; |
| 599 | 630 | p->type = CFTYPE_TICKET; |
| 631 | + }else if( p->zEventId ){ | |
| 632 | + if( p->rDate<=0.0 ) goto manifest_syntax_error; | |
| 633 | + if( p->nCChild>0 ) goto manifest_syntax_error; | |
| 634 | + if( p->zTicketUuid!=0 ) goto manifest_syntax_error; | |
| 635 | + if( p->zWikiTitle!=0 ) goto manifest_syntax_error; | |
| 636 | + if( p->zWiki==0 ) goto manifest_syntax_error; | |
| 637 | + if( p->zAttachName ) goto manifest_syntax_error; | |
| 638 | + for(i=0; i<p->nTag; i++){ | |
| 639 | + if( p->aTag[i].zName[0]!='+' ) goto manifest_syntax_error; | |
| 640 | + if( p->aTag[i].zUuid!=0 ) goto manifest_syntax_error; | |
| 641 | + } | |
| 642 | + if( !seenZ ) goto manifest_syntax_error; | |
| 643 | + p->type = CFTYPE_EVENT; | |
| 600 | 644 | }else if( p->zWiki!=0 ){ |
| 601 | - if( p->rDate==0.0 ) goto manifest_syntax_error; | |
| 645 | + if( p->rDate<=0.0 ) goto manifest_syntax_error; | |
| 602 | 646 | if( p->nCChild>0 ) goto manifest_syntax_error; |
| 603 | 647 | if( p->nTag>0 ) goto manifest_syntax_error; |
| 604 | 648 | if( p->zTicketUuid!=0 ) goto manifest_syntax_error; |
| 605 | 649 | if( p->zWikiTitle==0 ) goto manifest_syntax_error; |
| 606 | 650 | if( p->zAttachName ) goto manifest_syntax_error; |
| @@ -614,11 +658,11 @@ | ||
| 614 | 658 | if( p->zAttachName ) goto manifest_syntax_error; |
| 615 | 659 | if( !seenZ ) goto manifest_syntax_error; |
| 616 | 660 | p->type = CFTYPE_CONTROL; |
| 617 | 661 | }else if( p->zAttachName ){ |
| 618 | 662 | if( p->nCChild>0 ) goto manifest_syntax_error; |
| 619 | - if( p->rDate==0.0 ) goto manifest_syntax_error; | |
| 663 | + if( p->rDate<=0.0 ) goto manifest_syntax_error; | |
| 620 | 664 | if( p->zTicketUuid ) goto manifest_syntax_error; |
| 621 | 665 | if( p->zWikiTitle ) goto manifest_syntax_error; |
| 622 | 666 | if( !seenZ ) goto manifest_syntax_error; |
| 623 | 667 | p->type = CFTYPE_ATTACHMENT; |
| 624 | 668 | }else{ |
| @@ -625,14 +669,12 @@ | ||
| 625 | 669 | if( p->nCChild>0 ) goto manifest_syntax_error; |
| 626 | 670 | if( p->rDate<=0.0 ) goto manifest_syntax_error; |
| 627 | 671 | if( p->nParent>0 ) goto manifest_syntax_error; |
| 628 | 672 | if( p->nField>0 ) goto manifest_syntax_error; |
| 629 | 673 | if( p->zTicketUuid ) goto manifest_syntax_error; |
| 630 | - if( p->zWiki ) goto manifest_syntax_error; | |
| 631 | 674 | if( p->zWikiTitle ) goto manifest_syntax_error; |
| 632 | 675 | if( p->zTicketUuid ) goto manifest_syntax_error; |
| 633 | - if( p->zAttachName ) goto manifest_syntax_error; | |
| 634 | 676 | p->type = CFTYPE_MANIFEST; |
| 635 | 677 | } |
| 636 | 678 | md5sum_init(); |
| 637 | 679 | return 1; |
| 638 | 680 | |
| @@ -964,10 +1006,12 @@ | ||
| 964 | 1006 | ** * Manifest |
| 965 | 1007 | ** * Control |
| 966 | 1008 | ** * Wiki Page |
| 967 | 1009 | ** * Ticket Change |
| 968 | 1010 | ** * Cluster |
| 1011 | +** * Attachment | |
| 1012 | +** * Event | |
| 969 | 1013 | ** |
| 970 | 1014 | ** If the input is a control artifact, then make appropriate entries |
| 971 | 1015 | ** in the auxiliary tables of the database in order to crosslink the |
| 972 | 1016 | ** artifact. |
| 973 | 1017 | ** |
| @@ -1043,11 +1087,14 @@ | ||
| 1043 | 1087 | if( mid>0 ){ |
| 1044 | 1088 | db_multi_exec("DELETE FROM unclustered WHERE rid=%d", mid); |
| 1045 | 1089 | } |
| 1046 | 1090 | } |
| 1047 | 1091 | } |
| 1048 | - if( m.type==CFTYPE_CONTROL || m.type==CFTYPE_MANIFEST ){ | |
| 1092 | + if( m.type==CFTYPE_CONTROL | |
| 1093 | + || m.type==CFTYPE_MANIFEST | |
| 1094 | + || m.type==CFTYPE_EVENT | |
| 1095 | + ){ | |
| 1049 | 1096 | for(i=0; i<m.nTag; i++){ |
| 1050 | 1097 | int tid; |
| 1051 | 1098 | int type; |
| 1052 | 1099 | if( m.aTag[i].zUuid ){ |
| 1053 | 1100 | tid = uuid_to_rid(m.aTag[i].zUuid, 1); |
| @@ -1109,10 +1156,55 @@ | ||
| 1109 | 1156 | TAG_BGCOLOR, rid, |
| 1110 | 1157 | TAG_USER, rid, |
| 1111 | 1158 | TAG_COMMENT, rid |
| 1112 | 1159 | ); |
| 1113 | 1160 | free(zComment); |
| 1161 | + } | |
| 1162 | + if( m.type==CFTYPE_EVENT ){ | |
| 1163 | + char *zTag = mprintf("event-%s", m.zEventId); | |
| 1164 | + int tagid = tag_findid(zTag, 1); | |
| 1165 | + int prior, subsequent; | |
| 1166 | + int nWiki; | |
| 1167 | + char zLength[40]; | |
| 1168 | + while( isspace(m.zWiki[0]) ) m.zWiki++; | |
| 1169 | + nWiki = strlen(m.zWiki); | |
| 1170 | + sqlite3_snprintf(sizeof(zLength), zLength, "%d", nWiki); | |
| 1171 | + tag_insert(zTag, 1, zLength, rid, m.rDate, rid); | |
| 1172 | + free(zTag); | |
| 1173 | + prior = db_int(0, | |
| 1174 | + "SELECT rid FROM tagxref" | |
| 1175 | + " WHERE tagid=%d AND mtime<%.17g" | |
| 1176 | + " ORDER BY mtime DESC", | |
| 1177 | + tagid, m.rDate | |
| 1178 | + ); | |
| 1179 | + if( prior ){ | |
| 1180 | + content_deltify(prior, rid, 0); | |
| 1181 | + db_multi_exec( | |
| 1182 | + "DELETE FROM event" | |
| 1183 | + " WHERE type='e'" | |
| 1184 | + " AND tagid=%d" | |
| 1185 | + " AND objid IN (SELECT rid FROM tagxref WHERE tagid=%d)", | |
| 1186 | + tagid, tagid | |
| 1187 | + ); | |
| 1188 | + } | |
| 1189 | + subsequent = db_int(0, | |
| 1190 | + "SELECT rid FROM tagxref" | |
| 1191 | + " WHERE tagid=%d AND mtime>%.17g" | |
| 1192 | + " ORDER BY mtime", | |
| 1193 | + tagid, m.rDate | |
| 1194 | + ); | |
| 1195 | + if( subsequent ){ | |
| 1196 | + content_deltify(rid, subsequent, 0); | |
| 1197 | + }else{ | |
| 1198 | + db_multi_exec( | |
| 1199 | + "REPLACE INTO event(type,mtime,objid,tagid,user,comment,bgcolor)" | |
| 1200 | + "VALUES('e',%.17g,%d,%d,%Q,%Q," | |
| 1201 | + " (SELECT value FROM tagxref WHERE tagid=%d AND rid=%d));", | |
| 1202 | + m.rEventDate, rid, tagid, m.zUser, m.zComment, | |
| 1203 | + TAG_BGCOLOR, rid | |
| 1204 | + ); | |
| 1205 | + } | |
| 1114 | 1206 | } |
| 1115 | 1207 | if( m.type==CFTYPE_TICKET ){ |
| 1116 | 1208 | char *zTag; |
| 1117 | 1209 | |
| 1118 | 1210 | assert( manifest_crosslink_busy==1 ); |
| 1119 | 1211 |
| --- src/manifest.c | |
| +++ src/manifest.c | |
| @@ -32,10 +32,11 @@ | |
| 32 | #define CFTYPE_CLUSTER 2 |
| 33 | #define CFTYPE_CONTROL 3 |
| 34 | #define CFTYPE_WIKI 4 |
| 35 | #define CFTYPE_TICKET 5 |
| 36 | #define CFTYPE_ATTACHMENT 6 |
| 37 | |
| 38 | /* |
| 39 | ** A parsed manifest or cluster. |
| 40 | */ |
| 41 | struct Manifest { |
| @@ -45,10 +46,12 @@ | |
| 45 | double rDate; /* Date and time from D card. 0.0 if no D card. */ |
| 46 | char *zUser; /* Name of the user from the U card. */ |
| 47 | char *zRepoCksum; /* MD5 checksum of the baseline content. R card. */ |
| 48 | char *zWiki; /* Text of the wiki page. W card. */ |
| 49 | char *zWikiTitle; /* Name of the wiki page. L card. */ |
| 50 | char *zTicketUuid; /* UUID for a ticket. K card. */ |
| 51 | char *zAttachName; /* Filename of an attachment. A card. */ |
| 52 | char *zAttachSrc; /* UUID of document being attached. A card. */ |
| 53 | char *zAttachTarget; /* Ticket or wiki that attachment applies to. A card */ |
| 54 | int nFile; /* Number of F cards */ |
| @@ -230,10 +233,35 @@ | |
| 230 | if( blob_token(&line, &a2)!=0 ) goto manifest_syntax_error; |
| 231 | zDate = blob_terminate(&a1); |
| 232 | p->rDate = db_double(0.0, "SELECT julianday(%Q)", zDate); |
| 233 | break; |
| 234 | } |
| 235 | |
| 236 | /* |
| 237 | ** F <filename> <uuid> ?<permissions>? ?<old-name>? |
| 238 | ** |
| 239 | ** Identifies a file in a manifest. Multiple F lines are |
| @@ -563,15 +591,16 @@ | |
| 563 | } |
| 564 | if( !seenHeader ) goto manifest_syntax_error; |
| 565 | |
| 566 | if( p->nFile>0 || p->zRepoCksum!=0 ){ |
| 567 | if( p->nCChild>0 ) goto manifest_syntax_error; |
| 568 | if( p->rDate==0.0 ) goto manifest_syntax_error; |
| 569 | if( p->nField>0 ) goto manifest_syntax_error; |
| 570 | if( p->zTicketUuid ) goto manifest_syntax_error; |
| 571 | if( p->zWiki ) goto manifest_syntax_error; |
| 572 | if( p->zWikiTitle ) goto manifest_syntax_error; |
| 573 | if( p->zTicketUuid ) goto manifest_syntax_error; |
| 574 | if( p->zAttachName ) goto manifest_syntax_error; |
| 575 | p->type = CFTYPE_MANIFEST; |
| 576 | }else if( p->nCChild>0 ){ |
| 577 | if( p->rDate>0.0 ) goto manifest_syntax_error; |
| @@ -581,26 +610,41 @@ | |
| 581 | if( p->nParent>0 ) goto manifest_syntax_error; |
| 582 | if( p->nField>0 ) goto manifest_syntax_error; |
| 583 | if( p->zTicketUuid ) goto manifest_syntax_error; |
| 584 | if( p->zWiki ) goto manifest_syntax_error; |
| 585 | if( p->zWikiTitle ) goto manifest_syntax_error; |
| 586 | if( p->zAttachName ) goto manifest_syntax_error; |
| 587 | if( !seenZ ) goto manifest_syntax_error; |
| 588 | p->type = CFTYPE_CLUSTER; |
| 589 | }else if( p->nField>0 ){ |
| 590 | if( p->rDate==0.0 ) goto manifest_syntax_error; |
| 591 | if( p->zWiki ) goto manifest_syntax_error; |
| 592 | if( p->zWikiTitle ) goto manifest_syntax_error; |
| 593 | if( p->nCChild>0 ) goto manifest_syntax_error; |
| 594 | if( p->nTag>0 ) goto manifest_syntax_error; |
| 595 | if( p->zTicketUuid==0 ) goto manifest_syntax_error; |
| 596 | if( p->zUser==0 ) goto manifest_syntax_error; |
| 597 | if( p->zAttachName ) goto manifest_syntax_error; |
| 598 | if( !seenZ ) goto manifest_syntax_error; |
| 599 | p->type = CFTYPE_TICKET; |
| 600 | }else if( p->zWiki!=0 ){ |
| 601 | if( p->rDate==0.0 ) goto manifest_syntax_error; |
| 602 | if( p->nCChild>0 ) goto manifest_syntax_error; |
| 603 | if( p->nTag>0 ) goto manifest_syntax_error; |
| 604 | if( p->zTicketUuid!=0 ) goto manifest_syntax_error; |
| 605 | if( p->zWikiTitle==0 ) goto manifest_syntax_error; |
| 606 | if( p->zAttachName ) goto manifest_syntax_error; |
| @@ -614,11 +658,11 @@ | |
| 614 | if( p->zAttachName ) goto manifest_syntax_error; |
| 615 | if( !seenZ ) goto manifest_syntax_error; |
| 616 | p->type = CFTYPE_CONTROL; |
| 617 | }else if( p->zAttachName ){ |
| 618 | if( p->nCChild>0 ) goto manifest_syntax_error; |
| 619 | if( p->rDate==0.0 ) goto manifest_syntax_error; |
| 620 | if( p->zTicketUuid ) goto manifest_syntax_error; |
| 621 | if( p->zWikiTitle ) goto manifest_syntax_error; |
| 622 | if( !seenZ ) goto manifest_syntax_error; |
| 623 | p->type = CFTYPE_ATTACHMENT; |
| 624 | }else{ |
| @@ -625,14 +669,12 @@ | |
| 625 | if( p->nCChild>0 ) goto manifest_syntax_error; |
| 626 | if( p->rDate<=0.0 ) goto manifest_syntax_error; |
| 627 | if( p->nParent>0 ) goto manifest_syntax_error; |
| 628 | if( p->nField>0 ) goto manifest_syntax_error; |
| 629 | if( p->zTicketUuid ) goto manifest_syntax_error; |
| 630 | if( p->zWiki ) goto manifest_syntax_error; |
| 631 | if( p->zWikiTitle ) goto manifest_syntax_error; |
| 632 | if( p->zTicketUuid ) goto manifest_syntax_error; |
| 633 | if( p->zAttachName ) goto manifest_syntax_error; |
| 634 | p->type = CFTYPE_MANIFEST; |
| 635 | } |
| 636 | md5sum_init(); |
| 637 | return 1; |
| 638 | |
| @@ -964,10 +1006,12 @@ | |
| 964 | ** * Manifest |
| 965 | ** * Control |
| 966 | ** * Wiki Page |
| 967 | ** * Ticket Change |
| 968 | ** * Cluster |
| 969 | ** |
| 970 | ** If the input is a control artifact, then make appropriate entries |
| 971 | ** in the auxiliary tables of the database in order to crosslink the |
| 972 | ** artifact. |
| 973 | ** |
| @@ -1043,11 +1087,14 @@ | |
| 1043 | if( mid>0 ){ |
| 1044 | db_multi_exec("DELETE FROM unclustered WHERE rid=%d", mid); |
| 1045 | } |
| 1046 | } |
| 1047 | } |
| 1048 | if( m.type==CFTYPE_CONTROL || m.type==CFTYPE_MANIFEST ){ |
| 1049 | for(i=0; i<m.nTag; i++){ |
| 1050 | int tid; |
| 1051 | int type; |
| 1052 | if( m.aTag[i].zUuid ){ |
| 1053 | tid = uuid_to_rid(m.aTag[i].zUuid, 1); |
| @@ -1109,10 +1156,55 @@ | |
| 1109 | TAG_BGCOLOR, rid, |
| 1110 | TAG_USER, rid, |
| 1111 | TAG_COMMENT, rid |
| 1112 | ); |
| 1113 | free(zComment); |
| 1114 | } |
| 1115 | if( m.type==CFTYPE_TICKET ){ |
| 1116 | char *zTag; |
| 1117 | |
| 1118 | assert( manifest_crosslink_busy==1 ); |
| 1119 |
| --- src/manifest.c | |
| +++ src/manifest.c | |
| @@ -32,10 +32,11 @@ | |
| 32 | #define CFTYPE_CLUSTER 2 |
| 33 | #define CFTYPE_CONTROL 3 |
| 34 | #define CFTYPE_WIKI 4 |
| 35 | #define CFTYPE_TICKET 5 |
| 36 | #define CFTYPE_ATTACHMENT 6 |
| 37 | #define CFTYPE_EVENT 7 |
| 38 | |
| 39 | /* |
| 40 | ** A parsed manifest or cluster. |
| 41 | */ |
| 42 | struct Manifest { |
| @@ -45,10 +46,12 @@ | |
| 46 | double rDate; /* Date and time from D card. 0.0 if no D card. */ |
| 47 | char *zUser; /* Name of the user from the U card. */ |
| 48 | char *zRepoCksum; /* MD5 checksum of the baseline content. R card. */ |
| 49 | char *zWiki; /* Text of the wiki page. W card. */ |
| 50 | char *zWikiTitle; /* Name of the wiki page. L card. */ |
| 51 | double rEventDate; /* Date of an event. E card. */ |
| 52 | char *zEventId; /* UUID for an event. E card. */ |
| 53 | char *zTicketUuid; /* UUID for a ticket. K card. */ |
| 54 | char *zAttachName; /* Filename of an attachment. A card. */ |
| 55 | char *zAttachSrc; /* UUID of document being attached. A card. */ |
| 56 | char *zAttachTarget; /* Ticket or wiki that attachment applies to. A card */ |
| 57 | int nFile; /* Number of F cards */ |
| @@ -230,10 +233,35 @@ | |
| 233 | if( blob_token(&line, &a2)!=0 ) goto manifest_syntax_error; |
| 234 | zDate = blob_terminate(&a1); |
| 235 | p->rDate = db_double(0.0, "SELECT julianday(%Q)", zDate); |
| 236 | break; |
| 237 | } |
| 238 | |
| 239 | /* |
| 240 | ** E <timestamp> <uuid> |
| 241 | ** |
| 242 | ** An "event" card that contains the timestamp of the event in the |
| 243 | ** format YYYY-MM-DDtHH:MM:SS and a unique identifier for the event. |
| 244 | ** The event timestamp is distinct from the D timestamp. The D |
| 245 | ** timestamp is when the artifact was created whereas the E timestamp |
| 246 | ** is when the specific event is said to occur. |
| 247 | */ |
| 248 | case 'E': { |
| 249 | char *zEDate; |
| 250 | md5sum_step_text(blob_buffer(&line), blob_size(&line)); |
| 251 | if( p->rEventDate!=0.0 ) goto manifest_syntax_error; |
| 252 | if( blob_token(&line, &a1)==0 ) goto manifest_syntax_error; |
| 253 | if( blob_token(&line, &a2)==0 ) goto manifest_syntax_error; |
| 254 | if( blob_token(&line, &a3)!=0 ) goto manifest_syntax_error; |
| 255 | zEDate = blob_terminate(&a1); |
| 256 | p->rEventDate = db_double(0.0, "SELECT julianday(%Q)", zEDate); |
| 257 | if( p->rEventDate<=0.0 ) goto manifest_syntax_error; |
| 258 | if( blob_size(&a2)!=UUID_SIZE ) goto manifest_syntax_error; |
| 259 | p->zEventId = blob_terminate(&a2); |
| 260 | if( !validate16(p->zEventId, UUID_SIZE) ) goto manifest_syntax_error; |
| 261 | break; |
| 262 | } |
| 263 | |
| 264 | /* |
| 265 | ** F <filename> <uuid> ?<permissions>? ?<old-name>? |
| 266 | ** |
| 267 | ** Identifies a file in a manifest. Multiple F lines are |
| @@ -563,15 +591,16 @@ | |
| 591 | } |
| 592 | if( !seenHeader ) goto manifest_syntax_error; |
| 593 | |
| 594 | if( p->nFile>0 || p->zRepoCksum!=0 ){ |
| 595 | if( p->nCChild>0 ) goto manifest_syntax_error; |
| 596 | if( p->rDate<=0.0 ) goto manifest_syntax_error; |
| 597 | if( p->nField>0 ) goto manifest_syntax_error; |
| 598 | if( p->zTicketUuid ) goto manifest_syntax_error; |
| 599 | if( p->zWiki ) goto manifest_syntax_error; |
| 600 | if( p->zWikiTitle ) goto manifest_syntax_error; |
| 601 | if( p->zEventId ) goto manifest_syntax_error; |
| 602 | if( p->zTicketUuid ) goto manifest_syntax_error; |
| 603 | if( p->zAttachName ) goto manifest_syntax_error; |
| 604 | p->type = CFTYPE_MANIFEST; |
| 605 | }else if( p->nCChild>0 ){ |
| 606 | if( p->rDate>0.0 ) goto manifest_syntax_error; |
| @@ -581,26 +610,41 @@ | |
| 610 | if( p->nParent>0 ) goto manifest_syntax_error; |
| 611 | if( p->nField>0 ) goto manifest_syntax_error; |
| 612 | if( p->zTicketUuid ) goto manifest_syntax_error; |
| 613 | if( p->zWiki ) goto manifest_syntax_error; |
| 614 | if( p->zWikiTitle ) goto manifest_syntax_error; |
| 615 | if( p->zEventId ) goto manifest_syntax_error; |
| 616 | if( p->zAttachName ) goto manifest_syntax_error; |
| 617 | if( !seenZ ) goto manifest_syntax_error; |
| 618 | p->type = CFTYPE_CLUSTER; |
| 619 | }else if( p->nField>0 ){ |
| 620 | if( p->rDate<=0.0 ) goto manifest_syntax_error; |
| 621 | if( p->zWiki ) goto manifest_syntax_error; |
| 622 | if( p->zWikiTitle ) goto manifest_syntax_error; |
| 623 | if( p->zEventId ) goto manifest_syntax_error; |
| 624 | if( p->nCChild>0 ) goto manifest_syntax_error; |
| 625 | if( p->nTag>0 ) goto manifest_syntax_error; |
| 626 | if( p->zTicketUuid==0 ) goto manifest_syntax_error; |
| 627 | if( p->zUser==0 ) goto manifest_syntax_error; |
| 628 | if( p->zAttachName ) goto manifest_syntax_error; |
| 629 | if( !seenZ ) goto manifest_syntax_error; |
| 630 | p->type = CFTYPE_TICKET; |
| 631 | }else if( p->zEventId ){ |
| 632 | if( p->rDate<=0.0 ) goto manifest_syntax_error; |
| 633 | if( p->nCChild>0 ) goto manifest_syntax_error; |
| 634 | if( p->zTicketUuid!=0 ) goto manifest_syntax_error; |
| 635 | if( p->zWikiTitle!=0 ) goto manifest_syntax_error; |
| 636 | if( p->zWiki==0 ) goto manifest_syntax_error; |
| 637 | if( p->zAttachName ) goto manifest_syntax_error; |
| 638 | for(i=0; i<p->nTag; i++){ |
| 639 | if( p->aTag[i].zName[0]!='+' ) goto manifest_syntax_error; |
| 640 | if( p->aTag[i].zUuid!=0 ) goto manifest_syntax_error; |
| 641 | } |
| 642 | if( !seenZ ) goto manifest_syntax_error; |
| 643 | p->type = CFTYPE_EVENT; |
| 644 | }else if( p->zWiki!=0 ){ |
| 645 | if( p->rDate<=0.0 ) goto manifest_syntax_error; |
| 646 | if( p->nCChild>0 ) goto manifest_syntax_error; |
| 647 | if( p->nTag>0 ) goto manifest_syntax_error; |
| 648 | if( p->zTicketUuid!=0 ) goto manifest_syntax_error; |
| 649 | if( p->zWikiTitle==0 ) goto manifest_syntax_error; |
| 650 | if( p->zAttachName ) goto manifest_syntax_error; |
| @@ -614,11 +658,11 @@ | |
| 658 | if( p->zAttachName ) goto manifest_syntax_error; |
| 659 | if( !seenZ ) goto manifest_syntax_error; |
| 660 | p->type = CFTYPE_CONTROL; |
| 661 | }else if( p->zAttachName ){ |
| 662 | if( p->nCChild>0 ) goto manifest_syntax_error; |
| 663 | if( p->rDate<=0.0 ) goto manifest_syntax_error; |
| 664 | if( p->zTicketUuid ) goto manifest_syntax_error; |
| 665 | if( p->zWikiTitle ) goto manifest_syntax_error; |
| 666 | if( !seenZ ) goto manifest_syntax_error; |
| 667 | p->type = CFTYPE_ATTACHMENT; |
| 668 | }else{ |
| @@ -625,14 +669,12 @@ | |
| 669 | if( p->nCChild>0 ) goto manifest_syntax_error; |
| 670 | if( p->rDate<=0.0 ) goto manifest_syntax_error; |
| 671 | if( p->nParent>0 ) goto manifest_syntax_error; |
| 672 | if( p->nField>0 ) goto manifest_syntax_error; |
| 673 | if( p->zTicketUuid ) goto manifest_syntax_error; |
| 674 | if( p->zWikiTitle ) goto manifest_syntax_error; |
| 675 | if( p->zTicketUuid ) goto manifest_syntax_error; |
| 676 | p->type = CFTYPE_MANIFEST; |
| 677 | } |
| 678 | md5sum_init(); |
| 679 | return 1; |
| 680 | |
| @@ -964,10 +1006,12 @@ | |
| 1006 | ** * Manifest |
| 1007 | ** * Control |
| 1008 | ** * Wiki Page |
| 1009 | ** * Ticket Change |
| 1010 | ** * Cluster |
| 1011 | ** * Attachment |
| 1012 | ** * Event |
| 1013 | ** |
| 1014 | ** If the input is a control artifact, then make appropriate entries |
| 1015 | ** in the auxiliary tables of the database in order to crosslink the |
| 1016 | ** artifact. |
| 1017 | ** |
| @@ -1043,11 +1087,14 @@ | |
| 1087 | if( mid>0 ){ |
| 1088 | db_multi_exec("DELETE FROM unclustered WHERE rid=%d", mid); |
| 1089 | } |
| 1090 | } |
| 1091 | } |
| 1092 | if( m.type==CFTYPE_CONTROL |
| 1093 | || m.type==CFTYPE_MANIFEST |
| 1094 | || m.type==CFTYPE_EVENT |
| 1095 | ){ |
| 1096 | for(i=0; i<m.nTag; i++){ |
| 1097 | int tid; |
| 1098 | int type; |
| 1099 | if( m.aTag[i].zUuid ){ |
| 1100 | tid = uuid_to_rid(m.aTag[i].zUuid, 1); |
| @@ -1109,10 +1156,55 @@ | |
| 1156 | TAG_BGCOLOR, rid, |
| 1157 | TAG_USER, rid, |
| 1158 | TAG_COMMENT, rid |
| 1159 | ); |
| 1160 | free(zComment); |
| 1161 | } |
| 1162 | if( m.type==CFTYPE_EVENT ){ |
| 1163 | char *zTag = mprintf("event-%s", m.zEventId); |
| 1164 | int tagid = tag_findid(zTag, 1); |
| 1165 | int prior, subsequent; |
| 1166 | int nWiki; |
| 1167 | char zLength[40]; |
| 1168 | while( isspace(m.zWiki[0]) ) m.zWiki++; |
| 1169 | nWiki = strlen(m.zWiki); |
| 1170 | sqlite3_snprintf(sizeof(zLength), zLength, "%d", nWiki); |
| 1171 | tag_insert(zTag, 1, zLength, rid, m.rDate, rid); |
| 1172 | free(zTag); |
| 1173 | prior = db_int(0, |
| 1174 | "SELECT rid FROM tagxref" |
| 1175 | " WHERE tagid=%d AND mtime<%.17g" |
| 1176 | " ORDER BY mtime DESC", |
| 1177 | tagid, m.rDate |
| 1178 | ); |
| 1179 | if( prior ){ |
| 1180 | content_deltify(prior, rid, 0); |
| 1181 | db_multi_exec( |
| 1182 | "DELETE FROM event" |
| 1183 | " WHERE type='e'" |
| 1184 | " AND tagid=%d" |
| 1185 | " AND objid IN (SELECT rid FROM tagxref WHERE tagid=%d)", |
| 1186 | tagid, tagid |
| 1187 | ); |
| 1188 | } |
| 1189 | subsequent = db_int(0, |
| 1190 | "SELECT rid FROM tagxref" |
| 1191 | " WHERE tagid=%d AND mtime>%.17g" |
| 1192 | " ORDER BY mtime", |
| 1193 | tagid, m.rDate |
| 1194 | ); |
| 1195 | if( subsequent ){ |
| 1196 | content_deltify(rid, subsequent, 0); |
| 1197 | }else{ |
| 1198 | db_multi_exec( |
| 1199 | "REPLACE INTO event(type,mtime,objid,tagid,user,comment,bgcolor)" |
| 1200 | "VALUES('e',%.17g,%d,%d,%Q,%Q," |
| 1201 | " (SELECT value FROM tagxref WHERE tagid=%d AND rid=%d));", |
| 1202 | m.rEventDate, rid, tagid, m.zUser, m.zComment, |
| 1203 | TAG_BGCOLOR, rid |
| 1204 | ); |
| 1205 | } |
| 1206 | } |
| 1207 | if( m.type==CFTYPE_TICKET ){ |
| 1208 | char *zTag; |
| 1209 | |
| 1210 | assert( manifest_crosslink_busy==1 ); |
| 1211 |
+3
-3
| --- src/printf.c | ||
| +++ src/printf.c | ||
| @@ -44,13 +44,13 @@ | ||
| 44 | 44 | #define etHTMLIZE 16 /* Make text safe for HTML */ |
| 45 | 45 | #define etHTTPIZE 17 /* Make text safe for HTTP. "/" encoded as %2f */ |
| 46 | 46 | #define etURLIZE 18 /* Make text safe for HTTP. "/" not encoded */ |
| 47 | 47 | #define etFOSSILIZE 19 /* The fossil header encoding format. */ |
| 48 | 48 | #define etPATH 20 /* Path type */ |
| 49 | -#define etWIKISTR 21 /* Wiki text rendered from a char* */ | |
| 50 | -#define etWIKIBLOB 22 /* Wiki text rendered from a Blob* */ | |
| 51 | -#define etSTRINGID 23 /* String with length limit for a UUID prefix */ | |
| 49 | +#define etWIKISTR 21 /* Wiki text rendered from a char*: %w */ | |
| 50 | +#define etWIKIBLOB 22 /* Wiki text rendered from a Blob*: %W */ | |
| 51 | +#define etSTRINGID 23 /* String with length limit for a UUID prefix: %S */ | |
| 52 | 52 | |
| 53 | 53 | |
| 54 | 54 | /* |
| 55 | 55 | ** An "etByte" is an 8-bit unsigned value. |
| 56 | 56 | */ |
| 57 | 57 |
| --- src/printf.c | |
| +++ src/printf.c | |
| @@ -44,13 +44,13 @@ | |
| 44 | #define etHTMLIZE 16 /* Make text safe for HTML */ |
| 45 | #define etHTTPIZE 17 /* Make text safe for HTTP. "/" encoded as %2f */ |
| 46 | #define etURLIZE 18 /* Make text safe for HTTP. "/" not encoded */ |
| 47 | #define etFOSSILIZE 19 /* The fossil header encoding format. */ |
| 48 | #define etPATH 20 /* Path type */ |
| 49 | #define etWIKISTR 21 /* Wiki text rendered from a char* */ |
| 50 | #define etWIKIBLOB 22 /* Wiki text rendered from a Blob* */ |
| 51 | #define etSTRINGID 23 /* String with length limit for a UUID prefix */ |
| 52 | |
| 53 | |
| 54 | /* |
| 55 | ** An "etByte" is an 8-bit unsigned value. |
| 56 | */ |
| 57 |
| --- src/printf.c | |
| +++ src/printf.c | |
| @@ -44,13 +44,13 @@ | |
| 44 | #define etHTMLIZE 16 /* Make text safe for HTML */ |
| 45 | #define etHTTPIZE 17 /* Make text safe for HTTP. "/" encoded as %2f */ |
| 46 | #define etURLIZE 18 /* Make text safe for HTTP. "/" not encoded */ |
| 47 | #define etFOSSILIZE 19 /* The fossil header encoding format. */ |
| 48 | #define etPATH 20 /* Path type */ |
| 49 | #define etWIKISTR 21 /* Wiki text rendered from a char*: %w */ |
| 50 | #define etWIKIBLOB 22 /* Wiki text rendered from a Blob*: %W */ |
| 51 | #define etSTRINGID 23 /* String with length limit for a UUID prefix: %S */ |
| 52 | |
| 53 | |
| 54 | /* |
| 55 | ** An "etByte" is an 8-bit unsigned value. |
| 56 | */ |
| 57 |
+72
-66
| --- src/rebuild.c | ||
| +++ src/rebuild.c | ||
| @@ -121,76 +121,82 @@ | ||
| 121 | 121 | Bag children; |
| 122 | 122 | Blob copy; |
| 123 | 123 | Blob *pUse; |
| 124 | 124 | int nChild, i, cid; |
| 125 | 125 | |
| 126 | - /* Fix up the "blob.size" field if needed. */ | |
| 127 | - if( size!=blob_size(pBase) ){ | |
| 128 | - db_multi_exec( | |
| 129 | - "UPDATE blob SET size=%d WHERE rid=%d", blob_size(pBase), rid | |
| 130 | - ); | |
| 131 | - } | |
| 132 | - | |
| 133 | - /* Find all children of artifact rid */ | |
| 134 | - db_static_prepare(&q1, "SELECT rid FROM delta WHERE srcid=:rid"); | |
| 135 | - db_bind_int(&q1, ":rid", rid); | |
| 136 | - bag_init(&children); | |
| 137 | - while( db_step(&q1)==SQLITE_ROW ){ | |
| 138 | - int cid = db_column_int(&q1, 0); | |
| 139 | - if( !bag_find(&bagDone, cid) ){ | |
| 140 | - bag_insert(&children, cid); | |
| 141 | - } | |
| 142 | - } | |
| 143 | - nChild = bag_count(&children); | |
| 144 | - db_reset(&q1); | |
| 145 | - | |
| 146 | - /* Crosslink the artifact */ | |
| 147 | - if( nChild==0 ){ | |
| 148 | - pUse = pBase; | |
| 149 | - }else{ | |
| 150 | - blob_copy(©, pBase); | |
| 151 | - pUse = © | |
| 152 | - } | |
| 153 | - if( zFNameFormat==0 ){ | |
| 154 | - /* We are doing "fossil rebuild" */ | |
| 155 | - manifest_crosslink(rid, pUse); | |
| 156 | - }else{ | |
| 157 | - /* We are doing "fossil deconstruct" */ | |
| 158 | - char *zUuid = db_text(0, "SELECT uuid FROM blob WHERE rid=%d", rid); | |
| 159 | - char *zFile = mprintf(zFNameFormat, zUuid, zUuid+prefixLength); | |
| 160 | - blob_write_to_file(pUse,zFile); | |
| 161 | - free(zFile); | |
| 162 | - free(zUuid); | |
| 163 | - } | |
| 164 | - blob_reset(pUse); | |
| 165 | - rebuild_step_done(rid); | |
| 166 | - | |
| 167 | - /* Call all children recursively */ | |
| 168 | - for(cid=bag_first(&children), i=1; cid; cid=bag_next(&children, cid), i++){ | |
| 169 | - Stmt q2; | |
| 170 | - int sz; | |
| 171 | - if( nChild==i ){ | |
| 172 | - pUse = pBase; | |
| 173 | - }else{ | |
| 174 | - blob_copy(©, pBase); | |
| 175 | - pUse = © | |
| 176 | - } | |
| 177 | - db_prepare(&q2, "SELECT content, size FROM blob WHERE rid=%d", cid); | |
| 178 | - if( db_step(&q2)==SQLITE_ROW && (sz = db_column_int(&q2,1))>=0 ){ | |
| 179 | - Blob delta; | |
| 180 | - db_ephemeral_blob(&q2, 0, &delta); | |
| 181 | - blob_uncompress(&delta, &delta); | |
| 182 | - blob_delta_apply(pUse, &delta, pUse); | |
| 183 | - blob_reset(&delta); | |
| 184 | - db_finalize(&q2); | |
| 185 | - rebuild_step(cid, sz, pUse); | |
| 186 | - }else{ | |
| 187 | - db_finalize(&q2); | |
| 188 | - blob_reset(pUse); | |
| 189 | - } | |
| 190 | - } | |
| 191 | - bag_clear(&children); | |
| 126 | + while( rid>0 ){ | |
| 127 | + | |
| 128 | + /* Fix up the "blob.size" field if needed. */ | |
| 129 | + if( size!=blob_size(pBase) ){ | |
| 130 | + db_multi_exec( | |
| 131 | + "UPDATE blob SET size=%d WHERE rid=%d", blob_size(pBase), rid | |
| 132 | + ); | |
| 133 | + } | |
| 134 | + | |
| 135 | + /* Find all children of artifact rid */ | |
| 136 | + db_static_prepare(&q1, "SELECT rid FROM delta WHERE srcid=:rid"); | |
| 137 | + db_bind_int(&q1, ":rid", rid); | |
| 138 | + bag_init(&children); | |
| 139 | + while( db_step(&q1)==SQLITE_ROW ){ | |
| 140 | + int cid = db_column_int(&q1, 0); | |
| 141 | + if( !bag_find(&bagDone, cid) ){ | |
| 142 | + bag_insert(&children, cid); | |
| 143 | + } | |
| 144 | + } | |
| 145 | + nChild = bag_count(&children); | |
| 146 | + db_reset(&q1); | |
| 147 | + | |
| 148 | + /* Crosslink the artifact */ | |
| 149 | + if( nChild==0 ){ | |
| 150 | + pUse = pBase; | |
| 151 | + }else{ | |
| 152 | + blob_copy(©, pBase); | |
| 153 | + pUse = © | |
| 154 | + } | |
| 155 | + if( zFNameFormat==0 ){ | |
| 156 | + /* We are doing "fossil rebuild" */ | |
| 157 | + manifest_crosslink(rid, pUse); | |
| 158 | + }else{ | |
| 159 | + /* We are doing "fossil deconstruct" */ | |
| 160 | + char *zUuid = db_text(0, "SELECT uuid FROM blob WHERE rid=%d", rid); | |
| 161 | + char *zFile = mprintf(zFNameFormat, zUuid, zUuid+prefixLength); | |
| 162 | + blob_write_to_file(pUse,zFile); | |
| 163 | + free(zFile); | |
| 164 | + free(zUuid); | |
| 165 | + } | |
| 166 | + blob_reset(pUse); | |
| 167 | + rebuild_step_done(rid); | |
| 168 | + | |
| 169 | + /* Call all children recursively */ | |
| 170 | + rid = 0; | |
| 171 | + for(cid=bag_first(&children), i=1; cid; cid=bag_next(&children, cid), i++){ | |
| 172 | + Stmt q2; | |
| 173 | + int sz; | |
| 174 | + db_prepare(&q2, "SELECT content, size FROM blob WHERE rid=%d", cid); | |
| 175 | + if( db_step(&q2)==SQLITE_ROW && (sz = db_column_int(&q2,1))>=0 ){ | |
| 176 | + Blob delta, next; | |
| 177 | + db_ephemeral_blob(&q2, 0, &delta); | |
| 178 | + blob_uncompress(&delta, &delta); | |
| 179 | + blob_delta_apply(pBase, &delta, &next); | |
| 180 | + blob_reset(&delta); | |
| 181 | + db_finalize(&q2); | |
| 182 | + if( i<nChild ){ | |
| 183 | + rebuild_step(cid, sz, &next); | |
| 184 | + }else{ | |
| 185 | + /* Tail recursion */ | |
| 186 | + rid = cid; | |
| 187 | + size = sz; | |
| 188 | + blob_reset(pBase); | |
| 189 | + *pBase = next; | |
| 190 | + } | |
| 191 | + }else{ | |
| 192 | + db_finalize(&q2); | |
| 193 | + blob_reset(pBase); | |
| 194 | + } | |
| 195 | + } | |
| 196 | + bag_clear(&children); | |
| 197 | + } | |
| 192 | 198 | } |
| 193 | 199 | |
| 194 | 200 | /* |
| 195 | 201 | ** Check to see if the "sym-trunk" tag exists. If not, create it |
| 196 | 202 | ** and attach it to the very first check-in. |
| 197 | 203 |
| --- src/rebuild.c | |
| +++ src/rebuild.c | |
| @@ -121,76 +121,82 @@ | |
| 121 | Bag children; |
| 122 | Blob copy; |
| 123 | Blob *pUse; |
| 124 | int nChild, i, cid; |
| 125 | |
| 126 | /* Fix up the "blob.size" field if needed. */ |
| 127 | if( size!=blob_size(pBase) ){ |
| 128 | db_multi_exec( |
| 129 | "UPDATE blob SET size=%d WHERE rid=%d", blob_size(pBase), rid |
| 130 | ); |
| 131 | } |
| 132 | |
| 133 | /* Find all children of artifact rid */ |
| 134 | db_static_prepare(&q1, "SELECT rid FROM delta WHERE srcid=:rid"); |
| 135 | db_bind_int(&q1, ":rid", rid); |
| 136 | bag_init(&children); |
| 137 | while( db_step(&q1)==SQLITE_ROW ){ |
| 138 | int cid = db_column_int(&q1, 0); |
| 139 | if( !bag_find(&bagDone, cid) ){ |
| 140 | bag_insert(&children, cid); |
| 141 | } |
| 142 | } |
| 143 | nChild = bag_count(&children); |
| 144 | db_reset(&q1); |
| 145 | |
| 146 | /* Crosslink the artifact */ |
| 147 | if( nChild==0 ){ |
| 148 | pUse = pBase; |
| 149 | }else{ |
| 150 | blob_copy(©, pBase); |
| 151 | pUse = © |
| 152 | } |
| 153 | if( zFNameFormat==0 ){ |
| 154 | /* We are doing "fossil rebuild" */ |
| 155 | manifest_crosslink(rid, pUse); |
| 156 | }else{ |
| 157 | /* We are doing "fossil deconstruct" */ |
| 158 | char *zUuid = db_text(0, "SELECT uuid FROM blob WHERE rid=%d", rid); |
| 159 | char *zFile = mprintf(zFNameFormat, zUuid, zUuid+prefixLength); |
| 160 | blob_write_to_file(pUse,zFile); |
| 161 | free(zFile); |
| 162 | free(zUuid); |
| 163 | } |
| 164 | blob_reset(pUse); |
| 165 | rebuild_step_done(rid); |
| 166 | |
| 167 | /* Call all children recursively */ |
| 168 | for(cid=bag_first(&children), i=1; cid; cid=bag_next(&children, cid), i++){ |
| 169 | Stmt q2; |
| 170 | int sz; |
| 171 | if( nChild==i ){ |
| 172 | pUse = pBase; |
| 173 | }else{ |
| 174 | blob_copy(©, pBase); |
| 175 | pUse = © |
| 176 | } |
| 177 | db_prepare(&q2, "SELECT content, size FROM blob WHERE rid=%d", cid); |
| 178 | if( db_step(&q2)==SQLITE_ROW && (sz = db_column_int(&q2,1))>=0 ){ |
| 179 | Blob delta; |
| 180 | db_ephemeral_blob(&q2, 0, &delta); |
| 181 | blob_uncompress(&delta, &delta); |
| 182 | blob_delta_apply(pUse, &delta, pUse); |
| 183 | blob_reset(&delta); |
| 184 | db_finalize(&q2); |
| 185 | rebuild_step(cid, sz, pUse); |
| 186 | }else{ |
| 187 | db_finalize(&q2); |
| 188 | blob_reset(pUse); |
| 189 | } |
| 190 | } |
| 191 | bag_clear(&children); |
| 192 | } |
| 193 | |
| 194 | /* |
| 195 | ** Check to see if the "sym-trunk" tag exists. If not, create it |
| 196 | ** and attach it to the very first check-in. |
| 197 |
| --- src/rebuild.c | |
| +++ src/rebuild.c | |
| @@ -121,76 +121,82 @@ | |
| 121 | Bag children; |
| 122 | Blob copy; |
| 123 | Blob *pUse; |
| 124 | int nChild, i, cid; |
| 125 | |
| 126 | while( rid>0 ){ |
| 127 | |
| 128 | /* Fix up the "blob.size" field if needed. */ |
| 129 | if( size!=blob_size(pBase) ){ |
| 130 | db_multi_exec( |
| 131 | "UPDATE blob SET size=%d WHERE rid=%d", blob_size(pBase), rid |
| 132 | ); |
| 133 | } |
| 134 | |
| 135 | /* Find all children of artifact rid */ |
| 136 | db_static_prepare(&q1, "SELECT rid FROM delta WHERE srcid=:rid"); |
| 137 | db_bind_int(&q1, ":rid", rid); |
| 138 | bag_init(&children); |
| 139 | while( db_step(&q1)==SQLITE_ROW ){ |
| 140 | int cid = db_column_int(&q1, 0); |
| 141 | if( !bag_find(&bagDone, cid) ){ |
| 142 | bag_insert(&children, cid); |
| 143 | } |
| 144 | } |
| 145 | nChild = bag_count(&children); |
| 146 | db_reset(&q1); |
| 147 | |
| 148 | /* Crosslink the artifact */ |
| 149 | if( nChild==0 ){ |
| 150 | pUse = pBase; |
| 151 | }else{ |
| 152 | blob_copy(©, pBase); |
| 153 | pUse = © |
| 154 | } |
| 155 | if( zFNameFormat==0 ){ |
| 156 | /* We are doing "fossil rebuild" */ |
| 157 | manifest_crosslink(rid, pUse); |
| 158 | }else{ |
| 159 | /* We are doing "fossil deconstruct" */ |
| 160 | char *zUuid = db_text(0, "SELECT uuid FROM blob WHERE rid=%d", rid); |
| 161 | char *zFile = mprintf(zFNameFormat, zUuid, zUuid+prefixLength); |
| 162 | blob_write_to_file(pUse,zFile); |
| 163 | free(zFile); |
| 164 | free(zUuid); |
| 165 | } |
| 166 | blob_reset(pUse); |
| 167 | rebuild_step_done(rid); |
| 168 | |
| 169 | /* Call all children recursively */ |
| 170 | rid = 0; |
| 171 | for(cid=bag_first(&children), i=1; cid; cid=bag_next(&children, cid), i++){ |
| 172 | Stmt q2; |
| 173 | int sz; |
| 174 | db_prepare(&q2, "SELECT content, size FROM blob WHERE rid=%d", cid); |
| 175 | if( db_step(&q2)==SQLITE_ROW && (sz = db_column_int(&q2,1))>=0 ){ |
| 176 | Blob delta, next; |
| 177 | db_ephemeral_blob(&q2, 0, &delta); |
| 178 | blob_uncompress(&delta, &delta); |
| 179 | blob_delta_apply(pBase, &delta, &next); |
| 180 | blob_reset(&delta); |
| 181 | db_finalize(&q2); |
| 182 | if( i<nChild ){ |
| 183 | rebuild_step(cid, sz, &next); |
| 184 | }else{ |
| 185 | /* Tail recursion */ |
| 186 | rid = cid; |
| 187 | size = sz; |
| 188 | blob_reset(pBase); |
| 189 | *pBase = next; |
| 190 | } |
| 191 | }else{ |
| 192 | db_finalize(&q2); |
| 193 | blob_reset(pBase); |
| 194 | } |
| 195 | } |
| 196 | bag_clear(&children); |
| 197 | } |
| 198 | } |
| 199 | |
| 200 | /* |
| 201 | ** Check to see if the "sym-trunk" tag exists. If not, create it |
| 202 | ** and attach it to the very first check-in. |
| 203 |
+223
-268
| --- src/sqlite3.c | ||
| +++ src/sqlite3.c | ||
| @@ -650,11 +650,11 @@ | ||
| 650 | 650 | ** [sqlite3_libversion_number()], [sqlite3_sourceid()], |
| 651 | 651 | ** [sqlite_version()] and [sqlite_source_id()]. |
| 652 | 652 | */ |
| 653 | 653 | #define SQLITE_VERSION "3.7.3" |
| 654 | 654 | #define SQLITE_VERSION_NUMBER 3007003 |
| 655 | -#define SQLITE_SOURCE_ID "2010-09-29 23:09:23 1ef0dc9328f47506cb2dcd142150e96cb4755216" | |
| 655 | +#define SQLITE_SOURCE_ID "2010-10-04 23:55:51 ece641eb8951c6314cedbdb3243f91cb199c3239" | |
| 656 | 656 | |
| 657 | 657 | /* |
| 658 | 658 | ** CAPI3REF: Run-Time Library Version Numbers |
| 659 | 659 | ** KEYWORDS: sqlite3_version, sqlite3_sourceid |
| 660 | 660 | ** |
| @@ -10570,11 +10570,10 @@ | ||
| 10570 | 10570 | |
| 10571 | 10571 | /* |
| 10572 | 10572 | ** Internal function prototypes |
| 10573 | 10573 | */ |
| 10574 | 10574 | SQLITE_PRIVATE int sqlite3StrICmp(const char *, const char *); |
| 10575 | -SQLITE_PRIVATE int sqlite3IsNumber(const char*, int*, u8); | |
| 10576 | 10575 | SQLITE_PRIVATE int sqlite3Strlen30(const char*); |
| 10577 | 10576 | #define sqlite3StrNICmp sqlite3_strnicmp |
| 10578 | 10577 | |
| 10579 | 10578 | SQLITE_PRIVATE int sqlite3MallocInit(void); |
| 10580 | 10579 | SQLITE_PRIVATE void sqlite3MallocEnd(void); |
| @@ -10890,13 +10889,12 @@ | ||
| 10890 | 10889 | SQLITE_PRIVATE int sqlite3FixSrcList(DbFixer*, SrcList*); |
| 10891 | 10890 | SQLITE_PRIVATE int sqlite3FixSelect(DbFixer*, Select*); |
| 10892 | 10891 | SQLITE_PRIVATE int sqlite3FixExpr(DbFixer*, Expr*); |
| 10893 | 10892 | SQLITE_PRIVATE int sqlite3FixExprList(DbFixer*, ExprList*); |
| 10894 | 10893 | SQLITE_PRIVATE int sqlite3FixTriggerStep(DbFixer*, TriggerStep*); |
| 10895 | -SQLITE_PRIVATE int sqlite3AtoF(const char *z, double*); | |
| 10894 | +SQLITE_PRIVATE int sqlite3AtoF(const char *z, double*, int, u8); | |
| 10896 | 10895 | SQLITE_PRIVATE int sqlite3GetInt32(const char *, int*); |
| 10897 | -SQLITE_PRIVATE int sqlite3FitsIn64Bits(const char *, int); | |
| 10898 | 10896 | SQLITE_PRIVATE int sqlite3Utf16ByteLen(const void *pData, int nChar); |
| 10899 | 10897 | SQLITE_PRIVATE int sqlite3Utf8CharLen(const char *pData, int nByte); |
| 10900 | 10898 | SQLITE_PRIVATE int sqlite3Utf8Read(const u8*, const u8**); |
| 10901 | 10899 | |
| 10902 | 10900 | /* |
| @@ -10938,11 +10936,11 @@ | ||
| 10938 | 10936 | SQLITE_PRIVATE const char *sqlite3IndexAffinityStr(Vdbe *, Index *); |
| 10939 | 10937 | SQLITE_PRIVATE void sqlite3TableAffinityStr(Vdbe *, Table *); |
| 10940 | 10938 | SQLITE_PRIVATE char sqlite3CompareAffinity(Expr *pExpr, char aff2); |
| 10941 | 10939 | SQLITE_PRIVATE int sqlite3IndexAffinityOk(Expr *pExpr, char idx_affinity); |
| 10942 | 10940 | SQLITE_PRIVATE char sqlite3ExprAffinity(Expr *pExpr); |
| 10943 | -SQLITE_PRIVATE int sqlite3Atoi64(const char*, i64*); | |
| 10941 | +SQLITE_PRIVATE int sqlite3Atoi64(const char*, i64*, int, u8); | |
| 10944 | 10942 | SQLITE_PRIVATE void sqlite3Error(sqlite3*, int, const char*,...); |
| 10945 | 10943 | SQLITE_PRIVATE void *sqlite3HexToBlob(sqlite3*, const char *z, int n); |
| 10946 | 10944 | SQLITE_PRIVATE int sqlite3TwoPartName(Parse *, Token *, Token *, Token **); |
| 10947 | 10945 | SQLITE_PRIVATE const char *sqlite3ErrStr(int); |
| 10948 | 10946 | SQLITE_PRIVATE int sqlite3ReadSchema(Parse *pParse); |
| @@ -12637,16 +12635,10 @@ | ||
| 12637 | 12635 | end_getDigits: |
| 12638 | 12636 | va_end(ap); |
| 12639 | 12637 | return cnt; |
| 12640 | 12638 | } |
| 12641 | 12639 | |
| 12642 | -/* | |
| 12643 | -** Read text from z[] and convert into a floating point number. Return | |
| 12644 | -** the number of digits converted. | |
| 12645 | -*/ | |
| 12646 | -#define getValue sqlite3AtoF | |
| 12647 | - | |
| 12648 | 12640 | /* |
| 12649 | 12641 | ** Parse a timezone extension on the end of a date-time. |
| 12650 | 12642 | ** The extension is of the form: |
| 12651 | 12643 | ** |
| 12652 | 12644 | ** (+/-)HH:MM |
| @@ -12844,21 +12836,19 @@ | ||
| 12844 | 12836 | static int parseDateOrTime( |
| 12845 | 12837 | sqlite3_context *context, |
| 12846 | 12838 | const char *zDate, |
| 12847 | 12839 | DateTime *p |
| 12848 | 12840 | ){ |
| 12849 | - int isRealNum; /* Return from sqlite3IsNumber(). Not used */ | |
| 12841 | + double r; | |
| 12850 | 12842 | if( parseYyyyMmDd(zDate,p)==0 ){ |
| 12851 | 12843 | return 0; |
| 12852 | 12844 | }else if( parseHhMmSs(zDate, p)==0 ){ |
| 12853 | 12845 | return 0; |
| 12854 | 12846 | }else if( sqlite3StrICmp(zDate,"now")==0){ |
| 12855 | 12847 | setDateTimeToCurrent(context, p); |
| 12856 | 12848 | return 0; |
| 12857 | - }else if( sqlite3IsNumber(zDate, &isRealNum, SQLITE_UTF8) ){ | |
| 12858 | - double r; | |
| 12859 | - getValue(zDate, &r); | |
| 12849 | + }else if( sqlite3AtoF(zDate, &r, sqlite3Strlen30(zDate), SQLITE_UTF8) ){ | |
| 12860 | 12850 | p->iJD = (sqlite3_int64)(r*86400000.0 + 0.5); |
| 12861 | 12851 | p->validJD = 1; |
| 12862 | 12852 | return 0; |
| 12863 | 12853 | } |
| 12864 | 12854 | return 1; |
| @@ -13075,12 +13065,13 @@ | ||
| 13075 | 13065 | ** |
| 13076 | 13066 | ** Move the date to the same time on the next occurrence of |
| 13077 | 13067 | ** weekday N where 0==Sunday, 1==Monday, and so forth. If the |
| 13078 | 13068 | ** date is already on the appropriate weekday, this is a no-op. |
| 13079 | 13069 | */ |
| 13080 | - if( strncmp(z, "weekday ", 8)==0 && getValue(&z[8],&r)>0 | |
| 13081 | - && (n=(int)r)==r && n>=0 && r<7 ){ | |
| 13070 | + if( strncmp(z, "weekday ", 8)==0 | |
| 13071 | + && sqlite3AtoF(&z[8], &r, sqlite3Strlen30(&z[8]), SQLITE_UTF8) | |
| 13072 | + && (n=(int)r)==r && n>=0 && r<7 ){ | |
| 13082 | 13073 | sqlite3_int64 Z; |
| 13083 | 13074 | computeYMD_HMS(p); |
| 13084 | 13075 | p->validTZ = 0; |
| 13085 | 13076 | p->validJD = 0; |
| 13086 | 13077 | computeJD(p); |
| @@ -13131,12 +13122,15 @@ | ||
| 13131 | 13122 | case '6': |
| 13132 | 13123 | case '7': |
| 13133 | 13124 | case '8': |
| 13134 | 13125 | case '9': { |
| 13135 | 13126 | double rRounder; |
| 13136 | - n = getValue(z, &r); | |
| 13137 | - assert( n>=1 ); | |
| 13127 | + for(n=1; z[n] && z[n]!=':' && !sqlite3Isspace(z[n]); n++){} | |
| 13128 | + if( !sqlite3AtoF(z, &r, n, SQLITE_UTF8) ){ | |
| 13129 | + rc = 1; | |
| 13130 | + break; | |
| 13131 | + } | |
| 13138 | 13132 | if( z[n]==':' ){ |
| 13139 | 13133 | /* A modifier of the form (+|-)HH:MM:SS.FFF adds (or subtracts) the |
| 13140 | 13134 | ** specified number of hours, minutes, seconds, and fractional seconds |
| 13141 | 13135 | ** to the time. The ".FFF" may be omitted. The ":SS.FFF" may be |
| 13142 | 13136 | ** omitted. |
| @@ -17502,11 +17496,11 @@ | ||
| 17502 | 17496 | sqlite3MemoryAlarm(softHeapLimitEnforcer, 0, n); |
| 17503 | 17497 | }else{ |
| 17504 | 17498 | sqlite3MemoryAlarm(0, 0, 0); |
| 17505 | 17499 | } |
| 17506 | 17500 | excess = sqlite3_memory_used() - n; |
| 17507 | - if( excess>0 ) sqlite3_release_memory(excess & 0x7fffffff); | |
| 17501 | + if( excess>0 ) sqlite3_release_memory((int)(excess & 0x7fffffff)); | |
| 17508 | 17502 | return priorLimit; |
| 17509 | 17503 | } |
| 17510 | 17504 | SQLITE_API void sqlite3_soft_heap_limit(int n){ |
| 17511 | 17505 | if( n<0 ) n = 0; |
| 17512 | 17506 | sqlite3_soft_heap_limit64(n); |
| @@ -20096,125 +20090,115 @@ | ||
| 20096 | 20090 | while( N-- > 0 && *a!=0 && UpperToLower[*a]==UpperToLower[*b]){ a++; b++; } |
| 20097 | 20091 | return N<0 ? 0 : UpperToLower[*a] - UpperToLower[*b]; |
| 20098 | 20092 | } |
| 20099 | 20093 | |
| 20100 | 20094 | /* |
| 20101 | -** Return TRUE if z is a pure numeric string. Return FALSE and leave | |
| 20102 | -** *realnum unchanged if the string contains any character which is not | |
| 20103 | -** part of a number. | |
| 20104 | -** | |
| 20105 | -** If the string is pure numeric, set *realnum to TRUE if the string | |
| 20106 | -** contains the '.' character or an "E+000" style exponentiation suffix. | |
| 20107 | -** Otherwise set *realnum to FALSE. Note that just becaue *realnum is | |
| 20108 | -** false does not mean that the number can be successfully converted into | |
| 20109 | -** an integer - it might be too big. | |
| 20110 | -** | |
| 20111 | -** An empty string is considered non-numeric. | |
| 20112 | -*/ | |
| 20113 | -SQLITE_PRIVATE int sqlite3IsNumber(const char *z, int *realnum, u8 enc){ | |
| 20095 | +** The string z[] is an text representation of a real number. | |
| 20096 | +** Convert this string to a double and write it into *pResult. | |
| 20097 | +** | |
| 20098 | +** The string z[] is length bytes in length (bytes, not characters) and | |
| 20099 | +** uses the encoding enc. The string is not necessarily zero-terminated. | |
| 20100 | +** | |
| 20101 | +** Return TRUE if the result is a valid real number (or integer) and FALSE | |
| 20102 | +** if the string is empty or contains extraneous text. Valid numbers | |
| 20103 | +** are in one of these formats: | |
| 20104 | +** | |
| 20105 | +** [+-]digits[E[+-]digits] | |
| 20106 | +** [+-]digits.[digits][E[+-]digits] | |
| 20107 | +** [+-].digits[E[+-]digits] | |
| 20108 | +** | |
| 20109 | +** Leading and trailing whitespace is ignored for the purpose of determining | |
| 20110 | +** validity. | |
| 20111 | +** | |
| 20112 | +** If some prefix of the input string is a valid number, this routine | |
| 20113 | +** returns FALSE but it still converts the prefix and writes the result | |
| 20114 | +** into *pResult. | |
| 20115 | +*/ | |
| 20116 | +SQLITE_PRIVATE int sqlite3AtoF(const char *z, double *pResult, int length, u8 enc){ | |
| 20117 | +#ifndef SQLITE_OMIT_FLOATING_POINT | |
| 20114 | 20118 | int incr = (enc==SQLITE_UTF8?1:2); |
| 20115 | - if( enc==SQLITE_UTF16BE ) z++; | |
| 20116 | - if( *z=='-' || *z=='+' ) z += incr; | |
| 20117 | - if( !sqlite3Isdigit(*z) ){ | |
| 20118 | - return 0; | |
| 20119 | - } | |
| 20120 | - z += incr; | |
| 20121 | - *realnum = 0; | |
| 20122 | - while( sqlite3Isdigit(*z) ){ z += incr; } | |
| 20123 | -#ifndef SQLITE_OMIT_FLOATING_POINT | |
| 20124 | - if( *z=='.' ){ | |
| 20125 | - z += incr; | |
| 20126 | - if( !sqlite3Isdigit(*z) ) return 0; | |
| 20127 | - while( sqlite3Isdigit(*z) ){ z += incr; } | |
| 20128 | - *realnum = 1; | |
| 20129 | - } | |
| 20130 | - if( *z=='e' || *z=='E' ){ | |
| 20131 | - z += incr; | |
| 20132 | - if( *z=='+' || *z=='-' ) z += incr; | |
| 20133 | - if( !sqlite3Isdigit(*z) ) return 0; | |
| 20134 | - while( sqlite3Isdigit(*z) ){ z += incr; } | |
| 20135 | - *realnum = 1; | |
| 20136 | - } | |
| 20137 | -#endif | |
| 20138 | - return *z==0; | |
| 20139 | -} | |
| 20140 | - | |
| 20141 | -/* | |
| 20142 | -** The string z[] is an ASCII representation of a real number. | |
| 20143 | -** Convert this string to a double. | |
| 20144 | -** | |
| 20145 | -** This routine assumes that z[] really is a valid number. If it | |
| 20146 | -** is not, the result is undefined. | |
| 20147 | -** | |
| 20148 | -** This routine is used instead of the library atof() function because | |
| 20149 | -** the library atof() might want to use "," as the decimal point instead | |
| 20150 | -** of "." depending on how locale is set. But that would cause problems | |
| 20151 | -** for SQL. So this routine always uses "." regardless of locale. | |
| 20152 | -*/ | |
| 20153 | -SQLITE_PRIVATE int sqlite3AtoF(const char *z, double *pResult){ | |
| 20154 | -#ifndef SQLITE_OMIT_FLOATING_POINT | |
| 20155 | - const char *zBegin = z; | |
| 20119 | + const char *zEnd = z + length; | |
| 20156 | 20120 | /* sign * significand * (10 ^ (esign * exponent)) */ |
| 20157 | - int sign = 1; /* sign of significand */ | |
| 20158 | - i64 s = 0; /* significand */ | |
| 20159 | - int d = 0; /* adjust exponent for shifting decimal point */ | |
| 20160 | - int esign = 1; /* sign of exponent */ | |
| 20161 | - int e = 0; /* exponent */ | |
| 20121 | + int sign = 1; /* sign of significand */ | |
| 20122 | + i64 s = 0; /* significand */ | |
| 20123 | + int d = 0; /* adjust exponent for shifting decimal point */ | |
| 20124 | + int esign = 1; /* sign of exponent */ | |
| 20125 | + int e = 0; /* exponent */ | |
| 20126 | + int eValid = 1; /* True exponent is either not used or is well-formed */ | |
| 20162 | 20127 | double result; |
| 20163 | 20128 | int nDigits = 0; |
| 20164 | 20129 | |
| 20130 | + *pResult = 0.0; /* Default return value, in case of an error */ | |
| 20131 | + | |
| 20132 | + if( enc==SQLITE_UTF16BE ) z++; | |
| 20133 | + | |
| 20165 | 20134 | /* skip leading spaces */ |
| 20166 | - while( sqlite3Isspace(*z) ) z++; | |
| 20135 | + while( z<zEnd && sqlite3Isspace(*z) ) z+=incr; | |
| 20136 | + if( z>=zEnd ) return 0; | |
| 20137 | + | |
| 20167 | 20138 | /* get sign of significand */ |
| 20168 | 20139 | if( *z=='-' ){ |
| 20169 | 20140 | sign = -1; |
| 20170 | - z++; | |
| 20141 | + z+=incr; | |
| 20171 | 20142 | }else if( *z=='+' ){ |
| 20172 | - z++; | |
| 20143 | + z+=incr; | |
| 20173 | 20144 | } |
| 20145 | + | |
| 20174 | 20146 | /* skip leading zeroes */ |
| 20175 | - while( z[0]=='0' ) z++, nDigits++; | |
| 20147 | + while( z<zEnd && z[0]=='0' ) z+=incr, nDigits++; | |
| 20176 | 20148 | |
| 20177 | 20149 | /* copy max significant digits to significand */ |
| 20178 | - while( sqlite3Isdigit(*z) && s<((LARGEST_INT64-9)/10) ){ | |
| 20150 | + while( z<zEnd && sqlite3Isdigit(*z) && s<((LARGEST_INT64-9)/10) ){ | |
| 20179 | 20151 | s = s*10 + (*z - '0'); |
| 20180 | - z++, nDigits++; | |
| 20152 | + z+=incr, nDigits++; | |
| 20181 | 20153 | } |
| 20154 | + | |
| 20182 | 20155 | /* skip non-significant significand digits |
| 20183 | 20156 | ** (increase exponent by d to shift decimal left) */ |
| 20184 | - while( sqlite3Isdigit(*z) ) z++, nDigits++, d++; | |
| 20157 | + while( z<zEnd && sqlite3Isdigit(*z) ) z+=incr, nDigits++, d++; | |
| 20158 | + if( z>=zEnd ) goto do_atof_calc; | |
| 20185 | 20159 | |
| 20186 | 20160 | /* if decimal point is present */ |
| 20187 | 20161 | if( *z=='.' ){ |
| 20188 | - z++; | |
| 20162 | + z+=incr; | |
| 20189 | 20163 | /* copy digits from after decimal to significand |
| 20190 | 20164 | ** (decrease exponent by d to shift decimal right) */ |
| 20191 | - while( sqlite3Isdigit(*z) && s<((LARGEST_INT64-9)/10) ){ | |
| 20165 | + while( z<zEnd && sqlite3Isdigit(*z) && s<((LARGEST_INT64-9)/10) ){ | |
| 20192 | 20166 | s = s*10 + (*z - '0'); |
| 20193 | - z++, nDigits++, d--; | |
| 20167 | + z+=incr, nDigits++, d--; | |
| 20194 | 20168 | } |
| 20195 | 20169 | /* skip non-significant digits */ |
| 20196 | - while( sqlite3Isdigit(*z) ) z++, nDigits++; | |
| 20170 | + while( z<zEnd && sqlite3Isdigit(*z) ) z+=incr, nDigits++; | |
| 20197 | 20171 | } |
| 20172 | + if( z>=zEnd ) goto do_atof_calc; | |
| 20198 | 20173 | |
| 20199 | 20174 | /* if exponent is present */ |
| 20200 | 20175 | if( *z=='e' || *z=='E' ){ |
| 20201 | - z++; | |
| 20176 | + z+=incr; | |
| 20177 | + eValid = 0; | |
| 20178 | + if( z>=zEnd ) goto do_atof_calc; | |
| 20202 | 20179 | /* get sign of exponent */ |
| 20203 | 20180 | if( *z=='-' ){ |
| 20204 | 20181 | esign = -1; |
| 20205 | - z++; | |
| 20182 | + z+=incr; | |
| 20206 | 20183 | }else if( *z=='+' ){ |
| 20207 | - z++; | |
| 20184 | + z+=incr; | |
| 20208 | 20185 | } |
| 20209 | 20186 | /* copy digits to exponent */ |
| 20210 | - while( sqlite3Isdigit(*z) ){ | |
| 20187 | + while( z<zEnd && sqlite3Isdigit(*z) ){ | |
| 20211 | 20188 | e = e*10 + (*z - '0'); |
| 20212 | - z++; | |
| 20189 | + z+=incr; | |
| 20190 | + eValid = 1; | |
| 20213 | 20191 | } |
| 20214 | 20192 | } |
| 20215 | 20193 | |
| 20194 | + /* skip trailing spaces */ | |
| 20195 | + if( nDigits && eValid ){ | |
| 20196 | + while( z<zEnd && sqlite3Isspace(*z) ) z+=incr; | |
| 20197 | + } | |
| 20198 | + | |
| 20199 | +do_atof_calc: | |
| 20216 | 20200 | /* adjust exponent by d, and update sign */ |
| 20217 | 20201 | e = (e*esign) + d; |
| 20218 | 20202 | if( e<0 ) { |
| 20219 | 20203 | esign = -1; |
| 20220 | 20204 | e *= -1; |
| @@ -20269,132 +20253,104 @@ | ||
| 20269 | 20253 | } |
| 20270 | 20254 | |
| 20271 | 20255 | /* store the result */ |
| 20272 | 20256 | *pResult = result; |
| 20273 | 20257 | |
| 20274 | - /* return number of characters used */ | |
| 20275 | - return (int)(z - zBegin); | |
| 20258 | + /* return true if number and no extra non-whitespace chracters after */ | |
| 20259 | + return z>=zEnd && nDigits>0 && eValid; | |
| 20276 | 20260 | #else |
| 20277 | - return sqlite3Atoi64(z, pResult); | |
| 20261 | + return !sqlite3Atoi64(z, pResult, length, enc); | |
| 20278 | 20262 | #endif /* SQLITE_OMIT_FLOATING_POINT */ |
| 20279 | 20263 | } |
| 20280 | 20264 | |
| 20281 | 20265 | /* |
| 20282 | 20266 | ** Compare the 19-character string zNum against the text representation |
| 20283 | 20267 | ** value 2^63: 9223372036854775808. Return negative, zero, or positive |
| 20284 | 20268 | ** if zNum is less than, equal to, or greater than the string. |
| 20269 | +** Note that zNum must contain exactly 19 characters. | |
| 20285 | 20270 | ** |
| 20286 | 20271 | ** Unlike memcmp() this routine is guaranteed to return the difference |
| 20287 | 20272 | ** in the values of the last digit if the only difference is in the |
| 20288 | 20273 | ** last digit. So, for example, |
| 20289 | 20274 | ** |
| 20290 | -** compare2pow63("9223372036854775800") | |
| 20275 | +** compare2pow63("9223372036854775800", 1) | |
| 20291 | 20276 | ** |
| 20292 | 20277 | ** will return -8. |
| 20293 | 20278 | */ |
| 20294 | -static int compare2pow63(const char *zNum){ | |
| 20295 | - int c; | |
| 20296 | - c = memcmp(zNum,"922337203685477580",18)*10; | |
| 20279 | +static int compare2pow63(const char *zNum, int incr){ | |
| 20280 | + int c = 0; | |
| 20281 | + int i; | |
| 20282 | + /* 012345678901234567 */ | |
| 20283 | + const char *pow63 = "922337203685477580"; | |
| 20284 | + for(i=0; c==0 && i<18; i++){ | |
| 20285 | + c = (zNum[i*incr]-pow63[i])*10; | |
| 20286 | + } | |
| 20297 | 20287 | if( c==0 ){ |
| 20298 | - c = zNum[18] - '8'; | |
| 20288 | + c = zNum[18*incr] - '8'; | |
| 20299 | 20289 | testcase( c==(-1) ); |
| 20300 | 20290 | testcase( c==0 ); |
| 20301 | 20291 | testcase( c==(+1) ); |
| 20302 | 20292 | } |
| 20303 | 20293 | return c; |
| 20304 | 20294 | } |
| 20305 | 20295 | |
| 20306 | 20296 | |
| 20307 | 20297 | /* |
| 20308 | -** Return TRUE if zNum is a 64-bit signed integer and write | |
| 20309 | -** the value of the integer into *pNum. If zNum is not an integer | |
| 20310 | -** or is an integer that is too large to be expressed with 64 bits, | |
| 20311 | -** then return false. | |
| 20298 | +** Convert zNum to a 64-bit signed integer and write | |
| 20299 | +** the value of the integer into *pNum. | |
| 20300 | +** If zNum is exactly 9223372036854665808, return 2. | |
| 20301 | +** This is a special case as the context will determine | |
| 20302 | +** if it is too big (used as a negative). | |
| 20303 | +** If zNum is not an integer or is an integer that | |
| 20304 | +** is too large to be expressed with 64 bits, | |
| 20305 | +** then return 1. Otherwise return 0. | |
| 20312 | 20306 | ** |
| 20313 | -** When this routine was originally written it dealt with only | |
| 20314 | -** 32-bit numbers. At that time, it was much faster than the | |
| 20315 | -** atoi() library routine in RedHat 7.2. | |
| 20307 | +** length is the number of bytes in the string (bytes, not characters). | |
| 20308 | +** The string is not necessarily zero-terminated. The encoding is | |
| 20309 | +** given by enc. | |
| 20316 | 20310 | */ |
| 20317 | -SQLITE_PRIVATE int sqlite3Atoi64(const char *zNum, i64 *pNum){ | |
| 20311 | +SQLITE_PRIVATE int sqlite3Atoi64(const char *zNum, i64 *pNum, int length, u8 enc){ | |
| 20312 | + int incr = (enc==SQLITE_UTF8?1:2); | |
| 20318 | 20313 | i64 v = 0; |
| 20319 | - int neg; | |
| 20320 | - int i, c; | |
| 20314 | + int neg = 0; /* assume positive */ | |
| 20315 | + int i; | |
| 20316 | + int c = 0; | |
| 20321 | 20317 | const char *zStart; |
| 20322 | - while( sqlite3Isspace(*zNum) ) zNum++; | |
| 20318 | + const char *zEnd = zNum + length; | |
| 20319 | + if( enc==SQLITE_UTF16BE ) zNum++; | |
| 20320 | + while( zNum<zEnd && sqlite3Isspace(*zNum) ) zNum+=incr; | |
| 20321 | + if( zNum>=zEnd ) goto do_atoi_calc; | |
| 20323 | 20322 | if( *zNum=='-' ){ |
| 20324 | 20323 | neg = 1; |
| 20325 | - zNum++; | |
| 20324 | + zNum+=incr; | |
| 20326 | 20325 | }else if( *zNum=='+' ){ |
| 20327 | - neg = 0; | |
| 20328 | - zNum++; | |
| 20329 | - }else{ | |
| 20330 | - neg = 0; | |
| 20326 | + zNum+=incr; | |
| 20331 | 20327 | } |
| 20328 | +do_atoi_calc: | |
| 20332 | 20329 | zStart = zNum; |
| 20333 | - while( zNum[0]=='0' ){ zNum++; } /* Skip over leading zeros. Ticket #2454 */ | |
| 20334 | - for(i=0; (c=zNum[i])>='0' && c<='9'; i++){ | |
| 20330 | + while( zNum<zEnd && zNum[0]=='0' ){ zNum+=incr; } /* Skip leading zeros. */ | |
| 20331 | + for(i=0; &zNum[i]<zEnd && (c=zNum[i])>='0' && c<='9'; i+=incr){ | |
| 20335 | 20332 | v = v*10 + c - '0'; |
| 20336 | 20333 | } |
| 20337 | 20334 | *pNum = neg ? -v : v; |
| 20338 | 20335 | testcase( i==18 ); |
| 20339 | 20336 | testcase( i==19 ); |
| 20340 | 20337 | testcase( i==20 ); |
| 20341 | - if( c!=0 || (i==0 && zStart==zNum) || i>19 ){ | |
| 20338 | + if( (c!=0 && &zNum[i]<zEnd) || (i==0 && zStart==zNum) || i>19*incr ){ | |
| 20342 | 20339 | /* zNum is empty or contains non-numeric text or is longer |
| 20343 | - ** than 19 digits (thus guaranting that it is too large) */ | |
| 20344 | - return 0; | |
| 20345 | - }else if( i<19 ){ | |
| 20340 | + ** than 19 digits (thus guaranteeing that it is too large) */ | |
| 20341 | + return 1; | |
| 20342 | + }else if( i<19*incr ){ | |
| 20346 | 20343 | /* Less than 19 digits, so we know that it fits in 64 bits */ |
| 20347 | - return 1; | |
| 20344 | + return 0; | |
| 20348 | 20345 | }else{ |
| 20349 | 20346 | /* 19-digit numbers must be no larger than 9223372036854775807 if positive |
| 20350 | 20347 | ** or 9223372036854775808 if negative. Note that 9223372036854665808 |
| 20351 | - ** is 2^63. */ | |
| 20352 | - return compare2pow63(zNum)<neg; | |
| 20353 | - } | |
| 20354 | -} | |
| 20355 | - | |
| 20356 | -/* | |
| 20357 | -** The string zNum represents an unsigned integer. The zNum string | |
| 20358 | -** consists of one or more digit characters and is terminated by | |
| 20359 | -** a zero character. Any stray characters in zNum result in undefined | |
| 20360 | -** behavior. | |
| 20361 | -** | |
| 20362 | -** If the unsigned integer that zNum represents will fit in a | |
| 20363 | -** 64-bit signed integer, return TRUE. Otherwise return FALSE. | |
| 20364 | -** | |
| 20365 | -** If the negFlag parameter is true, that means that zNum really represents | |
| 20366 | -** a negative number. (The leading "-" is omitted from zNum.) This | |
| 20367 | -** parameter is needed to determine a boundary case. A string | |
| 20368 | -** of "9223373036854775808" returns false if negFlag is false or true | |
| 20369 | -** if negFlag is true. | |
| 20370 | -** | |
| 20371 | -** Leading zeros are ignored. | |
| 20372 | -*/ | |
| 20373 | -SQLITE_PRIVATE int sqlite3FitsIn64Bits(const char *zNum, int negFlag){ | |
| 20374 | - int i; | |
| 20375 | - int neg = 0; | |
| 20376 | - | |
| 20377 | - assert( zNum[0]>='0' && zNum[0]<='9' ); /* zNum is an unsigned number */ | |
| 20378 | - | |
| 20379 | - if( negFlag ) neg = 1-neg; | |
| 20380 | - while( *zNum=='0' ){ | |
| 20381 | - zNum++; /* Skip leading zeros. Ticket #2454 */ | |
| 20382 | - } | |
| 20383 | - for(i=0; zNum[i]; i++){ assert( zNum[i]>='0' && zNum[i]<='9' ); } | |
| 20384 | - testcase( i==18 ); | |
| 20385 | - testcase( i==19 ); | |
| 20386 | - testcase( i==20 ); | |
| 20387 | - if( i<19 ){ | |
| 20388 | - /* Guaranteed to fit if less than 19 digits */ | |
| 20389 | - return 1; | |
| 20390 | - }else if( i>19 ){ | |
| 20391 | - /* Guaranteed to be too big if greater than 19 digits */ | |
| 20392 | - return 0; | |
| 20393 | - }else{ | |
| 20394 | - /* Compare against 2^63. */ | |
| 20395 | - return compare2pow63(zNum)<neg; | |
| 20348 | + ** is 2^63. Return 1 if to large */ | |
| 20349 | + c=compare2pow63(zNum, incr); | |
| 20350 | + if( c==0 && neg==0 ) return 2; /* too big, exactly 9223372036854665808 */ | |
| 20351 | + return c<neg ? 0 : 1; | |
| 20396 | 20352 | } |
| 20397 | 20353 | } |
| 20398 | 20354 | |
| 20399 | 20355 | /* |
| 20400 | 20356 | ** If zNum represents an integer that will fit in 32-bits, then set |
| @@ -54149,17 +54105,13 @@ | ||
| 54149 | 54105 | return pMem->u.i; |
| 54150 | 54106 | }else if( flags & MEM_Real ){ |
| 54151 | 54107 | return doubleToInt64(pMem->r); |
| 54152 | 54108 | }else if( flags & (MEM_Str|MEM_Blob) ){ |
| 54153 | 54109 | i64 value; |
| 54154 | - pMem->flags |= MEM_Str; | |
| 54155 | - if( sqlite3VdbeChangeEncoding(pMem, SQLITE_UTF8) | |
| 54156 | - || sqlite3VdbeMemNulTerminate(pMem) ){ | |
| 54157 | - return 0; | |
| 54158 | - } | |
| 54159 | - assert( pMem->z ); | |
| 54160 | - sqlite3Atoi64(pMem->z, &value); | |
| 54110 | + assert( pMem->z || pMem->n==0 ); | |
| 54111 | + testcase( pMem->z==0 ); | |
| 54112 | + sqlite3Atoi64(pMem->z, &value, pMem->n, pMem->enc); | |
| 54161 | 54113 | return value; |
| 54162 | 54114 | }else{ |
| 54163 | 54115 | return 0; |
| 54164 | 54116 | } |
| 54165 | 54117 | } |
| @@ -54185,11 +54137,11 @@ | ||
| 54185 | 54137 | || sqlite3VdbeMemNulTerminate(pMem) ){ |
| 54186 | 54138 | /* (double)0 In case of SQLITE_OMIT_FLOATING_POINT... */ |
| 54187 | 54139 | return (double)0; |
| 54188 | 54140 | } |
| 54189 | 54141 | assert( pMem->z ); |
| 54190 | - sqlite3AtoF(pMem->z, &val); | |
| 54142 | + sqlite3AtoF(pMem->z, &val, pMem->n, SQLITE_UTF8); | |
| 54191 | 54143 | return val; |
| 54192 | 54144 | }else{ |
| 54193 | 54145 | /* (double)0 In case of SQLITE_OMIT_FLOATING_POINT... */ |
| 54194 | 54146 | return (double)0; |
| 54195 | 54147 | } |
| @@ -54258,25 +54210,23 @@ | ||
| 54258 | 54210 | ** Every effort is made to force the conversion, even if the input |
| 54259 | 54211 | ** is a string that does not look completely like a number. Convert |
| 54260 | 54212 | ** as much of the string as we can and ignore the rest. |
| 54261 | 54213 | */ |
| 54262 | 54214 | SQLITE_PRIVATE int sqlite3VdbeMemNumerify(Mem *pMem){ |
| 54263 | - int rc; | |
| 54264 | - assert( (pMem->flags & (MEM_Int|MEM_Real|MEM_Null))==0 ); | |
| 54265 | - assert( (pMem->flags & (MEM_Blob|MEM_Str))!=0 ); | |
| 54266 | - assert( pMem->db==0 || sqlite3_mutex_held(pMem->db->mutex) ); | |
| 54267 | - rc = sqlite3VdbeChangeEncoding(pMem, SQLITE_UTF8); | |
| 54268 | - if( rc ) return rc; | |
| 54269 | - rc = sqlite3VdbeMemNulTerminate(pMem); | |
| 54270 | - if( rc ) return rc; | |
| 54271 | - if( sqlite3Atoi64(pMem->z, &pMem->u.i) ){ | |
| 54272 | - MemSetTypeFlag(pMem, MEM_Int); | |
| 54273 | - }else{ | |
| 54274 | - pMem->r = sqlite3VdbeRealValue(pMem); | |
| 54275 | - MemSetTypeFlag(pMem, MEM_Real); | |
| 54276 | - sqlite3VdbeIntegerAffinity(pMem); | |
| 54277 | - } | |
| 54215 | + if( (pMem->flags & (MEM_Int|MEM_Real|MEM_Null))==0 ){ | |
| 54216 | + assert( (pMem->flags & (MEM_Blob|MEM_Str))!=0 ); | |
| 54217 | + assert( pMem->db==0 || sqlite3_mutex_held(pMem->db->mutex) ); | |
| 54218 | + if( 0==sqlite3Atoi64(pMem->z, &pMem->u.i, pMem->n, pMem->enc) ){ | |
| 54219 | + MemSetTypeFlag(pMem, MEM_Int); | |
| 54220 | + }else{ | |
| 54221 | + pMem->r = sqlite3VdbeRealValue(pMem); | |
| 54222 | + MemSetTypeFlag(pMem, MEM_Real); | |
| 54223 | + sqlite3VdbeIntegerAffinity(pMem); | |
| 54224 | + } | |
| 54225 | + } | |
| 54226 | + assert( (pMem->flags & (MEM_Int|MEM_Real|MEM_Null))!=0 ); | |
| 54227 | + pMem->flags &= ~(MEM_Str|MEM_Blob); | |
| 54278 | 54228 | return SQLITE_OK; |
| 54279 | 54229 | } |
| 54280 | 54230 | |
| 54281 | 54231 | /* |
| 54282 | 54232 | ** Delete any previous value and set the value stored in *pMem to NULL. |
| @@ -54815,10 +54765,12 @@ | ||
| 54815 | 54765 | sqlite3_value **ppVal /* Write the new value here */ |
| 54816 | 54766 | ){ |
| 54817 | 54767 | int op; |
| 54818 | 54768 | char *zVal = 0; |
| 54819 | 54769 | sqlite3_value *pVal = 0; |
| 54770 | + int negInt = 1; | |
| 54771 | + const char *zNeg = ""; | |
| 54820 | 54772 | |
| 54821 | 54773 | if( !pExpr ){ |
| 54822 | 54774 | *ppVal = 0; |
| 54823 | 54775 | return SQLITE_OK; |
| 54824 | 54776 | } |
| @@ -54831,35 +54783,50 @@ | ||
| 54831 | 54783 | #ifdef SQLITE_ENABLE_STAT2 |
| 54832 | 54784 | if( op==TK_REGISTER ) op = pExpr->op2; |
| 54833 | 54785 | #else |
| 54834 | 54786 | if( NEVER(op==TK_REGISTER) ) op = pExpr->op2; |
| 54835 | 54787 | #endif |
| 54788 | + | |
| 54789 | + /* Handle negative integers in a single step. This is needed in the | |
| 54790 | + ** case when the value is -9223372036854775808. | |
| 54791 | + */ | |
| 54792 | + if( op==TK_UMINUS | |
| 54793 | + && (pExpr->pLeft->op==TK_INTEGER || pExpr->pLeft->op==TK_FLOAT) ){ | |
| 54794 | + pExpr = pExpr->pLeft; | |
| 54795 | + op = pExpr->op; | |
| 54796 | + negInt = -1; | |
| 54797 | + zNeg = "-"; | |
| 54798 | + } | |
| 54836 | 54799 | |
| 54837 | 54800 | if( op==TK_STRING || op==TK_FLOAT || op==TK_INTEGER ){ |
| 54838 | 54801 | pVal = sqlite3ValueNew(db); |
| 54839 | 54802 | if( pVal==0 ) goto no_mem; |
| 54840 | 54803 | if( ExprHasProperty(pExpr, EP_IntValue) ){ |
| 54841 | - sqlite3VdbeMemSetInt64(pVal, (i64)pExpr->u.iValue); | |
| 54804 | + sqlite3VdbeMemSetInt64(pVal, (i64)pExpr->u.iValue*negInt); | |
| 54842 | 54805 | }else{ |
| 54843 | - zVal = sqlite3DbStrDup(db, pExpr->u.zToken); | |
| 54806 | + zVal = sqlite3MPrintf(db, "%s%s", zNeg, pExpr->u.zToken); | |
| 54844 | 54807 | if( zVal==0 ) goto no_mem; |
| 54845 | 54808 | sqlite3ValueSetStr(pVal, -1, zVal, SQLITE_UTF8, SQLITE_DYNAMIC); |
| 54846 | 54809 | if( op==TK_FLOAT ) pVal->type = SQLITE_FLOAT; |
| 54847 | 54810 | } |
| 54848 | 54811 | if( (op==TK_INTEGER || op==TK_FLOAT ) && affinity==SQLITE_AFF_NONE ){ |
| 54849 | 54812 | sqlite3ValueApplyAffinity(pVal, SQLITE_AFF_NUMERIC, SQLITE_UTF8); |
| 54850 | 54813 | }else{ |
| 54851 | 54814 | sqlite3ValueApplyAffinity(pVal, affinity, SQLITE_UTF8); |
| 54852 | 54815 | } |
| 54816 | + if( pVal->flags & (MEM_Int|MEM_Real) ) pVal->flags &= ~MEM_Str; | |
| 54853 | 54817 | if( enc!=SQLITE_UTF8 ){ |
| 54854 | 54818 | sqlite3VdbeChangeEncoding(pVal, enc); |
| 54855 | 54819 | } |
| 54856 | 54820 | }else if( op==TK_UMINUS ) { |
| 54821 | + /* This branch happens for multiple negative signs. Ex: -(-5) */ | |
| 54857 | 54822 | if( SQLITE_OK==sqlite3ValueFromExpr(db,pExpr->pLeft,enc,affinity,&pVal) ){ |
| 54823 | + sqlite3VdbeMemNumerify(pVal); | |
| 54858 | 54824 | pVal->u.i = -1 * pVal->u.i; |
| 54859 | 54825 | /* (double)-1 In case of SQLITE_OMIT_FLOATING_POINT... */ |
| 54860 | 54826 | pVal->r = (double)-1 * pVal->r; |
| 54827 | + sqlite3ValueApplyAffinity(pVal, affinity, enc); | |
| 54861 | 54828 | } |
| 54862 | 54829 | } |
| 54863 | 54830 | #ifndef SQLITE_OMIT_BLOB_LITERAL |
| 54864 | 54831 | else if( op==TK_BLOB ){ |
| 54865 | 54832 | int nVal; |
| @@ -59770,35 +59737,21 @@ | ||
| 59770 | 59737 | ** looks like a number, convert it into a number. If it does not |
| 59771 | 59738 | ** look like a number, leave it alone. |
| 59772 | 59739 | */ |
| 59773 | 59740 | static void applyNumericAffinity(Mem *pRec){ |
| 59774 | 59741 | if( (pRec->flags & (MEM_Real|MEM_Int))==0 ){ |
| 59775 | - int realnum; | |
| 59742 | + double rValue; | |
| 59743 | + i64 iValue; | |
| 59776 | 59744 | u8 enc = pRec->enc; |
| 59777 | - sqlite3VdbeMemNulTerminate(pRec); | |
| 59778 | - if( (pRec->flags&MEM_Str) && sqlite3IsNumber(pRec->z, &realnum, enc) ){ | |
| 59779 | - i64 value; | |
| 59780 | - char *zUtf8 = pRec->z; | |
| 59781 | -#ifndef SQLITE_OMIT_UTF16 | |
| 59782 | - if( enc!=SQLITE_UTF8 ){ | |
| 59783 | - assert( pRec->db ); | |
| 59784 | - zUtf8 = sqlite3Utf16to8(pRec->db, pRec->z, pRec->n, enc); | |
| 59785 | - if( !zUtf8 ) return; | |
| 59786 | - } | |
| 59787 | -#endif | |
| 59788 | - if( !realnum && sqlite3Atoi64(zUtf8, &value) ){ | |
| 59789 | - pRec->u.i = value; | |
| 59790 | - MemSetTypeFlag(pRec, MEM_Int); | |
| 59791 | - }else{ | |
| 59792 | - sqlite3AtoF(zUtf8, &pRec->r); | |
| 59793 | - MemSetTypeFlag(pRec, MEM_Real); | |
| 59794 | - } | |
| 59795 | -#ifndef SQLITE_OMIT_UTF16 | |
| 59796 | - if( enc!=SQLITE_UTF8 ){ | |
| 59797 | - sqlite3DbFree(pRec->db, zUtf8); | |
| 59798 | - } | |
| 59799 | -#endif | |
| 59745 | + if( (pRec->flags&MEM_Str)==0 ) return; | |
| 59746 | + if( sqlite3AtoF(pRec->z, &rValue, pRec->n, enc)==0 ) return; | |
| 59747 | + if( 0==sqlite3Atoi64(pRec->z, &iValue, pRec->n, enc) ){ | |
| 59748 | + pRec->u.i = iValue; | |
| 59749 | + pRec->flags |= MEM_Int; | |
| 59750 | + }else{ | |
| 59751 | + pRec->r = rValue; | |
| 59752 | + pRec->flags |= MEM_Real; | |
| 59800 | 59753 | } |
| 59801 | 59754 | } |
| 59802 | 59755 | } |
| 59803 | 59756 | |
| 59804 | 59757 | /* |
| @@ -61580,11 +61533,10 @@ | ||
| 61580 | 61533 | ** integers, for space efficiency, but after extraction we want them |
| 61581 | 61534 | ** to have only a real value. |
| 61582 | 61535 | */ |
| 61583 | 61536 | case OP_RealAffinity: { /* in1 */ |
| 61584 | 61537 | pIn1 = &aMem[pOp->p1]; |
| 61585 | - memAboutToChange(p, pIn1); | |
| 61586 | 61538 | if( pIn1->flags & MEM_Int ){ |
| 61587 | 61539 | sqlite3VdbeMemRealify(pIn1); |
| 61588 | 61540 | } |
| 61589 | 61541 | break; |
| 61590 | 61542 | } |
| @@ -61623,11 +61575,10 @@ | ||
| 61623 | 61575 | ** |
| 61624 | 61576 | ** A NULL value is not changed by this routine. It remains NULL. |
| 61625 | 61577 | */ |
| 61626 | 61578 | case OP_ToBlob: { /* same as TK_TO_BLOB, in1 */ |
| 61627 | 61579 | pIn1 = &aMem[pOp->p1]; |
| 61628 | - memAboutToChange(p, pIn1); | |
| 61629 | 61580 | if( pIn1->flags & MEM_Null ) break; |
| 61630 | 61581 | if( (pIn1->flags & MEM_Blob)==0 ){ |
| 61631 | 61582 | applyAffinity(pIn1, SQLITE_AFF_TEXT, encoding); |
| 61632 | 61583 | assert( pIn1->flags & MEM_Str || db->mallocFailed ); |
| 61633 | 61584 | MemSetTypeFlag(pIn1, MEM_Blob); |
| @@ -61648,14 +61599,11 @@ | ||
| 61648 | 61599 | ** |
| 61649 | 61600 | ** A NULL value is not changed by this routine. It remains NULL. |
| 61650 | 61601 | */ |
| 61651 | 61602 | case OP_ToNumeric: { /* same as TK_TO_NUMERIC, in1 */ |
| 61652 | 61603 | pIn1 = &aMem[pOp->p1]; |
| 61653 | - memAboutToChange(p, pIn1); | |
| 61654 | - if( (pIn1->flags & (MEM_Null|MEM_Int|MEM_Real))==0 ){ | |
| 61655 | - sqlite3VdbeMemNumerify(pIn1); | |
| 61656 | - } | |
| 61604 | + sqlite3VdbeMemNumerify(pIn1); | |
| 61657 | 61605 | break; |
| 61658 | 61606 | } |
| 61659 | 61607 | #endif /* SQLITE_OMIT_CAST */ |
| 61660 | 61608 | |
| 61661 | 61609 | /* Opcode: ToInt P1 * * * * |
| @@ -61667,11 +61615,10 @@ | ||
| 61667 | 61615 | ** |
| 61668 | 61616 | ** A NULL value is not changed by this routine. It remains NULL. |
| 61669 | 61617 | */ |
| 61670 | 61618 | case OP_ToInt: { /* same as TK_TO_INT, in1 */ |
| 61671 | 61619 | pIn1 = &aMem[pOp->p1]; |
| 61672 | - memAboutToChange(p, pIn1); | |
| 61673 | 61620 | if( (pIn1->flags & MEM_Null)==0 ){ |
| 61674 | 61621 | sqlite3VdbeMemIntegerify(pIn1); |
| 61675 | 61622 | } |
| 61676 | 61623 | break; |
| 61677 | 61624 | } |
| @@ -61781,12 +61728,10 @@ | ||
| 61781 | 61728 | u16 flags3; /* Copy of initial value of pIn3->flags */ |
| 61782 | 61729 | #endif /* local variables moved into u.ai */ |
| 61783 | 61730 | |
| 61784 | 61731 | pIn1 = &aMem[pOp->p1]; |
| 61785 | 61732 | pIn3 = &aMem[pOp->p3]; |
| 61786 | - memAboutToChange(p, pIn1); | |
| 61787 | - memAboutToChange(p, pIn3); | |
| 61788 | 61733 | u.ai.flags1 = pIn1->flags; |
| 61789 | 61734 | u.ai.flags3 = pIn3->flags; |
| 61790 | 61735 | if( (pIn1->flags | pIn3->flags)&MEM_Null ){ |
| 61791 | 61736 | /* One or both operands are NULL */ |
| 61792 | 61737 | if( pOp->p5 & SQLITE_NULLEQ ){ |
| @@ -62415,11 +62360,10 @@ | ||
| 62415 | 62360 | assert( u.an.zAffinity[pOp->p2]==0 ); |
| 62416 | 62361 | pIn1 = &aMem[pOp->p1]; |
| 62417 | 62362 | while( (u.an.cAff = *(u.an.zAffinity++))!=0 ){ |
| 62418 | 62363 | assert( pIn1 <= &p->aMem[p->nMem] ); |
| 62419 | 62364 | assert( memIsValid(pIn1) ); |
| 62420 | - memAboutToChange(p, pIn1); | |
| 62421 | 62365 | ExpandBlob(pIn1); |
| 62422 | 62366 | applyAffinity(pIn1, u.an.cAff, encoding); |
| 62423 | 62367 | pIn1++; |
| 62424 | 62368 | } |
| 62425 | 62369 | break; |
| @@ -62495,11 +62439,10 @@ | ||
| 62495 | 62439 | ** out how much space is required for the new record. |
| 62496 | 62440 | */ |
| 62497 | 62441 | for(u.ao.pRec=u.ao.pData0; u.ao.pRec<=u.ao.pLast; u.ao.pRec++){ |
| 62498 | 62442 | assert( memIsValid(u.ao.pRec) ); |
| 62499 | 62443 | if( u.ao.zAffinity ){ |
| 62500 | - memAboutToChange(p, u.ao.pRec); | |
| 62501 | 62444 | applyAffinity(u.ao.pRec, u.ao.zAffinity[u.ao.pRec-u.ao.pData0], encoding); |
| 62502 | 62445 | } |
| 62503 | 62446 | if( u.ao.pRec->flags&MEM_Zero && u.ao.pRec->n>0 ){ |
| 62504 | 62447 | sqlite3VdbeMemExpandBlob(u.ao.pRec); |
| 62505 | 62448 | } |
| @@ -68880,11 +68823,11 @@ | ||
| 68880 | 68823 | pExpr->iColumn = (ynVar)(++pParse->nVar); |
| 68881 | 68824 | }else if( z[0]=='?' ){ |
| 68882 | 68825 | /* Wildcard of the form "?nnn". Convert "nnn" to an integer and |
| 68883 | 68826 | ** use it as the variable number */ |
| 68884 | 68827 | i64 i; |
| 68885 | - int bOk = sqlite3Atoi64(&z[1], &i); | |
| 68828 | + int bOk = 0==sqlite3Atoi64(&z[1], &i, sqlite3Strlen30(&z[1]), SQLITE_UTF8); | |
| 68886 | 68829 | pExpr->iColumn = (ynVar)i; |
| 68887 | 68830 | testcase( i==0 ); |
| 68888 | 68831 | testcase( i==1 ); |
| 68889 | 68832 | testcase( i==db->aLimit[SQLITE_LIMIT_VARIABLE_NUMBER]-1 ); |
| 68890 | 68833 | testcase( i==db->aLimit[SQLITE_LIMIT_VARIABLE_NUMBER] ); |
| @@ -70243,11 +70186,11 @@ | ||
| 70243 | 70186 | */ |
| 70244 | 70187 | static void codeReal(Vdbe *v, const char *z, int negateFlag, int iMem){ |
| 70245 | 70188 | if( ALWAYS(z!=0) ){ |
| 70246 | 70189 | double value; |
| 70247 | 70190 | char *zV; |
| 70248 | - sqlite3AtoF(z, &value); | |
| 70191 | + sqlite3AtoF(z, &value, sqlite3Strlen30(z), SQLITE_UTF8); | |
| 70249 | 70192 | assert( !sqlite3IsNaN(value) ); /* The new AtoF never returns NaN */ |
| 70250 | 70193 | if( negateFlag ) value = -value; |
| 70251 | 70194 | zV = dup8bytes(v, (char*)&value); |
| 70252 | 70195 | sqlite3VdbeAddOp4(v, OP_Real, 0, iMem, 0, zV, P4_REAL); |
| 70253 | 70196 | } |
| @@ -70257,28 +70200,27 @@ | ||
| 70257 | 70200 | |
| 70258 | 70201 | /* |
| 70259 | 70202 | ** Generate an instruction that will put the integer describe by |
| 70260 | 70203 | ** text z[0..n-1] into register iMem. |
| 70261 | 70204 | ** |
| 70262 | -** The z[] string will probably not be zero-terminated. But the | |
| 70263 | -** z[n] character is guaranteed to be something that does not look | |
| 70264 | -** like the continuation of the number. | |
| 70205 | +** Expr.u.zToken is always UTF8 and zero-terminated. | |
| 70265 | 70206 | */ |
| 70266 | 70207 | static void codeInteger(Parse *pParse, Expr *pExpr, int negFlag, int iMem){ |
| 70267 | 70208 | Vdbe *v = pParse->pVdbe; |
| 70268 | 70209 | if( pExpr->flags & EP_IntValue ){ |
| 70269 | 70210 | int i = pExpr->u.iValue; |
| 70270 | 70211 | if( negFlag ) i = -i; |
| 70271 | 70212 | sqlite3VdbeAddOp2(v, OP_Integer, i, iMem); |
| 70272 | 70213 | }else{ |
| 70214 | + int c; | |
| 70215 | + i64 value; | |
| 70273 | 70216 | const char *z = pExpr->u.zToken; |
| 70274 | 70217 | assert( z!=0 ); |
| 70275 | - if( sqlite3FitsIn64Bits(z, negFlag) ){ | |
| 70276 | - i64 value; | |
| 70218 | + c = sqlite3Atoi64(z, &value, sqlite3Strlen30(z), SQLITE_UTF8); | |
| 70219 | + if( c==0 || (c==2 && negFlag) ){ | |
| 70277 | 70220 | char *zV; |
| 70278 | - sqlite3Atoi64(z, &value); | |
| 70279 | - if( negFlag ) value = -value; | |
| 70221 | + if( negFlag ){ value = -value; } | |
| 70280 | 70222 | zV = dup8bytes(v, (char*)&value); |
| 70281 | 70223 | sqlite3VdbeAddOp4(v, OP_Int64, 0, iMem, 0, zV, P4_INT64); |
| 70282 | 70224 | }else{ |
| 70283 | 70225 | #ifdef SQLITE_OMIT_FLOATING_POINT |
| 70284 | 70226 | sqlite3ErrorMsg(pParse, "oversized integer: %s%s", negFlag ? "-" : "", z); |
| @@ -79414,11 +79356,11 @@ | ||
| 79414 | 79356 | zBuf = sqlite3_mprintf("%.*f",n,r); |
| 79415 | 79357 | if( zBuf==0 ){ |
| 79416 | 79358 | sqlite3_result_error_nomem(context); |
| 79417 | 79359 | return; |
| 79418 | 79360 | } |
| 79419 | - sqlite3AtoF(zBuf, &r); | |
| 79361 | + sqlite3AtoF(zBuf, &r, sqlite3Strlen30(zBuf), SQLITE_UTF8); | |
| 79420 | 79362 | sqlite3_free(zBuf); |
| 79421 | 79363 | } |
| 79422 | 79364 | sqlite3_result_double(context, r); |
| 79423 | 79365 | } |
| 79424 | 79366 | #endif |
| @@ -85472,11 +85414,11 @@ | ||
| 85472 | 85414 | */ |
| 85473 | 85415 | if( sqlite3StrICmp(zLeft,"journal_size_limit")==0 ){ |
| 85474 | 85416 | Pager *pPager = sqlite3BtreePager(pDb->pBt); |
| 85475 | 85417 | i64 iLimit = -2; |
| 85476 | 85418 | if( zRight ){ |
| 85477 | - sqlite3Atoi64(zRight, &iLimit); | |
| 85419 | + sqlite3Atoi64(zRight, &iLimit, 1000000, SQLITE_UTF8); | |
| 85478 | 85420 | if( iLimit<-1 ) iLimit = -1; |
| 85479 | 85421 | } |
| 85480 | 85422 | iLimit = sqlite3PagerJournalSizeLimit(pPager, iLimit); |
| 85481 | 85423 | returnSingleInt(pParse, "journal_size_limit", iLimit); |
| 85482 | 85424 | }else |
| @@ -96380,11 +96322,12 @@ | ||
| 96380 | 96322 | |
| 96381 | 96323 | /* |
| 96382 | 96324 | ** Required because bestIndex() is called by bestOrClauseIndex() |
| 96383 | 96325 | */ |
| 96384 | 96326 | static void bestIndex( |
| 96385 | - Parse*, WhereClause*, struct SrcList_item*, Bitmask, ExprList*, WhereCost*); | |
| 96327 | + Parse*, WhereClause*, struct SrcList_item*, | |
| 96328 | + Bitmask, Bitmask, ExprList*, WhereCost*); | |
| 96386 | 96329 | |
| 96387 | 96330 | /* |
| 96388 | 96331 | ** This routine attempts to find an scanning strategy that can be used |
| 96389 | 96332 | ** to optimize an 'OR' expression that is part of a WHERE clause. |
| 96390 | 96333 | ** |
| @@ -96393,11 +96336,12 @@ | ||
| 96393 | 96336 | */ |
| 96394 | 96337 | static void bestOrClauseIndex( |
| 96395 | 96338 | Parse *pParse, /* The parsing context */ |
| 96396 | 96339 | WhereClause *pWC, /* The WHERE clause */ |
| 96397 | 96340 | struct SrcList_item *pSrc, /* The FROM clause term to search */ |
| 96398 | - Bitmask notReady, /* Mask of cursors that are not available */ | |
| 96341 | + Bitmask notReady, /* Mask of cursors not available for indexing */ | |
| 96342 | + Bitmask notValid, /* Cursors not available for any purpose */ | |
| 96399 | 96343 | ExprList *pOrderBy, /* The ORDER BY clause */ |
| 96400 | 96344 | WhereCost *pCost /* Lowest cost query plan */ |
| 96401 | 96345 | ){ |
| 96402 | 96346 | #ifndef SQLITE_OMIT_OR_OPTIMIZATION |
| 96403 | 96347 | const int iCur = pSrc->iCursor; /* The cursor of the table to be accessed */ |
| @@ -96429,19 +96373,19 @@ | ||
| 96429 | 96373 | WHERETRACE(("... Multi-index OR testing for term %d of %d....\n", |
| 96430 | 96374 | (pOrTerm - pOrWC->a), (pTerm - pWC->a) |
| 96431 | 96375 | )); |
| 96432 | 96376 | if( pOrTerm->eOperator==WO_AND ){ |
| 96433 | 96377 | WhereClause *pAndWC = &pOrTerm->u.pAndInfo->wc; |
| 96434 | - bestIndex(pParse, pAndWC, pSrc, notReady, 0, &sTermCost); | |
| 96378 | + bestIndex(pParse, pAndWC, pSrc, notReady, notValid, 0, &sTermCost); | |
| 96435 | 96379 | }else if( pOrTerm->leftCursor==iCur ){ |
| 96436 | 96380 | WhereClause tempWC; |
| 96437 | 96381 | tempWC.pParse = pWC->pParse; |
| 96438 | 96382 | tempWC.pMaskSet = pWC->pMaskSet; |
| 96439 | 96383 | tempWC.op = TK_AND; |
| 96440 | 96384 | tempWC.a = pOrTerm; |
| 96441 | 96385 | tempWC.nTerm = 1; |
| 96442 | - bestIndex(pParse, &tempWC, pSrc, notReady, 0, &sTermCost); | |
| 96386 | + bestIndex(pParse, &tempWC, pSrc, notReady, notValid, 0, &sTermCost); | |
| 96443 | 96387 | }else{ |
| 96444 | 96388 | continue; |
| 96445 | 96389 | } |
| 96446 | 96390 | rTotal += sTermCost.rCost; |
| 96447 | 96391 | nRow += sTermCost.nRow; |
| @@ -96884,11 +96828,12 @@ | ||
| 96884 | 96828 | */ |
| 96885 | 96829 | static void bestVirtualIndex( |
| 96886 | 96830 | Parse *pParse, /* The parsing context */ |
| 96887 | 96831 | WhereClause *pWC, /* The WHERE clause */ |
| 96888 | 96832 | struct SrcList_item *pSrc, /* The FROM clause term to search */ |
| 96889 | - Bitmask notReady, /* Mask of cursors that are not available */ | |
| 96833 | + Bitmask notReady, /* Mask of cursors not available for index */ | |
| 96834 | + Bitmask notValid, /* Cursors not valid for any purpose */ | |
| 96890 | 96835 | ExprList *pOrderBy, /* The order by clause */ |
| 96891 | 96836 | WhereCost *pCost, /* Lowest cost query plan */ |
| 96892 | 96837 | sqlite3_index_info **ppIdxInfo /* Index information passed to xBestIndex */ |
| 96893 | 96838 | ){ |
| 96894 | 96839 | Table *pTab = pSrc->pTab; |
| @@ -97014,11 +96959,11 @@ | ||
| 97014 | 96959 | pIdxInfo->nOrderBy = nOrderBy; |
| 97015 | 96960 | |
| 97016 | 96961 | /* Try to find a more efficient access pattern by using multiple indexes |
| 97017 | 96962 | ** to optimize an OR expression within the WHERE clause. |
| 97018 | 96963 | */ |
| 97019 | - bestOrClauseIndex(pParse, pWC, pSrc, notReady, pOrderBy, pCost); | |
| 96964 | + bestOrClauseIndex(pParse, pWC, pSrc, notReady, notValid, pOrderBy, pCost); | |
| 97020 | 96965 | } |
| 97021 | 96966 | #endif /* SQLITE_OMIT_VIRTUALTABLE */ |
| 97022 | 96967 | |
| 97023 | 96968 | /* |
| 97024 | 96969 | ** Argument pIdx is a pointer to an index structure that has an array of |
| @@ -97295,11 +97240,12 @@ | ||
| 97295 | 97240 | */ |
| 97296 | 97241 | static void bestBtreeIndex( |
| 97297 | 97242 | Parse *pParse, /* The parsing context */ |
| 97298 | 97243 | WhereClause *pWC, /* The WHERE clause */ |
| 97299 | 97244 | struct SrcList_item *pSrc, /* The FROM clause term to search */ |
| 97300 | - Bitmask notReady, /* Mask of cursors that are not available */ | |
| 97245 | + Bitmask notReady, /* Mask of cursors not available for indexing */ | |
| 97246 | + Bitmask notValid, /* Cursors not available for any purpose */ | |
| 97301 | 97247 | ExprList *pOrderBy, /* The ORDER BY clause */ |
| 97302 | 97248 | WhereCost *pCost /* Lowest cost query plan */ |
| 97303 | 97249 | ){ |
| 97304 | 97250 | int iCur = pSrc->iCursor; /* The cursor of the table to be accessed */ |
| 97305 | 97251 | Index *pProbe; /* An index we are evaluating */ |
| @@ -97557,29 +97503,29 @@ | ||
| 97557 | 97503 | ** of output rows, adjust the nRow value accordingly. This only |
| 97558 | 97504 | ** matters if the current index is the least costly, so do not bother |
| 97559 | 97505 | ** with this step if we already know this index will not be chosen. |
| 97560 | 97506 | ** Also, never reduce the output row count below 2 using this step. |
| 97561 | 97507 | ** |
| 97562 | - ** Do not reduce the output row count if pSrc is the only table that | |
| 97563 | - ** is notReady; if notReady is a power of two. This will be the case | |
| 97564 | - ** when the main sqlite3WhereBegin() loop is scanning for a table with | |
| 97565 | - ** and "optimal" index, and on such a scan the output row count | |
| 97566 | - ** reduction is not valid because it does not update the "pCost->used" | |
| 97567 | - ** bitmap. The notReady bitmap will also be a power of two when we | |
| 97568 | - ** are scanning for the last table in a 64-way join. We are willing | |
| 97569 | - ** to bypass this optimization in that corner case. | |
| 97508 | + ** It is critical that the notValid mask be used here instead of | |
| 97509 | + ** the notReady mask. When computing an "optimal" index, the notReady | |
| 97510 | + ** mask will only have one bit set - the bit for the current table. | |
| 97511 | + ** The notValid mask, on the other hand, always has all bits set for | |
| 97512 | + ** tables that are not in outer loops. If notReady is used here instead | |
| 97513 | + ** of notValid, then a optimal index that depends on inner joins loops | |
| 97514 | + ** might be selected even when there exists an optimal index that has | |
| 97515 | + ** no such dependency. | |
| 97570 | 97516 | */ |
| 97571 | - if( nRow>2 && cost<=pCost->rCost && (notReady & (notReady-1))!=0 ){ | |
| 97517 | + if( nRow>2 && cost<=pCost->rCost ){ | |
| 97572 | 97518 | int k; /* Loop counter */ |
| 97573 | 97519 | int nSkipEq = nEq; /* Number of == constraints to skip */ |
| 97574 | 97520 | int nSkipRange = nBound; /* Number of < constraints to skip */ |
| 97575 | 97521 | Bitmask thisTab; /* Bitmap for pSrc */ |
| 97576 | 97522 | |
| 97577 | 97523 | thisTab = getMask(pWC->pMaskSet, iCur); |
| 97578 | 97524 | for(pTerm=pWC->a, k=pWC->nTerm; nRow>2 && k; k--, pTerm++){ |
| 97579 | 97525 | if( pTerm->wtFlags & TERM_VIRTUAL ) continue; |
| 97580 | - if( (pTerm->prereqAll & notReady)!=thisTab ) continue; | |
| 97526 | + if( (pTerm->prereqAll & notValid)!=thisTab ) continue; | |
| 97581 | 97527 | if( pTerm->eOperator & (WO_EQ|WO_IN|WO_ISNULL) ){ |
| 97582 | 97528 | if( nSkipEq ){ |
| 97583 | 97529 | /* Ignore the first nEq equality matches since the index |
| 97584 | 97530 | ** has already accounted for these */ |
| 97585 | 97531 | nSkipEq--; |
| @@ -97657,11 +97603,11 @@ | ||
| 97657 | 97603 | WHERETRACE(("best index is: %s\n", |
| 97658 | 97604 | ((pCost->plan.wsFlags & WHERE_NOT_FULLSCAN)==0 ? "none" : |
| 97659 | 97605 | pCost->plan.u.pIdx ? pCost->plan.u.pIdx->zName : "ipk") |
| 97660 | 97606 | )); |
| 97661 | 97607 | |
| 97662 | - bestOrClauseIndex(pParse, pWC, pSrc, notReady, pOrderBy, pCost); | |
| 97608 | + bestOrClauseIndex(pParse, pWC, pSrc, notReady, notValid, pOrderBy, pCost); | |
| 97663 | 97609 | bestAutomaticIndex(pParse, pWC, pSrc, notReady, pCost); |
| 97664 | 97610 | pCost->plan.wsFlags |= eqTermMask; |
| 97665 | 97611 | } |
| 97666 | 97612 | |
| 97667 | 97613 | /* |
| @@ -97672,26 +97618,27 @@ | ||
| 97672 | 97618 | */ |
| 97673 | 97619 | static void bestIndex( |
| 97674 | 97620 | Parse *pParse, /* The parsing context */ |
| 97675 | 97621 | WhereClause *pWC, /* The WHERE clause */ |
| 97676 | 97622 | struct SrcList_item *pSrc, /* The FROM clause term to search */ |
| 97677 | - Bitmask notReady, /* Mask of cursors that are not available */ | |
| 97623 | + Bitmask notReady, /* Mask of cursors not available for indexing */ | |
| 97624 | + Bitmask notValid, /* Cursors not available for any purpose */ | |
| 97678 | 97625 | ExprList *pOrderBy, /* The ORDER BY clause */ |
| 97679 | 97626 | WhereCost *pCost /* Lowest cost query plan */ |
| 97680 | 97627 | ){ |
| 97681 | 97628 | #ifndef SQLITE_OMIT_VIRTUALTABLE |
| 97682 | 97629 | if( IsVirtual(pSrc->pTab) ){ |
| 97683 | 97630 | sqlite3_index_info *p = 0; |
| 97684 | - bestVirtualIndex(pParse, pWC, pSrc, notReady, pOrderBy, pCost, &p); | |
| 97631 | + bestVirtualIndex(pParse, pWC, pSrc, notReady, notValid, pOrderBy, pCost,&p); | |
| 97685 | 97632 | if( p->needToFreeIdxStr ){ |
| 97686 | 97633 | sqlite3_free(p->idxStr); |
| 97687 | 97634 | } |
| 97688 | 97635 | sqlite3DbFree(pParse->db, p); |
| 97689 | 97636 | }else |
| 97690 | 97637 | #endif |
| 97691 | 97638 | { |
| 97692 | - bestBtreeIndex(pParse, pWC, pSrc, notReady, pOrderBy, pCost); | |
| 97639 | + bestBtreeIndex(pParse, pWC, pSrc, notReady, notValid, pOrderBy, pCost); | |
| 97693 | 97640 | } |
| 97694 | 97641 | } |
| 97695 | 97642 | |
| 97696 | 97643 | /* |
| 97697 | 97644 | ** Disable a term in the WHERE clause. Except, do not disable the term |
| @@ -98902,14 +98849,20 @@ | ||
| 98902 | 98849 | ** by waiting for other tables to run first. This "optimal" test works |
| 98903 | 98850 | ** by first assuming that the FROM clause is on the inner loop and finding |
| 98904 | 98851 | ** its query plan, then checking to see if that query plan uses any |
| 98905 | 98852 | ** other FROM clause terms that are notReady. If no notReady terms are |
| 98906 | 98853 | ** used then the "optimal" query plan works. |
| 98854 | + ** | |
| 98855 | + ** Note that the WhereCost.nRow parameter for an optimal scan might | |
| 98856 | + ** not be as small as it would be if the table really were the innermost | |
| 98857 | + ** join. The nRow value can be reduced by WHERE clause constraints | |
| 98858 | + ** that do not use indices. But this nRow reduction only happens if the | |
| 98859 | + ** table really is the innermost join. | |
| 98907 | 98860 | ** |
| 98908 | 98861 | ** The second loop iteration is only performed if no optimal scan |
| 98909 | - ** strategies were found by the first loop. This 2nd iteration is used to | |
| 98910 | - ** search for the lowest cost scan overall. | |
| 98862 | + ** strategies were found by the first iteration. This second iteration | |
| 98863 | + ** is used to search for the lowest cost scan overall. | |
| 98911 | 98864 | ** |
| 98912 | 98865 | ** Previous versions of SQLite performed only the second iteration - |
| 98913 | 98866 | ** the next outermost loop was always that with the lowest overall |
| 98914 | 98867 | ** cost. However, this meant that SQLite could select the wrong plan |
| 98915 | 98868 | ** for scripts such as the following: |
| @@ -98925,11 +98878,11 @@ | ||
| 98925 | 98878 | ** algorithm may choose to use t2 for the outer loop, which is a much |
| 98926 | 98879 | ** costlier approach. |
| 98927 | 98880 | */ |
| 98928 | 98881 | nUnconstrained = 0; |
| 98929 | 98882 | notIndexed = 0; |
| 98930 | - for(isOptimal=(iFrom<nTabList-1); isOptimal>=0; isOptimal--){ | |
| 98883 | + for(isOptimal=(iFrom<nTabList-1); isOptimal>=0 && bestJ<0; isOptimal--){ | |
| 98931 | 98884 | Bitmask mask; /* Mask of tables not yet ready */ |
| 98932 | 98885 | for(j=iFrom, pTabItem=&pTabList->a[j]; j<nTabList; j++, pTabItem++){ |
| 98933 | 98886 | int doNotReorder; /* True if this table should not be reordered */ |
| 98934 | 98887 | WhereCost sCost; /* Cost information from best[Virtual]Index() */ |
| 98935 | 98888 | ExprList *pOrderBy; /* ORDER BY clause for index to optimize */ |
| @@ -98947,15 +98900,17 @@ | ||
| 98947 | 98900 | |
| 98948 | 98901 | assert( pTabItem->pTab ); |
| 98949 | 98902 | #ifndef SQLITE_OMIT_VIRTUALTABLE |
| 98950 | 98903 | if( IsVirtual(pTabItem->pTab) ){ |
| 98951 | 98904 | sqlite3_index_info **pp = &pWInfo->a[j].pIdxInfo; |
| 98952 | - bestVirtualIndex(pParse, pWC, pTabItem, mask, pOrderBy, &sCost, pp); | |
| 98905 | + bestVirtualIndex(pParse, pWC, pTabItem, mask, notReady, pOrderBy, | |
| 98906 | + &sCost, pp); | |
| 98953 | 98907 | }else |
| 98954 | 98908 | #endif |
| 98955 | 98909 | { |
| 98956 | - bestBtreeIndex(pParse, pWC, pTabItem, mask, pOrderBy, &sCost); | |
| 98910 | + bestBtreeIndex(pParse, pWC, pTabItem, mask, notReady, pOrderBy, | |
| 98911 | + &sCost); | |
| 98957 | 98912 | } |
| 98958 | 98913 | assert( isOptimal || (sCost.used¬Ready)==0 ); |
| 98959 | 98914 | |
| 98960 | 98915 | /* If an INDEXED BY clause is present, then the plan must use that |
| 98961 | 98916 | ** index if it uses any index at all */ |
| 98962 | 98917 |
| --- src/sqlite3.c | |
| +++ src/sqlite3.c | |
| @@ -650,11 +650,11 @@ | |
| 650 | ** [sqlite3_libversion_number()], [sqlite3_sourceid()], |
| 651 | ** [sqlite_version()] and [sqlite_source_id()]. |
| 652 | */ |
| 653 | #define SQLITE_VERSION "3.7.3" |
| 654 | #define SQLITE_VERSION_NUMBER 3007003 |
| 655 | #define SQLITE_SOURCE_ID "2010-09-29 23:09:23 1ef0dc9328f47506cb2dcd142150e96cb4755216" |
| 656 | |
| 657 | /* |
| 658 | ** CAPI3REF: Run-Time Library Version Numbers |
| 659 | ** KEYWORDS: sqlite3_version, sqlite3_sourceid |
| 660 | ** |
| @@ -10570,11 +10570,10 @@ | |
| 10570 | |
| 10571 | /* |
| 10572 | ** Internal function prototypes |
| 10573 | */ |
| 10574 | SQLITE_PRIVATE int sqlite3StrICmp(const char *, const char *); |
| 10575 | SQLITE_PRIVATE int sqlite3IsNumber(const char*, int*, u8); |
| 10576 | SQLITE_PRIVATE int sqlite3Strlen30(const char*); |
| 10577 | #define sqlite3StrNICmp sqlite3_strnicmp |
| 10578 | |
| 10579 | SQLITE_PRIVATE int sqlite3MallocInit(void); |
| 10580 | SQLITE_PRIVATE void sqlite3MallocEnd(void); |
| @@ -10890,13 +10889,12 @@ | |
| 10890 | SQLITE_PRIVATE int sqlite3FixSrcList(DbFixer*, SrcList*); |
| 10891 | SQLITE_PRIVATE int sqlite3FixSelect(DbFixer*, Select*); |
| 10892 | SQLITE_PRIVATE int sqlite3FixExpr(DbFixer*, Expr*); |
| 10893 | SQLITE_PRIVATE int sqlite3FixExprList(DbFixer*, ExprList*); |
| 10894 | SQLITE_PRIVATE int sqlite3FixTriggerStep(DbFixer*, TriggerStep*); |
| 10895 | SQLITE_PRIVATE int sqlite3AtoF(const char *z, double*); |
| 10896 | SQLITE_PRIVATE int sqlite3GetInt32(const char *, int*); |
| 10897 | SQLITE_PRIVATE int sqlite3FitsIn64Bits(const char *, int); |
| 10898 | SQLITE_PRIVATE int sqlite3Utf16ByteLen(const void *pData, int nChar); |
| 10899 | SQLITE_PRIVATE int sqlite3Utf8CharLen(const char *pData, int nByte); |
| 10900 | SQLITE_PRIVATE int sqlite3Utf8Read(const u8*, const u8**); |
| 10901 | |
| 10902 | /* |
| @@ -10938,11 +10936,11 @@ | |
| 10938 | SQLITE_PRIVATE const char *sqlite3IndexAffinityStr(Vdbe *, Index *); |
| 10939 | SQLITE_PRIVATE void sqlite3TableAffinityStr(Vdbe *, Table *); |
| 10940 | SQLITE_PRIVATE char sqlite3CompareAffinity(Expr *pExpr, char aff2); |
| 10941 | SQLITE_PRIVATE int sqlite3IndexAffinityOk(Expr *pExpr, char idx_affinity); |
| 10942 | SQLITE_PRIVATE char sqlite3ExprAffinity(Expr *pExpr); |
| 10943 | SQLITE_PRIVATE int sqlite3Atoi64(const char*, i64*); |
| 10944 | SQLITE_PRIVATE void sqlite3Error(sqlite3*, int, const char*,...); |
| 10945 | SQLITE_PRIVATE void *sqlite3HexToBlob(sqlite3*, const char *z, int n); |
| 10946 | SQLITE_PRIVATE int sqlite3TwoPartName(Parse *, Token *, Token *, Token **); |
| 10947 | SQLITE_PRIVATE const char *sqlite3ErrStr(int); |
| 10948 | SQLITE_PRIVATE int sqlite3ReadSchema(Parse *pParse); |
| @@ -12637,16 +12635,10 @@ | |
| 12637 | end_getDigits: |
| 12638 | va_end(ap); |
| 12639 | return cnt; |
| 12640 | } |
| 12641 | |
| 12642 | /* |
| 12643 | ** Read text from z[] and convert into a floating point number. Return |
| 12644 | ** the number of digits converted. |
| 12645 | */ |
| 12646 | #define getValue sqlite3AtoF |
| 12647 | |
| 12648 | /* |
| 12649 | ** Parse a timezone extension on the end of a date-time. |
| 12650 | ** The extension is of the form: |
| 12651 | ** |
| 12652 | ** (+/-)HH:MM |
| @@ -12844,21 +12836,19 @@ | |
| 12844 | static int parseDateOrTime( |
| 12845 | sqlite3_context *context, |
| 12846 | const char *zDate, |
| 12847 | DateTime *p |
| 12848 | ){ |
| 12849 | int isRealNum; /* Return from sqlite3IsNumber(). Not used */ |
| 12850 | if( parseYyyyMmDd(zDate,p)==0 ){ |
| 12851 | return 0; |
| 12852 | }else if( parseHhMmSs(zDate, p)==0 ){ |
| 12853 | return 0; |
| 12854 | }else if( sqlite3StrICmp(zDate,"now")==0){ |
| 12855 | setDateTimeToCurrent(context, p); |
| 12856 | return 0; |
| 12857 | }else if( sqlite3IsNumber(zDate, &isRealNum, SQLITE_UTF8) ){ |
| 12858 | double r; |
| 12859 | getValue(zDate, &r); |
| 12860 | p->iJD = (sqlite3_int64)(r*86400000.0 + 0.5); |
| 12861 | p->validJD = 1; |
| 12862 | return 0; |
| 12863 | } |
| 12864 | return 1; |
| @@ -13075,12 +13065,13 @@ | |
| 13075 | ** |
| 13076 | ** Move the date to the same time on the next occurrence of |
| 13077 | ** weekday N where 0==Sunday, 1==Monday, and so forth. If the |
| 13078 | ** date is already on the appropriate weekday, this is a no-op. |
| 13079 | */ |
| 13080 | if( strncmp(z, "weekday ", 8)==0 && getValue(&z[8],&r)>0 |
| 13081 | && (n=(int)r)==r && n>=0 && r<7 ){ |
| 13082 | sqlite3_int64 Z; |
| 13083 | computeYMD_HMS(p); |
| 13084 | p->validTZ = 0; |
| 13085 | p->validJD = 0; |
| 13086 | computeJD(p); |
| @@ -13131,12 +13122,15 @@ | |
| 13131 | case '6': |
| 13132 | case '7': |
| 13133 | case '8': |
| 13134 | case '9': { |
| 13135 | double rRounder; |
| 13136 | n = getValue(z, &r); |
| 13137 | assert( n>=1 ); |
| 13138 | if( z[n]==':' ){ |
| 13139 | /* A modifier of the form (+|-)HH:MM:SS.FFF adds (or subtracts) the |
| 13140 | ** specified number of hours, minutes, seconds, and fractional seconds |
| 13141 | ** to the time. The ".FFF" may be omitted. The ":SS.FFF" may be |
| 13142 | ** omitted. |
| @@ -17502,11 +17496,11 @@ | |
| 17502 | sqlite3MemoryAlarm(softHeapLimitEnforcer, 0, n); |
| 17503 | }else{ |
| 17504 | sqlite3MemoryAlarm(0, 0, 0); |
| 17505 | } |
| 17506 | excess = sqlite3_memory_used() - n; |
| 17507 | if( excess>0 ) sqlite3_release_memory(excess & 0x7fffffff); |
| 17508 | return priorLimit; |
| 17509 | } |
| 17510 | SQLITE_API void sqlite3_soft_heap_limit(int n){ |
| 17511 | if( n<0 ) n = 0; |
| 17512 | sqlite3_soft_heap_limit64(n); |
| @@ -20096,125 +20090,115 @@ | |
| 20096 | while( N-- > 0 && *a!=0 && UpperToLower[*a]==UpperToLower[*b]){ a++; b++; } |
| 20097 | return N<0 ? 0 : UpperToLower[*a] - UpperToLower[*b]; |
| 20098 | } |
| 20099 | |
| 20100 | /* |
| 20101 | ** Return TRUE if z is a pure numeric string. Return FALSE and leave |
| 20102 | ** *realnum unchanged if the string contains any character which is not |
| 20103 | ** part of a number. |
| 20104 | ** |
| 20105 | ** If the string is pure numeric, set *realnum to TRUE if the string |
| 20106 | ** contains the '.' character or an "E+000" style exponentiation suffix. |
| 20107 | ** Otherwise set *realnum to FALSE. Note that just becaue *realnum is |
| 20108 | ** false does not mean that the number can be successfully converted into |
| 20109 | ** an integer - it might be too big. |
| 20110 | ** |
| 20111 | ** An empty string is considered non-numeric. |
| 20112 | */ |
| 20113 | SQLITE_PRIVATE int sqlite3IsNumber(const char *z, int *realnum, u8 enc){ |
| 20114 | int incr = (enc==SQLITE_UTF8?1:2); |
| 20115 | if( enc==SQLITE_UTF16BE ) z++; |
| 20116 | if( *z=='-' || *z=='+' ) z += incr; |
| 20117 | if( !sqlite3Isdigit(*z) ){ |
| 20118 | return 0; |
| 20119 | } |
| 20120 | z += incr; |
| 20121 | *realnum = 0; |
| 20122 | while( sqlite3Isdigit(*z) ){ z += incr; } |
| 20123 | #ifndef SQLITE_OMIT_FLOATING_POINT |
| 20124 | if( *z=='.' ){ |
| 20125 | z += incr; |
| 20126 | if( !sqlite3Isdigit(*z) ) return 0; |
| 20127 | while( sqlite3Isdigit(*z) ){ z += incr; } |
| 20128 | *realnum = 1; |
| 20129 | } |
| 20130 | if( *z=='e' || *z=='E' ){ |
| 20131 | z += incr; |
| 20132 | if( *z=='+' || *z=='-' ) z += incr; |
| 20133 | if( !sqlite3Isdigit(*z) ) return 0; |
| 20134 | while( sqlite3Isdigit(*z) ){ z += incr; } |
| 20135 | *realnum = 1; |
| 20136 | } |
| 20137 | #endif |
| 20138 | return *z==0; |
| 20139 | } |
| 20140 | |
| 20141 | /* |
| 20142 | ** The string z[] is an ASCII representation of a real number. |
| 20143 | ** Convert this string to a double. |
| 20144 | ** |
| 20145 | ** This routine assumes that z[] really is a valid number. If it |
| 20146 | ** is not, the result is undefined. |
| 20147 | ** |
| 20148 | ** This routine is used instead of the library atof() function because |
| 20149 | ** the library atof() might want to use "," as the decimal point instead |
| 20150 | ** of "." depending on how locale is set. But that would cause problems |
| 20151 | ** for SQL. So this routine always uses "." regardless of locale. |
| 20152 | */ |
| 20153 | SQLITE_PRIVATE int sqlite3AtoF(const char *z, double *pResult){ |
| 20154 | #ifndef SQLITE_OMIT_FLOATING_POINT |
| 20155 | const char *zBegin = z; |
| 20156 | /* sign * significand * (10 ^ (esign * exponent)) */ |
| 20157 | int sign = 1; /* sign of significand */ |
| 20158 | i64 s = 0; /* significand */ |
| 20159 | int d = 0; /* adjust exponent for shifting decimal point */ |
| 20160 | int esign = 1; /* sign of exponent */ |
| 20161 | int e = 0; /* exponent */ |
| 20162 | double result; |
| 20163 | int nDigits = 0; |
| 20164 | |
| 20165 | /* skip leading spaces */ |
| 20166 | while( sqlite3Isspace(*z) ) z++; |
| 20167 | /* get sign of significand */ |
| 20168 | if( *z=='-' ){ |
| 20169 | sign = -1; |
| 20170 | z++; |
| 20171 | }else if( *z=='+' ){ |
| 20172 | z++; |
| 20173 | } |
| 20174 | /* skip leading zeroes */ |
| 20175 | while( z[0]=='0' ) z++, nDigits++; |
| 20176 | |
| 20177 | /* copy max significant digits to significand */ |
| 20178 | while( sqlite3Isdigit(*z) && s<((LARGEST_INT64-9)/10) ){ |
| 20179 | s = s*10 + (*z - '0'); |
| 20180 | z++, nDigits++; |
| 20181 | } |
| 20182 | /* skip non-significant significand digits |
| 20183 | ** (increase exponent by d to shift decimal left) */ |
| 20184 | while( sqlite3Isdigit(*z) ) z++, nDigits++, d++; |
| 20185 | |
| 20186 | /* if decimal point is present */ |
| 20187 | if( *z=='.' ){ |
| 20188 | z++; |
| 20189 | /* copy digits from after decimal to significand |
| 20190 | ** (decrease exponent by d to shift decimal right) */ |
| 20191 | while( sqlite3Isdigit(*z) && s<((LARGEST_INT64-9)/10) ){ |
| 20192 | s = s*10 + (*z - '0'); |
| 20193 | z++, nDigits++, d--; |
| 20194 | } |
| 20195 | /* skip non-significant digits */ |
| 20196 | while( sqlite3Isdigit(*z) ) z++, nDigits++; |
| 20197 | } |
| 20198 | |
| 20199 | /* if exponent is present */ |
| 20200 | if( *z=='e' || *z=='E' ){ |
| 20201 | z++; |
| 20202 | /* get sign of exponent */ |
| 20203 | if( *z=='-' ){ |
| 20204 | esign = -1; |
| 20205 | z++; |
| 20206 | }else if( *z=='+' ){ |
| 20207 | z++; |
| 20208 | } |
| 20209 | /* copy digits to exponent */ |
| 20210 | while( sqlite3Isdigit(*z) ){ |
| 20211 | e = e*10 + (*z - '0'); |
| 20212 | z++; |
| 20213 | } |
| 20214 | } |
| 20215 | |
| 20216 | /* adjust exponent by d, and update sign */ |
| 20217 | e = (e*esign) + d; |
| 20218 | if( e<0 ) { |
| 20219 | esign = -1; |
| 20220 | e *= -1; |
| @@ -20269,132 +20253,104 @@ | |
| 20269 | } |
| 20270 | |
| 20271 | /* store the result */ |
| 20272 | *pResult = result; |
| 20273 | |
| 20274 | /* return number of characters used */ |
| 20275 | return (int)(z - zBegin); |
| 20276 | #else |
| 20277 | return sqlite3Atoi64(z, pResult); |
| 20278 | #endif /* SQLITE_OMIT_FLOATING_POINT */ |
| 20279 | } |
| 20280 | |
| 20281 | /* |
| 20282 | ** Compare the 19-character string zNum against the text representation |
| 20283 | ** value 2^63: 9223372036854775808. Return negative, zero, or positive |
| 20284 | ** if zNum is less than, equal to, or greater than the string. |
| 20285 | ** |
| 20286 | ** Unlike memcmp() this routine is guaranteed to return the difference |
| 20287 | ** in the values of the last digit if the only difference is in the |
| 20288 | ** last digit. So, for example, |
| 20289 | ** |
| 20290 | ** compare2pow63("9223372036854775800") |
| 20291 | ** |
| 20292 | ** will return -8. |
| 20293 | */ |
| 20294 | static int compare2pow63(const char *zNum){ |
| 20295 | int c; |
| 20296 | c = memcmp(zNum,"922337203685477580",18)*10; |
| 20297 | if( c==0 ){ |
| 20298 | c = zNum[18] - '8'; |
| 20299 | testcase( c==(-1) ); |
| 20300 | testcase( c==0 ); |
| 20301 | testcase( c==(+1) ); |
| 20302 | } |
| 20303 | return c; |
| 20304 | } |
| 20305 | |
| 20306 | |
| 20307 | /* |
| 20308 | ** Return TRUE if zNum is a 64-bit signed integer and write |
| 20309 | ** the value of the integer into *pNum. If zNum is not an integer |
| 20310 | ** or is an integer that is too large to be expressed with 64 bits, |
| 20311 | ** then return false. |
| 20312 | ** |
| 20313 | ** When this routine was originally written it dealt with only |
| 20314 | ** 32-bit numbers. At that time, it was much faster than the |
| 20315 | ** atoi() library routine in RedHat 7.2. |
| 20316 | */ |
| 20317 | SQLITE_PRIVATE int sqlite3Atoi64(const char *zNum, i64 *pNum){ |
| 20318 | i64 v = 0; |
| 20319 | int neg; |
| 20320 | int i, c; |
| 20321 | const char *zStart; |
| 20322 | while( sqlite3Isspace(*zNum) ) zNum++; |
| 20323 | if( *zNum=='-' ){ |
| 20324 | neg = 1; |
| 20325 | zNum++; |
| 20326 | }else if( *zNum=='+' ){ |
| 20327 | neg = 0; |
| 20328 | zNum++; |
| 20329 | }else{ |
| 20330 | neg = 0; |
| 20331 | } |
| 20332 | zStart = zNum; |
| 20333 | while( zNum[0]=='0' ){ zNum++; } /* Skip over leading zeros. Ticket #2454 */ |
| 20334 | for(i=0; (c=zNum[i])>='0' && c<='9'; i++){ |
| 20335 | v = v*10 + c - '0'; |
| 20336 | } |
| 20337 | *pNum = neg ? -v : v; |
| 20338 | testcase( i==18 ); |
| 20339 | testcase( i==19 ); |
| 20340 | testcase( i==20 ); |
| 20341 | if( c!=0 || (i==0 && zStart==zNum) || i>19 ){ |
| 20342 | /* zNum is empty or contains non-numeric text or is longer |
| 20343 | ** than 19 digits (thus guaranting that it is too large) */ |
| 20344 | return 0; |
| 20345 | }else if( i<19 ){ |
| 20346 | /* Less than 19 digits, so we know that it fits in 64 bits */ |
| 20347 | return 1; |
| 20348 | }else{ |
| 20349 | /* 19-digit numbers must be no larger than 9223372036854775807 if positive |
| 20350 | ** or 9223372036854775808 if negative. Note that 9223372036854665808 |
| 20351 | ** is 2^63. */ |
| 20352 | return compare2pow63(zNum)<neg; |
| 20353 | } |
| 20354 | } |
| 20355 | |
| 20356 | /* |
| 20357 | ** The string zNum represents an unsigned integer. The zNum string |
| 20358 | ** consists of one or more digit characters and is terminated by |
| 20359 | ** a zero character. Any stray characters in zNum result in undefined |
| 20360 | ** behavior. |
| 20361 | ** |
| 20362 | ** If the unsigned integer that zNum represents will fit in a |
| 20363 | ** 64-bit signed integer, return TRUE. Otherwise return FALSE. |
| 20364 | ** |
| 20365 | ** If the negFlag parameter is true, that means that zNum really represents |
| 20366 | ** a negative number. (The leading "-" is omitted from zNum.) This |
| 20367 | ** parameter is needed to determine a boundary case. A string |
| 20368 | ** of "9223373036854775808" returns false if negFlag is false or true |
| 20369 | ** if negFlag is true. |
| 20370 | ** |
| 20371 | ** Leading zeros are ignored. |
| 20372 | */ |
| 20373 | SQLITE_PRIVATE int sqlite3FitsIn64Bits(const char *zNum, int negFlag){ |
| 20374 | int i; |
| 20375 | int neg = 0; |
| 20376 | |
| 20377 | assert( zNum[0]>='0' && zNum[0]<='9' ); /* zNum is an unsigned number */ |
| 20378 | |
| 20379 | if( negFlag ) neg = 1-neg; |
| 20380 | while( *zNum=='0' ){ |
| 20381 | zNum++; /* Skip leading zeros. Ticket #2454 */ |
| 20382 | } |
| 20383 | for(i=0; zNum[i]; i++){ assert( zNum[i]>='0' && zNum[i]<='9' ); } |
| 20384 | testcase( i==18 ); |
| 20385 | testcase( i==19 ); |
| 20386 | testcase( i==20 ); |
| 20387 | if( i<19 ){ |
| 20388 | /* Guaranteed to fit if less than 19 digits */ |
| 20389 | return 1; |
| 20390 | }else if( i>19 ){ |
| 20391 | /* Guaranteed to be too big if greater than 19 digits */ |
| 20392 | return 0; |
| 20393 | }else{ |
| 20394 | /* Compare against 2^63. */ |
| 20395 | return compare2pow63(zNum)<neg; |
| 20396 | } |
| 20397 | } |
| 20398 | |
| 20399 | /* |
| 20400 | ** If zNum represents an integer that will fit in 32-bits, then set |
| @@ -54149,17 +54105,13 @@ | |
| 54149 | return pMem->u.i; |
| 54150 | }else if( flags & MEM_Real ){ |
| 54151 | return doubleToInt64(pMem->r); |
| 54152 | }else if( flags & (MEM_Str|MEM_Blob) ){ |
| 54153 | i64 value; |
| 54154 | pMem->flags |= MEM_Str; |
| 54155 | if( sqlite3VdbeChangeEncoding(pMem, SQLITE_UTF8) |
| 54156 | || sqlite3VdbeMemNulTerminate(pMem) ){ |
| 54157 | return 0; |
| 54158 | } |
| 54159 | assert( pMem->z ); |
| 54160 | sqlite3Atoi64(pMem->z, &value); |
| 54161 | return value; |
| 54162 | }else{ |
| 54163 | return 0; |
| 54164 | } |
| 54165 | } |
| @@ -54185,11 +54137,11 @@ | |
| 54185 | || sqlite3VdbeMemNulTerminate(pMem) ){ |
| 54186 | /* (double)0 In case of SQLITE_OMIT_FLOATING_POINT... */ |
| 54187 | return (double)0; |
| 54188 | } |
| 54189 | assert( pMem->z ); |
| 54190 | sqlite3AtoF(pMem->z, &val); |
| 54191 | return val; |
| 54192 | }else{ |
| 54193 | /* (double)0 In case of SQLITE_OMIT_FLOATING_POINT... */ |
| 54194 | return (double)0; |
| 54195 | } |
| @@ -54258,25 +54210,23 @@ | |
| 54258 | ** Every effort is made to force the conversion, even if the input |
| 54259 | ** is a string that does not look completely like a number. Convert |
| 54260 | ** as much of the string as we can and ignore the rest. |
| 54261 | */ |
| 54262 | SQLITE_PRIVATE int sqlite3VdbeMemNumerify(Mem *pMem){ |
| 54263 | int rc; |
| 54264 | assert( (pMem->flags & (MEM_Int|MEM_Real|MEM_Null))==0 ); |
| 54265 | assert( (pMem->flags & (MEM_Blob|MEM_Str))!=0 ); |
| 54266 | assert( pMem->db==0 || sqlite3_mutex_held(pMem->db->mutex) ); |
| 54267 | rc = sqlite3VdbeChangeEncoding(pMem, SQLITE_UTF8); |
| 54268 | if( rc ) return rc; |
| 54269 | rc = sqlite3VdbeMemNulTerminate(pMem); |
| 54270 | if( rc ) return rc; |
| 54271 | if( sqlite3Atoi64(pMem->z, &pMem->u.i) ){ |
| 54272 | MemSetTypeFlag(pMem, MEM_Int); |
| 54273 | }else{ |
| 54274 | pMem->r = sqlite3VdbeRealValue(pMem); |
| 54275 | MemSetTypeFlag(pMem, MEM_Real); |
| 54276 | sqlite3VdbeIntegerAffinity(pMem); |
| 54277 | } |
| 54278 | return SQLITE_OK; |
| 54279 | } |
| 54280 | |
| 54281 | /* |
| 54282 | ** Delete any previous value and set the value stored in *pMem to NULL. |
| @@ -54815,10 +54765,12 @@ | |
| 54815 | sqlite3_value **ppVal /* Write the new value here */ |
| 54816 | ){ |
| 54817 | int op; |
| 54818 | char *zVal = 0; |
| 54819 | sqlite3_value *pVal = 0; |
| 54820 | |
| 54821 | if( !pExpr ){ |
| 54822 | *ppVal = 0; |
| 54823 | return SQLITE_OK; |
| 54824 | } |
| @@ -54831,35 +54783,50 @@ | |
| 54831 | #ifdef SQLITE_ENABLE_STAT2 |
| 54832 | if( op==TK_REGISTER ) op = pExpr->op2; |
| 54833 | #else |
| 54834 | if( NEVER(op==TK_REGISTER) ) op = pExpr->op2; |
| 54835 | #endif |
| 54836 | |
| 54837 | if( op==TK_STRING || op==TK_FLOAT || op==TK_INTEGER ){ |
| 54838 | pVal = sqlite3ValueNew(db); |
| 54839 | if( pVal==0 ) goto no_mem; |
| 54840 | if( ExprHasProperty(pExpr, EP_IntValue) ){ |
| 54841 | sqlite3VdbeMemSetInt64(pVal, (i64)pExpr->u.iValue); |
| 54842 | }else{ |
| 54843 | zVal = sqlite3DbStrDup(db, pExpr->u.zToken); |
| 54844 | if( zVal==0 ) goto no_mem; |
| 54845 | sqlite3ValueSetStr(pVal, -1, zVal, SQLITE_UTF8, SQLITE_DYNAMIC); |
| 54846 | if( op==TK_FLOAT ) pVal->type = SQLITE_FLOAT; |
| 54847 | } |
| 54848 | if( (op==TK_INTEGER || op==TK_FLOAT ) && affinity==SQLITE_AFF_NONE ){ |
| 54849 | sqlite3ValueApplyAffinity(pVal, SQLITE_AFF_NUMERIC, SQLITE_UTF8); |
| 54850 | }else{ |
| 54851 | sqlite3ValueApplyAffinity(pVal, affinity, SQLITE_UTF8); |
| 54852 | } |
| 54853 | if( enc!=SQLITE_UTF8 ){ |
| 54854 | sqlite3VdbeChangeEncoding(pVal, enc); |
| 54855 | } |
| 54856 | }else if( op==TK_UMINUS ) { |
| 54857 | if( SQLITE_OK==sqlite3ValueFromExpr(db,pExpr->pLeft,enc,affinity,&pVal) ){ |
| 54858 | pVal->u.i = -1 * pVal->u.i; |
| 54859 | /* (double)-1 In case of SQLITE_OMIT_FLOATING_POINT... */ |
| 54860 | pVal->r = (double)-1 * pVal->r; |
| 54861 | } |
| 54862 | } |
| 54863 | #ifndef SQLITE_OMIT_BLOB_LITERAL |
| 54864 | else if( op==TK_BLOB ){ |
| 54865 | int nVal; |
| @@ -59770,35 +59737,21 @@ | |
| 59770 | ** looks like a number, convert it into a number. If it does not |
| 59771 | ** look like a number, leave it alone. |
| 59772 | */ |
| 59773 | static void applyNumericAffinity(Mem *pRec){ |
| 59774 | if( (pRec->flags & (MEM_Real|MEM_Int))==0 ){ |
| 59775 | int realnum; |
| 59776 | u8 enc = pRec->enc; |
| 59777 | sqlite3VdbeMemNulTerminate(pRec); |
| 59778 | if( (pRec->flags&MEM_Str) && sqlite3IsNumber(pRec->z, &realnum, enc) ){ |
| 59779 | i64 value; |
| 59780 | char *zUtf8 = pRec->z; |
| 59781 | #ifndef SQLITE_OMIT_UTF16 |
| 59782 | if( enc!=SQLITE_UTF8 ){ |
| 59783 | assert( pRec->db ); |
| 59784 | zUtf8 = sqlite3Utf16to8(pRec->db, pRec->z, pRec->n, enc); |
| 59785 | if( !zUtf8 ) return; |
| 59786 | } |
| 59787 | #endif |
| 59788 | if( !realnum && sqlite3Atoi64(zUtf8, &value) ){ |
| 59789 | pRec->u.i = value; |
| 59790 | MemSetTypeFlag(pRec, MEM_Int); |
| 59791 | }else{ |
| 59792 | sqlite3AtoF(zUtf8, &pRec->r); |
| 59793 | MemSetTypeFlag(pRec, MEM_Real); |
| 59794 | } |
| 59795 | #ifndef SQLITE_OMIT_UTF16 |
| 59796 | if( enc!=SQLITE_UTF8 ){ |
| 59797 | sqlite3DbFree(pRec->db, zUtf8); |
| 59798 | } |
| 59799 | #endif |
| 59800 | } |
| 59801 | } |
| 59802 | } |
| 59803 | |
| 59804 | /* |
| @@ -61580,11 +61533,10 @@ | |
| 61580 | ** integers, for space efficiency, but after extraction we want them |
| 61581 | ** to have only a real value. |
| 61582 | */ |
| 61583 | case OP_RealAffinity: { /* in1 */ |
| 61584 | pIn1 = &aMem[pOp->p1]; |
| 61585 | memAboutToChange(p, pIn1); |
| 61586 | if( pIn1->flags & MEM_Int ){ |
| 61587 | sqlite3VdbeMemRealify(pIn1); |
| 61588 | } |
| 61589 | break; |
| 61590 | } |
| @@ -61623,11 +61575,10 @@ | |
| 61623 | ** |
| 61624 | ** A NULL value is not changed by this routine. It remains NULL. |
| 61625 | */ |
| 61626 | case OP_ToBlob: { /* same as TK_TO_BLOB, in1 */ |
| 61627 | pIn1 = &aMem[pOp->p1]; |
| 61628 | memAboutToChange(p, pIn1); |
| 61629 | if( pIn1->flags & MEM_Null ) break; |
| 61630 | if( (pIn1->flags & MEM_Blob)==0 ){ |
| 61631 | applyAffinity(pIn1, SQLITE_AFF_TEXT, encoding); |
| 61632 | assert( pIn1->flags & MEM_Str || db->mallocFailed ); |
| 61633 | MemSetTypeFlag(pIn1, MEM_Blob); |
| @@ -61648,14 +61599,11 @@ | |
| 61648 | ** |
| 61649 | ** A NULL value is not changed by this routine. It remains NULL. |
| 61650 | */ |
| 61651 | case OP_ToNumeric: { /* same as TK_TO_NUMERIC, in1 */ |
| 61652 | pIn1 = &aMem[pOp->p1]; |
| 61653 | memAboutToChange(p, pIn1); |
| 61654 | if( (pIn1->flags & (MEM_Null|MEM_Int|MEM_Real))==0 ){ |
| 61655 | sqlite3VdbeMemNumerify(pIn1); |
| 61656 | } |
| 61657 | break; |
| 61658 | } |
| 61659 | #endif /* SQLITE_OMIT_CAST */ |
| 61660 | |
| 61661 | /* Opcode: ToInt P1 * * * * |
| @@ -61667,11 +61615,10 @@ | |
| 61667 | ** |
| 61668 | ** A NULL value is not changed by this routine. It remains NULL. |
| 61669 | */ |
| 61670 | case OP_ToInt: { /* same as TK_TO_INT, in1 */ |
| 61671 | pIn1 = &aMem[pOp->p1]; |
| 61672 | memAboutToChange(p, pIn1); |
| 61673 | if( (pIn1->flags & MEM_Null)==0 ){ |
| 61674 | sqlite3VdbeMemIntegerify(pIn1); |
| 61675 | } |
| 61676 | break; |
| 61677 | } |
| @@ -61781,12 +61728,10 @@ | |
| 61781 | u16 flags3; /* Copy of initial value of pIn3->flags */ |
| 61782 | #endif /* local variables moved into u.ai */ |
| 61783 | |
| 61784 | pIn1 = &aMem[pOp->p1]; |
| 61785 | pIn3 = &aMem[pOp->p3]; |
| 61786 | memAboutToChange(p, pIn1); |
| 61787 | memAboutToChange(p, pIn3); |
| 61788 | u.ai.flags1 = pIn1->flags; |
| 61789 | u.ai.flags3 = pIn3->flags; |
| 61790 | if( (pIn1->flags | pIn3->flags)&MEM_Null ){ |
| 61791 | /* One or both operands are NULL */ |
| 61792 | if( pOp->p5 & SQLITE_NULLEQ ){ |
| @@ -62415,11 +62360,10 @@ | |
| 62415 | assert( u.an.zAffinity[pOp->p2]==0 ); |
| 62416 | pIn1 = &aMem[pOp->p1]; |
| 62417 | while( (u.an.cAff = *(u.an.zAffinity++))!=0 ){ |
| 62418 | assert( pIn1 <= &p->aMem[p->nMem] ); |
| 62419 | assert( memIsValid(pIn1) ); |
| 62420 | memAboutToChange(p, pIn1); |
| 62421 | ExpandBlob(pIn1); |
| 62422 | applyAffinity(pIn1, u.an.cAff, encoding); |
| 62423 | pIn1++; |
| 62424 | } |
| 62425 | break; |
| @@ -62495,11 +62439,10 @@ | |
| 62495 | ** out how much space is required for the new record. |
| 62496 | */ |
| 62497 | for(u.ao.pRec=u.ao.pData0; u.ao.pRec<=u.ao.pLast; u.ao.pRec++){ |
| 62498 | assert( memIsValid(u.ao.pRec) ); |
| 62499 | if( u.ao.zAffinity ){ |
| 62500 | memAboutToChange(p, u.ao.pRec); |
| 62501 | applyAffinity(u.ao.pRec, u.ao.zAffinity[u.ao.pRec-u.ao.pData0], encoding); |
| 62502 | } |
| 62503 | if( u.ao.pRec->flags&MEM_Zero && u.ao.pRec->n>0 ){ |
| 62504 | sqlite3VdbeMemExpandBlob(u.ao.pRec); |
| 62505 | } |
| @@ -68880,11 +68823,11 @@ | |
| 68880 | pExpr->iColumn = (ynVar)(++pParse->nVar); |
| 68881 | }else if( z[0]=='?' ){ |
| 68882 | /* Wildcard of the form "?nnn". Convert "nnn" to an integer and |
| 68883 | ** use it as the variable number */ |
| 68884 | i64 i; |
| 68885 | int bOk = sqlite3Atoi64(&z[1], &i); |
| 68886 | pExpr->iColumn = (ynVar)i; |
| 68887 | testcase( i==0 ); |
| 68888 | testcase( i==1 ); |
| 68889 | testcase( i==db->aLimit[SQLITE_LIMIT_VARIABLE_NUMBER]-1 ); |
| 68890 | testcase( i==db->aLimit[SQLITE_LIMIT_VARIABLE_NUMBER] ); |
| @@ -70243,11 +70186,11 @@ | |
| 70243 | */ |
| 70244 | static void codeReal(Vdbe *v, const char *z, int negateFlag, int iMem){ |
| 70245 | if( ALWAYS(z!=0) ){ |
| 70246 | double value; |
| 70247 | char *zV; |
| 70248 | sqlite3AtoF(z, &value); |
| 70249 | assert( !sqlite3IsNaN(value) ); /* The new AtoF never returns NaN */ |
| 70250 | if( negateFlag ) value = -value; |
| 70251 | zV = dup8bytes(v, (char*)&value); |
| 70252 | sqlite3VdbeAddOp4(v, OP_Real, 0, iMem, 0, zV, P4_REAL); |
| 70253 | } |
| @@ -70257,28 +70200,27 @@ | |
| 70257 | |
| 70258 | /* |
| 70259 | ** Generate an instruction that will put the integer describe by |
| 70260 | ** text z[0..n-1] into register iMem. |
| 70261 | ** |
| 70262 | ** The z[] string will probably not be zero-terminated. But the |
| 70263 | ** z[n] character is guaranteed to be something that does not look |
| 70264 | ** like the continuation of the number. |
| 70265 | */ |
| 70266 | static void codeInteger(Parse *pParse, Expr *pExpr, int negFlag, int iMem){ |
| 70267 | Vdbe *v = pParse->pVdbe; |
| 70268 | if( pExpr->flags & EP_IntValue ){ |
| 70269 | int i = pExpr->u.iValue; |
| 70270 | if( negFlag ) i = -i; |
| 70271 | sqlite3VdbeAddOp2(v, OP_Integer, i, iMem); |
| 70272 | }else{ |
| 70273 | const char *z = pExpr->u.zToken; |
| 70274 | assert( z!=0 ); |
| 70275 | if( sqlite3FitsIn64Bits(z, negFlag) ){ |
| 70276 | i64 value; |
| 70277 | char *zV; |
| 70278 | sqlite3Atoi64(z, &value); |
| 70279 | if( negFlag ) value = -value; |
| 70280 | zV = dup8bytes(v, (char*)&value); |
| 70281 | sqlite3VdbeAddOp4(v, OP_Int64, 0, iMem, 0, zV, P4_INT64); |
| 70282 | }else{ |
| 70283 | #ifdef SQLITE_OMIT_FLOATING_POINT |
| 70284 | sqlite3ErrorMsg(pParse, "oversized integer: %s%s", negFlag ? "-" : "", z); |
| @@ -79414,11 +79356,11 @@ | |
| 79414 | zBuf = sqlite3_mprintf("%.*f",n,r); |
| 79415 | if( zBuf==0 ){ |
| 79416 | sqlite3_result_error_nomem(context); |
| 79417 | return; |
| 79418 | } |
| 79419 | sqlite3AtoF(zBuf, &r); |
| 79420 | sqlite3_free(zBuf); |
| 79421 | } |
| 79422 | sqlite3_result_double(context, r); |
| 79423 | } |
| 79424 | #endif |
| @@ -85472,11 +85414,11 @@ | |
| 85472 | */ |
| 85473 | if( sqlite3StrICmp(zLeft,"journal_size_limit")==0 ){ |
| 85474 | Pager *pPager = sqlite3BtreePager(pDb->pBt); |
| 85475 | i64 iLimit = -2; |
| 85476 | if( zRight ){ |
| 85477 | sqlite3Atoi64(zRight, &iLimit); |
| 85478 | if( iLimit<-1 ) iLimit = -1; |
| 85479 | } |
| 85480 | iLimit = sqlite3PagerJournalSizeLimit(pPager, iLimit); |
| 85481 | returnSingleInt(pParse, "journal_size_limit", iLimit); |
| 85482 | }else |
| @@ -96380,11 +96322,12 @@ | |
| 96380 | |
| 96381 | /* |
| 96382 | ** Required because bestIndex() is called by bestOrClauseIndex() |
| 96383 | */ |
| 96384 | static void bestIndex( |
| 96385 | Parse*, WhereClause*, struct SrcList_item*, Bitmask, ExprList*, WhereCost*); |
| 96386 | |
| 96387 | /* |
| 96388 | ** This routine attempts to find an scanning strategy that can be used |
| 96389 | ** to optimize an 'OR' expression that is part of a WHERE clause. |
| 96390 | ** |
| @@ -96393,11 +96336,12 @@ | |
| 96393 | */ |
| 96394 | static void bestOrClauseIndex( |
| 96395 | Parse *pParse, /* The parsing context */ |
| 96396 | WhereClause *pWC, /* The WHERE clause */ |
| 96397 | struct SrcList_item *pSrc, /* The FROM clause term to search */ |
| 96398 | Bitmask notReady, /* Mask of cursors that are not available */ |
| 96399 | ExprList *pOrderBy, /* The ORDER BY clause */ |
| 96400 | WhereCost *pCost /* Lowest cost query plan */ |
| 96401 | ){ |
| 96402 | #ifndef SQLITE_OMIT_OR_OPTIMIZATION |
| 96403 | const int iCur = pSrc->iCursor; /* The cursor of the table to be accessed */ |
| @@ -96429,19 +96373,19 @@ | |
| 96429 | WHERETRACE(("... Multi-index OR testing for term %d of %d....\n", |
| 96430 | (pOrTerm - pOrWC->a), (pTerm - pWC->a) |
| 96431 | )); |
| 96432 | if( pOrTerm->eOperator==WO_AND ){ |
| 96433 | WhereClause *pAndWC = &pOrTerm->u.pAndInfo->wc; |
| 96434 | bestIndex(pParse, pAndWC, pSrc, notReady, 0, &sTermCost); |
| 96435 | }else if( pOrTerm->leftCursor==iCur ){ |
| 96436 | WhereClause tempWC; |
| 96437 | tempWC.pParse = pWC->pParse; |
| 96438 | tempWC.pMaskSet = pWC->pMaskSet; |
| 96439 | tempWC.op = TK_AND; |
| 96440 | tempWC.a = pOrTerm; |
| 96441 | tempWC.nTerm = 1; |
| 96442 | bestIndex(pParse, &tempWC, pSrc, notReady, 0, &sTermCost); |
| 96443 | }else{ |
| 96444 | continue; |
| 96445 | } |
| 96446 | rTotal += sTermCost.rCost; |
| 96447 | nRow += sTermCost.nRow; |
| @@ -96884,11 +96828,12 @@ | |
| 96884 | */ |
| 96885 | static void bestVirtualIndex( |
| 96886 | Parse *pParse, /* The parsing context */ |
| 96887 | WhereClause *pWC, /* The WHERE clause */ |
| 96888 | struct SrcList_item *pSrc, /* The FROM clause term to search */ |
| 96889 | Bitmask notReady, /* Mask of cursors that are not available */ |
| 96890 | ExprList *pOrderBy, /* The order by clause */ |
| 96891 | WhereCost *pCost, /* Lowest cost query plan */ |
| 96892 | sqlite3_index_info **ppIdxInfo /* Index information passed to xBestIndex */ |
| 96893 | ){ |
| 96894 | Table *pTab = pSrc->pTab; |
| @@ -97014,11 +96959,11 @@ | |
| 97014 | pIdxInfo->nOrderBy = nOrderBy; |
| 97015 | |
| 97016 | /* Try to find a more efficient access pattern by using multiple indexes |
| 97017 | ** to optimize an OR expression within the WHERE clause. |
| 97018 | */ |
| 97019 | bestOrClauseIndex(pParse, pWC, pSrc, notReady, pOrderBy, pCost); |
| 97020 | } |
| 97021 | #endif /* SQLITE_OMIT_VIRTUALTABLE */ |
| 97022 | |
| 97023 | /* |
| 97024 | ** Argument pIdx is a pointer to an index structure that has an array of |
| @@ -97295,11 +97240,12 @@ | |
| 97295 | */ |
| 97296 | static void bestBtreeIndex( |
| 97297 | Parse *pParse, /* The parsing context */ |
| 97298 | WhereClause *pWC, /* The WHERE clause */ |
| 97299 | struct SrcList_item *pSrc, /* The FROM clause term to search */ |
| 97300 | Bitmask notReady, /* Mask of cursors that are not available */ |
| 97301 | ExprList *pOrderBy, /* The ORDER BY clause */ |
| 97302 | WhereCost *pCost /* Lowest cost query plan */ |
| 97303 | ){ |
| 97304 | int iCur = pSrc->iCursor; /* The cursor of the table to be accessed */ |
| 97305 | Index *pProbe; /* An index we are evaluating */ |
| @@ -97557,29 +97503,29 @@ | |
| 97557 | ** of output rows, adjust the nRow value accordingly. This only |
| 97558 | ** matters if the current index is the least costly, so do not bother |
| 97559 | ** with this step if we already know this index will not be chosen. |
| 97560 | ** Also, never reduce the output row count below 2 using this step. |
| 97561 | ** |
| 97562 | ** Do not reduce the output row count if pSrc is the only table that |
| 97563 | ** is notReady; if notReady is a power of two. This will be the case |
| 97564 | ** when the main sqlite3WhereBegin() loop is scanning for a table with |
| 97565 | ** and "optimal" index, and on such a scan the output row count |
| 97566 | ** reduction is not valid because it does not update the "pCost->used" |
| 97567 | ** bitmap. The notReady bitmap will also be a power of two when we |
| 97568 | ** are scanning for the last table in a 64-way join. We are willing |
| 97569 | ** to bypass this optimization in that corner case. |
| 97570 | */ |
| 97571 | if( nRow>2 && cost<=pCost->rCost && (notReady & (notReady-1))!=0 ){ |
| 97572 | int k; /* Loop counter */ |
| 97573 | int nSkipEq = nEq; /* Number of == constraints to skip */ |
| 97574 | int nSkipRange = nBound; /* Number of < constraints to skip */ |
| 97575 | Bitmask thisTab; /* Bitmap for pSrc */ |
| 97576 | |
| 97577 | thisTab = getMask(pWC->pMaskSet, iCur); |
| 97578 | for(pTerm=pWC->a, k=pWC->nTerm; nRow>2 && k; k--, pTerm++){ |
| 97579 | if( pTerm->wtFlags & TERM_VIRTUAL ) continue; |
| 97580 | if( (pTerm->prereqAll & notReady)!=thisTab ) continue; |
| 97581 | if( pTerm->eOperator & (WO_EQ|WO_IN|WO_ISNULL) ){ |
| 97582 | if( nSkipEq ){ |
| 97583 | /* Ignore the first nEq equality matches since the index |
| 97584 | ** has already accounted for these */ |
| 97585 | nSkipEq--; |
| @@ -97657,11 +97603,11 @@ | |
| 97657 | WHERETRACE(("best index is: %s\n", |
| 97658 | ((pCost->plan.wsFlags & WHERE_NOT_FULLSCAN)==0 ? "none" : |
| 97659 | pCost->plan.u.pIdx ? pCost->plan.u.pIdx->zName : "ipk") |
| 97660 | )); |
| 97661 | |
| 97662 | bestOrClauseIndex(pParse, pWC, pSrc, notReady, pOrderBy, pCost); |
| 97663 | bestAutomaticIndex(pParse, pWC, pSrc, notReady, pCost); |
| 97664 | pCost->plan.wsFlags |= eqTermMask; |
| 97665 | } |
| 97666 | |
| 97667 | /* |
| @@ -97672,26 +97618,27 @@ | |
| 97672 | */ |
| 97673 | static void bestIndex( |
| 97674 | Parse *pParse, /* The parsing context */ |
| 97675 | WhereClause *pWC, /* The WHERE clause */ |
| 97676 | struct SrcList_item *pSrc, /* The FROM clause term to search */ |
| 97677 | Bitmask notReady, /* Mask of cursors that are not available */ |
| 97678 | ExprList *pOrderBy, /* The ORDER BY clause */ |
| 97679 | WhereCost *pCost /* Lowest cost query plan */ |
| 97680 | ){ |
| 97681 | #ifndef SQLITE_OMIT_VIRTUALTABLE |
| 97682 | if( IsVirtual(pSrc->pTab) ){ |
| 97683 | sqlite3_index_info *p = 0; |
| 97684 | bestVirtualIndex(pParse, pWC, pSrc, notReady, pOrderBy, pCost, &p); |
| 97685 | if( p->needToFreeIdxStr ){ |
| 97686 | sqlite3_free(p->idxStr); |
| 97687 | } |
| 97688 | sqlite3DbFree(pParse->db, p); |
| 97689 | }else |
| 97690 | #endif |
| 97691 | { |
| 97692 | bestBtreeIndex(pParse, pWC, pSrc, notReady, pOrderBy, pCost); |
| 97693 | } |
| 97694 | } |
| 97695 | |
| 97696 | /* |
| 97697 | ** Disable a term in the WHERE clause. Except, do not disable the term |
| @@ -98902,14 +98849,20 @@ | |
| 98902 | ** by waiting for other tables to run first. This "optimal" test works |
| 98903 | ** by first assuming that the FROM clause is on the inner loop and finding |
| 98904 | ** its query plan, then checking to see if that query plan uses any |
| 98905 | ** other FROM clause terms that are notReady. If no notReady terms are |
| 98906 | ** used then the "optimal" query plan works. |
| 98907 | ** |
| 98908 | ** The second loop iteration is only performed if no optimal scan |
| 98909 | ** strategies were found by the first loop. This 2nd iteration is used to |
| 98910 | ** search for the lowest cost scan overall. |
| 98911 | ** |
| 98912 | ** Previous versions of SQLite performed only the second iteration - |
| 98913 | ** the next outermost loop was always that with the lowest overall |
| 98914 | ** cost. However, this meant that SQLite could select the wrong plan |
| 98915 | ** for scripts such as the following: |
| @@ -98925,11 +98878,11 @@ | |
| 98925 | ** algorithm may choose to use t2 for the outer loop, which is a much |
| 98926 | ** costlier approach. |
| 98927 | */ |
| 98928 | nUnconstrained = 0; |
| 98929 | notIndexed = 0; |
| 98930 | for(isOptimal=(iFrom<nTabList-1); isOptimal>=0; isOptimal--){ |
| 98931 | Bitmask mask; /* Mask of tables not yet ready */ |
| 98932 | for(j=iFrom, pTabItem=&pTabList->a[j]; j<nTabList; j++, pTabItem++){ |
| 98933 | int doNotReorder; /* True if this table should not be reordered */ |
| 98934 | WhereCost sCost; /* Cost information from best[Virtual]Index() */ |
| 98935 | ExprList *pOrderBy; /* ORDER BY clause for index to optimize */ |
| @@ -98947,15 +98900,17 @@ | |
| 98947 | |
| 98948 | assert( pTabItem->pTab ); |
| 98949 | #ifndef SQLITE_OMIT_VIRTUALTABLE |
| 98950 | if( IsVirtual(pTabItem->pTab) ){ |
| 98951 | sqlite3_index_info **pp = &pWInfo->a[j].pIdxInfo; |
| 98952 | bestVirtualIndex(pParse, pWC, pTabItem, mask, pOrderBy, &sCost, pp); |
| 98953 | }else |
| 98954 | #endif |
| 98955 | { |
| 98956 | bestBtreeIndex(pParse, pWC, pTabItem, mask, pOrderBy, &sCost); |
| 98957 | } |
| 98958 | assert( isOptimal || (sCost.used¬Ready)==0 ); |
| 98959 | |
| 98960 | /* If an INDEXED BY clause is present, then the plan must use that |
| 98961 | ** index if it uses any index at all */ |
| 98962 |
| --- src/sqlite3.c | |
| +++ src/sqlite3.c | |
| @@ -650,11 +650,11 @@ | |
| 650 | ** [sqlite3_libversion_number()], [sqlite3_sourceid()], |
| 651 | ** [sqlite_version()] and [sqlite_source_id()]. |
| 652 | */ |
| 653 | #define SQLITE_VERSION "3.7.3" |
| 654 | #define SQLITE_VERSION_NUMBER 3007003 |
| 655 | #define SQLITE_SOURCE_ID "2010-10-04 23:55:51 ece641eb8951c6314cedbdb3243f91cb199c3239" |
| 656 | |
| 657 | /* |
| 658 | ** CAPI3REF: Run-Time Library Version Numbers |
| 659 | ** KEYWORDS: sqlite3_version, sqlite3_sourceid |
| 660 | ** |
| @@ -10570,11 +10570,10 @@ | |
| 10570 | |
| 10571 | /* |
| 10572 | ** Internal function prototypes |
| 10573 | */ |
| 10574 | SQLITE_PRIVATE int sqlite3StrICmp(const char *, const char *); |
| 10575 | SQLITE_PRIVATE int sqlite3Strlen30(const char*); |
| 10576 | #define sqlite3StrNICmp sqlite3_strnicmp |
| 10577 | |
| 10578 | SQLITE_PRIVATE int sqlite3MallocInit(void); |
| 10579 | SQLITE_PRIVATE void sqlite3MallocEnd(void); |
| @@ -10890,13 +10889,12 @@ | |
| 10889 | SQLITE_PRIVATE int sqlite3FixSrcList(DbFixer*, SrcList*); |
| 10890 | SQLITE_PRIVATE int sqlite3FixSelect(DbFixer*, Select*); |
| 10891 | SQLITE_PRIVATE int sqlite3FixExpr(DbFixer*, Expr*); |
| 10892 | SQLITE_PRIVATE int sqlite3FixExprList(DbFixer*, ExprList*); |
| 10893 | SQLITE_PRIVATE int sqlite3FixTriggerStep(DbFixer*, TriggerStep*); |
| 10894 | SQLITE_PRIVATE int sqlite3AtoF(const char *z, double*, int, u8); |
| 10895 | SQLITE_PRIVATE int sqlite3GetInt32(const char *, int*); |
| 10896 | SQLITE_PRIVATE int sqlite3Utf16ByteLen(const void *pData, int nChar); |
| 10897 | SQLITE_PRIVATE int sqlite3Utf8CharLen(const char *pData, int nByte); |
| 10898 | SQLITE_PRIVATE int sqlite3Utf8Read(const u8*, const u8**); |
| 10899 | |
| 10900 | /* |
| @@ -10938,11 +10936,11 @@ | |
| 10936 | SQLITE_PRIVATE const char *sqlite3IndexAffinityStr(Vdbe *, Index *); |
| 10937 | SQLITE_PRIVATE void sqlite3TableAffinityStr(Vdbe *, Table *); |
| 10938 | SQLITE_PRIVATE char sqlite3CompareAffinity(Expr *pExpr, char aff2); |
| 10939 | SQLITE_PRIVATE int sqlite3IndexAffinityOk(Expr *pExpr, char idx_affinity); |
| 10940 | SQLITE_PRIVATE char sqlite3ExprAffinity(Expr *pExpr); |
| 10941 | SQLITE_PRIVATE int sqlite3Atoi64(const char*, i64*, int, u8); |
| 10942 | SQLITE_PRIVATE void sqlite3Error(sqlite3*, int, const char*,...); |
| 10943 | SQLITE_PRIVATE void *sqlite3HexToBlob(sqlite3*, const char *z, int n); |
| 10944 | SQLITE_PRIVATE int sqlite3TwoPartName(Parse *, Token *, Token *, Token **); |
| 10945 | SQLITE_PRIVATE const char *sqlite3ErrStr(int); |
| 10946 | SQLITE_PRIVATE int sqlite3ReadSchema(Parse *pParse); |
| @@ -12637,16 +12635,10 @@ | |
| 12635 | end_getDigits: |
| 12636 | va_end(ap); |
| 12637 | return cnt; |
| 12638 | } |
| 12639 | |
| 12640 | /* |
| 12641 | ** Parse a timezone extension on the end of a date-time. |
| 12642 | ** The extension is of the form: |
| 12643 | ** |
| 12644 | ** (+/-)HH:MM |
| @@ -12844,21 +12836,19 @@ | |
| 12836 | static int parseDateOrTime( |
| 12837 | sqlite3_context *context, |
| 12838 | const char *zDate, |
| 12839 | DateTime *p |
| 12840 | ){ |
| 12841 | double r; |
| 12842 | if( parseYyyyMmDd(zDate,p)==0 ){ |
| 12843 | return 0; |
| 12844 | }else if( parseHhMmSs(zDate, p)==0 ){ |
| 12845 | return 0; |
| 12846 | }else if( sqlite3StrICmp(zDate,"now")==0){ |
| 12847 | setDateTimeToCurrent(context, p); |
| 12848 | return 0; |
| 12849 | }else if( sqlite3AtoF(zDate, &r, sqlite3Strlen30(zDate), SQLITE_UTF8) ){ |
| 12850 | p->iJD = (sqlite3_int64)(r*86400000.0 + 0.5); |
| 12851 | p->validJD = 1; |
| 12852 | return 0; |
| 12853 | } |
| 12854 | return 1; |
| @@ -13075,12 +13065,13 @@ | |
| 13065 | ** |
| 13066 | ** Move the date to the same time on the next occurrence of |
| 13067 | ** weekday N where 0==Sunday, 1==Monday, and so forth. If the |
| 13068 | ** date is already on the appropriate weekday, this is a no-op. |
| 13069 | */ |
| 13070 | if( strncmp(z, "weekday ", 8)==0 |
| 13071 | && sqlite3AtoF(&z[8], &r, sqlite3Strlen30(&z[8]), SQLITE_UTF8) |
| 13072 | && (n=(int)r)==r && n>=0 && r<7 ){ |
| 13073 | sqlite3_int64 Z; |
| 13074 | computeYMD_HMS(p); |
| 13075 | p->validTZ = 0; |
| 13076 | p->validJD = 0; |
| 13077 | computeJD(p); |
| @@ -13131,12 +13122,15 @@ | |
| 13122 | case '6': |
| 13123 | case '7': |
| 13124 | case '8': |
| 13125 | case '9': { |
| 13126 | double rRounder; |
| 13127 | for(n=1; z[n] && z[n]!=':' && !sqlite3Isspace(z[n]); n++){} |
| 13128 | if( !sqlite3AtoF(z, &r, n, SQLITE_UTF8) ){ |
| 13129 | rc = 1; |
| 13130 | break; |
| 13131 | } |
| 13132 | if( z[n]==':' ){ |
| 13133 | /* A modifier of the form (+|-)HH:MM:SS.FFF adds (or subtracts) the |
| 13134 | ** specified number of hours, minutes, seconds, and fractional seconds |
| 13135 | ** to the time. The ".FFF" may be omitted. The ":SS.FFF" may be |
| 13136 | ** omitted. |
| @@ -17502,11 +17496,11 @@ | |
| 17496 | sqlite3MemoryAlarm(softHeapLimitEnforcer, 0, n); |
| 17497 | }else{ |
| 17498 | sqlite3MemoryAlarm(0, 0, 0); |
| 17499 | } |
| 17500 | excess = sqlite3_memory_used() - n; |
| 17501 | if( excess>0 ) sqlite3_release_memory((int)(excess & 0x7fffffff)); |
| 17502 | return priorLimit; |
| 17503 | } |
| 17504 | SQLITE_API void sqlite3_soft_heap_limit(int n){ |
| 17505 | if( n<0 ) n = 0; |
| 17506 | sqlite3_soft_heap_limit64(n); |
| @@ -20096,125 +20090,115 @@ | |
| 20090 | while( N-- > 0 && *a!=0 && UpperToLower[*a]==UpperToLower[*b]){ a++; b++; } |
| 20091 | return N<0 ? 0 : UpperToLower[*a] - UpperToLower[*b]; |
| 20092 | } |
| 20093 | |
| 20094 | /* |
| 20095 | ** The string z[] is an text representation of a real number. |
| 20096 | ** Convert this string to a double and write it into *pResult. |
| 20097 | ** |
| 20098 | ** The string z[] is length bytes in length (bytes, not characters) and |
| 20099 | ** uses the encoding enc. The string is not necessarily zero-terminated. |
| 20100 | ** |
| 20101 | ** Return TRUE if the result is a valid real number (or integer) and FALSE |
| 20102 | ** if the string is empty or contains extraneous text. Valid numbers |
| 20103 | ** are in one of these formats: |
| 20104 | ** |
| 20105 | ** [+-]digits[E[+-]digits] |
| 20106 | ** [+-]digits.[digits][E[+-]digits] |
| 20107 | ** [+-].digits[E[+-]digits] |
| 20108 | ** |
| 20109 | ** Leading and trailing whitespace is ignored for the purpose of determining |
| 20110 | ** validity. |
| 20111 | ** |
| 20112 | ** If some prefix of the input string is a valid number, this routine |
| 20113 | ** returns FALSE but it still converts the prefix and writes the result |
| 20114 | ** into *pResult. |
| 20115 | */ |
| 20116 | SQLITE_PRIVATE int sqlite3AtoF(const char *z, double *pResult, int length, u8 enc){ |
| 20117 | #ifndef SQLITE_OMIT_FLOATING_POINT |
| 20118 | int incr = (enc==SQLITE_UTF8?1:2); |
| 20119 | const char *zEnd = z + length; |
| 20120 | /* sign * significand * (10 ^ (esign * exponent)) */ |
| 20121 | int sign = 1; /* sign of significand */ |
| 20122 | i64 s = 0; /* significand */ |
| 20123 | int d = 0; /* adjust exponent for shifting decimal point */ |
| 20124 | int esign = 1; /* sign of exponent */ |
| 20125 | int e = 0; /* exponent */ |
| 20126 | int eValid = 1; /* True exponent is either not used or is well-formed */ |
| 20127 | double result; |
| 20128 | int nDigits = 0; |
| 20129 | |
| 20130 | *pResult = 0.0; /* Default return value, in case of an error */ |
| 20131 | |
| 20132 | if( enc==SQLITE_UTF16BE ) z++; |
| 20133 | |
| 20134 | /* skip leading spaces */ |
| 20135 | while( z<zEnd && sqlite3Isspace(*z) ) z+=incr; |
| 20136 | if( z>=zEnd ) return 0; |
| 20137 | |
| 20138 | /* get sign of significand */ |
| 20139 | if( *z=='-' ){ |
| 20140 | sign = -1; |
| 20141 | z+=incr; |
| 20142 | }else if( *z=='+' ){ |
| 20143 | z+=incr; |
| 20144 | } |
| 20145 | |
| 20146 | /* skip leading zeroes */ |
| 20147 | while( z<zEnd && z[0]=='0' ) z+=incr, nDigits++; |
| 20148 | |
| 20149 | /* copy max significant digits to significand */ |
| 20150 | while( z<zEnd && sqlite3Isdigit(*z) && s<((LARGEST_INT64-9)/10) ){ |
| 20151 | s = s*10 + (*z - '0'); |
| 20152 | z+=incr, nDigits++; |
| 20153 | } |
| 20154 | |
| 20155 | /* skip non-significant significand digits |
| 20156 | ** (increase exponent by d to shift decimal left) */ |
| 20157 | while( z<zEnd && sqlite3Isdigit(*z) ) z+=incr, nDigits++, d++; |
| 20158 | if( z>=zEnd ) goto do_atof_calc; |
| 20159 | |
| 20160 | /* if decimal point is present */ |
| 20161 | if( *z=='.' ){ |
| 20162 | z+=incr; |
| 20163 | /* copy digits from after decimal to significand |
| 20164 | ** (decrease exponent by d to shift decimal right) */ |
| 20165 | while( z<zEnd && sqlite3Isdigit(*z) && s<((LARGEST_INT64-9)/10) ){ |
| 20166 | s = s*10 + (*z - '0'); |
| 20167 | z+=incr, nDigits++, d--; |
| 20168 | } |
| 20169 | /* skip non-significant digits */ |
| 20170 | while( z<zEnd && sqlite3Isdigit(*z) ) z+=incr, nDigits++; |
| 20171 | } |
| 20172 | if( z>=zEnd ) goto do_atof_calc; |
| 20173 | |
| 20174 | /* if exponent is present */ |
| 20175 | if( *z=='e' || *z=='E' ){ |
| 20176 | z+=incr; |
| 20177 | eValid = 0; |
| 20178 | if( z>=zEnd ) goto do_atof_calc; |
| 20179 | /* get sign of exponent */ |
| 20180 | if( *z=='-' ){ |
| 20181 | esign = -1; |
| 20182 | z+=incr; |
| 20183 | }else if( *z=='+' ){ |
| 20184 | z+=incr; |
| 20185 | } |
| 20186 | /* copy digits to exponent */ |
| 20187 | while( z<zEnd && sqlite3Isdigit(*z) ){ |
| 20188 | e = e*10 + (*z - '0'); |
| 20189 | z+=incr; |
| 20190 | eValid = 1; |
| 20191 | } |
| 20192 | } |
| 20193 | |
| 20194 | /* skip trailing spaces */ |
| 20195 | if( nDigits && eValid ){ |
| 20196 | while( z<zEnd && sqlite3Isspace(*z) ) z+=incr; |
| 20197 | } |
| 20198 | |
| 20199 | do_atof_calc: |
| 20200 | /* adjust exponent by d, and update sign */ |
| 20201 | e = (e*esign) + d; |
| 20202 | if( e<0 ) { |
| 20203 | esign = -1; |
| 20204 | e *= -1; |
| @@ -20269,132 +20253,104 @@ | |
| 20253 | } |
| 20254 | |
| 20255 | /* store the result */ |
| 20256 | *pResult = result; |
| 20257 | |
| 20258 | /* return true if number and no extra non-whitespace chracters after */ |
| 20259 | return z>=zEnd && nDigits>0 && eValid; |
| 20260 | #else |
| 20261 | return !sqlite3Atoi64(z, pResult, length, enc); |
| 20262 | #endif /* SQLITE_OMIT_FLOATING_POINT */ |
| 20263 | } |
| 20264 | |
| 20265 | /* |
| 20266 | ** Compare the 19-character string zNum against the text representation |
| 20267 | ** value 2^63: 9223372036854775808. Return negative, zero, or positive |
| 20268 | ** if zNum is less than, equal to, or greater than the string. |
| 20269 | ** Note that zNum must contain exactly 19 characters. |
| 20270 | ** |
| 20271 | ** Unlike memcmp() this routine is guaranteed to return the difference |
| 20272 | ** in the values of the last digit if the only difference is in the |
| 20273 | ** last digit. So, for example, |
| 20274 | ** |
| 20275 | ** compare2pow63("9223372036854775800", 1) |
| 20276 | ** |
| 20277 | ** will return -8. |
| 20278 | */ |
| 20279 | static int compare2pow63(const char *zNum, int incr){ |
| 20280 | int c = 0; |
| 20281 | int i; |
| 20282 | /* 012345678901234567 */ |
| 20283 | const char *pow63 = "922337203685477580"; |
| 20284 | for(i=0; c==0 && i<18; i++){ |
| 20285 | c = (zNum[i*incr]-pow63[i])*10; |
| 20286 | } |
| 20287 | if( c==0 ){ |
| 20288 | c = zNum[18*incr] - '8'; |
| 20289 | testcase( c==(-1) ); |
| 20290 | testcase( c==0 ); |
| 20291 | testcase( c==(+1) ); |
| 20292 | } |
| 20293 | return c; |
| 20294 | } |
| 20295 | |
| 20296 | |
| 20297 | /* |
| 20298 | ** Convert zNum to a 64-bit signed integer and write |
| 20299 | ** the value of the integer into *pNum. |
| 20300 | ** If zNum is exactly 9223372036854665808, return 2. |
| 20301 | ** This is a special case as the context will determine |
| 20302 | ** if it is too big (used as a negative). |
| 20303 | ** If zNum is not an integer or is an integer that |
| 20304 | ** is too large to be expressed with 64 bits, |
| 20305 | ** then return 1. Otherwise return 0. |
| 20306 | ** |
| 20307 | ** length is the number of bytes in the string (bytes, not characters). |
| 20308 | ** The string is not necessarily zero-terminated. The encoding is |
| 20309 | ** given by enc. |
| 20310 | */ |
| 20311 | SQLITE_PRIVATE int sqlite3Atoi64(const char *zNum, i64 *pNum, int length, u8 enc){ |
| 20312 | int incr = (enc==SQLITE_UTF8?1:2); |
| 20313 | i64 v = 0; |
| 20314 | int neg = 0; /* assume positive */ |
| 20315 | int i; |
| 20316 | int c = 0; |
| 20317 | const char *zStart; |
| 20318 | const char *zEnd = zNum + length; |
| 20319 | if( enc==SQLITE_UTF16BE ) zNum++; |
| 20320 | while( zNum<zEnd && sqlite3Isspace(*zNum) ) zNum+=incr; |
| 20321 | if( zNum>=zEnd ) goto do_atoi_calc; |
| 20322 | if( *zNum=='-' ){ |
| 20323 | neg = 1; |
| 20324 | zNum+=incr; |
| 20325 | }else if( *zNum=='+' ){ |
| 20326 | zNum+=incr; |
| 20327 | } |
| 20328 | do_atoi_calc: |
| 20329 | zStart = zNum; |
| 20330 | while( zNum<zEnd && zNum[0]=='0' ){ zNum+=incr; } /* Skip leading zeros. */ |
| 20331 | for(i=0; &zNum[i]<zEnd && (c=zNum[i])>='0' && c<='9'; i+=incr){ |
| 20332 | v = v*10 + c - '0'; |
| 20333 | } |
| 20334 | *pNum = neg ? -v : v; |
| 20335 | testcase( i==18 ); |
| 20336 | testcase( i==19 ); |
| 20337 | testcase( i==20 ); |
| 20338 | if( (c!=0 && &zNum[i]<zEnd) || (i==0 && zStart==zNum) || i>19*incr ){ |
| 20339 | /* zNum is empty or contains non-numeric text or is longer |
| 20340 | ** than 19 digits (thus guaranteeing that it is too large) */ |
| 20341 | return 1; |
| 20342 | }else if( i<19*incr ){ |
| 20343 | /* Less than 19 digits, so we know that it fits in 64 bits */ |
| 20344 | return 0; |
| 20345 | }else{ |
| 20346 | /* 19-digit numbers must be no larger than 9223372036854775807 if positive |
| 20347 | ** or 9223372036854775808 if negative. Note that 9223372036854665808 |
| 20348 | ** is 2^63. Return 1 if to large */ |
| 20349 | c=compare2pow63(zNum, incr); |
| 20350 | if( c==0 && neg==0 ) return 2; /* too big, exactly 9223372036854665808 */ |
| 20351 | return c<neg ? 0 : 1; |
| 20352 | } |
| 20353 | } |
| 20354 | |
| 20355 | /* |
| 20356 | ** If zNum represents an integer that will fit in 32-bits, then set |
| @@ -54149,17 +54105,13 @@ | |
| 54105 | return pMem->u.i; |
| 54106 | }else if( flags & MEM_Real ){ |
| 54107 | return doubleToInt64(pMem->r); |
| 54108 | }else if( flags & (MEM_Str|MEM_Blob) ){ |
| 54109 | i64 value; |
| 54110 | assert( pMem->z || pMem->n==0 ); |
| 54111 | testcase( pMem->z==0 ); |
| 54112 | sqlite3Atoi64(pMem->z, &value, pMem->n, pMem->enc); |
| 54113 | return value; |
| 54114 | }else{ |
| 54115 | return 0; |
| 54116 | } |
| 54117 | } |
| @@ -54185,11 +54137,11 @@ | |
| 54137 | || sqlite3VdbeMemNulTerminate(pMem) ){ |
| 54138 | /* (double)0 In case of SQLITE_OMIT_FLOATING_POINT... */ |
| 54139 | return (double)0; |
| 54140 | } |
| 54141 | assert( pMem->z ); |
| 54142 | sqlite3AtoF(pMem->z, &val, pMem->n, SQLITE_UTF8); |
| 54143 | return val; |
| 54144 | }else{ |
| 54145 | /* (double)0 In case of SQLITE_OMIT_FLOATING_POINT... */ |
| 54146 | return (double)0; |
| 54147 | } |
| @@ -54258,25 +54210,23 @@ | |
| 54210 | ** Every effort is made to force the conversion, even if the input |
| 54211 | ** is a string that does not look completely like a number. Convert |
| 54212 | ** as much of the string as we can and ignore the rest. |
| 54213 | */ |
| 54214 | SQLITE_PRIVATE int sqlite3VdbeMemNumerify(Mem *pMem){ |
| 54215 | if( (pMem->flags & (MEM_Int|MEM_Real|MEM_Null))==0 ){ |
| 54216 | assert( (pMem->flags & (MEM_Blob|MEM_Str))!=0 ); |
| 54217 | assert( pMem->db==0 || sqlite3_mutex_held(pMem->db->mutex) ); |
| 54218 | if( 0==sqlite3Atoi64(pMem->z, &pMem->u.i, pMem->n, pMem->enc) ){ |
| 54219 | MemSetTypeFlag(pMem, MEM_Int); |
| 54220 | }else{ |
| 54221 | pMem->r = sqlite3VdbeRealValue(pMem); |
| 54222 | MemSetTypeFlag(pMem, MEM_Real); |
| 54223 | sqlite3VdbeIntegerAffinity(pMem); |
| 54224 | } |
| 54225 | } |
| 54226 | assert( (pMem->flags & (MEM_Int|MEM_Real|MEM_Null))!=0 ); |
| 54227 | pMem->flags &= ~(MEM_Str|MEM_Blob); |
| 54228 | return SQLITE_OK; |
| 54229 | } |
| 54230 | |
| 54231 | /* |
| 54232 | ** Delete any previous value and set the value stored in *pMem to NULL. |
| @@ -54815,10 +54765,12 @@ | |
| 54765 | sqlite3_value **ppVal /* Write the new value here */ |
| 54766 | ){ |
| 54767 | int op; |
| 54768 | char *zVal = 0; |
| 54769 | sqlite3_value *pVal = 0; |
| 54770 | int negInt = 1; |
| 54771 | const char *zNeg = ""; |
| 54772 | |
| 54773 | if( !pExpr ){ |
| 54774 | *ppVal = 0; |
| 54775 | return SQLITE_OK; |
| 54776 | } |
| @@ -54831,35 +54783,50 @@ | |
| 54783 | #ifdef SQLITE_ENABLE_STAT2 |
| 54784 | if( op==TK_REGISTER ) op = pExpr->op2; |
| 54785 | #else |
| 54786 | if( NEVER(op==TK_REGISTER) ) op = pExpr->op2; |
| 54787 | #endif |
| 54788 | |
| 54789 | /* Handle negative integers in a single step. This is needed in the |
| 54790 | ** case when the value is -9223372036854775808. |
| 54791 | */ |
| 54792 | if( op==TK_UMINUS |
| 54793 | && (pExpr->pLeft->op==TK_INTEGER || pExpr->pLeft->op==TK_FLOAT) ){ |
| 54794 | pExpr = pExpr->pLeft; |
| 54795 | op = pExpr->op; |
| 54796 | negInt = -1; |
| 54797 | zNeg = "-"; |
| 54798 | } |
| 54799 | |
| 54800 | if( op==TK_STRING || op==TK_FLOAT || op==TK_INTEGER ){ |
| 54801 | pVal = sqlite3ValueNew(db); |
| 54802 | if( pVal==0 ) goto no_mem; |
| 54803 | if( ExprHasProperty(pExpr, EP_IntValue) ){ |
| 54804 | sqlite3VdbeMemSetInt64(pVal, (i64)pExpr->u.iValue*negInt); |
| 54805 | }else{ |
| 54806 | zVal = sqlite3MPrintf(db, "%s%s", zNeg, pExpr->u.zToken); |
| 54807 | if( zVal==0 ) goto no_mem; |
| 54808 | sqlite3ValueSetStr(pVal, -1, zVal, SQLITE_UTF8, SQLITE_DYNAMIC); |
| 54809 | if( op==TK_FLOAT ) pVal->type = SQLITE_FLOAT; |
| 54810 | } |
| 54811 | if( (op==TK_INTEGER || op==TK_FLOAT ) && affinity==SQLITE_AFF_NONE ){ |
| 54812 | sqlite3ValueApplyAffinity(pVal, SQLITE_AFF_NUMERIC, SQLITE_UTF8); |
| 54813 | }else{ |
| 54814 | sqlite3ValueApplyAffinity(pVal, affinity, SQLITE_UTF8); |
| 54815 | } |
| 54816 | if( pVal->flags & (MEM_Int|MEM_Real) ) pVal->flags &= ~MEM_Str; |
| 54817 | if( enc!=SQLITE_UTF8 ){ |
| 54818 | sqlite3VdbeChangeEncoding(pVal, enc); |
| 54819 | } |
| 54820 | }else if( op==TK_UMINUS ) { |
| 54821 | /* This branch happens for multiple negative signs. Ex: -(-5) */ |
| 54822 | if( SQLITE_OK==sqlite3ValueFromExpr(db,pExpr->pLeft,enc,affinity,&pVal) ){ |
| 54823 | sqlite3VdbeMemNumerify(pVal); |
| 54824 | pVal->u.i = -1 * pVal->u.i; |
| 54825 | /* (double)-1 In case of SQLITE_OMIT_FLOATING_POINT... */ |
| 54826 | pVal->r = (double)-1 * pVal->r; |
| 54827 | sqlite3ValueApplyAffinity(pVal, affinity, enc); |
| 54828 | } |
| 54829 | } |
| 54830 | #ifndef SQLITE_OMIT_BLOB_LITERAL |
| 54831 | else if( op==TK_BLOB ){ |
| 54832 | int nVal; |
| @@ -59770,35 +59737,21 @@ | |
| 59737 | ** looks like a number, convert it into a number. If it does not |
| 59738 | ** look like a number, leave it alone. |
| 59739 | */ |
| 59740 | static void applyNumericAffinity(Mem *pRec){ |
| 59741 | if( (pRec->flags & (MEM_Real|MEM_Int))==0 ){ |
| 59742 | double rValue; |
| 59743 | i64 iValue; |
| 59744 | u8 enc = pRec->enc; |
| 59745 | if( (pRec->flags&MEM_Str)==0 ) return; |
| 59746 | if( sqlite3AtoF(pRec->z, &rValue, pRec->n, enc)==0 ) return; |
| 59747 | if( 0==sqlite3Atoi64(pRec->z, &iValue, pRec->n, enc) ){ |
| 59748 | pRec->u.i = iValue; |
| 59749 | pRec->flags |= MEM_Int; |
| 59750 | }else{ |
| 59751 | pRec->r = rValue; |
| 59752 | pRec->flags |= MEM_Real; |
| 59753 | } |
| 59754 | } |
| 59755 | } |
| 59756 | |
| 59757 | /* |
| @@ -61580,11 +61533,10 @@ | |
| 61533 | ** integers, for space efficiency, but after extraction we want them |
| 61534 | ** to have only a real value. |
| 61535 | */ |
| 61536 | case OP_RealAffinity: { /* in1 */ |
| 61537 | pIn1 = &aMem[pOp->p1]; |
| 61538 | if( pIn1->flags & MEM_Int ){ |
| 61539 | sqlite3VdbeMemRealify(pIn1); |
| 61540 | } |
| 61541 | break; |
| 61542 | } |
| @@ -61623,11 +61575,10 @@ | |
| 61575 | ** |
| 61576 | ** A NULL value is not changed by this routine. It remains NULL. |
| 61577 | */ |
| 61578 | case OP_ToBlob: { /* same as TK_TO_BLOB, in1 */ |
| 61579 | pIn1 = &aMem[pOp->p1]; |
| 61580 | if( pIn1->flags & MEM_Null ) break; |
| 61581 | if( (pIn1->flags & MEM_Blob)==0 ){ |
| 61582 | applyAffinity(pIn1, SQLITE_AFF_TEXT, encoding); |
| 61583 | assert( pIn1->flags & MEM_Str || db->mallocFailed ); |
| 61584 | MemSetTypeFlag(pIn1, MEM_Blob); |
| @@ -61648,14 +61599,11 @@ | |
| 61599 | ** |
| 61600 | ** A NULL value is not changed by this routine. It remains NULL. |
| 61601 | */ |
| 61602 | case OP_ToNumeric: { /* same as TK_TO_NUMERIC, in1 */ |
| 61603 | pIn1 = &aMem[pOp->p1]; |
| 61604 | sqlite3VdbeMemNumerify(pIn1); |
| 61605 | break; |
| 61606 | } |
| 61607 | #endif /* SQLITE_OMIT_CAST */ |
| 61608 | |
| 61609 | /* Opcode: ToInt P1 * * * * |
| @@ -61667,11 +61615,10 @@ | |
| 61615 | ** |
| 61616 | ** A NULL value is not changed by this routine. It remains NULL. |
| 61617 | */ |
| 61618 | case OP_ToInt: { /* same as TK_TO_INT, in1 */ |
| 61619 | pIn1 = &aMem[pOp->p1]; |
| 61620 | if( (pIn1->flags & MEM_Null)==0 ){ |
| 61621 | sqlite3VdbeMemIntegerify(pIn1); |
| 61622 | } |
| 61623 | break; |
| 61624 | } |
| @@ -61781,12 +61728,10 @@ | |
| 61728 | u16 flags3; /* Copy of initial value of pIn3->flags */ |
| 61729 | #endif /* local variables moved into u.ai */ |
| 61730 | |
| 61731 | pIn1 = &aMem[pOp->p1]; |
| 61732 | pIn3 = &aMem[pOp->p3]; |
| 61733 | u.ai.flags1 = pIn1->flags; |
| 61734 | u.ai.flags3 = pIn3->flags; |
| 61735 | if( (pIn1->flags | pIn3->flags)&MEM_Null ){ |
| 61736 | /* One or both operands are NULL */ |
| 61737 | if( pOp->p5 & SQLITE_NULLEQ ){ |
| @@ -62415,11 +62360,10 @@ | |
| 62360 | assert( u.an.zAffinity[pOp->p2]==0 ); |
| 62361 | pIn1 = &aMem[pOp->p1]; |
| 62362 | while( (u.an.cAff = *(u.an.zAffinity++))!=0 ){ |
| 62363 | assert( pIn1 <= &p->aMem[p->nMem] ); |
| 62364 | assert( memIsValid(pIn1) ); |
| 62365 | ExpandBlob(pIn1); |
| 62366 | applyAffinity(pIn1, u.an.cAff, encoding); |
| 62367 | pIn1++; |
| 62368 | } |
| 62369 | break; |
| @@ -62495,11 +62439,10 @@ | |
| 62439 | ** out how much space is required for the new record. |
| 62440 | */ |
| 62441 | for(u.ao.pRec=u.ao.pData0; u.ao.pRec<=u.ao.pLast; u.ao.pRec++){ |
| 62442 | assert( memIsValid(u.ao.pRec) ); |
| 62443 | if( u.ao.zAffinity ){ |
| 62444 | applyAffinity(u.ao.pRec, u.ao.zAffinity[u.ao.pRec-u.ao.pData0], encoding); |
| 62445 | } |
| 62446 | if( u.ao.pRec->flags&MEM_Zero && u.ao.pRec->n>0 ){ |
| 62447 | sqlite3VdbeMemExpandBlob(u.ao.pRec); |
| 62448 | } |
| @@ -68880,11 +68823,11 @@ | |
| 68823 | pExpr->iColumn = (ynVar)(++pParse->nVar); |
| 68824 | }else if( z[0]=='?' ){ |
| 68825 | /* Wildcard of the form "?nnn". Convert "nnn" to an integer and |
| 68826 | ** use it as the variable number */ |
| 68827 | i64 i; |
| 68828 | int bOk = 0==sqlite3Atoi64(&z[1], &i, sqlite3Strlen30(&z[1]), SQLITE_UTF8); |
| 68829 | pExpr->iColumn = (ynVar)i; |
| 68830 | testcase( i==0 ); |
| 68831 | testcase( i==1 ); |
| 68832 | testcase( i==db->aLimit[SQLITE_LIMIT_VARIABLE_NUMBER]-1 ); |
| 68833 | testcase( i==db->aLimit[SQLITE_LIMIT_VARIABLE_NUMBER] ); |
| @@ -70243,11 +70186,11 @@ | |
| 70186 | */ |
| 70187 | static void codeReal(Vdbe *v, const char *z, int negateFlag, int iMem){ |
| 70188 | if( ALWAYS(z!=0) ){ |
| 70189 | double value; |
| 70190 | char *zV; |
| 70191 | sqlite3AtoF(z, &value, sqlite3Strlen30(z), SQLITE_UTF8); |
| 70192 | assert( !sqlite3IsNaN(value) ); /* The new AtoF never returns NaN */ |
| 70193 | if( negateFlag ) value = -value; |
| 70194 | zV = dup8bytes(v, (char*)&value); |
| 70195 | sqlite3VdbeAddOp4(v, OP_Real, 0, iMem, 0, zV, P4_REAL); |
| 70196 | } |
| @@ -70257,28 +70200,27 @@ | |
| 70200 | |
| 70201 | /* |
| 70202 | ** Generate an instruction that will put the integer describe by |
| 70203 | ** text z[0..n-1] into register iMem. |
| 70204 | ** |
| 70205 | ** Expr.u.zToken is always UTF8 and zero-terminated. |
| 70206 | */ |
| 70207 | static void codeInteger(Parse *pParse, Expr *pExpr, int negFlag, int iMem){ |
| 70208 | Vdbe *v = pParse->pVdbe; |
| 70209 | if( pExpr->flags & EP_IntValue ){ |
| 70210 | int i = pExpr->u.iValue; |
| 70211 | if( negFlag ) i = -i; |
| 70212 | sqlite3VdbeAddOp2(v, OP_Integer, i, iMem); |
| 70213 | }else{ |
| 70214 | int c; |
| 70215 | i64 value; |
| 70216 | const char *z = pExpr->u.zToken; |
| 70217 | assert( z!=0 ); |
| 70218 | c = sqlite3Atoi64(z, &value, sqlite3Strlen30(z), SQLITE_UTF8); |
| 70219 | if( c==0 || (c==2 && negFlag) ){ |
| 70220 | char *zV; |
| 70221 | if( negFlag ){ value = -value; } |
| 70222 | zV = dup8bytes(v, (char*)&value); |
| 70223 | sqlite3VdbeAddOp4(v, OP_Int64, 0, iMem, 0, zV, P4_INT64); |
| 70224 | }else{ |
| 70225 | #ifdef SQLITE_OMIT_FLOATING_POINT |
| 70226 | sqlite3ErrorMsg(pParse, "oversized integer: %s%s", negFlag ? "-" : "", z); |
| @@ -79414,11 +79356,11 @@ | |
| 79356 | zBuf = sqlite3_mprintf("%.*f",n,r); |
| 79357 | if( zBuf==0 ){ |
| 79358 | sqlite3_result_error_nomem(context); |
| 79359 | return; |
| 79360 | } |
| 79361 | sqlite3AtoF(zBuf, &r, sqlite3Strlen30(zBuf), SQLITE_UTF8); |
| 79362 | sqlite3_free(zBuf); |
| 79363 | } |
| 79364 | sqlite3_result_double(context, r); |
| 79365 | } |
| 79366 | #endif |
| @@ -85472,11 +85414,11 @@ | |
| 85414 | */ |
| 85415 | if( sqlite3StrICmp(zLeft,"journal_size_limit")==0 ){ |
| 85416 | Pager *pPager = sqlite3BtreePager(pDb->pBt); |
| 85417 | i64 iLimit = -2; |
| 85418 | if( zRight ){ |
| 85419 | sqlite3Atoi64(zRight, &iLimit, 1000000, SQLITE_UTF8); |
| 85420 | if( iLimit<-1 ) iLimit = -1; |
| 85421 | } |
| 85422 | iLimit = sqlite3PagerJournalSizeLimit(pPager, iLimit); |
| 85423 | returnSingleInt(pParse, "journal_size_limit", iLimit); |
| 85424 | }else |
| @@ -96380,11 +96322,12 @@ | |
| 96322 | |
| 96323 | /* |
| 96324 | ** Required because bestIndex() is called by bestOrClauseIndex() |
| 96325 | */ |
| 96326 | static void bestIndex( |
| 96327 | Parse*, WhereClause*, struct SrcList_item*, |
| 96328 | Bitmask, Bitmask, ExprList*, WhereCost*); |
| 96329 | |
| 96330 | /* |
| 96331 | ** This routine attempts to find an scanning strategy that can be used |
| 96332 | ** to optimize an 'OR' expression that is part of a WHERE clause. |
| 96333 | ** |
| @@ -96393,11 +96336,12 @@ | |
| 96336 | */ |
| 96337 | static void bestOrClauseIndex( |
| 96338 | Parse *pParse, /* The parsing context */ |
| 96339 | WhereClause *pWC, /* The WHERE clause */ |
| 96340 | struct SrcList_item *pSrc, /* The FROM clause term to search */ |
| 96341 | Bitmask notReady, /* Mask of cursors not available for indexing */ |
| 96342 | Bitmask notValid, /* Cursors not available for any purpose */ |
| 96343 | ExprList *pOrderBy, /* The ORDER BY clause */ |
| 96344 | WhereCost *pCost /* Lowest cost query plan */ |
| 96345 | ){ |
| 96346 | #ifndef SQLITE_OMIT_OR_OPTIMIZATION |
| 96347 | const int iCur = pSrc->iCursor; /* The cursor of the table to be accessed */ |
| @@ -96429,19 +96373,19 @@ | |
| 96373 | WHERETRACE(("... Multi-index OR testing for term %d of %d....\n", |
| 96374 | (pOrTerm - pOrWC->a), (pTerm - pWC->a) |
| 96375 | )); |
| 96376 | if( pOrTerm->eOperator==WO_AND ){ |
| 96377 | WhereClause *pAndWC = &pOrTerm->u.pAndInfo->wc; |
| 96378 | bestIndex(pParse, pAndWC, pSrc, notReady, notValid, 0, &sTermCost); |
| 96379 | }else if( pOrTerm->leftCursor==iCur ){ |
| 96380 | WhereClause tempWC; |
| 96381 | tempWC.pParse = pWC->pParse; |
| 96382 | tempWC.pMaskSet = pWC->pMaskSet; |
| 96383 | tempWC.op = TK_AND; |
| 96384 | tempWC.a = pOrTerm; |
| 96385 | tempWC.nTerm = 1; |
| 96386 | bestIndex(pParse, &tempWC, pSrc, notReady, notValid, 0, &sTermCost); |
| 96387 | }else{ |
| 96388 | continue; |
| 96389 | } |
| 96390 | rTotal += sTermCost.rCost; |
| 96391 | nRow += sTermCost.nRow; |
| @@ -96884,11 +96828,12 @@ | |
| 96828 | */ |
| 96829 | static void bestVirtualIndex( |
| 96830 | Parse *pParse, /* The parsing context */ |
| 96831 | WhereClause *pWC, /* The WHERE clause */ |
| 96832 | struct SrcList_item *pSrc, /* The FROM clause term to search */ |
| 96833 | Bitmask notReady, /* Mask of cursors not available for index */ |
| 96834 | Bitmask notValid, /* Cursors not valid for any purpose */ |
| 96835 | ExprList *pOrderBy, /* The order by clause */ |
| 96836 | WhereCost *pCost, /* Lowest cost query plan */ |
| 96837 | sqlite3_index_info **ppIdxInfo /* Index information passed to xBestIndex */ |
| 96838 | ){ |
| 96839 | Table *pTab = pSrc->pTab; |
| @@ -97014,11 +96959,11 @@ | |
| 96959 | pIdxInfo->nOrderBy = nOrderBy; |
| 96960 | |
| 96961 | /* Try to find a more efficient access pattern by using multiple indexes |
| 96962 | ** to optimize an OR expression within the WHERE clause. |
| 96963 | */ |
| 96964 | bestOrClauseIndex(pParse, pWC, pSrc, notReady, notValid, pOrderBy, pCost); |
| 96965 | } |
| 96966 | #endif /* SQLITE_OMIT_VIRTUALTABLE */ |
| 96967 | |
| 96968 | /* |
| 96969 | ** Argument pIdx is a pointer to an index structure that has an array of |
| @@ -97295,11 +97240,12 @@ | |
| 97240 | */ |
| 97241 | static void bestBtreeIndex( |
| 97242 | Parse *pParse, /* The parsing context */ |
| 97243 | WhereClause *pWC, /* The WHERE clause */ |
| 97244 | struct SrcList_item *pSrc, /* The FROM clause term to search */ |
| 97245 | Bitmask notReady, /* Mask of cursors not available for indexing */ |
| 97246 | Bitmask notValid, /* Cursors not available for any purpose */ |
| 97247 | ExprList *pOrderBy, /* The ORDER BY clause */ |
| 97248 | WhereCost *pCost /* Lowest cost query plan */ |
| 97249 | ){ |
| 97250 | int iCur = pSrc->iCursor; /* The cursor of the table to be accessed */ |
| 97251 | Index *pProbe; /* An index we are evaluating */ |
| @@ -97557,29 +97503,29 @@ | |
| 97503 | ** of output rows, adjust the nRow value accordingly. This only |
| 97504 | ** matters if the current index is the least costly, so do not bother |
| 97505 | ** with this step if we already know this index will not be chosen. |
| 97506 | ** Also, never reduce the output row count below 2 using this step. |
| 97507 | ** |
| 97508 | ** It is critical that the notValid mask be used here instead of |
| 97509 | ** the notReady mask. When computing an "optimal" index, the notReady |
| 97510 | ** mask will only have one bit set - the bit for the current table. |
| 97511 | ** The notValid mask, on the other hand, always has all bits set for |
| 97512 | ** tables that are not in outer loops. If notReady is used here instead |
| 97513 | ** of notValid, then a optimal index that depends on inner joins loops |
| 97514 | ** might be selected even when there exists an optimal index that has |
| 97515 | ** no such dependency. |
| 97516 | */ |
| 97517 | if( nRow>2 && cost<=pCost->rCost ){ |
| 97518 | int k; /* Loop counter */ |
| 97519 | int nSkipEq = nEq; /* Number of == constraints to skip */ |
| 97520 | int nSkipRange = nBound; /* Number of < constraints to skip */ |
| 97521 | Bitmask thisTab; /* Bitmap for pSrc */ |
| 97522 | |
| 97523 | thisTab = getMask(pWC->pMaskSet, iCur); |
| 97524 | for(pTerm=pWC->a, k=pWC->nTerm; nRow>2 && k; k--, pTerm++){ |
| 97525 | if( pTerm->wtFlags & TERM_VIRTUAL ) continue; |
| 97526 | if( (pTerm->prereqAll & notValid)!=thisTab ) continue; |
| 97527 | if( pTerm->eOperator & (WO_EQ|WO_IN|WO_ISNULL) ){ |
| 97528 | if( nSkipEq ){ |
| 97529 | /* Ignore the first nEq equality matches since the index |
| 97530 | ** has already accounted for these */ |
| 97531 | nSkipEq--; |
| @@ -97657,11 +97603,11 @@ | |
| 97603 | WHERETRACE(("best index is: %s\n", |
| 97604 | ((pCost->plan.wsFlags & WHERE_NOT_FULLSCAN)==0 ? "none" : |
| 97605 | pCost->plan.u.pIdx ? pCost->plan.u.pIdx->zName : "ipk") |
| 97606 | )); |
| 97607 | |
| 97608 | bestOrClauseIndex(pParse, pWC, pSrc, notReady, notValid, pOrderBy, pCost); |
| 97609 | bestAutomaticIndex(pParse, pWC, pSrc, notReady, pCost); |
| 97610 | pCost->plan.wsFlags |= eqTermMask; |
| 97611 | } |
| 97612 | |
| 97613 | /* |
| @@ -97672,26 +97618,27 @@ | |
| 97618 | */ |
| 97619 | static void bestIndex( |
| 97620 | Parse *pParse, /* The parsing context */ |
| 97621 | WhereClause *pWC, /* The WHERE clause */ |
| 97622 | struct SrcList_item *pSrc, /* The FROM clause term to search */ |
| 97623 | Bitmask notReady, /* Mask of cursors not available for indexing */ |
| 97624 | Bitmask notValid, /* Cursors not available for any purpose */ |
| 97625 | ExprList *pOrderBy, /* The ORDER BY clause */ |
| 97626 | WhereCost *pCost /* Lowest cost query plan */ |
| 97627 | ){ |
| 97628 | #ifndef SQLITE_OMIT_VIRTUALTABLE |
| 97629 | if( IsVirtual(pSrc->pTab) ){ |
| 97630 | sqlite3_index_info *p = 0; |
| 97631 | bestVirtualIndex(pParse, pWC, pSrc, notReady, notValid, pOrderBy, pCost,&p); |
| 97632 | if( p->needToFreeIdxStr ){ |
| 97633 | sqlite3_free(p->idxStr); |
| 97634 | } |
| 97635 | sqlite3DbFree(pParse->db, p); |
| 97636 | }else |
| 97637 | #endif |
| 97638 | { |
| 97639 | bestBtreeIndex(pParse, pWC, pSrc, notReady, notValid, pOrderBy, pCost); |
| 97640 | } |
| 97641 | } |
| 97642 | |
| 97643 | /* |
| 97644 | ** Disable a term in the WHERE clause. Except, do not disable the term |
| @@ -98902,14 +98849,20 @@ | |
| 98849 | ** by waiting for other tables to run first. This "optimal" test works |
| 98850 | ** by first assuming that the FROM clause is on the inner loop and finding |
| 98851 | ** its query plan, then checking to see if that query plan uses any |
| 98852 | ** other FROM clause terms that are notReady. If no notReady terms are |
| 98853 | ** used then the "optimal" query plan works. |
| 98854 | ** |
| 98855 | ** Note that the WhereCost.nRow parameter for an optimal scan might |
| 98856 | ** not be as small as it would be if the table really were the innermost |
| 98857 | ** join. The nRow value can be reduced by WHERE clause constraints |
| 98858 | ** that do not use indices. But this nRow reduction only happens if the |
| 98859 | ** table really is the innermost join. |
| 98860 | ** |
| 98861 | ** The second loop iteration is only performed if no optimal scan |
| 98862 | ** strategies were found by the first iteration. This second iteration |
| 98863 | ** is used to search for the lowest cost scan overall. |
| 98864 | ** |
| 98865 | ** Previous versions of SQLite performed only the second iteration - |
| 98866 | ** the next outermost loop was always that with the lowest overall |
| 98867 | ** cost. However, this meant that SQLite could select the wrong plan |
| 98868 | ** for scripts such as the following: |
| @@ -98925,11 +98878,11 @@ | |
| 98878 | ** algorithm may choose to use t2 for the outer loop, which is a much |
| 98879 | ** costlier approach. |
| 98880 | */ |
| 98881 | nUnconstrained = 0; |
| 98882 | notIndexed = 0; |
| 98883 | for(isOptimal=(iFrom<nTabList-1); isOptimal>=0 && bestJ<0; isOptimal--){ |
| 98884 | Bitmask mask; /* Mask of tables not yet ready */ |
| 98885 | for(j=iFrom, pTabItem=&pTabList->a[j]; j<nTabList; j++, pTabItem++){ |
| 98886 | int doNotReorder; /* True if this table should not be reordered */ |
| 98887 | WhereCost sCost; /* Cost information from best[Virtual]Index() */ |
| 98888 | ExprList *pOrderBy; /* ORDER BY clause for index to optimize */ |
| @@ -98947,15 +98900,17 @@ | |
| 98900 | |
| 98901 | assert( pTabItem->pTab ); |
| 98902 | #ifndef SQLITE_OMIT_VIRTUALTABLE |
| 98903 | if( IsVirtual(pTabItem->pTab) ){ |
| 98904 | sqlite3_index_info **pp = &pWInfo->a[j].pIdxInfo; |
| 98905 | bestVirtualIndex(pParse, pWC, pTabItem, mask, notReady, pOrderBy, |
| 98906 | &sCost, pp); |
| 98907 | }else |
| 98908 | #endif |
| 98909 | { |
| 98910 | bestBtreeIndex(pParse, pWC, pTabItem, mask, notReady, pOrderBy, |
| 98911 | &sCost); |
| 98912 | } |
| 98913 | assert( isOptimal || (sCost.used¬Ready)==0 ); |
| 98914 | |
| 98915 | /* If an INDEXED BY clause is present, then the plan must use that |
| 98916 | ** index if it uses any index at all */ |
| 98917 |
+1
-1
| --- src/sqlite3.h | ||
| +++ src/sqlite3.h | ||
| @@ -107,11 +107,11 @@ | ||
| 107 | 107 | ** [sqlite3_libversion_number()], [sqlite3_sourceid()], |
| 108 | 108 | ** [sqlite_version()] and [sqlite_source_id()]. |
| 109 | 109 | */ |
| 110 | 110 | #define SQLITE_VERSION "3.7.3" |
| 111 | 111 | #define SQLITE_VERSION_NUMBER 3007003 |
| 112 | -#define SQLITE_SOURCE_ID "2010-09-29 23:09:23 1ef0dc9328f47506cb2dcd142150e96cb4755216" | |
| 112 | +#define SQLITE_SOURCE_ID "2010-10-04 23:55:51 ece641eb8951c6314cedbdb3243f91cb199c3239" | |
| 113 | 113 | |
| 114 | 114 | /* |
| 115 | 115 | ** CAPI3REF: Run-Time Library Version Numbers |
| 116 | 116 | ** KEYWORDS: sqlite3_version, sqlite3_sourceid |
| 117 | 117 | ** |
| 118 | 118 |
| --- src/sqlite3.h | |
| +++ src/sqlite3.h | |
| @@ -107,11 +107,11 @@ | |
| 107 | ** [sqlite3_libversion_number()], [sqlite3_sourceid()], |
| 108 | ** [sqlite_version()] and [sqlite_source_id()]. |
| 109 | */ |
| 110 | #define SQLITE_VERSION "3.7.3" |
| 111 | #define SQLITE_VERSION_NUMBER 3007003 |
| 112 | #define SQLITE_SOURCE_ID "2010-09-29 23:09:23 1ef0dc9328f47506cb2dcd142150e96cb4755216" |
| 113 | |
| 114 | /* |
| 115 | ** CAPI3REF: Run-Time Library Version Numbers |
| 116 | ** KEYWORDS: sqlite3_version, sqlite3_sourceid |
| 117 | ** |
| 118 |
| --- src/sqlite3.h | |
| +++ src/sqlite3.h | |
| @@ -107,11 +107,11 @@ | |
| 107 | ** [sqlite3_libversion_number()], [sqlite3_sourceid()], |
| 108 | ** [sqlite_version()] and [sqlite_source_id()]. |
| 109 | */ |
| 110 | #define SQLITE_VERSION "3.7.3" |
| 111 | #define SQLITE_VERSION_NUMBER 3007003 |
| 112 | #define SQLITE_SOURCE_ID "2010-10-04 23:55:51 ece641eb8951c6314cedbdb3243f91cb199c3239" |
| 113 | |
| 114 | /* |
| 115 | ** CAPI3REF: Run-Time Library Version Numbers |
| 116 | ** KEYWORDS: sqlite3_version, sqlite3_sourceid |
| 117 | ** |
| 118 |
+1
-1
| --- src/style.c | ||
| +++ src/style.c | ||
| @@ -199,11 +199,11 @@ | ||
| 199 | 199 | @ media="screen" /> |
| 200 | 200 | @ </head> |
| 201 | 201 | @ <body> |
| 202 | 202 | @ <div class="header"> |
| 203 | 203 | @ <div class="logo"> |
| 204 | -@ <img src="$baseurl/logo" alt="logo"> | |
| 204 | +@ <img src="$baseurl/logo" alt="logo" /> | |
| 205 | 205 | @ </div> |
| 206 | 206 | @ <div class="title"><small>$<project_name></small><br />$<title></div> |
| 207 | 207 | @ <div class="status"><th1> |
| 208 | 208 | @ if {[info exists login]} { |
| 209 | 209 | @ puts "Logged in as $login" |
| 210 | 210 |
| --- src/style.c | |
| +++ src/style.c | |
| @@ -199,11 +199,11 @@ | |
| 199 | @ media="screen" /> |
| 200 | @ </head> |
| 201 | @ <body> |
| 202 | @ <div class="header"> |
| 203 | @ <div class="logo"> |
| 204 | @ <img src="$baseurl/logo" alt="logo"> |
| 205 | @ </div> |
| 206 | @ <div class="title"><small>$<project_name></small><br />$<title></div> |
| 207 | @ <div class="status"><th1> |
| 208 | @ if {[info exists login]} { |
| 209 | @ puts "Logged in as $login" |
| 210 |
| --- src/style.c | |
| +++ src/style.c | |
| @@ -199,11 +199,11 @@ | |
| 199 | @ media="screen" /> |
| 200 | @ </head> |
| 201 | @ <body> |
| 202 | @ <div class="header"> |
| 203 | @ <div class="logo"> |
| 204 | @ <img src="$baseurl/logo" alt="logo" /> |
| 205 | @ </div> |
| 206 | @ <div class="title"><small>$<project_name></small><br />$<title></div> |
| 207 | @ <div class="status"><th1> |
| 208 | @ if {[info exists login]} { |
| 209 | @ puts "Logged in as $login" |
| 210 |
+1
-1
| --- src/style.c | ||
| +++ src/style.c | ||
| @@ -199,11 +199,11 @@ | ||
| 199 | 199 | @ media="screen" /> |
| 200 | 200 | @ </head> |
| 201 | 201 | @ <body> |
| 202 | 202 | @ <div class="header"> |
| 203 | 203 | @ <div class="logo"> |
| 204 | -@ <img src="$baseurl/logo" alt="logo"> | |
| 204 | +@ <img src="$baseurl/logo" alt="logo" /> | |
| 205 | 205 | @ </div> |
| 206 | 206 | @ <div class="title"><small>$<project_name></small><br />$<title></div> |
| 207 | 207 | @ <div class="status"><th1> |
| 208 | 208 | @ if {[info exists login]} { |
| 209 | 209 | @ puts "Logged in as $login" |
| 210 | 210 |
| --- src/style.c | |
| +++ src/style.c | |
| @@ -199,11 +199,11 @@ | |
| 199 | @ media="screen" /> |
| 200 | @ </head> |
| 201 | @ <body> |
| 202 | @ <div class="header"> |
| 203 | @ <div class="logo"> |
| 204 | @ <img src="$baseurl/logo" alt="logo"> |
| 205 | @ </div> |
| 206 | @ <div class="title"><small>$<project_name></small><br />$<title></div> |
| 207 | @ <div class="status"><th1> |
| 208 | @ if {[info exists login]} { |
| 209 | @ puts "Logged in as $login" |
| 210 |
| --- src/style.c | |
| +++ src/style.c | |
| @@ -199,11 +199,11 @@ | |
| 199 | @ media="screen" /> |
| 200 | @ </head> |
| 201 | @ <body> |
| 202 | @ <div class="header"> |
| 203 | @ <div class="logo"> |
| 204 | @ <img src="$baseurl/logo" alt="logo" /> |
| 205 | @ </div> |
| 206 | @ <div class="title"><small>$<project_name></small><br />$<title></div> |
| 207 | @ <div class="status"><th1> |
| 208 | @ if {[info exists login]} { |
| 209 | @ puts "Logged in as $login" |
| 210 |
+16
-8
| --- src/timeline.c | ||
| +++ src/timeline.c | ||
| @@ -165,11 +165,11 @@ | ||
| 165 | 165 | ** 4. User |
| 166 | 166 | ** 5. True if is a leaf |
| 167 | 167 | ** 6. background color |
| 168 | 168 | ** 7. type ("ci", "w", "t") |
| 169 | 169 | ** 8. list of symbolic tags. |
| 170 | -** 9. tagid for ticket or wiki | |
| 170 | +** 9. tagid for ticket or wiki or event | |
| 171 | 171 | ** 10. Short comment to user for repeated tickets and wiki |
| 172 | 172 | */ |
| 173 | 173 | void www_print_timeline( |
| 174 | 174 | Stmt *pQuery, /* Query to implement the timeline */ |
| 175 | 175 | int tmFlags, /* Flags controlling display behavior */ |
| @@ -288,10 +288,12 @@ | ||
| 288 | 288 | @ <span class="timelineLeaf">Closed-Leaf:</span> |
| 289 | 289 | }else{ |
| 290 | 290 | @ <span class="timelineLeaf">Leaf:</span> |
| 291 | 291 | } |
| 292 | 292 | } |
| 293 | + }else if( zType[0]=='e' && tagid ){ | |
| 294 | + hyperlink_to_event_tagid(tagid); | |
| 293 | 295 | }else if( (tmFlags & TIMELINE_ARTID)!=0 ){ |
| 294 | 296 | hyperlink_to_uuid(zUuid); |
| 295 | 297 | } |
| 296 | 298 | db_column_blob(pQuery, commentColumn, &comment); |
| 297 | 299 | if( mxWikiLen>0 && blob_size(&comment)>mxWikiLen ){ |
| @@ -639,11 +641,11 @@ | ||
| 639 | 641 | ** p=RID artifact RID and up to COUNT parents and ancestors |
| 640 | 642 | ** d=RID artifact RID and up to COUNT descendants |
| 641 | 643 | ** t=TAGID show only check-ins with the given tagid |
| 642 | 644 | ** r=TAGID show check-ins related to tagid |
| 643 | 645 | ** u=USER only if belonging to this user |
| 644 | -** y=TYPE 'ci', 'w', 't' | |
| 646 | +** y=TYPE 'ci', 'w', 't', 'e' | |
| 645 | 647 | ** s=TEXT string search (comment and brief) |
| 646 | 648 | ** ng Suppress the graph if present |
| 647 | 649 | ** |
| 648 | 650 | ** p= and d= can appear individually or together. If either p= or d= |
| 649 | 651 | ** appear, then u=, y=, a=, and b= are ignored. |
| @@ -749,22 +751,22 @@ | ||
| 749 | 751 | }else{ |
| 750 | 752 | blob_appendf(&desc, " of check-in [%.10s]", zUuid); |
| 751 | 753 | } |
| 752 | 754 | }else{ |
| 753 | 755 | int n; |
| 754 | - const char *zEType = "event"; | |
| 756 | + const char *zEType = "timeline item"; | |
| 755 | 757 | char *zDate; |
| 756 | 758 | char *zNEntry = mprintf("%d", nEntry); |
| 757 | 759 | url_initialize(&url, "timeline"); |
| 758 | 760 | url_add_parameter(&url, "n", zNEntry); |
| 759 | 761 | if( tagid>0 ){ |
| 760 | - zType = "ci"; | |
| 762 | + if( zType[0]!='e' ) zType = "ci"; | |
| 761 | 763 | blob_appendf(&sql, |
| 762 | 764 | "AND (EXISTS(SELECT 1 FROM tagxref" |
| 763 | 765 | " WHERE tagid=%d AND tagtype>0 AND rid=blob.rid)", tagid); |
| 764 | 766 | |
| 765 | - if( zBrName ){ | |
| 767 | + if( zBrName && zType[0]=='c' ){ | |
| 766 | 768 | /* The next two blob_appendf() calls add SQL that causes checkins that |
| 767 | 769 | ** are not part of the branch which are parents or childen of the branch |
| 768 | 770 | ** to be included in the report. This related check-ins are useful |
| 769 | 771 | ** in helping to visualize what has happened on a quiescent branch |
| 770 | 772 | ** that is infrequently merged with a much more activate branch. |
| @@ -781,10 +783,11 @@ | ||
| 781 | 783 | } |
| 782 | 784 | blob_appendf(&sql, ")"); |
| 783 | 785 | } |
| 784 | 786 | if( (zType[0]=='w' && !g.okRdWiki) |
| 785 | 787 | || (zType[0]=='t' && !g.okRdTkt) |
| 788 | + || (zType[0]=='e' && !g.okRdWiki) | |
| 786 | 789 | || (zType[0]=='c' && !g.okRead) |
| 787 | 790 | ){ |
| 788 | 791 | zType = "all"; |
| 789 | 792 | } |
| 790 | 793 | if( zType[0]=='a' ){ |
| @@ -794,11 +797,11 @@ | ||
| 794 | 797 | if( g.okRead ){ |
| 795 | 798 | blob_appendf(&sql, "%c'ci'", cSep); |
| 796 | 799 | cSep = ','; |
| 797 | 800 | } |
| 798 | 801 | if( g.okRdWiki ){ |
| 799 | - blob_appendf(&sql, "%c'w'", cSep); | |
| 802 | + blob_appendf(&sql, "%c'w','e'", cSep); | |
| 800 | 803 | cSep = ','; |
| 801 | 804 | } |
| 802 | 805 | if( g.okRdTkt ){ |
| 803 | 806 | blob_appendf(&sql, "%c't'", cSep); |
| 804 | 807 | cSep = ','; |
| @@ -812,10 +815,12 @@ | ||
| 812 | 815 | zEType = "checkin"; |
| 813 | 816 | }else if( zType[0]=='w' ){ |
| 814 | 817 | zEType = "wiki edit"; |
| 815 | 818 | }else if( zType[0]=='t' ){ |
| 816 | 819 | zEType = "ticket change"; |
| 820 | + }else if( zType[0]=='e' ){ | |
| 821 | + zEType = "event"; | |
| 817 | 822 | } |
| 818 | 823 | } |
| 819 | 824 | if( zUser ){ |
| 820 | 825 | blob_appendf(&sql, " AND event.user=%Q", zUser); |
| 821 | 826 | url_add_parameter(&url, "u", zUser); |
| @@ -926,16 +931,19 @@ | ||
| 926 | 931 | timeline_submenu(&url, "Checkins Only", "y", "ci", 0); |
| 927 | 932 | } |
| 928 | 933 | if( zType[0]!='t' && g.okRdTkt ){ |
| 929 | 934 | timeline_submenu(&url, "Tickets Only", "y", "t", 0); |
| 930 | 935 | } |
| 936 | + if( zType[0]!='e' && g.okRdWiki ){ | |
| 937 | + timeline_submenu(&url, "Events Only", "y", "e", 0); | |
| 938 | + } | |
| 931 | 939 | } |
| 932 | 940 | if( nEntry>20 ){ |
| 933 | - timeline_submenu(&url, "20 Events", "n", "20", 0); | |
| 941 | + timeline_submenu(&url, "20 Entries", "n", "20", 0); | |
| 934 | 942 | } |
| 935 | 943 | if( nEntry<200 ){ |
| 936 | - timeline_submenu(&url, "200 Events", "n", "200", 0); | |
| 944 | + timeline_submenu(&url, "200 Entries", "n", "200", 0); | |
| 937 | 945 | } |
| 938 | 946 | } |
| 939 | 947 | } |
| 940 | 948 | blob_zero(&sql); |
| 941 | 949 | db_prepare(&q, "SELECT * FROM timeline ORDER BY timestamp DESC /*scan*/"); |
| 942 | 950 |
| --- src/timeline.c | |
| +++ src/timeline.c | |
| @@ -165,11 +165,11 @@ | |
| 165 | ** 4. User |
| 166 | ** 5. True if is a leaf |
| 167 | ** 6. background color |
| 168 | ** 7. type ("ci", "w", "t") |
| 169 | ** 8. list of symbolic tags. |
| 170 | ** 9. tagid for ticket or wiki |
| 171 | ** 10. Short comment to user for repeated tickets and wiki |
| 172 | */ |
| 173 | void www_print_timeline( |
| 174 | Stmt *pQuery, /* Query to implement the timeline */ |
| 175 | int tmFlags, /* Flags controlling display behavior */ |
| @@ -288,10 +288,12 @@ | |
| 288 | @ <span class="timelineLeaf">Closed-Leaf:</span> |
| 289 | }else{ |
| 290 | @ <span class="timelineLeaf">Leaf:</span> |
| 291 | } |
| 292 | } |
| 293 | }else if( (tmFlags & TIMELINE_ARTID)!=0 ){ |
| 294 | hyperlink_to_uuid(zUuid); |
| 295 | } |
| 296 | db_column_blob(pQuery, commentColumn, &comment); |
| 297 | if( mxWikiLen>0 && blob_size(&comment)>mxWikiLen ){ |
| @@ -639,11 +641,11 @@ | |
| 639 | ** p=RID artifact RID and up to COUNT parents and ancestors |
| 640 | ** d=RID artifact RID and up to COUNT descendants |
| 641 | ** t=TAGID show only check-ins with the given tagid |
| 642 | ** r=TAGID show check-ins related to tagid |
| 643 | ** u=USER only if belonging to this user |
| 644 | ** y=TYPE 'ci', 'w', 't' |
| 645 | ** s=TEXT string search (comment and brief) |
| 646 | ** ng Suppress the graph if present |
| 647 | ** |
| 648 | ** p= and d= can appear individually or together. If either p= or d= |
| 649 | ** appear, then u=, y=, a=, and b= are ignored. |
| @@ -749,22 +751,22 @@ | |
| 749 | }else{ |
| 750 | blob_appendf(&desc, " of check-in [%.10s]", zUuid); |
| 751 | } |
| 752 | }else{ |
| 753 | int n; |
| 754 | const char *zEType = "event"; |
| 755 | char *zDate; |
| 756 | char *zNEntry = mprintf("%d", nEntry); |
| 757 | url_initialize(&url, "timeline"); |
| 758 | url_add_parameter(&url, "n", zNEntry); |
| 759 | if( tagid>0 ){ |
| 760 | zType = "ci"; |
| 761 | blob_appendf(&sql, |
| 762 | "AND (EXISTS(SELECT 1 FROM tagxref" |
| 763 | " WHERE tagid=%d AND tagtype>0 AND rid=blob.rid)", tagid); |
| 764 | |
| 765 | if( zBrName ){ |
| 766 | /* The next two blob_appendf() calls add SQL that causes checkins that |
| 767 | ** are not part of the branch which are parents or childen of the branch |
| 768 | ** to be included in the report. This related check-ins are useful |
| 769 | ** in helping to visualize what has happened on a quiescent branch |
| 770 | ** that is infrequently merged with a much more activate branch. |
| @@ -781,10 +783,11 @@ | |
| 781 | } |
| 782 | blob_appendf(&sql, ")"); |
| 783 | } |
| 784 | if( (zType[0]=='w' && !g.okRdWiki) |
| 785 | || (zType[0]=='t' && !g.okRdTkt) |
| 786 | || (zType[0]=='c' && !g.okRead) |
| 787 | ){ |
| 788 | zType = "all"; |
| 789 | } |
| 790 | if( zType[0]=='a' ){ |
| @@ -794,11 +797,11 @@ | |
| 794 | if( g.okRead ){ |
| 795 | blob_appendf(&sql, "%c'ci'", cSep); |
| 796 | cSep = ','; |
| 797 | } |
| 798 | if( g.okRdWiki ){ |
| 799 | blob_appendf(&sql, "%c'w'", cSep); |
| 800 | cSep = ','; |
| 801 | } |
| 802 | if( g.okRdTkt ){ |
| 803 | blob_appendf(&sql, "%c't'", cSep); |
| 804 | cSep = ','; |
| @@ -812,10 +815,12 @@ | |
| 812 | zEType = "checkin"; |
| 813 | }else if( zType[0]=='w' ){ |
| 814 | zEType = "wiki edit"; |
| 815 | }else if( zType[0]=='t' ){ |
| 816 | zEType = "ticket change"; |
| 817 | } |
| 818 | } |
| 819 | if( zUser ){ |
| 820 | blob_appendf(&sql, " AND event.user=%Q", zUser); |
| 821 | url_add_parameter(&url, "u", zUser); |
| @@ -926,16 +931,19 @@ | |
| 926 | timeline_submenu(&url, "Checkins Only", "y", "ci", 0); |
| 927 | } |
| 928 | if( zType[0]!='t' && g.okRdTkt ){ |
| 929 | timeline_submenu(&url, "Tickets Only", "y", "t", 0); |
| 930 | } |
| 931 | } |
| 932 | if( nEntry>20 ){ |
| 933 | timeline_submenu(&url, "20 Events", "n", "20", 0); |
| 934 | } |
| 935 | if( nEntry<200 ){ |
| 936 | timeline_submenu(&url, "200 Events", "n", "200", 0); |
| 937 | } |
| 938 | } |
| 939 | } |
| 940 | blob_zero(&sql); |
| 941 | db_prepare(&q, "SELECT * FROM timeline ORDER BY timestamp DESC /*scan*/"); |
| 942 |
| --- src/timeline.c | |
| +++ src/timeline.c | |
| @@ -165,11 +165,11 @@ | |
| 165 | ** 4. User |
| 166 | ** 5. True if is a leaf |
| 167 | ** 6. background color |
| 168 | ** 7. type ("ci", "w", "t") |
| 169 | ** 8. list of symbolic tags. |
| 170 | ** 9. tagid for ticket or wiki or event |
| 171 | ** 10. Short comment to user for repeated tickets and wiki |
| 172 | */ |
| 173 | void www_print_timeline( |
| 174 | Stmt *pQuery, /* Query to implement the timeline */ |
| 175 | int tmFlags, /* Flags controlling display behavior */ |
| @@ -288,10 +288,12 @@ | |
| 288 | @ <span class="timelineLeaf">Closed-Leaf:</span> |
| 289 | }else{ |
| 290 | @ <span class="timelineLeaf">Leaf:</span> |
| 291 | } |
| 292 | } |
| 293 | }else if( zType[0]=='e' && tagid ){ |
| 294 | hyperlink_to_event_tagid(tagid); |
| 295 | }else if( (tmFlags & TIMELINE_ARTID)!=0 ){ |
| 296 | hyperlink_to_uuid(zUuid); |
| 297 | } |
| 298 | db_column_blob(pQuery, commentColumn, &comment); |
| 299 | if( mxWikiLen>0 && blob_size(&comment)>mxWikiLen ){ |
| @@ -639,11 +641,11 @@ | |
| 641 | ** p=RID artifact RID and up to COUNT parents and ancestors |
| 642 | ** d=RID artifact RID and up to COUNT descendants |
| 643 | ** t=TAGID show only check-ins with the given tagid |
| 644 | ** r=TAGID show check-ins related to tagid |
| 645 | ** u=USER only if belonging to this user |
| 646 | ** y=TYPE 'ci', 'w', 't', 'e' |
| 647 | ** s=TEXT string search (comment and brief) |
| 648 | ** ng Suppress the graph if present |
| 649 | ** |
| 650 | ** p= and d= can appear individually or together. If either p= or d= |
| 651 | ** appear, then u=, y=, a=, and b= are ignored. |
| @@ -749,22 +751,22 @@ | |
| 751 | }else{ |
| 752 | blob_appendf(&desc, " of check-in [%.10s]", zUuid); |
| 753 | } |
| 754 | }else{ |
| 755 | int n; |
| 756 | const char *zEType = "timeline item"; |
| 757 | char *zDate; |
| 758 | char *zNEntry = mprintf("%d", nEntry); |
| 759 | url_initialize(&url, "timeline"); |
| 760 | url_add_parameter(&url, "n", zNEntry); |
| 761 | if( tagid>0 ){ |
| 762 | if( zType[0]!='e' ) zType = "ci"; |
| 763 | blob_appendf(&sql, |
| 764 | "AND (EXISTS(SELECT 1 FROM tagxref" |
| 765 | " WHERE tagid=%d AND tagtype>0 AND rid=blob.rid)", tagid); |
| 766 | |
| 767 | if( zBrName && zType[0]=='c' ){ |
| 768 | /* The next two blob_appendf() calls add SQL that causes checkins that |
| 769 | ** are not part of the branch which are parents or childen of the branch |
| 770 | ** to be included in the report. This related check-ins are useful |
| 771 | ** in helping to visualize what has happened on a quiescent branch |
| 772 | ** that is infrequently merged with a much more activate branch. |
| @@ -781,10 +783,11 @@ | |
| 783 | } |
| 784 | blob_appendf(&sql, ")"); |
| 785 | } |
| 786 | if( (zType[0]=='w' && !g.okRdWiki) |
| 787 | || (zType[0]=='t' && !g.okRdTkt) |
| 788 | || (zType[0]=='e' && !g.okRdWiki) |
| 789 | || (zType[0]=='c' && !g.okRead) |
| 790 | ){ |
| 791 | zType = "all"; |
| 792 | } |
| 793 | if( zType[0]=='a' ){ |
| @@ -794,11 +797,11 @@ | |
| 797 | if( g.okRead ){ |
| 798 | blob_appendf(&sql, "%c'ci'", cSep); |
| 799 | cSep = ','; |
| 800 | } |
| 801 | if( g.okRdWiki ){ |
| 802 | blob_appendf(&sql, "%c'w','e'", cSep); |
| 803 | cSep = ','; |
| 804 | } |
| 805 | if( g.okRdTkt ){ |
| 806 | blob_appendf(&sql, "%c't'", cSep); |
| 807 | cSep = ','; |
| @@ -812,10 +815,12 @@ | |
| 815 | zEType = "checkin"; |
| 816 | }else if( zType[0]=='w' ){ |
| 817 | zEType = "wiki edit"; |
| 818 | }else if( zType[0]=='t' ){ |
| 819 | zEType = "ticket change"; |
| 820 | }else if( zType[0]=='e' ){ |
| 821 | zEType = "event"; |
| 822 | } |
| 823 | } |
| 824 | if( zUser ){ |
| 825 | blob_appendf(&sql, " AND event.user=%Q", zUser); |
| 826 | url_add_parameter(&url, "u", zUser); |
| @@ -926,16 +931,19 @@ | |
| 931 | timeline_submenu(&url, "Checkins Only", "y", "ci", 0); |
| 932 | } |
| 933 | if( zType[0]!='t' && g.okRdTkt ){ |
| 934 | timeline_submenu(&url, "Tickets Only", "y", "t", 0); |
| 935 | } |
| 936 | if( zType[0]!='e' && g.okRdWiki ){ |
| 937 | timeline_submenu(&url, "Events Only", "y", "e", 0); |
| 938 | } |
| 939 | } |
| 940 | if( nEntry>20 ){ |
| 941 | timeline_submenu(&url, "20 Entries", "n", "20", 0); |
| 942 | } |
| 943 | if( nEntry<200 ){ |
| 944 | timeline_submenu(&url, "200 Entries", "n", "200", 0); |
| 945 | } |
| 946 | } |
| 947 | } |
| 948 | blob_zero(&sql); |
| 949 | db_prepare(&q, "SELECT * FROM timeline ORDER BY timestamp DESC /*scan*/"); |
| 950 |
+4
-1
| --- src/wiki.c | ||
| +++ src/wiki.c | ||
| @@ -106,11 +106,11 @@ | ||
| 106 | 106 | @ <p>This is a stub home-page for the project. |
| 107 | 107 | @ To fill in this page, first go to |
| 108 | 108 | @ <a href="%s(g.zBaseURL)/setup_config">setup/config</a> |
| 109 | 109 | @ and establish a "Project Name". Then create a |
| 110 | 110 | @ wiki page with that name. The content of that wiki page |
| 111 | - @ will be displayed in place of this message. | |
| 111 | + @ will be displayed in place of this message.</p> | |
| 112 | 112 | style_footer(); |
| 113 | 113 | } |
| 114 | 114 | |
| 115 | 115 | /* |
| 116 | 116 | ** Return true if the given pagename is the name of the sandbox |
| @@ -153,10 +153,13 @@ | ||
| 153 | 153 | @ wiki.</li> |
| 154 | 154 | @ <li> Use the <a href="%s(g.zBaseURL)/wiki?name=Sandbox">Sandbox</a> |
| 155 | 155 | @ to experiment.</li> |
| 156 | 156 | if( g.okNewWiki ){ |
| 157 | 157 | @ <li> Create a <a href="%s(g.zBaseURL)/wikinew">new wiki page</a>.</li> |
| 158 | + if( g.okWrite ){ | |
| 159 | + @ <li> Create a <a href="%s(g.zTop)/eventedit">new event</a>.</li> | |
| 160 | + } | |
| 158 | 161 | } |
| 159 | 162 | @ <li> <a href="%s(g.zBaseURL)/wcontent">List of All Wiki Pages</a> |
| 160 | 163 | @ available on this server.</li> |
| 161 | 164 | @ <li> <form method="get" action="%s(g.zBaseURL)/wfind"><div> |
| 162 | 165 | @ Search wiki titles: <input type="text" name="title"/> |
| 163 | 166 |
| --- src/wiki.c | |
| +++ src/wiki.c | |
| @@ -106,11 +106,11 @@ | |
| 106 | @ <p>This is a stub home-page for the project. |
| 107 | @ To fill in this page, first go to |
| 108 | @ <a href="%s(g.zBaseURL)/setup_config">setup/config</a> |
| 109 | @ and establish a "Project Name". Then create a |
| 110 | @ wiki page with that name. The content of that wiki page |
| 111 | @ will be displayed in place of this message. |
| 112 | style_footer(); |
| 113 | } |
| 114 | |
| 115 | /* |
| 116 | ** Return true if the given pagename is the name of the sandbox |
| @@ -153,10 +153,13 @@ | |
| 153 | @ wiki.</li> |
| 154 | @ <li> Use the <a href="%s(g.zBaseURL)/wiki?name=Sandbox">Sandbox</a> |
| 155 | @ to experiment.</li> |
| 156 | if( g.okNewWiki ){ |
| 157 | @ <li> Create a <a href="%s(g.zBaseURL)/wikinew">new wiki page</a>.</li> |
| 158 | } |
| 159 | @ <li> <a href="%s(g.zBaseURL)/wcontent">List of All Wiki Pages</a> |
| 160 | @ available on this server.</li> |
| 161 | @ <li> <form method="get" action="%s(g.zBaseURL)/wfind"><div> |
| 162 | @ Search wiki titles: <input type="text" name="title"/> |
| 163 |
| --- src/wiki.c | |
| +++ src/wiki.c | |
| @@ -106,11 +106,11 @@ | |
| 106 | @ <p>This is a stub home-page for the project. |
| 107 | @ To fill in this page, first go to |
| 108 | @ <a href="%s(g.zBaseURL)/setup_config">setup/config</a> |
| 109 | @ and establish a "Project Name". Then create a |
| 110 | @ wiki page with that name. The content of that wiki page |
| 111 | @ will be displayed in place of this message.</p> |
| 112 | style_footer(); |
| 113 | } |
| 114 | |
| 115 | /* |
| 116 | ** Return true if the given pagename is the name of the sandbox |
| @@ -153,10 +153,13 @@ | |
| 153 | @ wiki.</li> |
| 154 | @ <li> Use the <a href="%s(g.zBaseURL)/wiki?name=Sandbox">Sandbox</a> |
| 155 | @ to experiment.</li> |
| 156 | if( g.okNewWiki ){ |
| 157 | @ <li> Create a <a href="%s(g.zBaseURL)/wikinew">new wiki page</a>.</li> |
| 158 | if( g.okWrite ){ |
| 159 | @ <li> Create a <a href="%s(g.zTop)/eventedit">new event</a>.</li> |
| 160 | } |
| 161 | } |
| 162 | @ <li> <a href="%s(g.zBaseURL)/wcontent">List of All Wiki Pages</a> |
| 163 | @ available on this server.</li> |
| 164 | @ <li> <form method="get" action="%s(g.zBaseURL)/wfind"><div> |
| 165 | @ Search wiki titles: <input type="text" name="title"/> |
| 166 |
+4
-2
| --- src/xfer.c | ||
| +++ src/xfer.c | ||
| @@ -119,22 +119,24 @@ | ||
| 119 | 119 | if( uuid_is_shunned(blob_str(&pXfer->aToken[1])) ){ |
| 120 | 120 | /* Ignore files that have been shunned */ |
| 121 | 121 | return; |
| 122 | 122 | } |
| 123 | 123 | if( pXfer->nToken==4 ){ |
| 124 | - Blob src; | |
| 124 | + Blob src, next; | |
| 125 | 125 | srcid = rid_from_uuid(&pXfer->aToken[2], 1); |
| 126 | 126 | if( content_get(srcid, &src)==0 ){ |
| 127 | 127 | rid = content_put(&content, blob_str(&pXfer->aToken[1]), srcid); |
| 128 | 128 | pXfer->nDanglingFile++; |
| 129 | 129 | db_multi_exec("DELETE FROM phantom WHERE rid=%d", rid); |
| 130 | 130 | content_make_public(rid); |
| 131 | 131 | return; |
| 132 | 132 | } |
| 133 | 133 | pXfer->nDeltaRcvd++; |
| 134 | - blob_delta_apply(&src, &content, &content); | |
| 134 | + blob_delta_apply(&src, &content, &next); | |
| 135 | 135 | blob_reset(&src); |
| 136 | + blob_reset(&content); | |
| 137 | + content = next; | |
| 136 | 138 | }else{ |
| 137 | 139 | pXfer->nFileRcvd++; |
| 138 | 140 | } |
| 139 | 141 | sha1sum_blob(&content, &hash); |
| 140 | 142 | if( !blob_eq_str(&pXfer->aToken[1], blob_str(&hash), -1) ){ |
| 141 | 143 |
| --- src/xfer.c | |
| +++ src/xfer.c | |
| @@ -119,22 +119,24 @@ | |
| 119 | if( uuid_is_shunned(blob_str(&pXfer->aToken[1])) ){ |
| 120 | /* Ignore files that have been shunned */ |
| 121 | return; |
| 122 | } |
| 123 | if( pXfer->nToken==4 ){ |
| 124 | Blob src; |
| 125 | srcid = rid_from_uuid(&pXfer->aToken[2], 1); |
| 126 | if( content_get(srcid, &src)==0 ){ |
| 127 | rid = content_put(&content, blob_str(&pXfer->aToken[1]), srcid); |
| 128 | pXfer->nDanglingFile++; |
| 129 | db_multi_exec("DELETE FROM phantom WHERE rid=%d", rid); |
| 130 | content_make_public(rid); |
| 131 | return; |
| 132 | } |
| 133 | pXfer->nDeltaRcvd++; |
| 134 | blob_delta_apply(&src, &content, &content); |
| 135 | blob_reset(&src); |
| 136 | }else{ |
| 137 | pXfer->nFileRcvd++; |
| 138 | } |
| 139 | sha1sum_blob(&content, &hash); |
| 140 | if( !blob_eq_str(&pXfer->aToken[1], blob_str(&hash), -1) ){ |
| 141 |
| --- src/xfer.c | |
| +++ src/xfer.c | |
| @@ -119,22 +119,24 @@ | |
| 119 | if( uuid_is_shunned(blob_str(&pXfer->aToken[1])) ){ |
| 120 | /* Ignore files that have been shunned */ |
| 121 | return; |
| 122 | } |
| 123 | if( pXfer->nToken==4 ){ |
| 124 | Blob src, next; |
| 125 | srcid = rid_from_uuid(&pXfer->aToken[2], 1); |
| 126 | if( content_get(srcid, &src)==0 ){ |
| 127 | rid = content_put(&content, blob_str(&pXfer->aToken[1]), srcid); |
| 128 | pXfer->nDanglingFile++; |
| 129 | db_multi_exec("DELETE FROM phantom WHERE rid=%d", rid); |
| 130 | content_make_public(rid); |
| 131 | return; |
| 132 | } |
| 133 | pXfer->nDeltaRcvd++; |
| 134 | blob_delta_apply(&src, &content, &next); |
| 135 | blob_reset(&src); |
| 136 | blob_reset(&content); |
| 137 | content = next; |
| 138 | }else{ |
| 139 | pXfer->nFileRcvd++; |
| 140 | } |
| 141 | sha1sum_blob(&content, &hash); |
| 142 | if( !blob_eq_str(&pXfer->aToken[1], blob_str(&hash), -1) ){ |
| 143 |
+1
| --- www/branching.wiki | ||
| +++ www/branching.wiki | ||
| @@ -155,10 +155,11 @@ | ||
| 155 | 155 | accident that stems from concurrent development. In figure 4, giving |
| 156 | 156 | check-in 2 multiple children is a deliberate act. So, to a good |
| 157 | 157 | approximation, we define forking to be by accident and branching to |
| 158 | 158 | be by intent. Apart from that, they are the same. |
| 159 | 159 | |
| 160 | +<a name="tags"></a> | |
| 160 | 161 | <h2>Tags And Properties</h2> |
| 161 | 162 | |
| 162 | 163 | Tags and properties are used in fossil to help express the intent, and |
| 163 | 164 | thus to distinguish between forks and branches. Figure 5 shows the |
| 164 | 165 | same scenario as figure 4 but with tags and properties added: |
| 165 | 166 | |
| 166 | 167 | ADDED www/event.wiki |
| --- www/branching.wiki | |
| +++ www/branching.wiki | |
| @@ -155,10 +155,11 @@ | |
| 155 | accident that stems from concurrent development. In figure 4, giving |
| 156 | check-in 2 multiple children is a deliberate act. So, to a good |
| 157 | approximation, we define forking to be by accident and branching to |
| 158 | be by intent. Apart from that, they are the same. |
| 159 | |
| 160 | <h2>Tags And Properties</h2> |
| 161 | |
| 162 | Tags and properties are used in fossil to help express the intent, and |
| 163 | thus to distinguish between forks and branches. Figure 5 shows the |
| 164 | same scenario as figure 4 but with tags and properties added: |
| 165 | |
| 166 | DDED www/event.wiki |
| --- www/branching.wiki | |
| +++ www/branching.wiki | |
| @@ -155,10 +155,11 @@ | |
| 155 | accident that stems from concurrent development. In figure 4, giving |
| 156 | check-in 2 multiple children is a deliberate act. So, to a good |
| 157 | approximation, we define forking to be by accident and branching to |
| 158 | be by intent. Apart from that, they are the same. |
| 159 | |
| 160 | <a name="tags"></a> |
| 161 | <h2>Tags And Properties</h2> |
| 162 | |
| 163 | Tags and properties are used in fossil to help express the intent, and |
| 164 | thus to distinguish between forks and branches. Figure 5 shows the |
| 165 | same scenario as figure 4 but with tags and properties added: |
| 166 | |
| 167 | DDED www/event.wiki |
+35
| --- a/www/event.wiki | ||
| +++ b/www/event.wiki | ||
| @@ -0,0 +1,35 @@ | ||
| 1 | +<title>Eventes</title> | |
| 2 | + | |
| 3 | +<h2>What Is A "Event"?</h2>e?y=<titl | |
| 4 | +Possible uses for technotes include: | |
| 5 | + | |
| 6 | + * <b>Milestones</b>. Project milestones, such as releases or beta-test | |
| 7 | + cycles, can be recorded as technotes. The timeline entry for the technote | |
| 8 | + can be something simple like "Version 1.2.3" perhaps with a bright | |
| 9 | + color background to draw attention to the entry and the wiki content | |
| 10 | + can contain release notes, for example. | |
| 11 | + | |
| 12 | + * <b>Blog Entries</b>. Blog entries from developers describing the current | |
| 13 | + state of a project, or rational for v arious design decisions, or | |
| 14 | + roadmaps for future development, can be entered as technotes. | |
| 15 | + | |
| 16 | + * <b>Process Checkpoints</b>. For projects that have a formal process, | |
| 17 | + technotes can be used to record the completion or the initiation of | |
| 18 | + various process steps. For example, a technote can be used to record | |
| 19 | + the successful completion of a long-running test, perhaps w[./wikitheory.wiki | wiki page] | |
| 20 | +that is associated with a point in time rather thaevent causes a si [/timelinhyperlink ofTimeline Page]entry cause a jump to the wiThe wiki content, the timeline entry text, the | |
| 21 | +time of the eventTechnical Notes</title> | |
| 22 | + | |
| 23 | +<h2>What Is A "Technote"?</h2> | |
| 24 | + | |
| 25 | +In Fossil, a "technical note" or "technoteventerly called an "event") | |
| 26 | +is a speeventiki page] | |
| 27 | +that is associated with a point in time rather than having a page name. | |
| 28 | +eventaving a page name. | |
| 29 | +Each technote causes a single entry to appear on the | |
| 30 | +[/timeline?y=<titl | |
| 31 | +Possible uses for technotes include: | |
| 32 | + | |
| 33 | + * <b>Milestones</b>. Project milestones, such as releases or beta-test | |
| 34 | + cycles, can be recorded as technotes. The timeline entry for the technote | |
| 35 | + can be something simple like "V |
| --- a/www/event.wiki | |
| +++ b/www/event.wiki | |
| @@ -0,0 +1,35 @@ | |
| --- a/www/event.wiki | |
| +++ b/www/event.wiki | |
| @@ -0,0 +1,35 @@ | |
| 1 | <title>Eventes</title> |
| 2 | |
| 3 | <h2>What Is A "Event"?</h2>e?y=<titl |
| 4 | Possible uses for technotes include: |
| 5 | |
| 6 | * <b>Milestones</b>. Project milestones, such as releases or beta-test |
| 7 | cycles, can be recorded as technotes. The timeline entry for the technote |
| 8 | can be something simple like "Version 1.2.3" perhaps with a bright |
| 9 | color background to draw attention to the entry and the wiki content |
| 10 | can contain release notes, for example. |
| 11 | |
| 12 | * <b>Blog Entries</b>. Blog entries from developers describing the current |
| 13 | state of a project, or rational for v arious design decisions, or |
| 14 | roadmaps for future development, can be entered as technotes. |
| 15 | |
| 16 | * <b>Process Checkpoints</b>. For projects that have a formal process, |
| 17 | technotes can be used to record the completion or the initiation of |
| 18 | various process steps. For example, a technote can be used to record |
| 19 | the successful completion of a long-running test, perhaps w[./wikitheory.wiki | wiki page] |
| 20 | that is associated with a point in time rather thaevent causes a si [/timelinhyperlink ofTimeline Page]entry cause a jump to the wiThe wiki content, the timeline entry text, the |
| 21 | time of the eventTechnical Notes</title> |
| 22 | |
| 23 | <h2>What Is A "Technote"?</h2> |
| 24 | |
| 25 | In Fossil, a "technical note" or "technoteventerly called an "event") |
| 26 | is a speeventiki page] |
| 27 | that is associated with a point in time rather than having a page name. |
| 28 | eventaving a page name. |
| 29 | Each technote causes a single entry to appear on the |
| 30 | [/timeline?y=<titl |
| 31 | Possible uses for technotes include: |
| 32 | |
| 33 | * <b>Milestones</b>. Project milestones, such as releases or beta-test |
| 34 | cycles, can be recorded as technotes. The timeline entry for the technote |
| 35 | can be something simple like "V |
+106
-13
| --- www/fileformat.wiki | ||
| +++ www/fileformat.wiki | ||
| @@ -43,13 +43,14 @@ | ||
| 43 | 43 | <li> [#cluster | Clusters] </li> |
| 44 | 44 | <li> [#ctrl | Control Artifacts] </li> |
| 45 | 45 | <li> [#wikichng | Wiki Pages] </li> |
| 46 | 46 | <li> [#tktchng | Ticket Changes] </li> |
| 47 | 47 | <li> [#attachment | Attachments] </li> |
| 48 | +<li> [#event | Events] </li> | |
| 48 | 49 | </ul> |
| 49 | 50 | |
| 50 | -These five artifact types are described in the sequel. | |
| 51 | +These seven artifact types are described in the sequel. | |
| 51 | 52 | |
| 52 | 53 | In the current implementation (as of 2009-01-25) the artifacts that |
| 53 | 54 | make up a fossil repository are stored in in as delta- and zlib-compressed |
| 54 | 55 | blobs in an <a href="http://www.sqlite.org/">SQLite</a> database. This |
| 55 | 56 | is an implementation detail and might change in a future release. For |
| @@ -56,10 +57,13 @@ | ||
| 56 | 57 | the purpose of this article "file format" means the format of the artifacts, |
| 57 | 58 | not how the artifacts are stored on disk. It is the artifact format that |
| 58 | 59 | is intended to be enduring. The specifics of how artifacts are stored on |
| 59 | 60 | disk, though stable, is not intended to live as long as the |
| 60 | 61 | artifact format. |
| 62 | + | |
| 63 | +All of the artifacts can be extracted from a Fossil repository using | |
| 64 | +the "fossil deconstruct" command. | |
| 61 | 65 | |
| 62 | 66 | <a name="manifest"></a> |
| 63 | 67 | <h2>1.0 The Manifest</h2> |
| 64 | 68 | |
| 65 | 69 | A manifest defines a check-in or version of the project |
| @@ -165,12 +169,13 @@ | ||
| 165 | 169 | repository, append a single space (ASCII 0x20), the |
| 166 | 170 | size of the file in ASCII decimal, a single newline |
| 167 | 171 | character (ASCII 0x0A), and the complete text of the file. |
| 168 | 172 | Compute the MD5 checksum of the result. |
| 169 | 173 | |
| 170 | -A manifest might contain one or more T-cards used to set tags or | |
| 171 | -properties on the check-in. The format of the T-card is the same as | |
| 174 | +A manifest might contain one or more T-cards used to set | |
| 175 | +[./branching.wiki#tags | tags or properties] | |
| 176 | +on the check-in. The format of the T-card is the same as | |
| 172 | 177 | described in <i>Control Artifacts</i> section below, except that the |
| 173 | 178 | second argument is the single characcter "<b>*</b>" instead of an |
| 174 | 179 | artifact ID. The <b>*</b> in place of the artifact ID indicates that |
| 175 | 180 | the tag or property applies to the current artifact. It is not |
| 176 | 181 | possible to encode the current artifact ID as part of an artifact, |
| @@ -182,11 +187,11 @@ | ||
| 182 | 187 | Each manifest has a single U-card. The argument to the U-card is |
| 183 | 188 | the login of the user who created the manifest. The login name |
| 184 | 189 | is encoded using the same character escapes as is used for the |
| 185 | 190 | check-in comment argument to the C-card. |
| 186 | 191 | |
| 187 | -A manifest has an option Z-card as its last line. The argument | |
| 192 | +A manifest has an optional Z-card as its last line. The argument | |
| 188 | 193 | to the Z-card is a 32-character lowercase hexadecimal MD5 hash |
| 189 | 194 | of all prior lines of the manifest up to and including the newline |
| 190 | 195 | character that immediately precedes the "Z". The Z-card is just |
| 191 | 196 | a sanity check to prove that the manifest is well-formed and |
| 192 | 197 | consistent. |
| @@ -262,11 +267,12 @@ | ||
| 262 | 267 | clearsigned. |
| 263 | 268 | |
| 264 | 269 | The D card and the Z card of a control artifact are the same |
| 265 | 270 | as in a manifest. |
| 266 | 271 | |
| 267 | -The T card represents a "tag" or property that is applied to | |
| 272 | +The T card represents a [./branching.wiki#tags | tag or property] | |
| 273 | +that is applied to | |
| 268 | 274 | some other artifact. The T card has two or three values. The |
| 269 | 275 | second argument is the 40 character lowercase artifact ID of the artifact |
| 270 | 276 | to which the tag is to be applied. The |
| 271 | 277 | first value is the tag name. The first character of the tag |
| 272 | 278 | is either "+", "-", or "*". A "+" means the tag should be added |
| @@ -324,11 +330,11 @@ | ||
| 324 | 330 | of text in the wiki page. That text follows the newline character |
| 325 | 331 | that terminates the W card. The wiki text is always followed by one |
| 326 | 332 | extra newline. |
| 327 | 333 | |
| 328 | 334 | An example wiki artifact can be seen |
| 329 | -[/artifact/7b2f5fd0e0 | here]. | |
| 335 | +[/artifact?name=7b2f5fd0e0&txt=1 | here]. | |
| 330 | 336 | |
| 331 | 337 | <a name="tktchng"></a> |
| 332 | 338 | <h2>5.0 Ticket Changes</h2> |
| 333 | 339 | |
| 334 | 340 | A ticket-change artifact represents a change to a trouble ticket. |
| @@ -375,25 +381,25 @@ | ||
| 375 | 381 | |
| 376 | 382 | <a name="attachment"></a> |
| 377 | 383 | <h2>6.0 Attachments</h2> |
| 378 | 384 | |
| 379 | 385 | An attachment artifact associates some other artifact that is the |
| 380 | -attachment (the source artifact) with a ticket or wiki page to which | |
| 386 | +attachment (the source artifact) with a ticket or wiki page or event to which | |
| 381 | 387 | the attachment is connected (the target artifact). |
| 382 | 388 | The following cards are allowed on an attachment artifact: |
| 383 | 389 | |
| 384 | 390 | <blockquote> |
| 385 | -<b>A</b> <i>filename target</i> ?<i>source</i>? | |
| 386 | -<b>C</b> <i>comment</i><br> | |
| 391 | +<b>A</b> <i>filename target</i> ?<i>source</i>?<br /> | |
| 392 | +<b>C</b> <i>comment</i><br /> | |
| 387 | 393 | <b>D</b> <i>time-and-date-stamp</i><br /> |
| 388 | 394 | <b>U</b> <i>user-name</i><br /> |
| 389 | 395 | <b>Z</b> <i>checksum</i> |
| 390 | 396 | </blockquote> |
| 391 | 397 | |
| 392 | 398 | The A card specifies a filename for the attachment in its first argument. |
| 393 | 399 | The second argument to the A card is the name |
| 394 | -of the wiki page or ticket to which the attachment is connected. The | |
| 400 | +of the wiki page or ticket or event to which the attachment is connected. The | |
| 395 | 401 | third argument is either missing or else it is the 40-character artifact |
| 396 | 402 | ID of the attachment itself. A missing third argument means that the |
| 397 | 403 | attachment should be deleted. |
| 398 | 404 | |
| 399 | 405 | The C card is an optional comment describing what the attachment is about. |
| @@ -405,46 +411,111 @@ | ||
| 405 | 411 | A single U card gives the name of the user to added the attachment. |
| 406 | 412 | If an attachment is added anonymously, then the U card may be omitted. |
| 407 | 413 | |
| 408 | 414 | The Z card is the usual checksum over the rest of the attachment artifact. |
| 409 | 415 | |
| 416 | + | |
| 417 | +<a name="event"></a> | |
| 418 | +<h2>7.0 Events</h2> | |
| 419 | + | |
| 420 | +An event artifact associates a timeline comment and a page of text | |
| 421 | +(similar to a wiki page) with a point in time. Events can be used | |
| 422 | +to record project milestones, release notes, blog entries, process | |
| 423 | +checkpoints, or news articles. | |
| 424 | +The following cards are allowed on an event artifact: | |
| 425 | + | |
| 426 | +<blockquote> | |
| 427 | +<b>C</b> <i>comment</i><br> | |
| 428 | +<b>D</b> <i>time-and-date-stamp</i><br /> | |
| 429 | +<b>E</b> <i>event-time</i> <i>event-id</i><br /> | |
| 430 | +<b>P</b> <i>parent-artifact-id</i>+<br /> | |
| 431 | +<b>T</b> <b>+</b><i>tag-name</i> <b>*</b> <i>value</i><br /> | |
| 432 | +<b>U</b> <i>user-name</i><br /> | |
| 433 | +<b>W</b> <i>size</i> <b>\n</b> <i>text</i> <b>\n</b><br /> | |
| 434 | +<b>Z</b> <i>checksum</i> | |
| 435 | +</blockquote> | |
| 436 | + | |
| 437 | +The C card contains text that is displayed on the timeline for the | |
| 438 | +event. Exactly one C card is required on an event artifact. | |
| 439 | + | |
| 440 | +A single D card is required to give the date and time when the | |
| 441 | +event artifact was created. This is different from the time at which | |
| 442 | +the event occurs. | |
| 443 | + | |
| 444 | +A single E card gives the time of the event (the point on the timeline | |
| 445 | +where the event is displayed) and a unique identifier for the event. | |
| 446 | +When there are multiple artifacts with the same event-id, the one with | |
| 447 | +the most recent D card is the only one used. The event-id must be a | |
| 448 | +40-character lower-case hexadecimal string. | |
| 449 | + | |
| 450 | +The option P card specifies a prior event with the same event-id from | |
| 451 | +which the current event is an edit. The P card is a hint to the system | |
| 452 | +that it might be space efficient to store one event as a delta of the | |
| 453 | +other. | |
| 454 | + | |
| 455 | +An event might contain one or more T-cards used to set | |
| 456 | +[./branching.wiki#tags | tags or properties] | |
| 457 | +on the event. The format of the T-card is the same as | |
| 458 | +described in [#ctrl | Control Artifacts] section above, except that the | |
| 459 | +second argument is the single characcter "<b>*</b>" instead of an | |
| 460 | +artifact ID and the name is always prefaced by "<b>+</b>". | |
| 461 | +The <b>*</b> in place of the artifact ID indicates that | |
| 462 | +the tag or property applies to the current artifact. It is not | |
| 463 | +possible to encode the current artifact ID as part of an artifact, | |
| 464 | +since the act of inserting the artifact ID would change the artifact ID, | |
| 465 | +hence a <b>*</b> is used to represent "self". The "<b>+</b>" on the | |
| 466 | +name means that tags can only be add and they can only be non-propagating | |
| 467 | +tags. A an event, T cards are normally used to set the background | |
| 468 | +display color for timelines. | |
| 469 | + | |
| 470 | +The optional U card gives name of the user who entered the event. | |
| 471 | + | |
| 472 | +A single W card provides wiki text for the document associated with the | |
| 473 | +event. The format of the W card is exactly the same as for a | |
| 474 | +[#wikichng | wiki artifact]. | |
| 475 | + | |
| 476 | +The Z card is the usual checksum over the rest of the attachment artifact. | |
| 477 | + | |
| 410 | 478 | |
| 411 | 479 | <a name="summary"></a> |
| 412 | -<h2>7.0 Card Summary</h2> | |
| 480 | +<h2>8.0 Card Summary</h2> | |
| 413 | 481 | |
| 414 | 482 | The following table summaries the various kinds of cards that |
| 415 | 483 | appear on Fossil artifacts: |
| 416 | 484 | |
| 417 | 485 | <table border=1 width="100%"> |
| 418 | 486 | <tr> |
| 419 | 487 | <th rowspan=2 valign=bottom>Card Format</th> |
| 420 | -<th colspan=6>Used By</th> | |
| 488 | +<th colspan=7>Used By</th> | |
| 421 | 489 | </tr> |
| 422 | 490 | <tr> |
| 423 | 491 | <th>Manifest</th> |
| 424 | 492 | <th>Cluster</th> |
| 425 | 493 | <th>Control</th> |
| 426 | 494 | <th>Wiki</th> |
| 427 | 495 | <th>Ticket</th> |
| 428 | 496 | <th>Attachment</th> |
| 497 | +<th>Event</th> | |
| 429 | 498 | </tr> |
| 430 | 499 | <tr> |
| 431 | 500 | <td><b>A</b> <i>filename target source</i></td> |
| 432 | 501 | <td> </td> |
| 433 | 502 | <td> </td> |
| 434 | 503 | <td> </td> |
| 435 | 504 | <td> </td> |
| 436 | 505 | <td> </td> |
| 437 | 506 | <td align=center><b>X</b></td> |
| 507 | +<td> </td> | |
| 438 | 508 | </tr> |
| 439 | 509 | <tr> |
| 440 | -<td><b>C</b> <i>coment-text</i></td> | |
| 510 | +<td><b>C</b> <i>comment-text</i></td> | |
| 441 | 511 | <td align=center><b>X</b></td> |
| 442 | 512 | <td> </td> |
| 443 | 513 | <td> </td> |
| 444 | 514 | <td> </td> |
| 445 | 515 | <td> </td> |
| 516 | +<td align=center><b>X</b></td> | |
| 446 | 517 | <td align=center><b>X</b></td> |
| 447 | 518 | </tr> |
| 448 | 519 | <tr> |
| 449 | 520 | <td><b>D</b> <i>date-time-stamp</i></td> |
| 450 | 521 | <td align=center><b>X</b></td> |
| @@ -451,14 +522,26 @@ | ||
| 451 | 522 | <td align=center> </td> |
| 452 | 523 | <td align=center><b>X</b></td> |
| 453 | 524 | <td align=center><b>X</b></td> |
| 454 | 525 | <td align=center><b>X</b></td> |
| 455 | 526 | <td align=center><b>X</b></td> |
| 527 | +<td align=center><b>X</b></td> | |
| 528 | +</tr> | |
| 529 | +<tr> | |
| 530 | +<td><b>E</b> <i>event-time event-id</i></td> | |
| 531 | +<td align=center> </td> | |
| 532 | +<td align=center> </td> | |
| 533 | +<td align=center> </td> | |
| 534 | +<td align=center> </td> | |
| 535 | +<td align=center> </td> | |
| 536 | +<td align=center> </td> | |
| 537 | +<td align=center><b>X</b></td> | |
| 456 | 538 | </tr> |
| 457 | 539 | <tr> |
| 458 | 540 | <td><b>F</b> <i>filename uuid permissions oldname</i></td> |
| 459 | 541 | <td align=center><b>X</b></td> |
| 542 | +<td align=center> </td> | |
| 460 | 543 | <td align=center> </td> |
| 461 | 544 | <td align=center> </td> |
| 462 | 545 | <td align=center> </td> |
| 463 | 546 | <td align=center> </td> |
| 464 | 547 | <td align=center> </td> |
| @@ -469,18 +552,20 @@ | ||
| 469 | 552 | <td align=center> </td> |
| 470 | 553 | <td align=center> </td> |
| 471 | 554 | <td align=center> </td> |
| 472 | 555 | <td align=center><b>X</b></td> |
| 473 | 556 | <td align=center> </td> |
| 557 | +<td align=center> </td> | |
| 474 | 558 | </tr> |
| 475 | 559 | <tr> |
| 476 | 560 | <td><b>K</b> <i>ticket-uuid</i></td> |
| 477 | 561 | <td align=center> </td> |
| 478 | 562 | <td align=center> </td> |
| 479 | 563 | <td align=center> </td> |
| 480 | 564 | <td align=center> </td> |
| 481 | 565 | <td align=center><b>X</b></td> |
| 566 | +<td align=center> </td> | |
| 482 | 567 | <td align=center> </td> |
| 483 | 568 | </tr> |
| 484 | 569 | <tr> |
| 485 | 570 | <td><b>L</b> <i>wiki-title</i></td> |
| 486 | 571 | <td align=center> </td> |
| @@ -487,15 +572,17 @@ | ||
| 487 | 572 | <td align=center> </td> |
| 488 | 573 | <td align=center> </td> |
| 489 | 574 | <td align=center><b>X</b></td> |
| 490 | 575 | <td align=center> </td> |
| 491 | 576 | <td align=center> </td> |
| 577 | +<td align=center> </td> | |
| 492 | 578 | </tr> |
| 493 | 579 | <tr> |
| 494 | 580 | <td><b>M</b> <i>uuid</i></td> |
| 495 | 581 | <td align=center> </td> |
| 496 | 582 | <td align=center><b>X</b></td> |
| 583 | +<td align=center> </td> | |
| 497 | 584 | <td align=center> </td> |
| 498 | 585 | <td align=center> </td> |
| 499 | 586 | <td align=center> </td> |
| 500 | 587 | <td align=center> </td> |
| 501 | 588 | </tr> |
| @@ -505,14 +592,16 @@ | ||
| 505 | 592 | <td align=center> </td> |
| 506 | 593 | <td align=center> </td> |
| 507 | 594 | <td align=center><b>X</b></td> |
| 508 | 595 | <td align=center> </td> |
| 509 | 596 | <td align=center> </td> |
| 597 | +<td align=center> </td> | |
| 510 | 598 | </tr> |
| 511 | 599 | <tr> |
| 512 | 600 | <td><b>R</b> <i>md5sum</i></td> |
| 513 | 601 | <td align=center><b>X</b></td> |
| 602 | +<td align=center> </td> | |
| 514 | 603 | <td align=center> </td> |
| 515 | 604 | <td align=center> </td> |
| 516 | 605 | <td align=center> </td> |
| 517 | 606 | <td align=center> </td> |
| 518 | 607 | <td align=center> </td> |
| @@ -522,15 +611,17 @@ | ||
| 522 | 611 | <td align=center> </td> |
| 523 | 612 | <td align=center><b>X</b></td> |
| 524 | 613 | <td align=center> </td> |
| 525 | 614 | <td align=center> </td> |
| 526 | 615 | <td align=center> </td> |
| 616 | +<td align=center><b>X</b></td> | |
| 527 | 617 | </tr> |
| 528 | 618 | <tr> |
| 529 | 619 | <td><b>U</b> <i>username</i></td> |
| 530 | 620 | <td align=center><b>X</b></td> |
| 531 | 621 | <td align=center> </td> |
| 622 | +<td align=center><b>X</b></td> | |
| 532 | 623 | <td align=center><b>X</b></td> |
| 533 | 624 | <td align=center><b>X</b></td> |
| 534 | 625 | <td align=center><b>X</b></td> |
| 535 | 626 | <td align=center><b>X</b></td> |
| 536 | 627 | </tr> |
| @@ -540,16 +631,18 @@ | ||
| 540 | 631 | <td align=center> </td> |
| 541 | 632 | <td align=center> </td> |
| 542 | 633 | <td align=center><b>X</b></td> |
| 543 | 634 | <td align=center> </td> |
| 544 | 635 | <td align=center> </td> |
| 636 | +<td align=center><b>X</b></td> | |
| 545 | 637 | </tr> |
| 546 | 638 | <tr> |
| 547 | 639 | <td><b>Z</b> <i>md5sum</i></td> |
| 640 | +<td align=center><b>X</b></td> | |
| 548 | 641 | <td align=center><b>X</b></td> |
| 549 | 642 | <td align=center><b>X</b></td> |
| 550 | 643 | <td align=center><b>X</b></td> |
| 551 | 644 | <td align=center><b>X</b></td> |
| 552 | 645 | <td align=center><b>X</b></td> |
| 553 | 646 | <td align=center><b>X</b></td> |
| 554 | 647 | </tr> |
| 555 | 648 | </table> |
| 556 | 649 |
| --- www/fileformat.wiki | |
| +++ www/fileformat.wiki | |
| @@ -43,13 +43,14 @@ | |
| 43 | <li> [#cluster | Clusters] </li> |
| 44 | <li> [#ctrl | Control Artifacts] </li> |
| 45 | <li> [#wikichng | Wiki Pages] </li> |
| 46 | <li> [#tktchng | Ticket Changes] </li> |
| 47 | <li> [#attachment | Attachments] </li> |
| 48 | </ul> |
| 49 | |
| 50 | These five artifact types are described in the sequel. |
| 51 | |
| 52 | In the current implementation (as of 2009-01-25) the artifacts that |
| 53 | make up a fossil repository are stored in in as delta- and zlib-compressed |
| 54 | blobs in an <a href="http://www.sqlite.org/">SQLite</a> database. This |
| 55 | is an implementation detail and might change in a future release. For |
| @@ -56,10 +57,13 @@ | |
| 56 | the purpose of this article "file format" means the format of the artifacts, |
| 57 | not how the artifacts are stored on disk. It is the artifact format that |
| 58 | is intended to be enduring. The specifics of how artifacts are stored on |
| 59 | disk, though stable, is not intended to live as long as the |
| 60 | artifact format. |
| 61 | |
| 62 | <a name="manifest"></a> |
| 63 | <h2>1.0 The Manifest</h2> |
| 64 | |
| 65 | A manifest defines a check-in or version of the project |
| @@ -165,12 +169,13 @@ | |
| 165 | repository, append a single space (ASCII 0x20), the |
| 166 | size of the file in ASCII decimal, a single newline |
| 167 | character (ASCII 0x0A), and the complete text of the file. |
| 168 | Compute the MD5 checksum of the result. |
| 169 | |
| 170 | A manifest might contain one or more T-cards used to set tags or |
| 171 | properties on the check-in. The format of the T-card is the same as |
| 172 | described in <i>Control Artifacts</i> section below, except that the |
| 173 | second argument is the single characcter "<b>*</b>" instead of an |
| 174 | artifact ID. The <b>*</b> in place of the artifact ID indicates that |
| 175 | the tag or property applies to the current artifact. It is not |
| 176 | possible to encode the current artifact ID as part of an artifact, |
| @@ -182,11 +187,11 @@ | |
| 182 | Each manifest has a single U-card. The argument to the U-card is |
| 183 | the login of the user who created the manifest. The login name |
| 184 | is encoded using the same character escapes as is used for the |
| 185 | check-in comment argument to the C-card. |
| 186 | |
| 187 | A manifest has an option Z-card as its last line. The argument |
| 188 | to the Z-card is a 32-character lowercase hexadecimal MD5 hash |
| 189 | of all prior lines of the manifest up to and including the newline |
| 190 | character that immediately precedes the "Z". The Z-card is just |
| 191 | a sanity check to prove that the manifest is well-formed and |
| 192 | consistent. |
| @@ -262,11 +267,12 @@ | |
| 262 | clearsigned. |
| 263 | |
| 264 | The D card and the Z card of a control artifact are the same |
| 265 | as in a manifest. |
| 266 | |
| 267 | The T card represents a "tag" or property that is applied to |
| 268 | some other artifact. The T card has two or three values. The |
| 269 | second argument is the 40 character lowercase artifact ID of the artifact |
| 270 | to which the tag is to be applied. The |
| 271 | first value is the tag name. The first character of the tag |
| 272 | is either "+", "-", or "*". A "+" means the tag should be added |
| @@ -324,11 +330,11 @@ | |
| 324 | of text in the wiki page. That text follows the newline character |
| 325 | that terminates the W card. The wiki text is always followed by one |
| 326 | extra newline. |
| 327 | |
| 328 | An example wiki artifact can be seen |
| 329 | [/artifact/7b2f5fd0e0 | here]. |
| 330 | |
| 331 | <a name="tktchng"></a> |
| 332 | <h2>5.0 Ticket Changes</h2> |
| 333 | |
| 334 | A ticket-change artifact represents a change to a trouble ticket. |
| @@ -375,25 +381,25 @@ | |
| 375 | |
| 376 | <a name="attachment"></a> |
| 377 | <h2>6.0 Attachments</h2> |
| 378 | |
| 379 | An attachment artifact associates some other artifact that is the |
| 380 | attachment (the source artifact) with a ticket or wiki page to which |
| 381 | the attachment is connected (the target artifact). |
| 382 | The following cards are allowed on an attachment artifact: |
| 383 | |
| 384 | <blockquote> |
| 385 | <b>A</b> <i>filename target</i> ?<i>source</i>? |
| 386 | <b>C</b> <i>comment</i><br> |
| 387 | <b>D</b> <i>time-and-date-stamp</i><br /> |
| 388 | <b>U</b> <i>user-name</i><br /> |
| 389 | <b>Z</b> <i>checksum</i> |
| 390 | </blockquote> |
| 391 | |
| 392 | The A card specifies a filename for the attachment in its first argument. |
| 393 | The second argument to the A card is the name |
| 394 | of the wiki page or ticket to which the attachment is connected. The |
| 395 | third argument is either missing or else it is the 40-character artifact |
| 396 | ID of the attachment itself. A missing third argument means that the |
| 397 | attachment should be deleted. |
| 398 | |
| 399 | The C card is an optional comment describing what the attachment is about. |
| @@ -405,46 +411,111 @@ | |
| 405 | A single U card gives the name of the user to added the attachment. |
| 406 | If an attachment is added anonymously, then the U card may be omitted. |
| 407 | |
| 408 | The Z card is the usual checksum over the rest of the attachment artifact. |
| 409 | |
| 410 | |
| 411 | <a name="summary"></a> |
| 412 | <h2>7.0 Card Summary</h2> |
| 413 | |
| 414 | The following table summaries the various kinds of cards that |
| 415 | appear on Fossil artifacts: |
| 416 | |
| 417 | <table border=1 width="100%"> |
| 418 | <tr> |
| 419 | <th rowspan=2 valign=bottom>Card Format</th> |
| 420 | <th colspan=6>Used By</th> |
| 421 | </tr> |
| 422 | <tr> |
| 423 | <th>Manifest</th> |
| 424 | <th>Cluster</th> |
| 425 | <th>Control</th> |
| 426 | <th>Wiki</th> |
| 427 | <th>Ticket</th> |
| 428 | <th>Attachment</th> |
| 429 | </tr> |
| 430 | <tr> |
| 431 | <td><b>A</b> <i>filename target source</i></td> |
| 432 | <td> </td> |
| 433 | <td> </td> |
| 434 | <td> </td> |
| 435 | <td> </td> |
| 436 | <td> </td> |
| 437 | <td align=center><b>X</b></td> |
| 438 | </tr> |
| 439 | <tr> |
| 440 | <td><b>C</b> <i>coment-text</i></td> |
| 441 | <td align=center><b>X</b></td> |
| 442 | <td> </td> |
| 443 | <td> </td> |
| 444 | <td> </td> |
| 445 | <td> </td> |
| 446 | <td align=center><b>X</b></td> |
| 447 | </tr> |
| 448 | <tr> |
| 449 | <td><b>D</b> <i>date-time-stamp</i></td> |
| 450 | <td align=center><b>X</b></td> |
| @@ -451,14 +522,26 @@ | |
| 451 | <td align=center> </td> |
| 452 | <td align=center><b>X</b></td> |
| 453 | <td align=center><b>X</b></td> |
| 454 | <td align=center><b>X</b></td> |
| 455 | <td align=center><b>X</b></td> |
| 456 | </tr> |
| 457 | <tr> |
| 458 | <td><b>F</b> <i>filename uuid permissions oldname</i></td> |
| 459 | <td align=center><b>X</b></td> |
| 460 | <td align=center> </td> |
| 461 | <td align=center> </td> |
| 462 | <td align=center> </td> |
| 463 | <td align=center> </td> |
| 464 | <td align=center> </td> |
| @@ -469,18 +552,20 @@ | |
| 469 | <td align=center> </td> |
| 470 | <td align=center> </td> |
| 471 | <td align=center> </td> |
| 472 | <td align=center><b>X</b></td> |
| 473 | <td align=center> </td> |
| 474 | </tr> |
| 475 | <tr> |
| 476 | <td><b>K</b> <i>ticket-uuid</i></td> |
| 477 | <td align=center> </td> |
| 478 | <td align=center> </td> |
| 479 | <td align=center> </td> |
| 480 | <td align=center> </td> |
| 481 | <td align=center><b>X</b></td> |
| 482 | <td align=center> </td> |
| 483 | </tr> |
| 484 | <tr> |
| 485 | <td><b>L</b> <i>wiki-title</i></td> |
| 486 | <td align=center> </td> |
| @@ -487,15 +572,17 @@ | |
| 487 | <td align=center> </td> |
| 488 | <td align=center> </td> |
| 489 | <td align=center><b>X</b></td> |
| 490 | <td align=center> </td> |
| 491 | <td align=center> </td> |
| 492 | </tr> |
| 493 | <tr> |
| 494 | <td><b>M</b> <i>uuid</i></td> |
| 495 | <td align=center> </td> |
| 496 | <td align=center><b>X</b></td> |
| 497 | <td align=center> </td> |
| 498 | <td align=center> </td> |
| 499 | <td align=center> </td> |
| 500 | <td align=center> </td> |
| 501 | </tr> |
| @@ -505,14 +592,16 @@ | |
| 505 | <td align=center> </td> |
| 506 | <td align=center> </td> |
| 507 | <td align=center><b>X</b></td> |
| 508 | <td align=center> </td> |
| 509 | <td align=center> </td> |
| 510 | </tr> |
| 511 | <tr> |
| 512 | <td><b>R</b> <i>md5sum</i></td> |
| 513 | <td align=center><b>X</b></td> |
| 514 | <td align=center> </td> |
| 515 | <td align=center> </td> |
| 516 | <td align=center> </td> |
| 517 | <td align=center> </td> |
| 518 | <td align=center> </td> |
| @@ -522,15 +611,17 @@ | |
| 522 | <td align=center> </td> |
| 523 | <td align=center><b>X</b></td> |
| 524 | <td align=center> </td> |
| 525 | <td align=center> </td> |
| 526 | <td align=center> </td> |
| 527 | </tr> |
| 528 | <tr> |
| 529 | <td><b>U</b> <i>username</i></td> |
| 530 | <td align=center><b>X</b></td> |
| 531 | <td align=center> </td> |
| 532 | <td align=center><b>X</b></td> |
| 533 | <td align=center><b>X</b></td> |
| 534 | <td align=center><b>X</b></td> |
| 535 | <td align=center><b>X</b></td> |
| 536 | </tr> |
| @@ -540,16 +631,18 @@ | |
| 540 | <td align=center> </td> |
| 541 | <td align=center> </td> |
| 542 | <td align=center><b>X</b></td> |
| 543 | <td align=center> </td> |
| 544 | <td align=center> </td> |
| 545 | </tr> |
| 546 | <tr> |
| 547 | <td><b>Z</b> <i>md5sum</i></td> |
| 548 | <td align=center><b>X</b></td> |
| 549 | <td align=center><b>X</b></td> |
| 550 | <td align=center><b>X</b></td> |
| 551 | <td align=center><b>X</b></td> |
| 552 | <td align=center><b>X</b></td> |
| 553 | <td align=center><b>X</b></td> |
| 554 | </tr> |
| 555 | </table> |
| 556 |
| --- www/fileformat.wiki | |
| +++ www/fileformat.wiki | |
| @@ -43,13 +43,14 @@ | |
| 43 | <li> [#cluster | Clusters] </li> |
| 44 | <li> [#ctrl | Control Artifacts] </li> |
| 45 | <li> [#wikichng | Wiki Pages] </li> |
| 46 | <li> [#tktchng | Ticket Changes] </li> |
| 47 | <li> [#attachment | Attachments] </li> |
| 48 | <li> [#event | Events] </li> |
| 49 | </ul> |
| 50 | |
| 51 | These seven artifact types are described in the sequel. |
| 52 | |
| 53 | In the current implementation (as of 2009-01-25) the artifacts that |
| 54 | make up a fossil repository are stored in in as delta- and zlib-compressed |
| 55 | blobs in an <a href="http://www.sqlite.org/">SQLite</a> database. This |
| 56 | is an implementation detail and might change in a future release. For |
| @@ -56,10 +57,13 @@ | |
| 57 | the purpose of this article "file format" means the format of the artifacts, |
| 58 | not how the artifacts are stored on disk. It is the artifact format that |
| 59 | is intended to be enduring. The specifics of how artifacts are stored on |
| 60 | disk, though stable, is not intended to live as long as the |
| 61 | artifact format. |
| 62 | |
| 63 | All of the artifacts can be extracted from a Fossil repository using |
| 64 | the "fossil deconstruct" command. |
| 65 | |
| 66 | <a name="manifest"></a> |
| 67 | <h2>1.0 The Manifest</h2> |
| 68 | |
| 69 | A manifest defines a check-in or version of the project |
| @@ -165,12 +169,13 @@ | |
| 169 | repository, append a single space (ASCII 0x20), the |
| 170 | size of the file in ASCII decimal, a single newline |
| 171 | character (ASCII 0x0A), and the complete text of the file. |
| 172 | Compute the MD5 checksum of the result. |
| 173 | |
| 174 | A manifest might contain one or more T-cards used to set |
| 175 | [./branching.wiki#tags | tags or properties] |
| 176 | on the check-in. The format of the T-card is the same as |
| 177 | described in <i>Control Artifacts</i> section below, except that the |
| 178 | second argument is the single characcter "<b>*</b>" instead of an |
| 179 | artifact ID. The <b>*</b> in place of the artifact ID indicates that |
| 180 | the tag or property applies to the current artifact. It is not |
| 181 | possible to encode the current artifact ID as part of an artifact, |
| @@ -182,11 +187,11 @@ | |
| 187 | Each manifest has a single U-card. The argument to the U-card is |
| 188 | the login of the user who created the manifest. The login name |
| 189 | is encoded using the same character escapes as is used for the |
| 190 | check-in comment argument to the C-card. |
| 191 | |
| 192 | A manifest has an optional Z-card as its last line. The argument |
| 193 | to the Z-card is a 32-character lowercase hexadecimal MD5 hash |
| 194 | of all prior lines of the manifest up to and including the newline |
| 195 | character that immediately precedes the "Z". The Z-card is just |
| 196 | a sanity check to prove that the manifest is well-formed and |
| 197 | consistent. |
| @@ -262,11 +267,12 @@ | |
| 267 | clearsigned. |
| 268 | |
| 269 | The D card and the Z card of a control artifact are the same |
| 270 | as in a manifest. |
| 271 | |
| 272 | The T card represents a [./branching.wiki#tags | tag or property] |
| 273 | that is applied to |
| 274 | some other artifact. The T card has two or three values. The |
| 275 | second argument is the 40 character lowercase artifact ID of the artifact |
| 276 | to which the tag is to be applied. The |
| 277 | first value is the tag name. The first character of the tag |
| 278 | is either "+", "-", or "*". A "+" means the tag should be added |
| @@ -324,11 +330,11 @@ | |
| 330 | of text in the wiki page. That text follows the newline character |
| 331 | that terminates the W card. The wiki text is always followed by one |
| 332 | extra newline. |
| 333 | |
| 334 | An example wiki artifact can be seen |
| 335 | [/artifact?name=7b2f5fd0e0&txt=1 | here]. |
| 336 | |
| 337 | <a name="tktchng"></a> |
| 338 | <h2>5.0 Ticket Changes</h2> |
| 339 | |
| 340 | A ticket-change artifact represents a change to a trouble ticket. |
| @@ -375,25 +381,25 @@ | |
| 381 | |
| 382 | <a name="attachment"></a> |
| 383 | <h2>6.0 Attachments</h2> |
| 384 | |
| 385 | An attachment artifact associates some other artifact that is the |
| 386 | attachment (the source artifact) with a ticket or wiki page or event to which |
| 387 | the attachment is connected (the target artifact). |
| 388 | The following cards are allowed on an attachment artifact: |
| 389 | |
| 390 | <blockquote> |
| 391 | <b>A</b> <i>filename target</i> ?<i>source</i>?<br /> |
| 392 | <b>C</b> <i>comment</i><br /> |
| 393 | <b>D</b> <i>time-and-date-stamp</i><br /> |
| 394 | <b>U</b> <i>user-name</i><br /> |
| 395 | <b>Z</b> <i>checksum</i> |
| 396 | </blockquote> |
| 397 | |
| 398 | The A card specifies a filename for the attachment in its first argument. |
| 399 | The second argument to the A card is the name |
| 400 | of the wiki page or ticket or event to which the attachment is connected. The |
| 401 | third argument is either missing or else it is the 40-character artifact |
| 402 | ID of the attachment itself. A missing third argument means that the |
| 403 | attachment should be deleted. |
| 404 | |
| 405 | The C card is an optional comment describing what the attachment is about. |
| @@ -405,46 +411,111 @@ | |
| 411 | A single U card gives the name of the user to added the attachment. |
| 412 | If an attachment is added anonymously, then the U card may be omitted. |
| 413 | |
| 414 | The Z card is the usual checksum over the rest of the attachment artifact. |
| 415 | |
| 416 | |
| 417 | <a name="event"></a> |
| 418 | <h2>7.0 Events</h2> |
| 419 | |
| 420 | An event artifact associates a timeline comment and a page of text |
| 421 | (similar to a wiki page) with a point in time. Events can be used |
| 422 | to record project milestones, release notes, blog entries, process |
| 423 | checkpoints, or news articles. |
| 424 | The following cards are allowed on an event artifact: |
| 425 | |
| 426 | <blockquote> |
| 427 | <b>C</b> <i>comment</i><br> |
| 428 | <b>D</b> <i>time-and-date-stamp</i><br /> |
| 429 | <b>E</b> <i>event-time</i> <i>event-id</i><br /> |
| 430 | <b>P</b> <i>parent-artifact-id</i>+<br /> |
| 431 | <b>T</b> <b>+</b><i>tag-name</i> <b>*</b> <i>value</i><br /> |
| 432 | <b>U</b> <i>user-name</i><br /> |
| 433 | <b>W</b> <i>size</i> <b>\n</b> <i>text</i> <b>\n</b><br /> |
| 434 | <b>Z</b> <i>checksum</i> |
| 435 | </blockquote> |
| 436 | |
| 437 | The C card contains text that is displayed on the timeline for the |
| 438 | event. Exactly one C card is required on an event artifact. |
| 439 | |
| 440 | A single D card is required to give the date and time when the |
| 441 | event artifact was created. This is different from the time at which |
| 442 | the event occurs. |
| 443 | |
| 444 | A single E card gives the time of the event (the point on the timeline |
| 445 | where the event is displayed) and a unique identifier for the event. |
| 446 | When there are multiple artifacts with the same event-id, the one with |
| 447 | the most recent D card is the only one used. The event-id must be a |
| 448 | 40-character lower-case hexadecimal string. |
| 449 | |
| 450 | The option P card specifies a prior event with the same event-id from |
| 451 | which the current event is an edit. The P card is a hint to the system |
| 452 | that it might be space efficient to store one event as a delta of the |
| 453 | other. |
| 454 | |
| 455 | An event might contain one or more T-cards used to set |
| 456 | [./branching.wiki#tags | tags or properties] |
| 457 | on the event. The format of the T-card is the same as |
| 458 | described in [#ctrl | Control Artifacts] section above, except that the |
| 459 | second argument is the single characcter "<b>*</b>" instead of an |
| 460 | artifact ID and the name is always prefaced by "<b>+</b>". |
| 461 | The <b>*</b> in place of the artifact ID indicates that |
| 462 | the tag or property applies to the current artifact. It is not |
| 463 | possible to encode the current artifact ID as part of an artifact, |
| 464 | since the act of inserting the artifact ID would change the artifact ID, |
| 465 | hence a <b>*</b> is used to represent "self". The "<b>+</b>" on the |
| 466 | name means that tags can only be add and they can only be non-propagating |
| 467 | tags. A an event, T cards are normally used to set the background |
| 468 | display color for timelines. |
| 469 | |
| 470 | The optional U card gives name of the user who entered the event. |
| 471 | |
| 472 | A single W card provides wiki text for the document associated with the |
| 473 | event. The format of the W card is exactly the same as for a |
| 474 | [#wikichng | wiki artifact]. |
| 475 | |
| 476 | The Z card is the usual checksum over the rest of the attachment artifact. |
| 477 | |
| 478 | |
| 479 | <a name="summary"></a> |
| 480 | <h2>8.0 Card Summary</h2> |
| 481 | |
| 482 | The following table summaries the various kinds of cards that |
| 483 | appear on Fossil artifacts: |
| 484 | |
| 485 | <table border=1 width="100%"> |
| 486 | <tr> |
| 487 | <th rowspan=2 valign=bottom>Card Format</th> |
| 488 | <th colspan=7>Used By</th> |
| 489 | </tr> |
| 490 | <tr> |
| 491 | <th>Manifest</th> |
| 492 | <th>Cluster</th> |
| 493 | <th>Control</th> |
| 494 | <th>Wiki</th> |
| 495 | <th>Ticket</th> |
| 496 | <th>Attachment</th> |
| 497 | <th>Event</th> |
| 498 | </tr> |
| 499 | <tr> |
| 500 | <td><b>A</b> <i>filename target source</i></td> |
| 501 | <td> </td> |
| 502 | <td> </td> |
| 503 | <td> </td> |
| 504 | <td> </td> |
| 505 | <td> </td> |
| 506 | <td align=center><b>X</b></td> |
| 507 | <td> </td> |
| 508 | </tr> |
| 509 | <tr> |
| 510 | <td><b>C</b> <i>comment-text</i></td> |
| 511 | <td align=center><b>X</b></td> |
| 512 | <td> </td> |
| 513 | <td> </td> |
| 514 | <td> </td> |
| 515 | <td> </td> |
| 516 | <td align=center><b>X</b></td> |
| 517 | <td align=center><b>X</b></td> |
| 518 | </tr> |
| 519 | <tr> |
| 520 | <td><b>D</b> <i>date-time-stamp</i></td> |
| 521 | <td align=center><b>X</b></td> |
| @@ -451,14 +522,26 @@ | |
| 522 | <td align=center> </td> |
| 523 | <td align=center><b>X</b></td> |
| 524 | <td align=center><b>X</b></td> |
| 525 | <td align=center><b>X</b></td> |
| 526 | <td align=center><b>X</b></td> |
| 527 | <td align=center><b>X</b></td> |
| 528 | </tr> |
| 529 | <tr> |
| 530 | <td><b>E</b> <i>event-time event-id</i></td> |
| 531 | <td align=center> </td> |
| 532 | <td align=center> </td> |
| 533 | <td align=center> </td> |
| 534 | <td align=center> </td> |
| 535 | <td align=center> </td> |
| 536 | <td align=center> </td> |
| 537 | <td align=center><b>X</b></td> |
| 538 | </tr> |
| 539 | <tr> |
| 540 | <td><b>F</b> <i>filename uuid permissions oldname</i></td> |
| 541 | <td align=center><b>X</b></td> |
| 542 | <td align=center> </td> |
| 543 | <td align=center> </td> |
| 544 | <td align=center> </td> |
| 545 | <td align=center> </td> |
| 546 | <td align=center> </td> |
| 547 | <td align=center> </td> |
| @@ -469,18 +552,20 @@ | |
| 552 | <td align=center> </td> |
| 553 | <td align=center> </td> |
| 554 | <td align=center> </td> |
| 555 | <td align=center><b>X</b></td> |
| 556 | <td align=center> </td> |
| 557 | <td align=center> </td> |
| 558 | </tr> |
| 559 | <tr> |
| 560 | <td><b>K</b> <i>ticket-uuid</i></td> |
| 561 | <td align=center> </td> |
| 562 | <td align=center> </td> |
| 563 | <td align=center> </td> |
| 564 | <td align=center> </td> |
| 565 | <td align=center><b>X</b></td> |
| 566 | <td align=center> </td> |
| 567 | <td align=center> </td> |
| 568 | </tr> |
| 569 | <tr> |
| 570 | <td><b>L</b> <i>wiki-title</i></td> |
| 571 | <td align=center> </td> |
| @@ -487,15 +572,17 @@ | |
| 572 | <td align=center> </td> |
| 573 | <td align=center> </td> |
| 574 | <td align=center><b>X</b></td> |
| 575 | <td align=center> </td> |
| 576 | <td align=center> </td> |
| 577 | <td align=center> </td> |
| 578 | </tr> |
| 579 | <tr> |
| 580 | <td><b>M</b> <i>uuid</i></td> |
| 581 | <td align=center> </td> |
| 582 | <td align=center><b>X</b></td> |
| 583 | <td align=center> </td> |
| 584 | <td align=center> </td> |
| 585 | <td align=center> </td> |
| 586 | <td align=center> </td> |
| 587 | <td align=center> </td> |
| 588 | </tr> |
| @@ -505,14 +592,16 @@ | |
| 592 | <td align=center> </td> |
| 593 | <td align=center> </td> |
| 594 | <td align=center><b>X</b></td> |
| 595 | <td align=center> </td> |
| 596 | <td align=center> </td> |
| 597 | <td align=center> </td> |
| 598 | </tr> |
| 599 | <tr> |
| 600 | <td><b>R</b> <i>md5sum</i></td> |
| 601 | <td align=center><b>X</b></td> |
| 602 | <td align=center> </td> |
| 603 | <td align=center> </td> |
| 604 | <td align=center> </td> |
| 605 | <td align=center> </td> |
| 606 | <td align=center> </td> |
| 607 | <td align=center> </td> |
| @@ -522,15 +611,17 @@ | |
| 611 | <td align=center> </td> |
| 612 | <td align=center><b>X</b></td> |
| 613 | <td align=center> </td> |
| 614 | <td align=center> </td> |
| 615 | <td align=center> </td> |
| 616 | <td align=center><b>X</b></td> |
| 617 | </tr> |
| 618 | <tr> |
| 619 | <td><b>U</b> <i>username</i></td> |
| 620 | <td align=center><b>X</b></td> |
| 621 | <td align=center> </td> |
| 622 | <td align=center><b>X</b></td> |
| 623 | <td align=center><b>X</b></td> |
| 624 | <td align=center><b>X</b></td> |
| 625 | <td align=center><b>X</b></td> |
| 626 | <td align=center><b>X</b></td> |
| 627 | </tr> |
| @@ -540,16 +631,18 @@ | |
| 631 | <td align=center> </td> |
| 632 | <td align=center> </td> |
| 633 | <td align=center><b>X</b></td> |
| 634 | <td align=center> </td> |
| 635 | <td align=center> </td> |
| 636 | <td align=center><b>X</b></td> |
| 637 | </tr> |
| 638 | <tr> |
| 639 | <td><b>Z</b> <i>md5sum</i></td> |
| 640 | <td align=center><b>X</b></td> |
| 641 | <td align=center><b>X</b></td> |
| 642 | <td align=center><b>X</b></td> |
| 643 | <td align=center><b>X</b></td> |
| 644 | <td align=center><b>X</b></td> |
| 645 | <td align=center><b>X</b></td> |
| 646 | <td align=center><b>X</b></td> |
| 647 | </tr> |
| 648 | </table> |
| 649 |
+5
-2
| --- www/index.wiki | ||
| +++ www/index.wiki | ||
| @@ -40,12 +40,13 @@ | ||
| 40 | 40 | internet these days. What makes Fossil worthy of attention? |
| 41 | 41 | |
| 42 | 42 | 1. <b>Bug Tracking And Wiki</b> - |
| 43 | 43 | In addition to doing [./concepts.wiki | distributed version control] |
| 44 | 44 | like Git and Mercurial, |
| 45 | - Fossil also supports [./bugtheory.wiki | distributed bug tracking] and | |
| 46 | - [./wikitheory.wiki | distributed wiki] all in a single | |
| 45 | + Fossil also supports [./bugtheory.wiki | distributed bug tracking], | |
| 46 | + [./wikitheory.wiki | distributed wiki], and a | |
| 47 | + [./event.wiki | distributed blog] mechanism all in a single | |
| 47 | 48 | integrated package. |
| 48 | 49 | |
| 49 | 50 | 2. <b>Web Interface</b> - |
| 50 | 51 | Fossil has a built-in and easy-to-use [./webui.wiki | web interface] |
| 51 | 52 | that simplifies project tracking and promotes situational awareness. |
| @@ -111,10 +112,12 @@ | ||
| 111 | 112 | * A tutorial on [./branching.wiki | branching], what it means and how |
| 112 | 113 | to do it using fossil. |
| 113 | 114 | * The [./selfcheck.wiki | automatic self-check] mechanism |
| 114 | 115 | helps insure project integrity. |
| 115 | 116 | * Fossil contains a [./wikitheory.wiki | built-in wiki]. |
| 117 | + * An [./event.wiki | Event] is a special kind of wiki page associated | |
| 118 | + with a point in time rather than a name. | |
| 116 | 119 | * There is a |
| 117 | 120 | [http://lists.fossil-scm.org:8080/cgi-bin/mailman/listinfo/fossil-users | mailing list] (with publicly readable |
| 118 | 121 | [http://www.mail-archive.com/[email protected] | archives] |
| 119 | 122 | available for discussing fossil issues. |
| 120 | 123 | * [./stats.wiki | Performance statistics] taken from real-world projects |
| 121 | 124 |
| --- www/index.wiki | |
| +++ www/index.wiki | |
| @@ -40,12 +40,13 @@ | |
| 40 | internet these days. What makes Fossil worthy of attention? |
| 41 | |
| 42 | 1. <b>Bug Tracking And Wiki</b> - |
| 43 | In addition to doing [./concepts.wiki | distributed version control] |
| 44 | like Git and Mercurial, |
| 45 | Fossil also supports [./bugtheory.wiki | distributed bug tracking] and |
| 46 | [./wikitheory.wiki | distributed wiki] all in a single |
| 47 | integrated package. |
| 48 | |
| 49 | 2. <b>Web Interface</b> - |
| 50 | Fossil has a built-in and easy-to-use [./webui.wiki | web interface] |
| 51 | that simplifies project tracking and promotes situational awareness. |
| @@ -111,10 +112,12 @@ | |
| 111 | * A tutorial on [./branching.wiki | branching], what it means and how |
| 112 | to do it using fossil. |
| 113 | * The [./selfcheck.wiki | automatic self-check] mechanism |
| 114 | helps insure project integrity. |
| 115 | * Fossil contains a [./wikitheory.wiki | built-in wiki]. |
| 116 | * There is a |
| 117 | [http://lists.fossil-scm.org:8080/cgi-bin/mailman/listinfo/fossil-users | mailing list] (with publicly readable |
| 118 | [http://www.mail-archive.com/[email protected] | archives] |
| 119 | available for discussing fossil issues. |
| 120 | * [./stats.wiki | Performance statistics] taken from real-world projects |
| 121 |
| --- www/index.wiki | |
| +++ www/index.wiki | |
| @@ -40,12 +40,13 @@ | |
| 40 | internet these days. What makes Fossil worthy of attention? |
| 41 | |
| 42 | 1. <b>Bug Tracking And Wiki</b> - |
| 43 | In addition to doing [./concepts.wiki | distributed version control] |
| 44 | like Git and Mercurial, |
| 45 | Fossil also supports [./bugtheory.wiki | distributed bug tracking], |
| 46 | [./wikitheory.wiki | distributed wiki], and a |
| 47 | [./event.wiki | distributed blog] mechanism all in a single |
| 48 | integrated package. |
| 49 | |
| 50 | 2. <b>Web Interface</b> - |
| 51 | Fossil has a built-in and easy-to-use [./webui.wiki | web interface] |
| 52 | that simplifies project tracking and promotes situational awareness. |
| @@ -111,10 +112,12 @@ | |
| 112 | * A tutorial on [./branching.wiki | branching], what it means and how |
| 113 | to do it using fossil. |
| 114 | * The [./selfcheck.wiki | automatic self-check] mechanism |
| 115 | helps insure project integrity. |
| 116 | * Fossil contains a [./wikitheory.wiki | built-in wiki]. |
| 117 | * An [./event.wiki | Event] is a special kind of wiki page associated |
| 118 | with a point in time rather than a name. |
| 119 | * There is a |
| 120 | [http://lists.fossil-scm.org:8080/cgi-bin/mailman/listinfo/fossil-users | mailing list] (with publicly readable |
| 121 | [http://www.mail-archive.com/[email protected] | archives] |
| 122 | available for discussing fossil issues. |
| 123 | * [./stats.wiki | Performance statistics] taken from real-world projects |
| 124 |
+1
-1
| --- www/qandc.wiki | ||
| +++ www/qandc.wiki | ||
| @@ -63,11 +63,11 @@ | ||
| 63 | 63 | Other projects are also adopting fossil. But fossil does not yet have |
| 64 | 64 | the massive user base of git or mercurial. |
| 65 | 65 | </blockquote> |
| 66 | 66 | |
| 67 | 67 | <b>Fossil looks like the bug tracker that would be in your |
| 68 | -Linksys Router's administration screen.</p> | |
| 68 | +Linksys Router's administration screen.</b> | |
| 69 | 69 | |
| 70 | 70 | <blockquote> |
| 71 | 71 | <p>I take a pragmatic approach to software: form follows function. |
| 72 | 72 | To me, it is more important to have a reliable, fast, efficient, |
| 73 | 73 | enduring, and simple DVCS than one that looks pretty.</p> |
| 74 | 74 |
| --- www/qandc.wiki | |
| +++ www/qandc.wiki | |
| @@ -63,11 +63,11 @@ | |
| 63 | Other projects are also adopting fossil. But fossil does not yet have |
| 64 | the massive user base of git or mercurial. |
| 65 | </blockquote> |
| 66 | |
| 67 | <b>Fossil looks like the bug tracker that would be in your |
| 68 | Linksys Router's administration screen.</p> |
| 69 | |
| 70 | <blockquote> |
| 71 | <p>I take a pragmatic approach to software: form follows function. |
| 72 | To me, it is more important to have a reliable, fast, efficient, |
| 73 | enduring, and simple DVCS than one that looks pretty.</p> |
| 74 |
| --- www/qandc.wiki | |
| +++ www/qandc.wiki | |
| @@ -63,11 +63,11 @@ | |
| 63 | Other projects are also adopting fossil. But fossil does not yet have |
| 64 | the massive user base of git or mercurial. |
| 65 | </blockquote> |
| 66 | |
| 67 | <b>Fossil looks like the bug tracker that would be in your |
| 68 | Linksys Router's administration screen.</b> |
| 69 | |
| 70 | <blockquote> |
| 71 | <p>I take a pragmatic approach to software: form follows function. |
| 72 | To me, it is more important to have a reliable, fast, efficient, |
| 73 | enduring, and simple DVCS than one that looks pretty.</p> |
| 74 |
+1
| --- www/wikitheory.wiki | ||
| +++ www/wikitheory.wiki | ||
| @@ -5,10 +5,11 @@ | ||
| 5 | 5 | * Stand-alone wiki pages. |
| 6 | 6 | * Description and comments in [./bugtheory.wiki | bug reports]. |
| 7 | 7 | * Check-in comments. |
| 8 | 8 | * [./embeddeddoc.wiki | Embedded documentation] files whose |
| 9 | 9 | name ends in "wiki". |
| 10 | + * [./event.wiki | Event descriptions]. | |
| 10 | 11 | |
| 11 | 12 | The [/wiki_rules | formatting rules] for fossil wiki |
| 12 | 13 | are designed to be simple and intuitive. The idea is that wiki provides |
| 13 | 14 | paragraph breaks, numbered and bulleted lists, and hyperlinking for |
| 14 | 15 | simple documents together with a safe subset of HTML for more complex |
| 15 | 16 |
| --- www/wikitheory.wiki | |
| +++ www/wikitheory.wiki | |
| @@ -5,10 +5,11 @@ | |
| 5 | * Stand-alone wiki pages. |
| 6 | * Description and comments in [./bugtheory.wiki | bug reports]. |
| 7 | * Check-in comments. |
| 8 | * [./embeddeddoc.wiki | Embedded documentation] files whose |
| 9 | name ends in "wiki". |
| 10 | |
| 11 | The [/wiki_rules | formatting rules] for fossil wiki |
| 12 | are designed to be simple and intuitive. The idea is that wiki provides |
| 13 | paragraph breaks, numbered and bulleted lists, and hyperlinking for |
| 14 | simple documents together with a safe subset of HTML for more complex |
| 15 |
| --- www/wikitheory.wiki | |
| +++ www/wikitheory.wiki | |
| @@ -5,10 +5,11 @@ | |
| 5 | * Stand-alone wiki pages. |
| 6 | * Description and comments in [./bugtheory.wiki | bug reports]. |
| 7 | * Check-in comments. |
| 8 | * [./embeddeddoc.wiki | Embedded documentation] files whose |
| 9 | name ends in "wiki". |
| 10 | * [./event.wiki | Event descriptions]. |
| 11 | |
| 12 | The [/wiki_rules | formatting rules] for fossil wiki |
| 13 | are designed to be simple and intuitive. The idea is that wiki provides |
| 14 | paragraph breaks, numbered and bulleted lists, and hyperlinking for |
| 15 | simple documents together with a safe subset of HTML for more complex |
| 16 |