Many hyperlinks are disabled.
Use anonymous login
to enable hyperlinks.
Overview
| Comment: | merge trunk |
|---|---|
| Downloads: | Tarball | ZIP archive |
| Timelines: | family | ancestors | descendants | both | allow-backslash-in-card-filename |
| Files: | files | file ages | folders |
| SHA1: |
013854ae7616e0ae0dd7ab64639c11a4 |
| User & Date: | jan.nijtmans 2013-01-26 16:27:10.147 |
Context
|
2013-01-26
| ||
| 17:56 | don't let fossil choke any more (syntax error) when a card contains a backslash in a filename. This doesn't mean that a backslash is now allowed in a filename, only that fossil can handle the card, and show what's wrong. check-in: b178bcb897 user: jan.nijtmans tags: trunk | |
| 16:27 | merge trunk Closed-Leaf check-in: 013854ae76 user: jan.nijtmans tags: allow-backslash-in-card-filename | |
| 08:26 | Fix revert tests 5 and 6 so they aren't carbon copies. Minor comment cleanup check-in: 1a5ac30583 user: joel tags: trunk | |
|
2013-01-15
| ||
| 10:25 | formatting check-in: 9d657c3be5 user: jan.nijtmans tags: allow-backslash-in-card-filename | |
Changes
Changes to src/allrepo.c.
| ︙ | ︙ | |||
142 143 144 145 146 147 148 149 150 151 152 153 154 155 |
zCmd = "rebuild";
collect_argument(&extra, "cluster");
collect_argument(&extra, "compress");
collect_argument(&extra, "noverify");
collect_argument_value(&extra, "pagesize");
collect_argument(&extra, "vacuum");
collect_argument(&extra, "deanalyze");
collect_argument(&extra, "wal");
collect_argument(&extra, "stat");
}else if( strncmp(zCmd, "sync", n)==0 ){
zCmd = "sync -autourl -R";
collect_argument(&extra, "verbose");
}else if( strncmp(zCmd, "test-integrity", n)==0 ){
zCmd = "test-integrity";
| > | 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 |
zCmd = "rebuild";
collect_argument(&extra, "cluster");
collect_argument(&extra, "compress");
collect_argument(&extra, "noverify");
collect_argument_value(&extra, "pagesize");
collect_argument(&extra, "vacuum");
collect_argument(&extra, "deanalyze");
collect_argument(&extra, "analyze");
collect_argument(&extra, "wal");
collect_argument(&extra, "stat");
}else if( strncmp(zCmd, "sync", n)==0 ){
zCmd = "sync -autourl -R";
collect_argument(&extra, "verbose");
}else if( strncmp(zCmd, "test-integrity", n)==0 ){
zCmd = "test-integrity";
|
| ︙ | ︙ |
Changes to src/cson_amalgamation.h.
1 2 3 4 5 6 7 8 | #ifdef FOSSIL_ENABLE_JSON /* auto-generated! Do not edit! */ /* begin file include/wh/cson/cson.h */ #if !defined(WANDERINGHORSE_NET_CSON_H_INCLUDED) #define WANDERINGHORSE_NET_CSON_H_INCLUDED 1 /*#include <stdint.h> C99: fixed-size int types. */ #include <stdio.h> /* FILE decl */ | > > > | 1 2 3 4 5 6 7 8 9 10 11 | #ifdef FOSSIL_ENABLE_JSON #ifndef CSON_FOSSIL_MODE #define CSON_FOSSIL_MODE #endif /* auto-generated! Do not edit! */ /* begin file include/wh/cson/cson.h */ #if !defined(WANDERINGHORSE_NET_CSON_H_INCLUDED) #define WANDERINGHORSE_NET_CSON_H_INCLUDED 1 /*#include <stdint.h> C99: fixed-size int types. */ #include <stdio.h> /* FILE decl */ |
| ︙ | ︙ |
Changes to src/db.c.
| ︙ | ︙ | |||
482 483 484 485 486 487 488 |
}
/*
** Execute multiple SQL statements.
*/
int db_multi_exec(const char *zSql, ...){
Blob sql;
| | | > > > > | | > > > > | > > | 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 |
}
/*
** Execute multiple SQL statements.
*/
int db_multi_exec(const char *zSql, ...){
Blob sql;
int rc = SQLITE_OK;
va_list ap;
const char *z, *zEnd;
sqlite3_stmt *pStmt;
blob_init(&sql, 0, 0);
va_start(ap, zSql);
blob_vappendf(&sql, zSql, ap);
va_end(ap);
z = blob_str(&sql);
while( rc==SQLITE_OK && z[0] ){
pStmt = 0;
rc = sqlite3_prepare_v2(g.db, z, -1, &pStmt, &zEnd);
if( rc!=SQLITE_OK ) break;
if( pStmt ){
db.nPrepare++;
while( sqlite3_step(pStmt)==SQLITE_ROW ){}
rc = sqlite3_finalize(pStmt);
if( rc ) db_err("%s: {%.*s}", sqlite3_errmsg(g.db), (int)(zEnd-z), z);
}
z = zEnd;
}
blob_reset(&sql);
return rc;
}
/*
** Optionally make the following changes to the database if feasible and
|
| ︙ | ︙ | |||
640 641 642 643 644 645 646 |
... /* Additional SQL to run. Terminate with NULL. */
){
sqlite3 *db;
int rc;
const char *zSql;
va_list ap;
| | < < < < | 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 |
... /* Additional SQL to run. Terminate with NULL. */
){
sqlite3 *db;
int rc;
const char *zSql;
va_list ap;
db = db_open(zFileName);
sqlite3_exec(db, "BEGIN EXCLUSIVE", 0, 0, 0);
rc = sqlite3_exec(db, zSchema, 0, 0, 0);
if( rc!=SQLITE_OK ){
db_err(sqlite3_errmsg(db));
}
va_start(ap, zSchema);
while( (zSql = va_arg(ap, const char*))!=0 ){
|
| ︙ | ︙ | |||
695 696 697 698 699 700 701 | } /* ** Open a database file. Return a pointer to the new database ** connection. An error results in process abort. */ | | > > > > > > > > > > > > > | 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716 717 718 719 720 721 722 723 724 725 726 727 728 729 730 731 732 733 734 735 736 737 738 739 740 741 742 743 744 745 746 747 |
}
/*
** Open a database file. Return a pointer to the new database
** connection. An error results in process abort.
*/
LOCAL sqlite3 *db_open(const char *zDbName){
int rc;
const char *zVfs;
sqlite3 *db;
if( g.fSqlTrace ) fossil_trace("-- sqlite3_open: [%s]\n", zDbName);
zVfs = fossil_getenv("FOSSIL_VFS");
rc = sqlite3_open_v2(
zDbName, &db,
SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE,
zVfs
);
if( rc!=SQLITE_OK ){
db_err("[%s]: %s", zDbName, sqlite3_errmsg(db));
}
sqlite3_busy_timeout(db, 5000);
sqlite3_wal_autocheckpoint(db, 1); /* Set to checkpoint frequently */
sqlite3_create_function(db, "now", 0, SQLITE_ANY, 0, db_now_function, 0, 0);
sqlite3_create_function(db, "checkin_mtime", 2, SQLITE_ANY, 0,
db_checkin_mtime_function, 0, 0);
sqlite3_create_function(db, "user", 0, SQLITE_ANY, 0, db_sql_user, 0, 0);
sqlite3_create_function(db, "cgi", 1, SQLITE_ANY, 0, db_sql_cgi, 0, 0);
sqlite3_create_function(db, "cgi", 2, SQLITE_ANY, 0, db_sql_cgi, 0, 0);
sqlite3_create_function(db, "print", -1, SQLITE_UTF8, 0,db_sql_print,0,0);
sqlite3_create_function(
db, "is_selected", 1, SQLITE_UTF8, 0, file_is_selected,0,0
);
sqlite3_create_function(
db, "if_selected", 3, SQLITE_UTF8, 0, file_is_selected,0,0
);
if( g.fSqlTrace ) sqlite3_trace(db, db_sql_trace, 0);
re_add_sql_func(db);
sqlite3_exec(db, "PRAGMA foreign_keys=OFF;", 0, 0, 0);
return db;
}
/*
** Detaches the zLabel database.
*/
|
| ︙ | ︙ | |||
746 747 748 749 750 751 752 |
void db_open_or_attach(
const char *zDbName,
const char *zLabel,
int *pWasAttached
){
if( !g.db ){
assert( g.zMainDbType==0 );
| | < | 765 766 767 768 769 770 771 772 773 774 775 776 777 778 779 780 |
void db_open_or_attach(
const char *zDbName,
const char *zLabel,
int *pWasAttached
){
if( !g.db ){
assert( g.zMainDbType==0 );
g.db = db_open(zDbName);
g.zMainDbType = zLabel;
if ( pWasAttached ) *pWasAttached = 0;
}else{
assert( g.zMainDbType!=0 );
db_attach(zDbName, zLabel);
if ( pWasAttached ) *pWasAttached = 1;
}
}
|
| ︙ | ︙ | |||
819 820 821 822 823 824 825 |
}
if( useAttach ){
db_open_or_attach(zDbName, "configdb", &g.useAttach);
g.dbConfig = 0;
g.zConfigDbType = 0;
}else{
g.useAttach = 0;
| | | 837 838 839 840 841 842 843 844 845 846 847 848 849 850 851 |
}
if( useAttach ){
db_open_or_attach(zDbName, "configdb", &g.useAttach);
g.dbConfig = 0;
g.zConfigDbType = 0;
}else{
g.useAttach = 0;
g.dbConfig = db_open(zDbName);
g.zConfigDbType = "configdb";
}
g.configOpen = 1;
free(zDbName);
}
|
| ︙ | ︙ | |||
1443 1444 1445 1446 1447 1448 1449 | /* ** SQL functions for debugging. ** ** The print() function writes its arguments on stdout, but only ** if the -sqlprint command-line option is turned on. */ | | | | | | 1461 1462 1463 1464 1465 1466 1467 1468 1469 1470 1471 1472 1473 1474 1475 1476 1477 1478 1479 1480 1481 1482 1483 1484 1485 1486 1487 1488 1489 1490 1491 1492 1493 1494 1495 1496 1497 1498 1499 1500 1501 1502 1503 1504 1505 1506 1507 1508 1509 1510 1511 1512 1513 |
/*
** SQL functions for debugging.
**
** The print() function writes its arguments on stdout, but only
** if the -sqlprint command-line option is turned on.
*/
LOCAL void db_sql_print(
sqlite3_context *context,
int argc,
sqlite3_value **argv
){
int i;
if( g.fSqlPrint ){
for(i=0; i<argc; i++){
char c = i==argc-1 ? '\n' : ' ';
fossil_print("%s%c", sqlite3_value_text(argv[i]), c);
}
}
}
LOCAL void db_sql_trace(void *notUsed, const char *zSql){
int n = strlen(zSql);
fossil_trace("%s%s\n", zSql, (n>0 && zSql[n-1]==';') ? "" : ";");
}
/*
** Implement the user() SQL function. user() takes no arguments and
** returns the user ID of the current user.
*/
LOCAL void db_sql_user(
sqlite3_context *context,
int argc,
sqlite3_value **argv
){
if( g.zLogin!=0 ){
sqlite3_result_text(context, g.zLogin, -1, SQLITE_STATIC);
}
}
/*
** Implement the cgi() SQL function. cgi() takes an argument which is
** a name of CGI query parameter. The value of that parameter is returned,
** if available. Optional second argument will be returned if the first
** doesn't exist as a CGI parameter.
*/
LOCAL void db_sql_cgi(sqlite3_context *context, int argc, sqlite3_value **argv){
const char* zP;
if( argc!=1 && argc!=2 ) return;
zP = P((const char*)sqlite3_value_text(argv[0]));
if( zP ){
sqlite3_result_text(context, zP, -1, SQLITE_STATIC);
}else if( argc==2 ){
zP = (const char*)sqlite3_value_text(argv[1]);
|
| ︙ | ︙ | |||
1512 1513 1514 1515 1516 1517 1518 | ** named on the command line (g.aCommitFile is NULL meaning that all ** changes are to be committed) or if id is found in g.aCommitFile[] ** (meaning that id was named on the command-line). ** ** In the second form (3 arguments) return argument X if true and Y ** if false. Except if Y is NULL then always return X. */ | | | 1530 1531 1532 1533 1534 1535 1536 1537 1538 1539 1540 1541 1542 1543 1544 |
** named on the command line (g.aCommitFile is NULL meaning that all
** changes are to be committed) or if id is found in g.aCommitFile[]
** (meaning that id was named on the command-line).
**
** In the second form (3 arguments) return argument X if true and Y
** if false. Except if Y is NULL then always return X.
*/
LOCAL void file_is_selected(
sqlite3_context *context,
int argc,
sqlite3_value **argv
){
int rc = 0;
assert(argc==1 || argc==3);
|
| ︙ | ︙ | |||
1600 1601 1602 1603 1604 1605 1606 |
}
if( zOut==0 ){
zOut = mprintf("%s", zKey);
}
return zOut;
}
| < < < < < < < < < < < < < < < < < < < < < < | 1618 1619 1620 1621 1622 1623 1624 1625 1626 1627 1628 1629 1630 1631 |
}
if( zOut==0 ){
zOut = mprintf("%s", zKey);
}
return zOut;
}
/*
** Return true if the string zVal represents "true" (or "false").
*/
int is_truth(const char *zVal){
static const char *const azOn[] = { "on", "yes", "true", "1" };
int i;
for(i=0; i<sizeof(azOn)/sizeof(azOn[0]); i++){
|
| ︙ | ︙ | |||
2174 2175 2176 2177 2178 2179 2180 2181 2182 2183 2184 2185 2186 | ** external diff programs. If FALSE, skip these files. ** ** diff-command External command to run when performing a diff. ** If undefined, the internal text diff will be used. ** ** dont-push Prevent this repository from pushing from client to ** server. Useful when setting up a private branch. ** ** empty-dirs A comma or newline-separated list of pathnames. On ** (versionable) update and checkout commands, if no file or directory ** exists with that name, an empty directory will be ** created. ** | > > < < | 2170 2171 2172 2173 2174 2175 2176 2177 2178 2179 2180 2181 2182 2183 2184 2185 2186 2187 2188 2189 2190 2191 | ** external diff programs. If FALSE, skip these files. ** ** diff-command External command to run when performing a diff. ** If undefined, the internal text diff will be used. ** ** dont-push Prevent this repository from pushing from client to ** server. Useful when setting up a private branch. ** ** editor Text editor command used for check-in comments. ** ** empty-dirs A comma or newline-separated list of pathnames. On ** (versionable) update and checkout commands, if no file or directory ** exists with that name, an empty directory will be ** created. ** ** gdiff-command External command to run when performing a graphical ** diff. If undefined, text diff will be used. ** ** gmerge-command A graphical merge conflict resolver command operating ** on four files. ** Ex: kdiff3 "%baseline" "%original" "%merge" -o "%output" ** Ex: xxdiff "%original" "%baseline" "%merge" -M "%output" |
| ︙ | ︙ | |||
2243 2244 2245 2246 2247 2248 2249 2250 2251 2252 2253 2254 2255 2256 2257 2258 2259 2260 2261 2262 2263 2264 2265 2266 2267 2268 | ** Disable on large repositories for a performance ** improvement. ** ** self-register Allow users to register themselves through the HTTP UI. ** This is useful if you want to see other names than ** "Anonymous" in e.g. ticketing system. On the other hand ** users can not be deleted. Default: off. ** ** ssl-ca-location The full pathname to a file containing PEM encoded ** CA root certificates, or a directory of certificates ** with filenames formed from the certificate hashes as ** required by OpenSSL. ** If set, this will override the OS default list of ** OpenSSL CAs. If unset, the default list will be used. ** Some platforms may add additional certificates. ** Check your platform behaviour is as required if the ** exact contents of the CA root is critical for your ** application. ** ** ssl-identity The full pathname to a file containing a certificate ** and private key in PEM format. Create by concatenating ** the certificate and private key files. ** This identity will be presented to SSL servers to ** authenticate this client, in addition to the normal ** password authentication. ** | > > > < < < | 2239 2240 2241 2242 2243 2244 2245 2246 2247 2248 2249 2250 2251 2252 2253 2254 2255 2256 2257 2258 2259 2260 2261 2262 2263 2264 2265 2266 2267 2268 2269 2270 2271 2272 2273 2274 | ** Disable on large repositories for a performance ** improvement. ** ** self-register Allow users to register themselves through the HTTP UI. ** This is useful if you want to see other names than ** "Anonymous" in e.g. ticketing system. On the other hand ** users can not be deleted. Default: off. ** ** ssh-command Command used to talk to a remote machine with ** the "ssh://" protocol. ** ** ssl-ca-location The full pathname to a file containing PEM encoded ** CA root certificates, or a directory of certificates ** with filenames formed from the certificate hashes as ** required by OpenSSL. ** If set, this will override the OS default list of ** OpenSSL CAs. If unset, the default list will be used. ** Some platforms may add additional certificates. ** Check your platform behaviour is as required if the ** exact contents of the CA root is critical for your ** application. ** ** ssl-identity The full pathname to a file containing a certificate ** and private key in PEM format. Create by concatenating ** the certificate and private key files. ** This identity will be presented to SSL servers to ** authenticate this client, in addition to the normal ** password authentication. ** ** tcl If enabled (and Fossil was compiled with Tcl support), ** Tcl integration commands will be added to the TH1 ** interpreter, allowing arbitrary Tcl expressions and ** scripts to be evaluated from TH1. Additionally, the Tcl ** interpreter will be able to evaluate arbitrary TH1 ** expressions and scripts. Default: off. ** |
| ︙ | ︙ |
Changes to src/encode.c.
| ︙ | ︙ | |||
129 130 131 132 133 134 135 |
** This is the opposite of DeHttpizeString below.
*/
static char *EncodeHttp(const char *zIn, int n, int encodeSlash){
int c;
int i = 0;
int count = 0;
char *zOut;
| < | > < | 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 |
** This is the opposite of DeHttpizeString below.
*/
static char *EncodeHttp(const char *zIn, int n, int encodeSlash){
int c;
int i = 0;
int count = 0;
char *zOut;
# define IsSafeChar(X) \
(fossil_isalnum(X) || (X)=='.' || (X)=='$' \
|| (X)=='~' || (X)=='-' || (X)=='_' \
|| (!encodeSlash && ((X)=='/' || (X)==':')))
if( zIn==0 ) return 0;
if( n<0 ) n = strlen(zIn);
while( i<n && (c = zIn[i])!=0 ){
if( IsSafeChar(c) || c==' ' ){
count++;
}else{
count += 3;
}
i++;
|
| ︙ | ︙ |
Changes to src/file.c.
| ︙ | ︙ | |||
481 482 483 484 485 486 487 488 489 490 491 | /* ** Return true if the filename given is a valid filename for ** a file in a repository. Valid filenames follow all of the ** following rules: ** ** * Does not begin with "/" ** * Does not contain any path element named "." or ".." ** * Does not end with "/". ** * Does not contain two or more "/" characters in a row. ** * Contains at least one character ** | > | | | > > > > | > | < | > > > > > > > | > > > < | > | > > > | | | < > | > > > | | < | | | > > > > | | < | 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 |
/*
** Return true if the filename given is a valid filename for
** a file in a repository. Valid filenames follow all of the
** following rules:
**
** * Does not begin with "/"
** * Does not contain any path element named "." or ".."
** * Does not contain any of these characters in the path: "\"
** * Does not end with "/".
** * Does not contain two or more "/" characters in a row.
** * Contains at least one character
**
** Invalid UTF8 characters result in a false return if bStrictUtf8 is
** true. If bStrictUtf8 is false, invalid UTF8 characters are silently
** ignored. See http://en.wikipedia.org/wiki/UTF-8#Invalid_byte_sequences
** and http://en.wikipedia.org/wiki/Unicode (for the noncharacters)
**
** The bStrictUtf8 flag is true for new inputs, but is false when parsing
** legacy manifests, for backwards compatibility.
*/
int file_is_simple_pathname(const char *z, int bStrictUtf8){
int i;
unsigned char c = (unsigned char) z[0];
char maskNonAscii = bStrictUtf8 ? 0x80 : 0x00;
if( c=='/' || c==0 ) return 0;
if( c=='.' ){
if( z[1]=='/' || z[1]==0 ) return 0;
if( z[1]=='.' && (z[2]=='/' || z[2]==0) ) return 0;
}
for(i=0; (c=(unsigned char)z[i])!=0; i++){
if( c & maskNonAscii ){
if( (z[++i]&0xc0)!=0x80 ){
/* Invalid first continuation byte */
return 0;
}
if( c<0xc2 ){
/* Invalid 1-byte UTF-8 sequence, or 2-byte overlong form. */
return 0;
}else if( (c&0xe0)==0xe0 ){
/* 3-byte or more */
int unicode;
if( c&0x10 ){
/* Unicode characters > U+FFFF are not supported.
* Windows XP and earlier cannot handle them.
*/
return 0;
}
/* This is a 3-byte UTF-8 character */
unicode = ((c&0x0f)<<12) + ((z[i]&0x3f)<<6) + (z[i+1]&0x3f);
if( unicode <= 0x07ff ){
/* overlong form */
return 0;
}else if( unicode>=0xe000 ){
/* U+E000..U+FFFF */
if( (unicode<=0xf8ff) || (unicode>=0xfffe) ){
/* U+E000..U+F8FF are for private use.
* U+FFFE..U+FFFF are noncharacters. */
return 0;
} else if( (unicode>=0xfdd0) && (unicode<=0xfdef) ){
/* U+FDD0..U+FDEF are noncharacters. */
return 0;
}
}else if( (unicode>=0xd800) && (unicode<=0xdfff) ){
/* U+D800..U+DFFF are for surrogate pairs. */
return 0;
}
if( (z[++i]&0xc0)!=0x80 ){
/* Invalid second continuation byte */
return 0;
}
}
}else if( bStrictUtf8 && (c=='\\') ){
return 0;
}
if( c=='/' ){
if( z[i+1]=='/' ) return 0;
if( z[i+1]=='.' ){
if( z[i+2]=='/' || z[i+2]==0 ) return 0;
if( z[i+2]=='.' && (z[i+3]=='/' || z[i+3]==0) ) return 0;
}
|
| ︙ | ︙ | |||
576 577 578 579 580 581 582 |
#if defined(_WIN32)
for(i=0; i<n; i++){
if( z[i]=='\\' ) z[i] = '/';
}
#endif
/* Removing trailing "/" characters */
| | | 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 |
#if defined(_WIN32)
for(i=0; i<n; i++){
if( z[i]=='\\' ) z[i] = '/';
}
#endif
/* Removing trailing "/" characters */
if( !slash ){
while( n>1 && z[n-1]=='/' ){ n--; }
}
/* Remove duplicate '/' characters. Except, two // at the beginning
** of a pathname is allowed since this is important on windows. */
for(i=j=1; i<n; i++){
z[j++] = z[i];
|
| ︙ | ︙ | |||
833 834 835 836 837 838 839 |
if( zPath[i]==0 ){
blob_reset(pOut);
if( zPwd[i]==0 ){
blob_append(pOut, ".", 1);
}else{
blob_append(pOut, "..", 2);
for(j=i+1; zPwd[j]; j++){
| | | | 856 857 858 859 860 861 862 863 864 865 866 867 868 869 870 871 872 873 874 875 876 877 878 879 880 881 882 883 884 885 886 887 |
if( zPath[i]==0 ){
blob_reset(pOut);
if( zPwd[i]==0 ){
blob_append(pOut, ".", 1);
}else{
blob_append(pOut, "..", 2);
for(j=i+1; zPwd[j]; j++){
if( zPwd[j]=='/' ){
blob_append(pOut, "/..", 3);
}
}
}
return;
}
if( zPwd[i]==0 && zPath[i]=='/' ){
memcpy(&tmp, pOut, sizeof(tmp));
blob_set(pOut, "./");
blob_append(pOut, &zPath[i+1], -1);
blob_reset(&tmp);
return;
}
while( zPath[i-1]!='/' ){ i--; }
blob_set(&tmp, "../");
for(j=i; zPwd[j]; j++){
if( zPwd[j]=='/' ){
blob_append(&tmp, "../", 3);
}
}
blob_append(&tmp, &zPath[i], -1);
blob_reset(pOut);
memcpy(pOut, &tmp, sizeof(tmp));
}
|
| ︙ | ︙ |
Changes to src/info.c.
| ︙ | ︙ | |||
206 207 208 209 210 211 212 |
}
#endif
fossil_print("project-code: %s\n", db_get("project-code", ""));
vid = g.localOpen ? db_lget_int("checkout", 0) : 0;
if( vid ){
show_common_info(vid, "checkout:", 1, 1);
}
| | | 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 |
}
#endif
fossil_print("project-code: %s\n", db_get("project-code", ""));
vid = g.localOpen ? db_lget_int("checkout", 0) : 0;
if( vid ){
show_common_info(vid, "checkout:", 1, 1);
}
fossil_print("checkins: %d\n",
db_int(-1, "SELECT count(distinct mid) FROM mlink /*scan*/"));
}else{
int rid;
rid = name_to_rid(g.argv[2]);
if( rid==0 ){
fossil_panic("no such object: %s\n", g.argv[2]);
}
|
| ︙ | ︙ |
Changes to src/json.c.
| ︙ | ︙ | |||
2083 2084 2085 2086 2087 2088 2089 |
n = db_int(0, "SELECT count(*) FROM tag /*scan*/"
" WHERE +tagname GLOB 'tkt-*'");
cson_object_set(jo, "ticketCount", cson_value_new_integer((cson_int_t)n));
}/*full*/
n = db_int(0, "SELECT julianday('now') - (SELECT min(mtime) FROM event)"
" + 0.99");
cson_object_set(jo, "ageDays", cson_value_new_integer((cson_int_t)n));
| | < < | 2083 2084 2085 2086 2087 2088 2089 2090 2091 2092 2093 2094 2095 2096 2097 2098 2099 |
n = db_int(0, "SELECT count(*) FROM tag /*scan*/"
" WHERE +tagname GLOB 'tkt-*'");
cson_object_set(jo, "ticketCount", cson_value_new_integer((cson_int_t)n));
}/*full*/
n = db_int(0, "SELECT julianday('now') - (SELECT min(mtime) FROM event)"
" + 0.99");
cson_object_set(jo, "ageDays", cson_value_new_integer((cson_int_t)n));
cson_object_set(jo, "ageYears", cson_value_new_double(n/365.2425));
sqlite3_snprintf(BufLen, zBuf, db_get("project-code",""));
SETBUF(jo, "projectCode");
cson_object_set(jo, "compiler", cson_value_new_string(COMPILER_NAME, strlen(COMPILER_NAME)));
jv2 = cson_value_new_object();
jo2 = cson_value_get_object(jv2);
cson_object_set(jo, "sqlite", jv2);
sqlite3_snprintf(BufLen, zBuf, "%.19s [%.10s] (%s)",
SQLITE_SOURCE_ID, &SQLITE_SOURCE_ID[20], SQLITE_VERSION);
|
| ︙ | ︙ |
Changes to src/main.mk.
| ︙ | ︙ | |||
1144 1145 1146 1147 1148 1149 1150 | $(XTCC) -c $(SRCDIR)/th_lang.c -o $(OBJDIR)/th_lang.o $(OBJDIR)/th_tcl.o: $(SRCDIR)/th_tcl.c $(XTCC) -c $(SRCDIR)/th_tcl.c -o $(OBJDIR)/th_tcl.o $(OBJDIR)/cson_amalgamation.o: $(SRCDIR)/cson_amalgamation.c | | | 1144 1145 1146 1147 1148 1149 1150 1151 1152 1153 1154 1155 1156 1157 1158 | $(XTCC) -c $(SRCDIR)/th_lang.c -o $(OBJDIR)/th_lang.o $(OBJDIR)/th_tcl.o: $(SRCDIR)/th_tcl.c $(XTCC) -c $(SRCDIR)/th_tcl.c -o $(OBJDIR)/th_tcl.o $(OBJDIR)/cson_amalgamation.o: $(SRCDIR)/cson_amalgamation.c $(XTCC) -c $(SRCDIR)/cson_amalgamation.c -o $(OBJDIR)/cson_amalgamation.o # # The list of all the targets that do not correspond to real files. This stops # 'make' from getting confused when someone makes an error in a rule. # .PHONY: all install test clean |
| ︙ | ︙ |
Changes to src/makemake.tcl.
| ︙ | ︙ | |||
306 307 308 309 310 311 312 |
writeln "\$(OBJDIR)/th_tcl.o:\t\$(SRCDIR)/th_tcl.c"
writeln "\t\$(XTCC) -c \$(SRCDIR)/th_tcl.c -o \$(OBJDIR)/th_tcl.o\n"
set opt {}
writeln {
$(OBJDIR)/cson_amalgamation.o: $(SRCDIR)/cson_amalgamation.c
| | | 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 |
writeln "\$(OBJDIR)/th_tcl.o:\t\$(SRCDIR)/th_tcl.c"
writeln "\t\$(XTCC) -c \$(SRCDIR)/th_tcl.c -o \$(OBJDIR)/th_tcl.o\n"
set opt {}
writeln {
$(OBJDIR)/cson_amalgamation.o: $(SRCDIR)/cson_amalgamation.c
$(XTCC) -c $(SRCDIR)/cson_amalgamation.c -o $(OBJDIR)/cson_amalgamation.o
#
# The list of all the targets that do not correspond to real files. This stops
# 'make' from getting confused when someone makes an error in a rule.
#
.PHONY: all install test clean
|
| ︙ | ︙ | |||
749 750 751 752 753 754 755 |
writeln "\$(OBJDIR)/sqlite3.o:\t\$(SRCDIR)/sqlite3.c"
set opt $SQLITE_OPTIONS
writeln "\t\$(XTCC) $opt -c \$(SRCDIR)/sqlite3.c -o \$(OBJDIR)/sqlite3.o\n"
set opt {}
writeln "\$(OBJDIR)/cson_amalgamation.o:\t\$(SRCDIR)/cson_amalgamation.c"
| | | 749 750 751 752 753 754 755 756 757 758 759 760 761 762 763 |
writeln "\$(OBJDIR)/sqlite3.o:\t\$(SRCDIR)/sqlite3.c"
set opt $SQLITE_OPTIONS
writeln "\t\$(XTCC) $opt -c \$(SRCDIR)/sqlite3.c -o \$(OBJDIR)/sqlite3.o\n"
set opt {}
writeln "\$(OBJDIR)/cson_amalgamation.o:\t\$(SRCDIR)/cson_amalgamation.c"
writeln "\t\$(XTCC) $opt -c \$(SRCDIR)/cson_amalgamation.c -o \$(OBJDIR)/cson_amalgamation.o\n"
writeln "\$(OBJDIR)/json.o \$(OBJDIR)/json_artifact.o \$(OBJDIR)/json_branch.o \$(OBJDIR)/json_config.o \$(OBJDIR)/json_diff.o \$(OBJDIR)/json_dir.o \$(OBJDIR)/jsos_finfo.o \$(OBJDIR)/json_login.o \$(OBJDIR)/json_query.o \$(OBJDIR)/json_report.o \$(OBJDIR)/json_tag.o \$(OBJDIR)/json_timeline.o \$(OBJDIR)/json_user.o \$(OBJDIR)/json_wiki.o : \$(SRCDIR)/json_detail.h\n"
writeln "\$(OBJDIR)/shell.o:\t\$(SRCDIR)/shell.c \$(SRCDIR)/sqlite3.h"
set opt {-Dmain=sqlite3_shell}
append opt " -DSQLITE_OMIT_LOAD_EXTENSION=1"
writeln "\t\$(XTCC) $opt -c \$(SRCDIR)/shell.c -o \$(OBJDIR)/shell.o\n"
|
| ︙ | ︙ | |||
957 958 959 960 961 962 963 964 965 966 967 968 969 970 971 972 973 974 975 976 977 978 979 980 981 982 983 984 985 986 987 |
# SSL = -DFOSSIL_ENABLE_SSL=1
# SSLLIB = ssleay32.lib libeay32.lib user32.lib gdi32.lib advapi32.lib
# zlib options
ZINCDIR = $(B)\compat\zlib
ZLIBDIR = $(B)\compat\zlib
ZLIB = zlib.lib
INCL = -I. -I$(SRCDIR) -I$B\win\include -I$(ZINCDIR)
CFLAGS = -nologo -MT -O2
BCC = $(CC) $(CFLAGS)
TCC = $(CC) -c $(CFLAGS) $(MSCDEF) $(SSL) $(INCL)
LIBS = $(ZLIB) ws2_32.lib advapi32.lib $(SSLLIB)
LIBDIR = -LIBPATH:$(ZLIBDIR)
}
regsub -all {[-]D} $SQLITE_OPTIONS {/D} MSC_SQLITE_OPTIONS
set j " \\\n "
writeln "SQLITE_OPTIONS = [join $MSC_SQLITE_OPTIONS $j]\n"
writeln -nonewline "SRC = "
set i 0
foreach s [lsort $src] {
if {$i > 0} {
writeln " \\"
writeln -nonewline " "
}
writeln -nonewline "${s}_.c"; incr i
}
writeln "\n"
writeln -nonewline "OBJ = "
set i 0
| > > > > > > > > > > > > > > > > > > | < < | < | | | 957 958 959 960 961 962 963 964 965 966 967 968 969 970 971 972 973 974 975 976 977 978 979 980 981 982 983 984 985 986 987 988 989 990 991 992 993 994 995 996 997 998 999 1000 1001 1002 1003 1004 1005 1006 1007 1008 1009 1010 1011 1012 1013 1014 1015 1016 1017 1018 1019 1020 1021 1022 1023 1024 1025 1026 1027 1028 1029 1030 1031 1032 1033 1034 1035 1036 1037 |
# SSL = -DFOSSIL_ENABLE_SSL=1
# SSLLIB = ssleay32.lib libeay32.lib user32.lib gdi32.lib advapi32.lib
# zlib options
ZINCDIR = $(B)\compat\zlib
ZLIBDIR = $(B)\compat\zlib
ZLIB = zlib.lib
# Uncomment to enable JSON API
# FOSSIL_ENABLE_JSON = 1
# Uncomment to enable markdown support
# FOSSIL_ENABLE_MARKDOWN = 1
INCL = -I. -I$(SRCDIR) -I$B\win\include -I$(ZINCDIR)
CFLAGS = -nologo -MT -O2
BCC = $(CC) $(CFLAGS)
TCC = $(CC) -c $(CFLAGS) $(MSCDEF) $(SSL) $(INCL)
RCC = rc -D_WIN32 -D_MSC_VER $(INCL)
LIBS = $(ZLIB) ws2_32.lib advapi32.lib $(SSLLIB)
LIBDIR = -LIBPATH:$(ZLIBDIR)
!ifdef FOSSIL_ENABLE_JSON
TCC = $(TCC) -DFOSSIL_ENABLE_JSON
RCC = $(RCC) -DFOSSIL_ENABLE_JSON
!endif
!ifdef FOSSIL_ENABLE_MARKDOWN
TCC = $(TCC) -DFOSSIL_ENABLE_MARKDOWN
RCC = $(RCC) -DFOSSIL_ENABLE_MARKDOWN
!endif
}
regsub -all {[-]D} $SQLITE_OPTIONS {/D} MSC_SQLITE_OPTIONS
set j " \\\n "
writeln "SQLITE_OPTIONS = [join $MSC_SQLITE_OPTIONS $j]\n"
writeln -nonewline "SRC = "
set i 0
foreach s [lsort $src] {
if {$i > 0} {
writeln " \\"
writeln -nonewline " "
}
writeln -nonewline "${s}_.c"; incr i
}
writeln "\n"
set AdditionalObj [list shell sqlite3 th th_lang cson_amalgamation]
writeln -nonewline "OBJ = "
set i 0
foreach s [lsort [concat $src $AdditionalObj]] {
if {$i > 0} {
writeln " \\"
writeln -nonewline " "
}
writeln -nonewline "\$(OX)\\$s\$O"; incr i
}
writeln " \\"
writeln -nonewline " \$(OX)\\fossil.res\n"
writeln {
APPNAME = $(OX)\fossil$(E)
all: $(OX) $(APPNAME)
zlib:
@echo Building zlib from "$(ZLIBDIR)"...
@pushd "$(ZLIBDIR)" && nmake /f win32\Makefile.msc $(ZLIB) && popd
$(APPNAME) : translate$E mkindex$E headers $(OBJ) $(OX)\linkopts zlib
cd $(OX)
link /NODEFAULTLIB:msvcrt -OUT:$@ $(LIBDIR) Wsetargv.obj fossil.res @linkopts
$(OX)\linkopts: $B\win\Makefile.msc}
set redir {>}
foreach s [lsort [concat $src $AdditionalObj]] {
writeln "\techo \$(OX)\\$s.obj $redir \$@"
set redir {>>}
}
writeln "\techo \$(LIBS) >> \$@\n\n"
writeln {
|
| ︙ | ︙ | |||
1045 1046 1047 1048 1049 1050 1051 | $(TCC) /Fo$@ -c $** $(OX)\th_lang$O : $(SRCDIR)\th_lang.c $(TCC) /Fo$@ -c $** VERSION.h : mkversion$E $B\manifest.uuid $B\manifest $B\VERSION $** > $@ | < | > > | 1060 1061 1062 1063 1064 1065 1066 1067 1068 1069 1070 1071 1072 1073 1074 1075 1076 1077 1078 1079 1080 1081 1082 1083 1084 1085 1086 1087 1088 1089 | $(TCC) /Fo$@ -c $** $(OX)\th_lang$O : $(SRCDIR)\th_lang.c $(TCC) /Fo$@ -c $** VERSION.h : mkversion$E $B\manifest.uuid $B\manifest $B\VERSION $** > $@ $(OX)\cson_amalgamation$O : $(SRCDIR)\cson_amalgamation.c $(TCC) /Fo$@ -c $** page_index.h: mkindex$E $(SRC) $** > $@ clean: -del $(OX)\*.obj -del *.obj -del *_.c -del *.h -del *.map -del *.manifest -del headers -del linkopts -del *.res realclean: clean -del $(APPNAME) -del translate$E -del mkindex$E -del makeheaders$E -del mkversion$E |
| ︙ | ︙ | |||
1090 1091 1092 1093 1094 1095 1096 1097 1098 1099 1100 1101 1102 1103 |
}
foreach s [lsort $src] {
writeln "\$(OX)\\$s\$O : ${s}_.c ${s}.h"
writeln "\t\$(TCC) /Fo\$@ -c ${s}_.c\n"
writeln "${s}_.c : \$(SRCDIR)\\$s.c"
writeln "\ttranslate\$E \$** > \$@\n"
}
writeln "headers: makeheaders\$E page_index.h VERSION.h"
writeln -nonewline "\tmakeheaders\$E "
set i 0
foreach s [lsort $src] {
if {$i > 0} {
writeln " \\"
| > > > | 1106 1107 1108 1109 1110 1111 1112 1113 1114 1115 1116 1117 1118 1119 1120 1121 1122 |
}
foreach s [lsort $src] {
writeln "\$(OX)\\$s\$O : ${s}_.c ${s}.h"
writeln "\t\$(TCC) /Fo\$@ -c ${s}_.c\n"
writeln "${s}_.c : \$(SRCDIR)\\$s.c"
writeln "\ttranslate\$E \$** > \$@\n"
}
writeln "fossil.res : \$B\\win\\fossil.rc"
writeln "\t\$(RCC) -fo \$@ \$**"
writeln "headers: makeheaders\$E page_index.h VERSION.h"
writeln -nonewline "\tmakeheaders\$E "
set i 0
foreach s [lsort $src] {
if {$i > 0} {
writeln " \\"
|
| ︙ | ︙ |
Changes to src/merge.c.
| ︙ | ︙ | |||
203 204 205 206 207 208 209 210 211 212 213 214 215 216 |
db_column_text(&q, 3), db_column_text(&q, 2));
comment_print(zCom, 0, 79);
fossil_free(zCom);
}
db_finalize(&q);
}else{
usage("?OPTIONS? ?VERSION?");
}
if( zPivot ){
pid = name_to_typed_rid(zPivot, "ci");
if( pid==0 || !is_a_version(pid) ){
fossil_fatal("not a version: %s", zPivot);
}
| > | 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 |
db_column_text(&q, 3), db_column_text(&q, 2));
comment_print(zCom, 0, 79);
fossil_free(zCom);
}
db_finalize(&q);
}else{
usage("?OPTIONS? ?VERSION?");
return;
}
if( zPivot ){
pid = name_to_typed_rid(zPivot, "ci");
if( pid==0 || !is_a_version(pid) ){
fossil_fatal("not a version: %s", zPivot);
}
|
| ︙ | ︙ |
Changes to src/rebuild.c.
| ︙ | ︙ | |||
522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 |
** --compress Strive to make the database as small as possible
** --force Force the rebuild to complete even if errors are seen
** --noverify Skip the verification of changes to the BLOB table
** --pagesize N Set the database pagesize to N. (512..65536 and power of 2)
** --randomize Scan artifacts in a random order
** --vacuum Run VACUUM on the database after rebuilding
** --deanalyze Remove ANALYZE tables from the database
** --wal Set Write-Ahead-Log journalling mode on the database
** --stats Show artifact statistics after rebuilding
**
** See also: deconstruct, reconstruct
*/
void rebuild_database(void){
int forceFlag;
int randomizeFlag;
int errCnt;
int omitVerify;
int doClustering;
const char *zPagesize;
int newPagesize = 0;
int activateWal;
int runVacuum;
int runDeanalyze;
int runCompress;
int showStats;
omitVerify = find_option("noverify",0,0)!=0;
forceFlag = find_option("force","f",0)!=0;
randomizeFlag = find_option("randomize", 0, 0)!=0;
doClustering = find_option("cluster", 0, 0)!=0;
runVacuum = find_option("vacuum",0,0)!=0;
runDeanalyze = find_option("deanalyze",0,0)!=0;
runCompress = find_option("compress",0,0)!=0;
zPagesize = find_option("pagesize",0,1);
showStats = find_option("stats",0,0)!=0;
if( zPagesize ){
newPagesize = atoi(zPagesize);
if( newPagesize<512 || newPagesize>65536
|| (newPagesize&(newPagesize-1))!=0
| > > > | 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 |
** --compress Strive to make the database as small as possible
** --force Force the rebuild to complete even if errors are seen
** --noverify Skip the verification of changes to the BLOB table
** --pagesize N Set the database pagesize to N. (512..65536 and power of 2)
** --randomize Scan artifacts in a random order
** --vacuum Run VACUUM on the database after rebuilding
** --deanalyze Remove ANALYZE tables from the database
** --analyze Run ANALYZE on the database after rebuilding
** --wal Set Write-Ahead-Log journalling mode on the database
** --stats Show artifact statistics after rebuilding
**
** See also: deconstruct, reconstruct
*/
void rebuild_database(void){
int forceFlag;
int randomizeFlag;
int errCnt;
int omitVerify;
int doClustering;
const char *zPagesize;
int newPagesize = 0;
int activateWal;
int runVacuum;
int runDeanalyze;
int runAnalyze;
int runCompress;
int showStats;
omitVerify = find_option("noverify",0,0)!=0;
forceFlag = find_option("force","f",0)!=0;
randomizeFlag = find_option("randomize", 0, 0)!=0;
doClustering = find_option("cluster", 0, 0)!=0;
runVacuum = find_option("vacuum",0,0)!=0;
runDeanalyze = find_option("deanalyze",0,0)!=0;
runAnalyze = find_option("analyze",0,0)!=0;
runCompress = find_option("compress",0,0)!=0;
zPagesize = find_option("pagesize",0,1);
showStats = find_option("stats",0,0)!=0;
if( zPagesize ){
newPagesize = atoi(zPagesize);
if( newPagesize<512 || newPagesize>65536
|| (newPagesize&(newPagesize-1))!=0
|
| ︙ | ︙ | |||
603 604 605 606 607 608 609 610 611 612 613 614 615 616 |
db_multi_exec("PRAGMA page_size=%d", newPagesize);
runVacuum = 1;
}
if( runDeanalyze ){
db_multi_exec("DROP TABLE IF EXISTS sqlite_stat1;"
"DROP TABLE IF EXISTS sqlite_stat3;");
}
if( runVacuum ){
fossil_print("Vacuuming the database... "); fflush(stdout);
db_multi_exec("VACUUM");
fossil_print("done\n");
}
if( activateWal ){
db_multi_exec("PRAGMA journal_mode=WAL;");
| > > > > > | 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 |
db_multi_exec("PRAGMA page_size=%d", newPagesize);
runVacuum = 1;
}
if( runDeanalyze ){
db_multi_exec("DROP TABLE IF EXISTS sqlite_stat1;"
"DROP TABLE IF EXISTS sqlite_stat3;");
}
if( runAnalyze ){
fossil_print("Analyzing the database... "); fflush(stdout);
db_multi_exec("ANALYZE;");
fossil_print("done\n");
}
if( runVacuum ){
fossil_print("Vacuuming the database... "); fflush(stdout);
db_multi_exec("VACUUM");
fossil_print("done\n");
}
if( activateWal ){
db_multi_exec("PRAGMA journal_mode=WAL;");
|
| ︙ | ︙ |
Changes to src/regexp.c.
| ︙ | ︙ | |||
170 171 172 173 174 175 176 |
/* Return true if c is a "digit" character: [0-9] */
static int re_digit_char(int c){
return (c>='0' && c<='9');
}
/* Return true if c is a perl "space" character: [ \t\r\n\v\f] */
static int re_space_char(int c){
| | | 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 |
/* Return true if c is a "digit" character: [0-9] */
static int re_digit_char(int c){
return (c>='0' && c<='9');
}
/* Return true if c is a perl "space" character: [ \t\r\n\v\f] */
static int re_space_char(int c){
return c==' ' || c=='\t' || c=='\n' || c=='\r' || c=='\v' || c=='\f';
}
/* Run a compiled regular expression on the zero-terminated input
** string zIn[]. Return true on a match and false if there is no match.
*/
int re_match(ReCompiled *pRe, const unsigned char *zIn, int nIn){
ReStateSet aStateSet[2], *pThis, *pNext;
|
| ︙ | ︙ | |||
399 400 401 402 403 404 405 |
&& re_hex(zIn[3],&v)
&& re_hex(zIn[4],&v)
){
p->sIn.i += 5;
return v;
}
}
| | < | > > | | < | 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 |
&& re_hex(zIn[3],&v)
&& re_hex(zIn[4],&v)
){
p->sIn.i += 5;
return v;
}
}
if( c=='x' && p->sIn.i+2<p->sIn.mx ){
const unsigned char *zIn = p->sIn.z + p->sIn.i;
if( re_hex(zIn[1],&v)
&& re_hex(zIn[2],&v)
){
p->sIn.i += 3;
return v;
}
}
for(i=0; zEsc[i] && zEsc[i]!=c; i++){}
if( zEsc[i] ){
if( i<6 ) c = zTrans[i];
p->sIn.i++;
}else{
|
| ︙ | ︙ |
Changes to src/setup.c.
| ︙ | ︙ | |||
915 916 917 918 919 920 921 922 923 924 925 926 927 928 |
"5000000");
@ <p>Fossil tries to limit out-bound sync, clone, and pull packets
@ to this many bytes, uncompressed. If the client requires more data
@ than this, then the client will issue multiple HTTP requests.
@ Values below 1 million are not recommended. 5 million is a
@ reasonable number.</p>
@ <hr />
onoff_attribute(
"Enable hyperlinks for \"nobody\" based on User-Agent and Javascript",
"auto-hyperlink", "autohyperlink", 1);
@ <p>Enable hyperlinks (the equivalent of the "h" permission) for all users
@ including user "nobody", as long as (1) the User-Agent string in the
@ HTTP header indicates that the request is coming from an actual human
| > > > > > > > > > | 915 916 917 918 919 920 921 922 923 924 925 926 927 928 929 930 931 932 933 934 935 936 937 |
"5000000");
@ <p>Fossil tries to limit out-bound sync, clone, and pull packets
@ to this many bytes, uncompressed. If the client requires more data
@ than this, then the client will issue multiple HTTP requests.
@ Values below 1 million are not recommended. 5 million is a
@ reasonable number.</p>
@ <hr />
entry_attribute("Download time limit", 11, "max-download-time", "mxdwnt",
"30");
@ <p>Fossil tries to spend less than this many seconds gathering
@ the out-bound data of sync, clone, and pull packets.
@ If the client request takes longer, a partial reply is given similar
@ to the download packet limit. 30s is a reasonable default.</p>
@ <hr />
onoff_attribute(
"Enable hyperlinks for \"nobody\" based on User-Agent and Javascript",
"auto-hyperlink", "autohyperlink", 1);
@ <p>Enable hyperlinks (the equivalent of the "h" permission) for all users
@ including user "nobody", as long as (1) the User-Agent string in the
@ HTTP header indicates that the request is coming from an actual human
|
| ︙ | ︙ |
Changes to src/sqlite3.c.
| ︙ | ︙ | |||
671 672 673 674 675 676 677 | ** ** See also: [sqlite3_libversion()], ** [sqlite3_libversion_number()], [sqlite3_sourceid()], ** [sqlite_version()] and [sqlite_source_id()]. */ #define SQLITE_VERSION "3.7.16" #define SQLITE_VERSION_NUMBER 3007016 | | | 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 | ** ** See also: [sqlite3_libversion()], ** [sqlite3_libversion_number()], [sqlite3_sourceid()], ** [sqlite_version()] and [sqlite_source_id()]. */ #define SQLITE_VERSION "3.7.16" #define SQLITE_VERSION_NUMBER 3007016 #define SQLITE_SOURCE_ID "2013-01-17 17:20:49 38852f158ab20bb4d7b264af987ec1538052bec3" /* ** CAPI3REF: Run-Time Library Version Numbers ** KEYWORDS: sqlite3_version, sqlite3_sourceid ** ** These interfaces provide the same information as the [SQLITE_VERSION], ** [SQLITE_VERSION_NUMBER], and [SQLITE_SOURCE_ID] C preprocessor macros |
| ︙ | ︙ | |||
8236 8237 8238 8239 8240 8241 8242 8243 8244 8245 8246 8247 8248 8249 | /* ** A convenience macro that returns the number of elements in ** an array. */ #define ArraySize(X) ((int)(sizeof(X)/sizeof(X[0]))) /* ** The following value as a destructor means to use sqlite3DbFree(). ** The sqlite3DbFree() routine requires two parameters instead of the ** one parameter that destructors normally want. So we have to introduce ** this magic value that the code knows to handle differently. Any ** pointer will work here as long as it is distinct from SQLITE_STATIC ** and SQLITE_TRANSIENT. | > > > > > | 8236 8237 8238 8239 8240 8241 8242 8243 8244 8245 8246 8247 8248 8249 8250 8251 8252 8253 8254 | /* ** A convenience macro that returns the number of elements in ** an array. */ #define ArraySize(X) ((int)(sizeof(X)/sizeof(X[0]))) /* ** Determine if the argument is a power of two */ #define IsPowerOfTwo(X) (((X)&((X)-1))==0) /* ** The following value as a destructor means to use sqlite3DbFree(). ** The sqlite3DbFree() routine requires two parameters instead of the ** one parameter that destructors normally want. So we have to introduce ** this magic value that the code knows to handle differently. Any ** pointer will work here as long as it is distinct from SQLITE_STATIC ** and SQLITE_TRANSIENT. |
| ︙ | ︙ | |||
10040 10041 10042 10043 10044 10045 10046 10047 10048 10049 10050 10051 10052 10053 | #define SQLITE_GroupByOrder 0x0004 /* GROUPBY cover of ORDERBY */ #define SQLITE_FactorOutConst 0x0008 /* Constant factoring */ #define SQLITE_IdxRealAsInt 0x0010 /* Store REAL as INT in indices */ #define SQLITE_DistinctOpt 0x0020 /* DISTINCT using indexes */ #define SQLITE_CoverIdxScan 0x0040 /* Covering index scans */ #define SQLITE_OrderByIdxJoin 0x0080 /* ORDER BY of joins via index */ #define SQLITE_SubqCoroutine 0x0100 /* Evaluate subqueries as coroutines */ #define SQLITE_AllOpts 0xffff /* All optimizations */ /* ** Macros for testing whether or not optimizations are enabled or disabled. */ #ifndef SQLITE_OMIT_BUILTIN_TEST #define OptimizationDisabled(db, mask) (((db)->dbOptFlags&(mask))!=0) | > | 10045 10046 10047 10048 10049 10050 10051 10052 10053 10054 10055 10056 10057 10058 10059 | #define SQLITE_GroupByOrder 0x0004 /* GROUPBY cover of ORDERBY */ #define SQLITE_FactorOutConst 0x0008 /* Constant factoring */ #define SQLITE_IdxRealAsInt 0x0010 /* Store REAL as INT in indices */ #define SQLITE_DistinctOpt 0x0020 /* DISTINCT using indexes */ #define SQLITE_CoverIdxScan 0x0040 /* Covering index scans */ #define SQLITE_OrderByIdxJoin 0x0080 /* ORDER BY of joins via index */ #define SQLITE_SubqCoroutine 0x0100 /* Evaluate subqueries as coroutines */ #define SQLITE_Transitive 0x0200 /* Transitive constraints */ #define SQLITE_AllOpts 0xffff /* All optimizations */ /* ** Macros for testing whether or not optimizations are enabled or disabled. */ #ifndef SQLITE_OMIT_BUILTIN_TEST #define OptimizationDisabled(db, mask) (((db)->dbOptFlags&(mask))!=0) |
| ︙ | ︙ | |||
93579 93580 93581 93582 93583 93584 93585 |
sqlite3_key(db, zKey, i/2);
}else{
sqlite3_rekey(db, zKey, i/2);
}
}else
#endif
#if defined(SQLITE_HAS_CODEC) || defined(SQLITE_ENABLE_CEROD)
| | | 93585 93586 93587 93588 93589 93590 93591 93592 93593 93594 93595 93596 93597 93598 93599 |
sqlite3_key(db, zKey, i/2);
}else{
sqlite3_rekey(db, zKey, i/2);
}
}else
#endif
#if defined(SQLITE_HAS_CODEC) || defined(SQLITE_ENABLE_CEROD)
if( sqlite3StrICmp(zLeft, "activate_extensions")==0 && zRight ){
#ifdef SQLITE_HAS_CODEC
if( sqlite3StrNICmp(zRight, "see-", 4)==0 ){
sqlite3_activate_see(&zRight[4]);
}
#endif
#ifdef SQLITE_ENABLE_CEROD
if( sqlite3StrNICmp(zRight, "cerod-", 6)==0 ){
|
| ︙ | ︙ | |||
102800 102801 102802 102803 102804 102805 102806 |
typedef struct WhereTerm WhereTerm;
struct WhereTerm {
Expr *pExpr; /* Pointer to the subexpression that is this term */
int iParent; /* Disable pWC->a[iParent] when this term disabled */
int leftCursor; /* Cursor number of X in "X <op> <expr>" */
union {
int leftColumn; /* Column number of X in "X <op> <expr>" */
| | | | 102806 102807 102808 102809 102810 102811 102812 102813 102814 102815 102816 102817 102818 102819 102820 102821 |
typedef struct WhereTerm WhereTerm;
struct WhereTerm {
Expr *pExpr; /* Pointer to the subexpression that is this term */
int iParent; /* Disable pWC->a[iParent] when this term disabled */
int leftCursor; /* Cursor number of X in "X <op> <expr>" */
union {
int leftColumn; /* Column number of X in "X <op> <expr>" */
WhereOrInfo *pOrInfo; /* Extra information if (eOperator & WO_OR)!=0 */
WhereAndInfo *pAndInfo; /* Extra information if (eOperator& WO_AND)!=0 */
} u;
u16 eOperator; /* A WO_xx value describing <op> */
u8 wtFlags; /* TERM_xxx bit flags. See below */
u8 nChild; /* Number of children that must disable us */
WhereClause *pWC; /* The clause this term is part of */
Bitmask prereqRight; /* Bitmask of tables used by pExpr->pRight */
Bitmask prereqAll; /* Bitmask of tables referenced by pExpr */
|
| ︙ | ︙ | |||
102929 102930 102931 102932 102933 102934 102935 102936 102937 102938 102939 102940 102941 102942 | #define WO_LE (WO_EQ<<(TK_LE-TK_EQ)) #define WO_GT (WO_EQ<<(TK_GT-TK_EQ)) #define WO_GE (WO_EQ<<(TK_GE-TK_EQ)) #define WO_MATCH 0x040 #define WO_ISNULL 0x080 #define WO_OR 0x100 /* Two or more OR-connected terms */ #define WO_AND 0x200 /* Two or more AND-connected terms */ #define WO_NOOP 0x800 /* This term does not restrict search space */ #define WO_ALL 0xfff /* Mask of all possible WO_* values */ #define WO_SINGLE 0x0ff /* Mask of all non-compound WO_* values */ /* ** Value for wsFlags returned by bestIndex() and stored in | > | 102935 102936 102937 102938 102939 102940 102941 102942 102943 102944 102945 102946 102947 102948 102949 | #define WO_LE (WO_EQ<<(TK_LE-TK_EQ)) #define WO_GT (WO_EQ<<(TK_GT-TK_EQ)) #define WO_GE (WO_EQ<<(TK_GE-TK_EQ)) #define WO_MATCH 0x040 #define WO_ISNULL 0x080 #define WO_OR 0x100 /* Two or more OR-connected terms */ #define WO_AND 0x200 /* Two or more AND-connected terms */ #define WO_EQUIV 0x400 /* Of the form A==B, both columns */ #define WO_NOOP 0x800 /* This term does not restrict search space */ #define WO_ALL 0xfff /* Mask of all possible WO_* values */ #define WO_SINGLE 0x0ff /* Mask of all non-compound WO_* values */ /* ** Value for wsFlags returned by bestIndex() and stored in |
| ︙ | ︙ | |||
103331 103332 103333 103334 103335 103336 103337 103338 103339 103340 103341 103342 103343 103344 103345 103346 |
}
/*
** Search for a term in the WHERE clause that is of the form "X <op> <expr>"
** where X is a reference to the iColumn of table iCur and <op> is one of
** the WO_xx operator codes specified by the op parameter.
** Return a pointer to the term. Return 0 if not found.
*/
static WhereTerm *findTerm(
WhereClause *pWC, /* The WHERE clause to be searched */
int iCur, /* Cursor number of LHS */
int iColumn, /* Column number of LHS */
Bitmask notReady, /* RHS must not overlap with this mask */
u32 op, /* Mask of WO_xx values describing operator */
Index *pIdx /* Must be compatible with this index, if not NULL */
){
| > > > > > > > > > > > > > > > > > > | > > > > > > > > > | | > > | | | < | > > | | | < | | | > | < | | > > | | | | | | | | | | | | | > | > | > | > > > > > > > | > > > > | > > > > > > > > > | | 103338 103339 103340 103341 103342 103343 103344 103345 103346 103347 103348 103349 103350 103351 103352 103353 103354 103355 103356 103357 103358 103359 103360 103361 103362 103363 103364 103365 103366 103367 103368 103369 103370 103371 103372 103373 103374 103375 103376 103377 103378 103379 103380 103381 103382 103383 103384 103385 103386 103387 103388 103389 103390 103391 103392 103393 103394 103395 103396 103397 103398 103399 103400 103401 103402 103403 103404 103405 103406 103407 103408 103409 103410 103411 103412 103413 103414 103415 103416 103417 103418 103419 103420 103421 103422 103423 103424 103425 103426 103427 103428 103429 103430 103431 103432 103433 103434 103435 103436 103437 103438 103439 103440 103441 103442 103443 103444 103445 103446 103447 103448 103449 103450 103451 103452 103453 |
}
/*
** Search for a term in the WHERE clause that is of the form "X <op> <expr>"
** where X is a reference to the iColumn of table iCur and <op> is one of
** the WO_xx operator codes specified by the op parameter.
** Return a pointer to the term. Return 0 if not found.
**
** The term returned might by Y=<expr> if there is another constraint in
** the WHERE clause that specifies that X=Y. Any such constraints will be
** identified by the WO_EQUIV bit in the pTerm->eOperator field. The
** aEquiv[] array holds X and all its equivalents, with each SQL variable
** taking up two slots in aEquiv[]. The first slot is for the cursor number
** and the second is for the column number. There are 22 slots in aEquiv[]
** so that means we can look for X plus up to 10 other equivalent values.
** Hence a search for X will return <expr> if X=A1 and A1=A2 and A2=A3
** and ... and A9=A10 and A10=<expr>.
**
** If there are multiple terms in the WHERE clause of the form "X <op> <expr>"
** then try for the one with no dependencies on <expr> - in other words where
** <expr> is a constant expression of some kind. Only return entries of
** the form "X <op> Y" where Y is a column in another table if no terms of
** the form "X <op> <const-expr>" exist. Other than this priority, if there
** are two or more terms that match, then the choice of which term to return
** is arbitrary.
*/
static WhereTerm *findTerm(
WhereClause *pWC, /* The WHERE clause to be searched */
int iCur, /* Cursor number of LHS */
int iColumn, /* Column number of LHS */
Bitmask notReady, /* RHS must not overlap with this mask */
u32 op, /* Mask of WO_xx values describing operator */
Index *pIdx /* Must be compatible with this index, if not NULL */
){
WhereTerm *pTerm; /* Term being examined as possible result */
WhereTerm *pResult = 0; /* The answer to return */
WhereClause *pWCOrig = pWC; /* Original pWC value */
int j, k; /* Loop counters */
Expr *pX; /* Pointer to an expression */
Parse *pParse; /* Parsing context */
int iOrigCol = iColumn; /* Original value of iColumn */
int nEquiv = 2; /* Number of entires in aEquiv[] */
int iEquiv = 2; /* Number of entries of aEquiv[] processed so far */
int aEquiv[22]; /* iCur,iColumn and up to 10 other equivalents */
assert( iCur>=0 );
aEquiv[0] = iCur;
aEquiv[1] = iColumn;
for(;;){
for(pWC=pWCOrig; pWC; pWC=pWC->pOuter){
for(pTerm=pWC->a, k=pWC->nTerm; k; k--, pTerm++){
if( pTerm->leftCursor==iCur
&& pTerm->u.leftColumn==iColumn
){
if( (pTerm->prereqRight & notReady)==0
&& (pTerm->eOperator & op & WO_ALL)!=0
){
if( iOrigCol>=0 && pIdx && (pTerm->eOperator & WO_ISNULL)==0 ){
CollSeq *pColl;
char idxaff;
pX = pTerm->pExpr;
pParse = pWC->pParse;
idxaff = pIdx->pTable->aCol[iOrigCol].affinity;
if( !sqlite3IndexAffinityOk(pX, idxaff) ){
continue;
}
/* Figure out the collation sequence required from an index for
** it to be useful for optimising expression pX. Store this
** value in variable pColl.
*/
assert(pX->pLeft);
pColl = sqlite3BinaryCompareCollSeq(pParse,pX->pLeft,pX->pRight);
if( pColl==0 ) pColl = pParse->db->pDfltColl;
for(j=0; pIdx->aiColumn[j]!=iOrigCol; j++){
if( NEVER(j>=pIdx->nColumn) ) return 0;
}
if( sqlite3StrICmp(pColl->zName, pIdx->azColl[j]) ){
continue;
}
}
pResult = pTerm;
if( pTerm->prereqRight==0 ) goto findTerm_success;
}
if( (pTerm->eOperator & WO_EQUIV)!=0
&& nEquiv<ArraySize(aEquiv)
){
pX = sqlite3ExprSkipCollate(pTerm->pExpr->pRight);
assert( pX->op==TK_COLUMN );
for(j=0; j<nEquiv; j+=2){
if( aEquiv[j]==pX->iTable && aEquiv[j+1]==pX->iColumn ) break;
}
if( j==nEquiv ){
aEquiv[j] = pX->iTable;
aEquiv[j+1] = pX->iColumn;
nEquiv += 2;
}
}
}
}
}
if( iEquiv>=nEquiv ) break;
iCur = aEquiv[iEquiv++];
iColumn = aEquiv[iEquiv++];
}
findTerm_success:
return pResult;
}
/* Forward reference */
static void exprAnalyze(SrcList*, WhereClause*, int);
/*
** Call exprAnalyze on all terms in a WHERE clause.
|
| ︙ | ︙ | |||
103656 103657 103658 103659 103660 103661 103662 |
** Compute the set of tables that might satisfy cases 1 or 2.
*/
indexable = ~(Bitmask)0;
chngToIN = ~(pWC->vmask);
for(i=pOrWc->nTerm-1, pOrTerm=pOrWc->a; i>=0 && indexable; i--, pOrTerm++){
if( (pOrTerm->eOperator & WO_SINGLE)==0 ){
WhereAndInfo *pAndInfo;
| < | 103717 103718 103719 103720 103721 103722 103723 103724 103725 103726 103727 103728 103729 103730 |
** Compute the set of tables that might satisfy cases 1 or 2.
*/
indexable = ~(Bitmask)0;
chngToIN = ~(pWC->vmask);
for(i=pOrWc->nTerm-1, pOrTerm=pOrWc->a; i>=0 && indexable; i--, pOrTerm++){
if( (pOrTerm->eOperator & WO_SINGLE)==0 ){
WhereAndInfo *pAndInfo;
assert( (pOrTerm->wtFlags & (TERM_ANDINFO|TERM_ORINFO))==0 );
chngToIN = 0;
pAndInfo = sqlite3DbMallocRaw(db, sizeof(*pAndInfo));
if( pAndInfo ){
WhereClause *pAndWC;
WhereTerm *pAndTerm;
int j;
|
| ︙ | ︙ | |||
103695 103696 103697 103698 103699 103700 103701 |
Bitmask b;
b = getMask(pMaskSet, pOrTerm->leftCursor);
if( pOrTerm->wtFlags & TERM_VIRTUAL ){
WhereTerm *pOther = &pOrWc->a[pOrTerm->iParent];
b |= getMask(pMaskSet, pOther->leftCursor);
}
indexable &= b;
| | | 103755 103756 103757 103758 103759 103760 103761 103762 103763 103764 103765 103766 103767 103768 103769 |
Bitmask b;
b = getMask(pMaskSet, pOrTerm->leftCursor);
if( pOrTerm->wtFlags & TERM_VIRTUAL ){
WhereTerm *pOther = &pOrWc->a[pOrTerm->iParent];
b |= getMask(pMaskSet, pOther->leftCursor);
}
indexable &= b;
if( (pOrTerm->eOperator & WO_EQ)==0 ){
chngToIN = 0;
}else{
chngToIN &= b;
}
}
}
|
| ︙ | ︙ | |||
103746 103747 103748 103749 103750 103751 103752 |
** will be recorded in iCursor and iColumn. There might not be any
** such table and column. Set okToChngToIN if an appropriate table
** and column is found but leave okToChngToIN false if not found.
*/
for(j=0; j<2 && !okToChngToIN; j++){
pOrTerm = pOrWc->a;
for(i=pOrWc->nTerm-1; i>=0; i--, pOrTerm++){
| | | 103806 103807 103808 103809 103810 103811 103812 103813 103814 103815 103816 103817 103818 103819 103820 |
** will be recorded in iCursor and iColumn. There might not be any
** such table and column. Set okToChngToIN if an appropriate table
** and column is found but leave okToChngToIN false if not found.
*/
for(j=0; j<2 && !okToChngToIN; j++){
pOrTerm = pOrWc->a;
for(i=pOrWc->nTerm-1; i>=0; i--, pOrTerm++){
assert( pOrTerm->eOperator & WO_EQ );
pOrTerm->wtFlags &= ~TERM_OR_OK;
if( pOrTerm->leftCursor==iCursor ){
/* This is the 2-bit case and we are on the second iteration and
** current term is from the first iteration. So skip this term. */
assert( j==1 );
continue;
}
|
| ︙ | ︙ | |||
103772 103773 103774 103775 103776 103777 103778 |
iCursor = pOrTerm->leftCursor;
break;
}
if( i<0 ){
/* No candidate table+column was found. This can only occur
** on the second iteration */
assert( j==1 );
| | | | 103832 103833 103834 103835 103836 103837 103838 103839 103840 103841 103842 103843 103844 103845 103846 103847 103848 103849 103850 103851 103852 103853 103854 103855 103856 |
iCursor = pOrTerm->leftCursor;
break;
}
if( i<0 ){
/* No candidate table+column was found. This can only occur
** on the second iteration */
assert( j==1 );
assert( IsPowerOfTwo(chngToIN) );
assert( chngToIN==getMask(pMaskSet, iCursor) );
break;
}
testcase( j==1 );
/* We have found a candidate table and column. Check to see if that
** table and column is common to every term in the OR clause */
okToChngToIN = 1;
for(; i>=0 && okToChngToIN; i--, pOrTerm++){
assert( pOrTerm->eOperator & WO_EQ );
if( pOrTerm->leftCursor!=iCursor ){
pOrTerm->wtFlags &= ~TERM_OR_OK;
}else if( pOrTerm->u.leftColumn!=iColumn ){
okToChngToIN = 0;
}else{
int affLeft, affRight;
/* If the right-hand side is also a column, then the affinities
|
| ︙ | ︙ | |||
103818 103819 103820 103821 103822 103823 103824 |
Expr *pDup; /* A transient duplicate expression */
ExprList *pList = 0; /* The RHS of the IN operator */
Expr *pLeft = 0; /* The LHS of the IN operator */
Expr *pNew; /* The complete IN operator */
for(i=pOrWc->nTerm-1, pOrTerm=pOrWc->a; i>=0; i--, pOrTerm++){
if( (pOrTerm->wtFlags & TERM_OR_OK)==0 ) continue;
| | | 103878 103879 103880 103881 103882 103883 103884 103885 103886 103887 103888 103889 103890 103891 103892 |
Expr *pDup; /* A transient duplicate expression */
ExprList *pList = 0; /* The RHS of the IN operator */
Expr *pLeft = 0; /* The LHS of the IN operator */
Expr *pNew; /* The complete IN operator */
for(i=pOrWc->nTerm-1, pOrTerm=pOrWc->a; i>=0; i--, pOrTerm++){
if( (pOrTerm->wtFlags & TERM_OR_OK)==0 ) continue;
assert( pOrTerm->eOperator & WO_EQ );
assert( pOrTerm->leftCursor==iCursor );
assert( pOrTerm->u.leftColumn==iColumn );
pDup = sqlite3ExprDup(db, pOrTerm->pExpr->pRight, 0);
pList = sqlite3ExprListAppend(pWC->pParse, pList, pDup);
pLeft = pOrTerm->pExpr->pLeft;
}
assert( pLeft!=0 );
|
| ︙ | ︙ | |||
103847 103848 103849 103850 103851 103852 103853 |
sqlite3ExprListDelete(db, pList);
}
pTerm->eOperator = WO_NOOP; /* case 1 trumps case 2 */
}
}
}
#endif /* !SQLITE_OMIT_OR_OPTIMIZATION && !SQLITE_OMIT_SUBQUERY */
| < | 103907 103908 103909 103910 103911 103912 103913 103914 103915 103916 103917 103918 103919 103920 |
sqlite3ExprListDelete(db, pList);
}
pTerm->eOperator = WO_NOOP; /* case 1 trumps case 2 */
}
}
}
#endif /* !SQLITE_OMIT_OR_OPTIMIZATION && !SQLITE_OMIT_SUBQUERY */
/*
** The input to this routine is an WhereTerm structure with only the
** "pExpr" field filled in. The job of this routine is to analyze the
** subexpression and populate all the other fields of the WhereTerm
** structure.
**
|
| ︙ | ︙ | |||
103917 103918 103919 103920 103921 103922 103923 |
extraRight = x-1; /* ON clause terms may not be used with an index
** on left table of a LEFT JOIN. Ticket #3015 */
}
pTerm->prereqAll = prereqAll;
pTerm->leftCursor = -1;
pTerm->iParent = -1;
pTerm->eOperator = 0;
| | > | > > > > > > > > | | 103976 103977 103978 103979 103980 103981 103982 103983 103984 103985 103986 103987 103988 103989 103990 103991 103992 103993 103994 103995 103996 103997 103998 103999 104000 104001 104002 104003 104004 104005 104006 104007 104008 104009 104010 104011 104012 104013 104014 104015 104016 104017 104018 104019 104020 104021 104022 104023 104024 104025 104026 104027 104028 104029 104030 104031 104032 104033 104034 104035 |
extraRight = x-1; /* ON clause terms may not be used with an index
** on left table of a LEFT JOIN. Ticket #3015 */
}
pTerm->prereqAll = prereqAll;
pTerm->leftCursor = -1;
pTerm->iParent = -1;
pTerm->eOperator = 0;
if( allowedOp(op) ){
Expr *pLeft = sqlite3ExprSkipCollate(pExpr->pLeft);
Expr *pRight = sqlite3ExprSkipCollate(pExpr->pRight);
u16 opMask = (pTerm->prereqRight & prereqLeft)==0 ? WO_ALL : WO_EQUIV;
if( pLeft->op==TK_COLUMN ){
pTerm->leftCursor = pLeft->iTable;
pTerm->u.leftColumn = pLeft->iColumn;
pTerm->eOperator = operatorMask(op) & opMask;
}
if( pRight && pRight->op==TK_COLUMN ){
WhereTerm *pNew;
Expr *pDup;
u16 eExtraOp = 0; /* Extra bits for pNew->eOperator */
if( pTerm->leftCursor>=0 ){
int idxNew;
pDup = sqlite3ExprDup(db, pExpr, 0);
if( db->mallocFailed ){
sqlite3ExprDelete(db, pDup);
return;
}
idxNew = whereClauseInsert(pWC, pDup, TERM_VIRTUAL|TERM_DYNAMIC);
if( idxNew==0 ) return;
pNew = &pWC->a[idxNew];
pNew->iParent = idxTerm;
pTerm = &pWC->a[idxTerm];
pTerm->nChild = 1;
pTerm->wtFlags |= TERM_COPIED;
if( pExpr->op==TK_EQ
&& !ExprHasProperty(pExpr, EP_FromJoin)
&& OptimizationEnabled(db, SQLITE_Transitive)
){
pTerm->eOperator |= WO_EQUIV;
eExtraOp = WO_EQUIV;
}
}else{
pDup = pExpr;
pNew = pTerm;
}
exprCommute(pParse, pDup);
pLeft = sqlite3ExprSkipCollate(pDup->pLeft);
pNew->leftCursor = pLeft->iTable;
pNew->u.leftColumn = pLeft->iColumn;
testcase( (prereqLeft | extraRight) != prereqLeft );
pNew->prereqRight = prereqLeft | extraRight;
pNew->prereqAll = prereqAll;
pNew->eOperator = (operatorMask(pDup->op) + eExtraOp) & opMask;
}
}
#ifndef SQLITE_OMIT_BETWEEN_OPTIMIZATION
/* If a term is the BETWEEN operator, create two new virtual terms
** that define the range that the BETWEEN implements. For example:
**
|
| ︙ | ︙ | |||
104412 104413 104414 104415 104416 104417 104418 |
}
if( pWC->wctrlFlags & WHERE_AND_ONLY ){
return;
}
/* Search the WHERE clause terms for a usable WO_OR term. */
for(pTerm=pWC->a; pTerm<pWCEnd; pTerm++){
| | | | 104480 104481 104482 104483 104484 104485 104486 104487 104488 104489 104490 104491 104492 104493 104494 104495 104496 104497 104498 104499 104500 104501 104502 104503 104504 104505 104506 104507 104508 104509 104510 104511 104512 104513 104514 104515 |
}
if( pWC->wctrlFlags & WHERE_AND_ONLY ){
return;
}
/* Search the WHERE clause terms for a usable WO_OR term. */
for(pTerm=pWC->a; pTerm<pWCEnd; pTerm++){
if( (pTerm->eOperator & WO_OR)!=0
&& ((pTerm->prereqAll & ~maskSrc) & p->notReady)==0
&& (pTerm->u.pOrInfo->indexable & maskSrc)!=0
){
WhereClause * const pOrWC = &pTerm->u.pOrInfo->wc;
WhereTerm * const pOrWCEnd = &pOrWC->a[pOrWC->nTerm];
WhereTerm *pOrTerm;
int flags = WHERE_MULTI_OR;
double rTotal = 0;
double nRow = 0;
Bitmask used = 0;
WhereBestIdx sBOI;
sBOI = *p;
sBOI.pOrderBy = 0;
sBOI.pDistinct = 0;
sBOI.ppIdxInfo = 0;
for(pOrTerm=pOrWC->a; pOrTerm<pOrWCEnd; pOrTerm++){
WHERETRACE(("... Multi-index OR testing for term %d of %d....\n",
(pOrTerm - pOrWC->a), (pTerm - pWC->a)
));
if( (pOrTerm->eOperator& WO_AND)!=0 ){
sBOI.pWC = &pOrTerm->u.pAndInfo->wc;
bestIndex(&sBOI);
}else if( pOrTerm->leftCursor==iCur ){
WhereClause tempWC;
tempWC.pParse = pWC->pParse;
tempWC.pMaskSet = pWC->pMaskSet;
tempWC.pOuter = pWC;
|
| ︙ | ︙ | |||
104494 104495 104496 104497 104498 104499 104500 |
static int termCanDriveIndex(
WhereTerm *pTerm, /* WHERE clause term to check */
struct SrcList_item *pSrc, /* Table we are trying to access */
Bitmask notReady /* Tables in outer loops of the join */
){
char aff;
if( pTerm->leftCursor!=pSrc->iCursor ) return 0;
| | | 104562 104563 104564 104565 104566 104567 104568 104569 104570 104571 104572 104573 104574 104575 104576 |
static int termCanDriveIndex(
WhereTerm *pTerm, /* WHERE clause term to check */
struct SrcList_item *pSrc, /* Table we are trying to access */
Bitmask notReady /* Tables in outer loops of the join */
){
char aff;
if( pTerm->leftCursor!=pSrc->iCursor ) return 0;
if( (pTerm->eOperator & WO_EQ)==0 ) return 0;
if( (pTerm->prereqRight & notReady)!=0 ) return 0;
aff = pSrc->pTab->aCol[pTerm->u.leftColumn].affinity;
if( !sqlite3IndexAffinityOk(pTerm->pExpr, aff) ) return 0;
return 1;
}
#endif
|
| ︙ | ︙ | |||
104756 104757 104758 104759 104760 104761 104762 |
WHERETRACE(("Recomputing index info for %s...\n", pSrc->pTab->zName));
/* Count the number of possible WHERE clause constraints referring
** to this virtual table */
for(i=nTerm=0, pTerm=pWC->a; i<pWC->nTerm; i++, pTerm++){
if( pTerm->leftCursor != pSrc->iCursor ) continue;
| | | | | 104824 104825 104826 104827 104828 104829 104830 104831 104832 104833 104834 104835 104836 104837 104838 104839 104840 |
WHERETRACE(("Recomputing index info for %s...\n", pSrc->pTab->zName));
/* Count the number of possible WHERE clause constraints referring
** to this virtual table */
for(i=nTerm=0, pTerm=pWC->a; i<pWC->nTerm; i++, pTerm++){
if( pTerm->leftCursor != pSrc->iCursor ) continue;
assert( IsPowerOfTwo(pTerm->eOperator & ~WO_EQUIV) );
testcase( pTerm->eOperator & WO_IN );
testcase( pTerm->eOperator & WO_ISNULL );
if( pTerm->eOperator & (WO_ISNULL) ) continue;
if( pTerm->wtFlags & TERM_VNULL ) continue;
nTerm++;
}
/* If the ORDER BY clause contains only columns in the current
** virtual table then allocate space for the aOrderBy part of
|
| ︙ | ︙ | |||
104809 104810 104811 104812 104813 104814 104815 |
*(struct sqlite3_index_orderby**)&pIdxInfo->aOrderBy = pIdxOrderBy;
*(struct sqlite3_index_constraint_usage**)&pIdxInfo->aConstraintUsage =
pUsage;
for(i=j=0, pTerm=pWC->a; i<pWC->nTerm; i++, pTerm++){
u8 op;
if( pTerm->leftCursor != pSrc->iCursor ) continue;
| | | | | | 104877 104878 104879 104880 104881 104882 104883 104884 104885 104886 104887 104888 104889 104890 104891 104892 104893 104894 104895 104896 104897 104898 |
*(struct sqlite3_index_orderby**)&pIdxInfo->aOrderBy = pIdxOrderBy;
*(struct sqlite3_index_constraint_usage**)&pIdxInfo->aConstraintUsage =
pUsage;
for(i=j=0, pTerm=pWC->a; i<pWC->nTerm; i++, pTerm++){
u8 op;
if( pTerm->leftCursor != pSrc->iCursor ) continue;
assert( IsPowerOfTwo(pTerm->eOperator & ~WO_EQUIV) );
testcase( pTerm->eOperator & WO_IN );
testcase( pTerm->eOperator & WO_ISNULL );
if( pTerm->eOperator & (WO_ISNULL) ) continue;
if( pTerm->wtFlags & TERM_VNULL ) continue;
pIdxCons[j].iColumn = pTerm->u.leftColumn;
pIdxCons[j].iTermOffset = i;
op = (u8)pTerm->eOperator & WO_ALL;
if( op==WO_IN ) op = WO_EQ;
pIdxCons[j].op = op;
/* The direct assignment in the previous line is possible only because
** the WO_ and SQLITE_INDEX_CONSTRAINT_ codes are identical. The
** following asserts verify this fact. */
assert( WO_EQ==SQLITE_INDEX_CONSTRAINT_EQ );
assert( WO_LT==SQLITE_INDEX_CONSTRAINT_LT );
|
| ︙ | ︙ | |||
104986 104987 104988 104989 104990 104991 104992 |
*/
pIdxCons = *(struct sqlite3_index_constraint**)&pIdxInfo->aConstraint;
pUsage = pIdxInfo->aConstraintUsage;
for(i=0; i<pIdxInfo->nConstraint; i++, pIdxCons++){
j = pIdxCons->iTermOffset;
pTerm = &pWC->a[j];
if( (pTerm->prereqRight&p->notReady)==0
| | | 105054 105055 105056 105057 105058 105059 105060 105061 105062 105063 105064 105065 105066 105067 105068 |
*/
pIdxCons = *(struct sqlite3_index_constraint**)&pIdxInfo->aConstraint;
pUsage = pIdxInfo->aConstraintUsage;
for(i=0; i<pIdxInfo->nConstraint; i++, pIdxCons++){
j = pIdxCons->iTermOffset;
pTerm = &pWC->a[j];
if( (pTerm->prereqRight&p->notReady)==0
&& (bAllowIN || (pTerm->eOperator & WO_IN)==0)
){
pIdxCons->usable = 1;
}else{
pIdxCons->usable = 0;
}
}
memset(pUsage, 0, sizeof(pUsage[0])*pIdxInfo->nConstraint);
|
| ︙ | ︙ | |||
105018 105019 105020 105021 105022 105023 105024 |
pIdxCons = *(struct sqlite3_index_constraint**)&pIdxInfo->aConstraint;
for(i=0; i<pIdxInfo->nConstraint; i++, pIdxCons++){
if( pUsage[i].argvIndex>0 ){
j = pIdxCons->iTermOffset;
pTerm = &pWC->a[j];
p->cost.used |= pTerm->prereqRight;
| | | 105086 105087 105088 105089 105090 105091 105092 105093 105094 105095 105096 105097 105098 105099 105100 |
pIdxCons = *(struct sqlite3_index_constraint**)&pIdxInfo->aConstraint;
for(i=0; i<pIdxInfo->nConstraint; i++, pIdxCons++){
if( pUsage[i].argvIndex>0 ){
j = pIdxCons->iTermOffset;
pTerm = &pWC->a[j];
p->cost.used |= pTerm->prereqRight;
if( (pTerm->eOperator & WO_IN)!=0 && pUsage[i].omit==0 ){
/* Do not attempt to use an IN constraint if the virtual table
** says that the equivalent EQ constraint cannot be safely omitted.
** If we do attempt to use such a constraint, some rows might be
** repeated in the output. */
break;
}
}
|
| ︙ | ︙ | |||
105324 105325 105326 105327 105328 105329 105330 |
tRowcnt iUpper = p->aiRowEst[0];
tRowcnt a[2];
u8 aff = p->pTable->aCol[p->aiColumn[0]].affinity;
if( pLower ){
Expr *pExpr = pLower->pExpr->pRight;
rc = valueFromExpr(pParse, pExpr, aff, &pRangeVal);
| | | | | | 105392 105393 105394 105395 105396 105397 105398 105399 105400 105401 105402 105403 105404 105405 105406 105407 105408 105409 105410 105411 105412 105413 105414 105415 105416 105417 105418 105419 105420 105421 105422 105423 |
tRowcnt iUpper = p->aiRowEst[0];
tRowcnt a[2];
u8 aff = p->pTable->aCol[p->aiColumn[0]].affinity;
if( pLower ){
Expr *pExpr = pLower->pExpr->pRight;
rc = valueFromExpr(pParse, pExpr, aff, &pRangeVal);
assert( (pLower->eOperator & (WO_GT|WO_GE))!=0 );
if( rc==SQLITE_OK
&& whereKeyStats(pParse, p, pRangeVal, 0, a)==SQLITE_OK
){
iLower = a[0];
if( (pLower->eOperator & WO_GT)!=0 ) iLower += a[1];
}
sqlite3ValueFree(pRangeVal);
}
if( rc==SQLITE_OK && pUpper ){
Expr *pExpr = pUpper->pExpr->pRight;
rc = valueFromExpr(pParse, pExpr, aff, &pRangeVal);
assert( (pUpper->eOperator & (WO_LT|WO_LE))!=0 );
if( rc==SQLITE_OK
&& whereKeyStats(pParse, p, pRangeVal, 1, a)==SQLITE_OK
){
iUpper = a[0];
if( (pUpper->eOperator & WO_LE)!=0 ) iUpper += a[1];
}
sqlite3ValueFree(pRangeVal);
}
if( rc==SQLITE_OK ){
if( iUpper<=iLower ){
*pRangeDiv = (double)p->aiRowEst[0];
}else{
|
| ︙ | ︙ | |||
105649 105650 105651 105652 105653 105654 105655 |
/* If X is the column in the index and ORDER BY clause, check to see
** if there are any X= or X IS NULL constraints in the WHERE clause. */
pConstraint = findTerm(p->pWC, base, iColumn, p->notReady,
WO_EQ|WO_ISNULL|WO_IN, pIdx);
if( pConstraint==0 ){
isEq = 0;
| | | | 105717 105718 105719 105720 105721 105722 105723 105724 105725 105726 105727 105728 105729 105730 105731 105732 105733 105734 105735 105736 |
/* If X is the column in the index and ORDER BY clause, check to see
** if there are any X= or X IS NULL constraints in the WHERE clause. */
pConstraint = findTerm(p->pWC, base, iColumn, p->notReady,
WO_EQ|WO_ISNULL|WO_IN, pIdx);
if( pConstraint==0 ){
isEq = 0;
}else if( (pConstraint->eOperator & WO_IN)!=0 ){
/* Constraints of the form: "X IN ..." cannot be used with an ORDER BY
** because we do not know in what order the values on the RHS of the IN
** operator will occur. */
break;
}else if( (pConstraint->eOperator & WO_ISNULL)!=0 ){
uniqueNotNull = 0;
isEq = 1; /* "X IS NULL" means X has only a single value */
}else if( pConstraint->prereqRight==0 ){
isEq = 1; /* Constraint "X=constant" means X has only a single value */
}else{
Expr *pRight = pConstraint->pExpr->pRight;
if( pRight->op==TK_COLUMN ){
|
| ︙ | ︙ | |||
106067 106068 106069 106070 106071 106072 106073 |
** to get a better estimate on the number of rows based on
** VALUE and how common that value is according to the histogram.
*/
if( pc.plan.nRow>(double)1 && pc.plan.nEq==1
&& pFirstTerm!=0 && aiRowEst[1]>1 ){
assert( (pFirstTerm->eOperator & (WO_EQ|WO_ISNULL|WO_IN))!=0 );
if( pFirstTerm->eOperator & (WO_EQ|WO_ISNULL) ){
| | | > | | 106135 106136 106137 106138 106139 106140 106141 106142 106143 106144 106145 106146 106147 106148 106149 106150 106151 106152 106153 106154 106155 |
** to get a better estimate on the number of rows based on
** VALUE and how common that value is according to the histogram.
*/
if( pc.plan.nRow>(double)1 && pc.plan.nEq==1
&& pFirstTerm!=0 && aiRowEst[1]>1 ){
assert( (pFirstTerm->eOperator & (WO_EQ|WO_ISNULL|WO_IN))!=0 );
if( pFirstTerm->eOperator & (WO_EQ|WO_ISNULL) ){
testcase( pFirstTerm->eOperator & WO_EQ );
testcase( pFirstTerm->eOperator & WO_EQUIV );
testcase( pFirstTerm->eOperator & WO_ISNULL );
whereEqualScanEst(pParse, pProbe, pFirstTerm->pExpr->pRight,
&pc.plan.nRow);
}else if( bInEst==0 ){
assert( pFirstTerm->eOperator & WO_IN );
whereInScanEst(pParse, pProbe, pFirstTerm->pExpr->x.pList,
&pc.plan.nRow);
}
}
#endif /* SQLITE_ENABLE_STAT3 */
/* Adjust the number of output rows and downward to reflect rows
|
| ︙ | ︙ | |||
106219 106220 106221 106222 106223 106224 106225 |
** set size by a factor of 3. Indexed range constraints reduce
** the search space by a larger factor: 4. We make indexed range
** more selective intentionally because of the subjective
** observation that indexed range constraints really are more
** selective in practice, on average. */
pc.plan.nRow /= 3;
}
| | | 106288 106289 106290 106291 106292 106293 106294 106295 106296 106297 106298 106299 106300 106301 106302 |
** set size by a factor of 3. Indexed range constraints reduce
** the search space by a larger factor: 4. We make indexed range
** more selective intentionally because of the subjective
** observation that indexed range constraints really are more
** selective in practice, on average. */
pc.plan.nRow /= 3;
}
}else if( (pTerm->eOperator & WO_NOOP)==0 ){
/* Any other expression lowers the output row count by half */
pc.plan.nRow /= 2;
}
}
if( pc.plan.nRow<2 ) pc.plan.nRow = 2;
}
|
| ︙ | ︙ | |||
106271 106272 106273 106274 106275 106276 106277 |
assert( p->pOrderBy || (p->cost.plan.wsFlags&WHERE_ORDERED)==0 );
assert( p->cost.plan.u.pIdx==0 || (p->cost.plan.wsFlags&WHERE_ROWID_EQ)==0 );
assert( pSrc->pIndex==0
|| p->cost.plan.u.pIdx==0
|| p->cost.plan.u.pIdx==pSrc->pIndex
);
| | | > | 106340 106341 106342 106343 106344 106345 106346 106347 106348 106349 106350 106351 106352 106353 106354 106355 106356 |
assert( p->pOrderBy || (p->cost.plan.wsFlags&WHERE_ORDERED)==0 );
assert( p->cost.plan.u.pIdx==0 || (p->cost.plan.wsFlags&WHERE_ROWID_EQ)==0 );
assert( pSrc->pIndex==0
|| p->cost.plan.u.pIdx==0
|| p->cost.plan.u.pIdx==pSrc->pIndex
);
WHERETRACE((" best index is %s cost=%.1f\n",
p->cost.plan.u.pIdx ? p->cost.plan.u.pIdx->zName : "ipk",
p->cost.rCost));
bestOrClauseIndex(p);
bestAutomaticIndex(p);
p->cost.plan.wsFlags |= eqTermMask;
}
/*
|
| ︙ | ︙ | |||
106854 106855 106856 106857 106858 106859 106860 |
** we reference multiple rows using a "rowid IN (...)"
** construct.
*/
iReleaseReg = sqlite3GetTempReg(pParse);
pTerm = findTerm(pWC, iCur, -1, notReady, WO_EQ|WO_IN, 0);
assert( pTerm!=0 );
assert( pTerm->pExpr!=0 );
| < | 106924 106925 106926 106927 106928 106929 106930 106931 106932 106933 106934 106935 106936 106937 |
** we reference multiple rows using a "rowid IN (...)"
** construct.
*/
iReleaseReg = sqlite3GetTempReg(pParse);
pTerm = findTerm(pWC, iCur, -1, notReady, WO_EQ|WO_IN, 0);
assert( pTerm!=0 );
assert( pTerm->pExpr!=0 );
assert( omitTable==0 );
testcase( pTerm->wtFlags & TERM_VIRTUAL ); /* EV: R-30575-11662 */
iRowidReg = codeEqualityTerm(pParse, pTerm, pLevel, iReleaseReg);
addrNxt = pLevel->addrNxt;
sqlite3VdbeAddOp2(v, OP_MustBeInt, iRowidReg, addrNxt);
sqlite3VdbeAddOp3(v, OP_NotExists, iCur, addrNxt, iRowidReg);
sqlite3ExprCacheStore(pParse, iCur, -1, iRowidReg);
|
| ︙ | ︙ | |||
107245 107246 107247 107248 107249 107250 107251 |
int iRetInit; /* Address of regReturn init */
int untestedTerms = 0; /* Some terms not completely tested */
int ii; /* Loop counter */
Expr *pAndExpr = 0; /* An ".. AND (...)" expression */
pTerm = pLevel->plan.u.pTerm;
assert( pTerm!=0 );
| | | 107314 107315 107316 107317 107318 107319 107320 107321 107322 107323 107324 107325 107326 107327 107328 |
int iRetInit; /* Address of regReturn init */
int untestedTerms = 0; /* Some terms not completely tested */
int ii; /* Loop counter */
Expr *pAndExpr = 0; /* An ".. AND (...)" expression */
pTerm = pLevel->plan.u.pTerm;
assert( pTerm!=0 );
assert( pTerm->eOperator & WO_OR );
assert( (pTerm->wtFlags & TERM_ORINFO)!=0 );
pOrWc = &pTerm->u.pOrInfo->wc;
pLevel->op = OP_Return;
pLevel->p1 = regReturn;
/* Set up a new SrcList in pOrTab containing the table being scanned
** by this loop in the a[0] slot and all notReady tables in a[1..] slots.
|
| ︙ | ︙ | |||
107318 107319 107320 107321 107322 107323 107324 |
if( pAndExpr ){
pAndExpr = sqlite3PExpr(pParse, TK_AND, 0, pAndExpr, 0);
}
}
for(ii=0; ii<pOrWc->nTerm; ii++){
WhereTerm *pOrTerm = &pOrWc->a[ii];
| | | 107387 107388 107389 107390 107391 107392 107393 107394 107395 107396 107397 107398 107399 107400 107401 |
if( pAndExpr ){
pAndExpr = sqlite3PExpr(pParse, TK_AND, 0, pAndExpr, 0);
}
}
for(ii=0; ii<pOrWc->nTerm; ii++){
WhereTerm *pOrTerm = &pOrWc->a[ii];
if( pOrTerm->leftCursor==iCur || (pOrTerm->eOperator & WO_AND)!=0 ){
WhereInfo *pSubWInfo; /* Info for single OR-term scan */
Expr *pOrExpr = pOrTerm->pExpr;
if( pAndExpr ){
pAndExpr->pLeft = pOrExpr;
pOrExpr = pAndExpr;
}
/* Loop through table entries that match term pOrTerm. */
|
| ︙ | ︙ | |||
107773 107774 107775 107776 107777 107778 107779 107780 107781 107782 107783 107784 107785 107786 |
for(sWBI.i=iFrom=0, pLevel=pWInfo->a; sWBI.i<nTabList; sWBI.i++, pLevel++){
WhereCost bestPlan; /* Most efficient plan seen so far */
Index *pIdx; /* Index for FROM table at pTabItem */
int j; /* For looping over FROM tables */
int bestJ = -1; /* The value of j */
Bitmask m; /* Bitmask value for j or bestJ */
int isOptimal; /* Iterator for optimal/non-optimal search */
int nUnconstrained; /* Number tables without INDEXED BY */
Bitmask notIndexed; /* Mask of tables that cannot use an index */
memset(&bestPlan, 0, sizeof(bestPlan));
bestPlan.rCost = SQLITE_BIG_DBL;
WHERETRACE(("*** Begin search for loop %d ***\n", sWBI.i));
| > | 107842 107843 107844 107845 107846 107847 107848 107849 107850 107851 107852 107853 107854 107855 107856 |
for(sWBI.i=iFrom=0, pLevel=pWInfo->a; sWBI.i<nTabList; sWBI.i++, pLevel++){
WhereCost bestPlan; /* Most efficient plan seen so far */
Index *pIdx; /* Index for FROM table at pTabItem */
int j; /* For looping over FROM tables */
int bestJ = -1; /* The value of j */
Bitmask m; /* Bitmask value for j or bestJ */
int isOptimal; /* Iterator for optimal/non-optimal search */
int ckOptimal; /* Do the optimal scan check */
int nUnconstrained; /* Number tables without INDEXED BY */
Bitmask notIndexed; /* Mask of tables that cannot use an index */
memset(&bestPlan, 0, sizeof(bestPlan));
bestPlan.rCost = SQLITE_BIG_DBL;
WHERETRACE(("*** Begin search for loop %d ***\n", sWBI.i));
|
| ︙ | ︙ | |||
107807 107808 107809 107810 107811 107812 107813 |
** that do not use indices. But this nRow reduction only happens if the
** table really is the innermost join.
**
** The second loop iteration is only performed if no optimal scan
** strategies were found by the first iteration. This second iteration
** is used to search for the lowest cost scan overall.
**
| | < < | | > > > > > > < < < < > > > > > > > > > > > > > > > > > > > > > > | 107877 107878 107879 107880 107881 107882 107883 107884 107885 107886 107887 107888 107889 107890 107891 107892 107893 107894 107895 107896 107897 107898 107899 107900 107901 107902 107903 107904 107905 107906 107907 107908 107909 107910 107911 107912 107913 107914 107915 107916 107917 107918 107919 107920 107921 107922 107923 107924 107925 107926 107927 107928 107929 107930 107931 107932 107933 107934 107935 107936 107937 107938 107939 107940 |
** that do not use indices. But this nRow reduction only happens if the
** table really is the innermost join.
**
** The second loop iteration is only performed if no optimal scan
** strategies were found by the first iteration. This second iteration
** is used to search for the lowest cost scan overall.
**
** Without the optimal scan step (the first iteration) a suboptimal
** plan might be chosen for queries like this:
**
** CREATE TABLE t1(a, b);
** CREATE TABLE t2(c, d);
** SELECT * FROM t2, t1 WHERE t2.rowid = t1.a;
**
** The best strategy is to iterate through table t1 first. However it
** is not possible to determine this with a simple greedy algorithm.
** Since the cost of a linear scan through table t2 is the same
** as the cost of a linear scan through table t1, a simple greedy
** algorithm may choose to use t2 for the outer loop, which is a much
** costlier approach.
*/
nUnconstrained = 0;
notIndexed = 0;
/* The optimal scan check only occurs if there are two or more tables
** available to be reordered */
if( iFrom==nTabList-1 ){
ckOptimal = 0; /* Common case of just one table in the FROM clause */
}else{
ckOptimal = -1;
for(j=iFrom, sWBI.pSrc=&pTabList->a[j]; j<nTabList; j++, sWBI.pSrc++){
m = getMask(pMaskSet, sWBI.pSrc->iCursor);
if( (m & sWBI.notValid)==0 ){
if( j==iFrom ) iFrom++;
continue;
}
if( j>iFrom && (sWBI.pSrc->jointype & (JT_LEFT|JT_CROSS))!=0 ) break;
if( ++ckOptimal ) break;
if( (sWBI.pSrc->jointype & JT_LEFT)!=0 ) break;
}
}
assert( ckOptimal==0 || ckOptimal==1 );
for(isOptimal=ckOptimal; isOptimal>=0 && bestJ<0; isOptimal--){
for(j=iFrom, sWBI.pSrc=&pTabList->a[j]; j<nTabList; j++, sWBI.pSrc++){
if( j>iFrom && (sWBI.pSrc->jointype & (JT_LEFT|JT_CROSS))!=0 ){
/* This break and one like it in the ckOptimal computation loop
** above prevent table reordering across LEFT and CROSS JOINs.
** The LEFT JOIN case is necessary for correctness. The prohibition
** against reordering across a CROSS JOIN is an SQLite feature that
** allows the developer to control table reordering */
break;
}
m = getMask(pMaskSet, sWBI.pSrc->iCursor);
if( (m & sWBI.notValid)==0 ){
assert( j>iFrom );
continue;
}
sWBI.notReady = (isOptimal ? m : sWBI.notValid);
if( sWBI.pSrc->pIndex==0 ) nUnconstrained++;
WHERETRACE((" === trying table %d (%s) with isOptimal=%d ===\n",
j, sWBI.pSrc->pTab->zName, isOptimal));
assert( sWBI.pSrc->pTab );
|
| ︙ | ︙ | |||
107864 107865 107866 107867 107868 107869 107870 |
|| sWBI.cost.plan.u.pIdx==sWBI.pSrc->pIndex );
if( isOptimal && (sWBI.cost.plan.wsFlags & WHERE_NOT_FULLSCAN)==0 ){
notIndexed |= m;
}
if( isOptimal ){
pWInfo->a[j].rOptCost = sWBI.cost.rCost;
| | | | 107956 107957 107958 107959 107960 107961 107962 107963 107964 107965 107966 107967 107968 107969 107970 107971 |
|| sWBI.cost.plan.u.pIdx==sWBI.pSrc->pIndex );
if( isOptimal && (sWBI.cost.plan.wsFlags & WHERE_NOT_FULLSCAN)==0 ){
notIndexed |= m;
}
if( isOptimal ){
pWInfo->a[j].rOptCost = sWBI.cost.rCost;
}else if( ckOptimal ){
/* If two or more tables have nearly the same outer loop cost, but
** very different inner loop (optimal) cost, we want to choose
** for the outer loop that table which benefits the least from
** being in the inner loop. The following code scales the
** outer loop cost estimate to accomplish that. */
WHERETRACE((" scaling cost from %.1f to %.1f\n",
sWBI.cost.rCost,
sWBI.cost.rCost/pWInfo->a[j].rOptCost));
|
| ︙ | ︙ | |||
107910 107911 107912 107913 107914 107915 107916 |
" cost=%.1f, nRow=%.1f, nOBSat=%d, wsFlags=%08x\n",
j, sWBI.pSrc->pTab->zName,
sWBI.cost.rCost, sWBI.cost.plan.nRow,
sWBI.cost.plan.nOBSat, sWBI.cost.plan.wsFlags));
bestPlan = sWBI.cost;
bestJ = j;
}
| | > > > > > > > > | 108002 108003 108004 108005 108006 108007 108008 108009 108010 108011 108012 108013 108014 108015 108016 108017 108018 108019 108020 108021 108022 108023 108024 108025 108026 108027 108028 |
" cost=%.1f, nRow=%.1f, nOBSat=%d, wsFlags=%08x\n",
j, sWBI.pSrc->pTab->zName,
sWBI.cost.rCost, sWBI.cost.plan.nRow,
sWBI.cost.plan.nOBSat, sWBI.cost.plan.wsFlags));
bestPlan = sWBI.cost;
bestJ = j;
}
/* In a join like "w JOIN x LEFT JOIN y JOIN z" make sure that
** table y (and not table z) is always the next inner loop inside
** of table x. */
if( (sWBI.pSrc->jointype & JT_LEFT)!=0 ) break;
}
}
assert( bestJ>=0 );
assert( sWBI.notValid & getMask(pMaskSet, pTabList->a[bestJ].iCursor) );
assert( bestJ==iFrom || (pTabList->a[iFrom].jointype & JT_LEFT)==0 );
testcase( bestJ>iFrom && (pTabList->a[iFrom].jointype & JT_CROSS)!=0 );
testcase( bestJ>iFrom && bestJ<nTabList-1
&& (pTabList->a[bestJ+1].jointype & JT_LEFT)!=0 );
WHERETRACE(("*** Optimizer selects table %d (%s) for loop %d with:\n"
" cost=%.1f, nRow=%.1f, nOBSat=%d, wsFlags=0x%08x\n",
bestJ, pTabList->a[bestJ].pTab->zName,
pLevel-pWInfo->a, bestPlan.rCost, bestPlan.plan.nRow,
bestPlan.plan.nOBSat, bestPlan.plan.wsFlags));
if( (bestPlan.plan.wsFlags & WHERE_DISTINCT)!=0 ){
assert( pWInfo->eDistinct==0 );
|
| ︙ | ︙ | |||
136644 136645 136646 136647 136648 136649 136650 | ** This ensures that each node is stored on a single database page. If the ** database page-size is so large that more than RTREE_MAXCELLS entries ** would fit in a single node, use a smaller node-size. */ static int getNodeSize( sqlite3 *db, /* Database handle */ Rtree *pRtree, /* Rtree handle */ | | > > > > > > | 136744 136745 136746 136747 136748 136749 136750 136751 136752 136753 136754 136755 136756 136757 136758 136759 136760 136761 136762 136763 136764 136765 136766 136767 136768 136769 136770 136771 136772 136773 136774 136775 136776 136777 136778 136779 136780 136781 136782 136783 |
** This ensures that each node is stored on a single database page. If the
** database page-size is so large that more than RTREE_MAXCELLS entries
** would fit in a single node, use a smaller node-size.
*/
static int getNodeSize(
sqlite3 *db, /* Database handle */
Rtree *pRtree, /* Rtree handle */
int isCreate, /* True for xCreate, false for xConnect */
char **pzErr /* OUT: Error message, if any */
){
int rc;
char *zSql;
if( isCreate ){
int iPageSize = 0;
zSql = sqlite3_mprintf("PRAGMA %Q.page_size", pRtree->zDb);
rc = getIntFromStmt(db, zSql, &iPageSize);
if( rc==SQLITE_OK ){
pRtree->iNodeSize = iPageSize-64;
if( (4+pRtree->nBytesPerCell*RTREE_MAXCELLS)<pRtree->iNodeSize ){
pRtree->iNodeSize = 4+pRtree->nBytesPerCell*RTREE_MAXCELLS;
}
}else{
*pzErr = sqlite3_mprintf("%s", sqlite3_errmsg(db));
}
}else{
zSql = sqlite3_mprintf(
"SELECT length(data) FROM '%q'.'%q_node' WHERE nodeno = 1",
pRtree->zDb, pRtree->zName
);
rc = getIntFromStmt(db, zSql, &pRtree->iNodeSize);
if( rc!=SQLITE_OK ){
*pzErr = sqlite3_mprintf("%s", sqlite3_errmsg(db));
}
}
sqlite3_free(zSql);
return rc;
}
/*
|
| ︙ | ︙ | |||
136727 136728 136729 136730 136731 136732 136733 | pRtree->nDim = (argc-4)/2; pRtree->nBytesPerCell = 8 + pRtree->nDim*4*2; pRtree->eCoordType = eCoordType; memcpy(pRtree->zDb, argv[1], nDb); memcpy(pRtree->zName, argv[2], nName); /* Figure out the node size to use. */ | | | 136833 136834 136835 136836 136837 136838 136839 136840 136841 136842 136843 136844 136845 136846 136847 |
pRtree->nDim = (argc-4)/2;
pRtree->nBytesPerCell = 8 + pRtree->nDim*4*2;
pRtree->eCoordType = eCoordType;
memcpy(pRtree->zDb, argv[1], nDb);
memcpy(pRtree->zName, argv[2], nName);
/* Figure out the node size to use. */
rc = getNodeSize(db, pRtree, isCreate, pzErr);
/* Create/Connect to the underlying relational database schema. If
** that is successful, call sqlite3_declare_vtab() to configure
** the r-tree table schema.
*/
if( rc==SQLITE_OK ){
if( (rc = rtreeSqlInit(pRtree, db, argv[1], argv[2], isCreate)) ){
|
| ︙ | ︙ |
Changes to src/sqlite3.h.
| ︙ | ︙ | |||
105 106 107 108 109 110 111 | ** ** See also: [sqlite3_libversion()], ** [sqlite3_libversion_number()], [sqlite3_sourceid()], ** [sqlite_version()] and [sqlite_source_id()]. */ #define SQLITE_VERSION "3.7.16" #define SQLITE_VERSION_NUMBER 3007016 | | | 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 | ** ** See also: [sqlite3_libversion()], ** [sqlite3_libversion_number()], [sqlite3_sourceid()], ** [sqlite_version()] and [sqlite_source_id()]. */ #define SQLITE_VERSION "3.7.16" #define SQLITE_VERSION_NUMBER 3007016 #define SQLITE_SOURCE_ID "2013-01-17 17:20:49 38852f158ab20bb4d7b264af987ec1538052bec3" /* ** CAPI3REF: Run-Time Library Version Numbers ** KEYWORDS: sqlite3_version, sqlite3_sourceid ** ** These interfaces provide the same information as the [SQLITE_VERSION], ** [SQLITE_VERSION_NUMBER], and [SQLITE_SOURCE_ID] C preprocessor macros |
| ︙ | ︙ |
Changes to src/stat.c.
| ︙ | ︙ | |||
64 65 66 67 68 69 70 |
bigSizeName(sizeof(zBuf), zBuf, fsize);
@ %s(zBuf)
@ </td></tr>
if( !brief ){
@ <tr><th>Number Of Artifacts:</th><td>
n = db_int(0, "SELECT count(*) FROM blob");
m = db_int(0, "SELECT count(*) FROM delta");
| | | 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 |
bigSizeName(sizeof(zBuf), zBuf, fsize);
@ %s(zBuf)
@ </td></tr>
if( !brief ){
@ <tr><th>Number Of Artifacts:</th><td>
n = db_int(0, "SELECT count(*) FROM blob");
m = db_int(0, "SELECT count(*) FROM delta");
@ %d(n) (%d(n-m) fulltext and %d(m) deltas)
@ </td></tr>
if( n>0 ){
int a, b;
Stmt q;
@ <tr><th>Uncompressed Artifact Size:</th><td>
db_prepare(&q, "SELECT total(size), avg(size), max(size)"
" FROM blob WHERE size>0");
|
| ︙ | ︙ | |||
92 93 94 95 96 97 98 |
b = 1;
}
a = t/fsize;
@ %d(a):%d(b)
@ </td></tr>
}
@ <tr><th>Number Of Check-ins:</th><td>
| | | < | | | > > | > > > > > | 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 |
b = 1;
}
a = t/fsize;
@ %d(a):%d(b)
@ </td></tr>
}
@ <tr><th>Number Of Check-ins:</th><td>
n = db_int(0, "SELECT count(*) FROM event WHERE type='ci' /*scan*/");
@ %d(n)
@ </td></tr>
@ <tr><th>Number Of Files:</th><td>
n = db_int(0, "SELECT count(*) FROM filename /*scan*/");
@ %d(n)
@ </td></tr>
@ <tr><th>Number Of Wiki Pages:</th><td>
n = db_int(0, "SELECT count(*) FROM tag /*scan*/"
" WHERE +tagname GLOB 'wiki-*'");
@ %d(n)
@ </td></tr>
@ <tr><th>Number Of Tickets:</th><td>
n = db_int(0, "SELECT count(*) FROM tag /*scan*/"
" WHERE +tagname GLOB 'tkt-*'");
@ %d(n)
@ </td></tr>
}
@ <tr><th>Duration Of Project:</th><td>
n = db_int(0, "SELECT julianday('now') - (SELECT min(mtime) FROM event)"
" + 0.99");
@ %d(n) days or approximately %.2f(n/365.2425) years.
@ </td></tr>
@ <tr><th>Project ID:</th><td>%h(db_get("project-code",""))</td></tr>
@ <tr><th>Fossil Version:</th><td>
@ %h(MANIFEST_DATE) %h(MANIFEST_VERSION)
@ (%h(RELEASE_VERSION)) [compiled using %h(COMPILER_NAME)]
@ </td></tr>
@ <tr><th>SQLite Version:</th><td>%.19s(SQLITE_SOURCE_ID)
@ [%.10s(&SQLITE_SOURCE_ID[20])] (%s(SQLITE_VERSION))</td></tr>
@ <tr><th>Database Stats:</th><td>
zDb = db_name("repository");
@ %d(db_int(0, "PRAGMA %s.page_count", zDb)) pages,
@ %d(db_int(0, "PRAGMA %s.page_size", zDb)) bytes/page,
@ %d(db_int(0, "PRAGMA %s.freelist_count", zDb)) free pages,
@ %s(db_text(0, "PRAGMA %s.encoding", zDb)),
@ %s(db_text(0, "PRAGMA %s.journal_mode", zDb)) mode
@ </td></tr>
@ </table>
style_footer();
}
/*
** COMMAND: dbstat*
**
** Usage: %fossil dbstat ?-brief | -b?
**
** Shows statistics and global information about the repository.
**
** The (-brief|-b) option removes any "long-running" statistics, namely
** those whose calculations are known to slow down as the repository
** grows.
**
*/
void dbstat_cmd(void){
i64 t, fsize;
int n, m;
int szMax, szAvg;
const char *zDb;
int brief;
|
| ︙ | ︙ | |||
185 186 187 188 189 190 191 |
}else{
b = 1;
}
a = t/fsize;
fossil_print("%*s%d:%d\n", colWidth, "compression-ratio:", a, b);
}
n = db_int(0, "SELECT COUNT(*) FROM event e WHERE e.type='ci'");
| | < < < < < < | < < | < < | < < | > | < | < > > | < | 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 |
}else{
b = 1;
}
a = t/fsize;
fossil_print("%*s%d:%d\n", colWidth, "compression-ratio:", a, b);
}
n = db_int(0, "SELECT COUNT(*) FROM event e WHERE e.type='ci'");
fossil_print("%*s%d\n", colWidth, "checkins:", n);
n = db_int(0, "SELECT count(*) FROM filename /*scan*/");
fossil_print("%*s%d across all branches\n", colWidth, "files:", n);
n = db_int(0, "SELECT count(*) FROM tag /*scan*/"
" WHERE tagname GLOB 'wiki-*'");
m = db_int(0, "SELECT COUNT(*) FROM event WHERE type='w'");
fossil_print("%*s%d (%d changes)\n", colWidth, "wikipages:", n, m);
n = db_int(0, "SELECT count(*) FROM tag /*scan*/"
" WHERE tagname GLOB 'tkt-*'");
m = db_int(0, "SELECT COUNT(*) FROM event WHERE type='t'");
fossil_print("%*s%d (%d changes)\n", colWidth, "tickets:", n, m);
n = db_int(0, "SELECT COUNT(*) FROM event WHERE type='e'");
fossil_print("%*s%d\n", colWidth, "events:", n);
n = db_int(0, "SELECT COUNT(*) FROM event WHERE type='g'");
fossil_print("%*s%d\n", colWidth, "tagchanges:", n);
}
n = db_int(0, "SELECT julianday('now') - (SELECT min(mtime) FROM event)"
" + 0.99");
fossil_print("%*s%d days or approximately %.2f years.\n",
colWidth, "project-age:", n, n/365.2425);
fossil_print("%*s%s\n", colWidth, "project-id:", db_get("project-code",""));
fossil_print("%*s%s %s %s (%s)\n",
colWidth, "fossil-version:",
RELEASE_VERSION, MANIFEST_DATE, MANIFEST_VERSION,
COMPILER_NAME);
fossil_print("%*s%.19s [%.10s] (%s)\n",
colWidth, "sqlite-version:",
SQLITE_SOURCE_ID, &SQLITE_SOURCE_ID[20],
|
| ︙ | ︙ |
Changes to src/timeline.c.
| ︙ | ︙ | |||
1505 1506 1507 1508 1509 1510 1511 |
@ AND tagxref.rid=blob.rid AND tagxref.tagtype>0))
@ || ')' as comment,
@ (SELECT count(*) FROM plink WHERE pid=blob.rid AND isprim)
@ AS primPlinkCount,
@ (SELECT count(*) FROM plink WHERE cid=blob.rid) AS plinkCount,
@ event.mtime AS mtime,
@ tagxref.value AS branch
| | < < | > > | 1505 1506 1507 1508 1509 1510 1511 1512 1513 1514 1515 1516 1517 1518 1519 1520 1521 1522 1523 1524 |
@ AND tagxref.rid=blob.rid AND tagxref.tagtype>0))
@ || ')' as comment,
@ (SELECT count(*) FROM plink WHERE pid=blob.rid AND isprim)
@ AS primPlinkCount,
@ (SELECT count(*) FROM plink WHERE cid=blob.rid) AS plinkCount,
@ event.mtime AS mtime,
@ tagxref.value AS branch
@ FROM tag CROSS JOIN event CROSS JOIN blob
@ LEFT JOIN tagxref ON tagxref.tagid=tag.tagid
@ AND tagxref.tagtype>0
@ AND tagxref.rid=blob.rid
@ WHERE blob.rid=event.objid
@ AND tag.tagname='branch'
;
return zBaseSql;
}
/*
** Return true if the input string is a date in the ISO 8601 format:
** YYYY-MM-DD.
|
| ︙ | ︙ |
Changes to src/update.c.
| ︙ | ︙ | |||
665 666 667 668 669 670 671 672 673 674 675 676 677 678 | ** COMMAND: revert ** ** Usage: %fossil revert ?-r REVISION? ?FILE ...? ** ** Revert to the current repository version of FILE, or to ** the version associated with baseline REVISION if the -r flag ** appears. ** ** Revert all files if no file name is provided. ** ** If a file is reverted accidently, it can be restored using ** the "fossil undo" command. ** ** Options: | > > > | 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 | ** COMMAND: revert ** ** Usage: %fossil revert ?-r REVISION? ?FILE ...? ** ** Revert to the current repository version of FILE, or to ** the version associated with baseline REVISION if the -r flag ** appears. ** ** If FILE was part of a rename operation, both the original file ** and the renamed file are reverted. ** ** Revert all files if no file name is provided. ** ** If a file is reverted accidently, it can be restored using ** the "fossil undo" command. ** ** Options: |
| ︙ | ︙ | |||
704 705 706 707 708 709 710 |
db_multi_exec("CREATE TEMP TABLE torevert(name UNIQUE);");
if( g.argc>2 ){
for(i=2; i<g.argc; i++){
Blob fname;
zFile = mprintf("%/", g.argv[i]);
file_tree_name(zFile, &fname, 1);
| > | > > > > > > > > > > | 707 708 709 710 711 712 713 714 715 716 717 718 719 720 721 722 723 724 725 726 727 728 729 730 731 732 |
db_multi_exec("CREATE TEMP TABLE torevert(name UNIQUE);");
if( g.argc>2 ){
for(i=2; i<g.argc; i++){
Blob fname;
zFile = mprintf("%/", g.argv[i]);
file_tree_name(zFile, &fname, 1);
db_multi_exec(
"REPLACE INTO torevert VALUES(%B);"
"INSERT OR IGNORE INTO torevert"
" SELECT pathname"
" FROM vfile"
" WHERE origname IN(%B)"
" UNION ALL"
" SELECT origname"
" FROM vfile"
" WHERE pathname IN(%B) AND origname IS NOT NULL;",
&fname, &fname, &fname
);
blob_reset(&fname);
}
}else{
int vid;
vid = db_lget_int("checkout", 0);
vfile_check_signature(vid, 0);
db_multi_exec(
|
| ︙ | ︙ | |||
746 747 748 749 750 751 752 |
zFile, zFile)==0 ){
fossil_print("UNMANAGE: %s\n", zFile);
}else{
undo_save(zFile);
file_delete(zFull);
fossil_print("DELETE: %s\n", zFile);
}
| > > > > | > > | < | | | 760 761 762 763 764 765 766 767 768 769 770 771 772 773 774 775 776 777 778 779 780 781 782 783 784 785 786 787 788 789 790 791 792 793 794 795 796 797 798 799 800 801 |
zFile, zFile)==0 ){
fossil_print("UNMANAGE: %s\n", zFile);
}else{
undo_save(zFile);
file_delete(zFull);
fossil_print("DELETE: %s\n", zFile);
}
db_multi_exec(
"UPDATE vfile"
" SET pathname=origname, origname=NULL"
" WHERE pathname=%Q AND origname!=pathname AND origname IS NOT NULL;"
"DELETE FROM vfile WHERE pathname=%Q",
zFile, zFile
);
}else{
sqlite3_int64 mtime;
undo_save(zFile);
if( file_wd_size(zFull)>=0 && (isLink || file_wd_islink(zFull)) ){
file_delete(zFull);
}
if( isLink ){
symlink_create(blob_str(&record), zFull);
}else{
blob_write_to_file(&record, zFull);
}
file_wd_setexe(zFull, isExe);
fossil_print("REVERTED: %s\n", zFile);
mtime = file_wd_mtime(zFull);
db_multi_exec(
"UPDATE vfile"
" SET mtime=%lld, chnged=0, deleted=0, isexe=%d, islink=%d,mrid=rid"
" WHERE pathname=%Q OR origname=%Q",
mtime, isExe, isLink, zFile, zFile
);
}
blob_reset(&record);
free(zFull);
}
db_finalize(&q);
undo_finish();
db_end_transaction(0);
}
|
Changes to src/xfer.c.
| ︙ | ︙ | |||
16 17 18 19 20 21 22 23 24 25 26 27 28 29 |
*******************************************************************************
**
** This file contains code to implement the file transfer protocol.
*/
#include "config.h"
#include "xfer.h"
/*
** This structure holds information about the current state of either
** a client or a server that is participating in xfer.
*/
typedef struct Xfer Xfer;
struct Xfer {
Blob *pIn; /* Input text from the other side */
| > > | 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 |
*******************************************************************************
**
** This file contains code to implement the file transfer protocol.
*/
#include "config.h"
#include "xfer.h"
#include <time.h>
/*
** This structure holds information about the current state of either
** a client or a server that is participating in xfer.
*/
typedef struct Xfer Xfer;
struct Xfer {
Blob *pIn; /* Input text from the other side */
|
| ︙ | ︙ | |||
38 39 40 41 42 43 44 45 46 47 48 49 50 51 | int nDeltaSent; /* Number of deltas sent */ int nFileRcvd; /* Number of files received */ int nDeltaRcvd; /* Number of deltas received */ int nDanglingFile; /* Number of dangling deltas received */ int mxSend; /* Stop sending "file" with pOut reaches this size */ u8 syncPrivate; /* True to enable syncing private content */ u8 nextIsPrivate; /* If true, next "file" received is a private */ }; /* ** The input blob contains a UUID. Convert it into a record ID. ** Create a phantom record if no prior record exists and ** phantomize is true. | > | 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 | int nDeltaSent; /* Number of deltas sent */ int nFileRcvd; /* Number of files received */ int nDeltaRcvd; /* Number of deltas received */ int nDanglingFile; /* Number of dangling deltas received */ int mxSend; /* Stop sending "file" with pOut reaches this size */ u8 syncPrivate; /* True to enable syncing private content */ u8 nextIsPrivate; /* If true, next "file" received is a private */ time_t maxTime; /* Time when this transfer should be finished */ }; /* ** The input blob contains a UUID. Convert it into a record ID. ** Create a phantom record if no prior record exists and ** phantomize is true. |
| ︙ | ︙ | |||
391 392 393 394 395 396 397 |
}else{
pUuid = &uuid;
}
if( uuid_is_shunned(blob_str(pUuid)) ){
blob_reset(&uuid);
return;
}
| > | | 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 |
}else{
pUuid = &uuid;
}
if( uuid_is_shunned(blob_str(pUuid)) ){
blob_reset(&uuid);
return;
}
if( (pXfer->maxTime != -1 && time(NULL) >= pXfer->maxTime) ||
pXfer->mxSend<=blob_size(pXfer->pOut) ){
const char *zFormat = isPriv ? "igot %b 1\n" : "igot %b\n";
blob_appendf(pXfer->pOut, zFormat, pUuid);
pXfer->nIGotSent++;
blob_reset(&uuid);
return;
}
if( nativeDelta ){
|
| ︙ | ︙ | |||
865 866 867 868 869 870 871 872 873 874 875 876 877 878 |
@ error database\sschema\sis\sout-of-date\son\sthe\sserver.
return;
}
blob_zero(&xfer.err);
xfer.pIn = &g.cgiIn;
xfer.pOut = cgi_output_blob();
xfer.mxSend = db_get_int("max-download", 5000000);
g.xferPanic = 1;
db_begin_transaction();
db_multi_exec(
"CREATE TEMP TABLE onremote(rid INTEGER PRIMARY KEY);"
);
manifest_crosslink_begin();
| > > > | 869 870 871 872 873 874 875 876 877 878 879 880 881 882 883 884 885 |
@ error database\sschema\sis\sout-of-date\son\sthe\sserver.
return;
}
blob_zero(&xfer.err);
xfer.pIn = &g.cgiIn;
xfer.pOut = cgi_output_blob();
xfer.mxSend = db_get_int("max-download", 5000000);
xfer.maxTime = db_get_int("max-download-time", 30);
if( xfer.maxTime<1 ) xfer.maxTime = 1;
xfer.maxTime += time(NULL);
g.xferPanic = 1;
db_begin_transaction();
db_multi_exec(
"CREATE TEMP TABLE onremote(rid INTEGER PRIMARY KEY);"
);
manifest_crosslink_begin();
|
| ︙ | ︙ | |||
1032 1033 1034 1035 1036 1037 1038 |
){
int seqno, max;
if( iVers>=3 ){
cgi_set_content_type("application/x-fossil-uncompressed");
}
blob_is_int(&xfer.aToken[2], &seqno);
max = db_int(0, "SELECT max(rid) FROM blob");
| | > | 1039 1040 1041 1042 1043 1044 1045 1046 1047 1048 1049 1050 1051 1052 1053 1054 |
){
int seqno, max;
if( iVers>=3 ){
cgi_set_content_type("application/x-fossil-uncompressed");
}
blob_is_int(&xfer.aToken[2], &seqno);
max = db_int(0, "SELECT max(rid) FROM blob");
while( xfer.mxSend>blob_size(xfer.pOut) && seqno<=max){
if( time(NULL) >= xfer.maxTime ) break;
if( iVers>=3 ){
send_compressed_file(&xfer, seqno);
}else{
send_file(&xfer, seqno, 0, 1);
}
seqno++;
}
|
| ︙ | ︙ | |||
1326 1327 1328 1329 1330 1331 1332 1333 1334 1335 1336 1337 1338 1339 |
transport_stats(0, 0, 1);
socket_global_init();
memset(&xfer, 0, sizeof(xfer));
xfer.pIn = &recv;
xfer.pOut = &send;
xfer.mxSend = db_get_int("max-upload", 250000);
if( syncFlags & SYNC_PRIVATE ){
g.perm.Private = 1;
xfer.syncPrivate = 1;
}
db_begin_transaction();
db_record_repository_filename(0);
| > | 1334 1335 1336 1337 1338 1339 1340 1341 1342 1343 1344 1345 1346 1347 1348 |
transport_stats(0, 0, 1);
socket_global_init();
memset(&xfer, 0, sizeof(xfer));
xfer.pIn = &recv;
xfer.pOut = &send;
xfer.mxSend = db_get_int("max-upload", 250000);
xfer.maxTime = -1;
if( syncFlags & SYNC_PRIVATE ){
g.perm.Private = 1;
xfer.syncPrivate = 1;
}
db_begin_transaction();
db_record_repository_filename(0);
|
| ︙ | ︙ |
Added test/revert.test.
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 |
#
# Tests for 'fossil revert'
#
#
catch {exec $::fossilexe info} res
puts res=$res
if {![regexp {use --repository} $res]} {
puts stderr "Cannot run this test within an open checkout"
return
}
# Fossil will write data on $HOME, running 'fossil new' here.
# We need not to clutter the $HOME of the test caller.
#
set env(HOME) [pwd]
# Normalize file status lists (like those returned by 'fossil changes')
# so they can be compared using simple string comparison
#
proc normalize-status-list {list} {
set normalized [list]
set matches [regexp -all -inline -line {^\s*([A-Z]+)\s+(.*)$} $list]
foreach {_ status file} $matches {
lappend normalized [list $status [string trim $file]]
}
set normalized [lsort -index 1 $normalized]
return $normalized
}
# Test 'fossil revert' against expected results from 'fossil changes' and
# 'fossil addremove --test', as well as by verifying the existence of files
# on the file system. 'fossil undo' is called after each test
#
proc revert-test {testid args} {
global RESULT
set passed 1
if {[llength $args] % 2} {
set revertArgs [lindex $args 0]
set args [lrange $args 1 end]
} else {
set revertArgs {}
}
set args [dict merge {
-changes {} -addremove {} -exists {} -notexists {}
} $args]
fossil revert {*}$revertArgs
set statusListTests [list -changes changes -addremove {addremove --test}]
foreach {key fossilArgs} $statusListTests {
set expected [normalize-status-list [dict get $args $key]]
set result [normalize-status-list [fossil {*}$fossilArgs]]
if {$result ne $expected} {
set passed 0
protOut " Expected:\n [join $expected "\n "]"
protOut " Got:\n [join $result "\n "]"
}
}
set fileExistsTests [list -exists 1 does -notexists 0 should]
foreach {key expected verb} $fileExistsTests {
foreach path [dict get $args $key] {
if {[file exists $path] != $expected} {
set passed 0
protOut " Failure: File $verb not exist: $path"
}
}
}
fossil undo
test revert-$testid $passed
}
# Create the repo
#
fossil new rep.fossil
fossil open rep.fossil
# Prepare first commit
#
write_file f1 "f1"
write_file f2 "f2"
write_file f3 "f3"
fossil add f1 f2 f3
fossil commit -m "c1"
# Make changes to be reverted
#
# Add f0
write_file f0 "f0"
fossil add f0
# Remove f1
exec rm f1
fossil rm f1
# Edit f2
write_file f2 "f2.1"
# Rename f3 to f3n
exec mv f3 f3n
fossil mv f3 f3n
# Test 'fossil revert' with no arguments
#
revert-test 1 -addremove {
ADDED f0
} -exists {f0 f1 f2 f3} -notexists f3n
# Test with a single filename argument
#
revert-test 2 f0 -changes {
DELETED f1
EDITED f2
RENAMED f3n
} -addremove {
ADDED f0
} -exists {f0 f2 f3n} -notexists f3
revert-test 3 f1 -changes {
ADDED f0
EDITED f2
RENAMED f3n
} -exists {f0 f1 f2 f3n} -notexists f3
revert-test 4 f2 -changes {
ADDED f0
DELETED f1
RENAMED f3n
} -exists {f0 f2 f3n} -notexists {f1 f3}
# Both files involved in a rename are reverted regardless of which filename
# is used as an argument to 'fossil revert'
#
revert-test 5 f3 -changes {
ADDED f0
DELETED f1
EDITED f2
} -exists {f0 f2 f3} -notexists {f1 f3n}
revert-test 6 f3n -changes {
ADDED f0
DELETED f1
EDITED f2
} -exists {f0 f2 f3} -notexists {f1 f3n}
# Test with multiple filename arguments
#
revert-test 7 {f0 f2 f3n} -changes {
DELETED f1
} -addremove {
ADDED f0
} -exists {f0 f2 f3} -notexists {f1 f3n}
|
Changes to win/Makefile.mingw.
| ︙ | ︙ | |||
1631 1632 1633 1634 1635 1636 1637 | $(OBJDIR)/zip.h: $(OBJDIR)/headers $(OBJDIR)/sqlite3.o: $(SRCDIR)/sqlite3.c $(XTCC) -DSQLITE_OMIT_LOAD_EXTENSION=1 -DSQLITE_THREADSAFE=0 -DSQLITE_DEFAULT_FILE_FORMAT=4 -DSQLITE_ENABLE_STAT3 -Dlocaltime=fossil_localtime -DSQLITE_ENABLE_LOCKING_STYLE=0 -c $(SRCDIR)/sqlite3.c -o $(OBJDIR)/sqlite3.o $(OBJDIR)/cson_amalgamation.o: $(SRCDIR)/cson_amalgamation.c | | | 1631 1632 1633 1634 1635 1636 1637 1638 1639 1640 1641 1642 1643 1644 1645 | $(OBJDIR)/zip.h: $(OBJDIR)/headers $(OBJDIR)/sqlite3.o: $(SRCDIR)/sqlite3.c $(XTCC) -DSQLITE_OMIT_LOAD_EXTENSION=1 -DSQLITE_THREADSAFE=0 -DSQLITE_DEFAULT_FILE_FORMAT=4 -DSQLITE_ENABLE_STAT3 -Dlocaltime=fossil_localtime -DSQLITE_ENABLE_LOCKING_STYLE=0 -c $(SRCDIR)/sqlite3.c -o $(OBJDIR)/sqlite3.o $(OBJDIR)/cson_amalgamation.o: $(SRCDIR)/cson_amalgamation.c $(XTCC) -c $(SRCDIR)/cson_amalgamation.c -o $(OBJDIR)/cson_amalgamation.o $(OBJDIR)/json.o $(OBJDIR)/json_artifact.o $(OBJDIR)/json_branch.o $(OBJDIR)/json_config.o $(OBJDIR)/json_diff.o $(OBJDIR)/json_dir.o $(OBJDIR)/jsos_finfo.o $(OBJDIR)/json_login.o $(OBJDIR)/json_query.o $(OBJDIR)/json_report.o $(OBJDIR)/json_tag.o $(OBJDIR)/json_timeline.o $(OBJDIR)/json_user.o $(OBJDIR)/json_wiki.o : $(SRCDIR)/json_detail.h $(OBJDIR)/shell.o: $(SRCDIR)/shell.c $(SRCDIR)/sqlite3.h $(XTCC) -Dmain=sqlite3_shell -DSQLITE_OMIT_LOAD_EXTENSION=1 -c $(SRCDIR)/shell.c -o $(OBJDIR)/shell.o $(OBJDIR)/th.o: $(SRCDIR)/th.c |
| ︙ | ︙ |
Changes to win/Makefile.msc.
| ︙ | ︙ | |||
20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 |
# SSL = -DFOSSIL_ENABLE_SSL=1
# SSLLIB = ssleay32.lib libeay32.lib user32.lib gdi32.lib advapi32.lib
# zlib options
ZINCDIR = $(B)\compat\zlib
ZLIBDIR = $(B)\compat\zlib
ZLIB = zlib.lib
INCL = -I. -I$(SRCDIR) -I$B\win\include -I$(ZINCDIR)
CFLAGS = -nologo -MT -O2
BCC = $(CC) $(CFLAGS)
TCC = $(CC) -c $(CFLAGS) $(MSCDEF) $(SSL) $(INCL)
LIBS = $(ZLIB) ws2_32.lib advapi32.lib $(SSLLIB)
LIBDIR = -LIBPATH:$(ZLIBDIR)
SQLITE_OPTIONS = /DSQLITE_OMIT_LOAD_EXTENSION=1 \
/DSQLITE_THREADSAFE=0 \
/DSQLITE_DEFAULT_FILE_FORMAT=4 \
/DSQLITE_ENABLE_STAT3 \
/Dlocaltime=fossil_localtime \
/DSQLITE_ENABLE_LOCKING_STYLE=0
| > > > > > > > > > > > > > > > > > | 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 |
# SSL = -DFOSSIL_ENABLE_SSL=1
# SSLLIB = ssleay32.lib libeay32.lib user32.lib gdi32.lib advapi32.lib
# zlib options
ZINCDIR = $(B)\compat\zlib
ZLIBDIR = $(B)\compat\zlib
ZLIB = zlib.lib
# Uncomment to enable JSON API
# FOSSIL_ENABLE_JSON = 1
# Uncomment to enable markdown support
# FOSSIL_ENABLE_MARKDOWN = 1
INCL = -I. -I$(SRCDIR) -I$B\win\include -I$(ZINCDIR)
CFLAGS = -nologo -MT -O2
BCC = $(CC) $(CFLAGS)
TCC = $(CC) -c $(CFLAGS) $(MSCDEF) $(SSL) $(INCL)
RCC = rc -D_WIN32 -D_MSC_VER $(INCL)
LIBS = $(ZLIB) ws2_32.lib advapi32.lib $(SSLLIB)
LIBDIR = -LIBPATH:$(ZLIBDIR)
!ifdef FOSSIL_ENABLE_JSON
TCC = $(TCC) -DFOSSIL_ENABLE_JSON
RCC = $(RCC) -DFOSSIL_ENABLE_JSON
!endif
!ifdef FOSSIL_ENABLE_MARKDOWN
TCC = $(TCC) -DFOSSIL_ENABLE_MARKDOWN
RCC = $(RCC) -DFOSSIL_ENABLE_MARKDOWN
!endif
SQLITE_OPTIONS = /DSQLITE_OMIT_LOAD_EXTENSION=1 \
/DSQLITE_THREADSAFE=0 \
/DSQLITE_DEFAULT_FILE_FORMAT=4 \
/DSQLITE_ENABLE_STAT3 \
/Dlocaltime=fossil_localtime \
/DSQLITE_ENABLE_LOCKING_STYLE=0
|
| ︙ | ︙ | |||
158 159 160 161 162 163 164 165 166 167 168 169 170 171 |
$(OX)\checkin$O \
$(OX)\checkout$O \
$(OX)\clearsign$O \
$(OX)\clone$O \
$(OX)\comformat$O \
$(OX)\configure$O \
$(OX)\content$O \
$(OX)\db$O \
$(OX)\delta$O \
$(OX)\deltacmd$O \
$(OX)\descendants$O \
$(OX)\diff$O \
$(OX)\diffcmd$O \
$(OX)\doc$O \
| > | 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 |
$(OX)\checkin$O \
$(OX)\checkout$O \
$(OX)\clearsign$O \
$(OX)\clone$O \
$(OX)\comformat$O \
$(OX)\configure$O \
$(OX)\content$O \
$(OX)\cson_amalgamation$O \
$(OX)\db$O \
$(OX)\delta$O \
$(OX)\deltacmd$O \
$(OX)\descendants$O \
$(OX)\diff$O \
$(OX)\diffcmd$O \
$(OX)\doc$O \
|
| ︙ | ︙ | |||
217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 |
$(OX)\regexp$O \
$(OX)\report$O \
$(OX)\rss$O \
$(OX)\schema$O \
$(OX)\search$O \
$(OX)\setup$O \
$(OX)\sha1$O \
$(OX)\shun$O \
$(OX)\skins$O \
$(OX)\sqlcmd$O \
$(OX)\stash$O \
$(OX)\stat$O \
$(OX)\style$O \
$(OX)\sync$O \
$(OX)\tag$O \
$(OX)\tar$O \
$(OX)\th_main$O \
$(OX)\timeline$O \
$(OX)\tkt$O \
$(OX)\tktsetup$O \
$(OX)\undo$O \
$(OX)\unicode$O \
$(OX)\update$O \
$(OX)\url$O \
$(OX)\user$O \
$(OX)\utf8$O \
$(OX)\verify$O \
$(OX)\vfile$O \
$(OX)\wiki$O \
$(OX)\wikiformat$O \
$(OX)\winhttp$O \
$(OX)\wysiwyg$O \
$(OX)\xfer$O \
$(OX)\xfersetup$O \
$(OX)\zip$O \
| > > > > < < | < | > | 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 |
$(OX)\regexp$O \
$(OX)\report$O \
$(OX)\rss$O \
$(OX)\schema$O \
$(OX)\search$O \
$(OX)\setup$O \
$(OX)\sha1$O \
$(OX)\shell$O \
$(OX)\shun$O \
$(OX)\skins$O \
$(OX)\sqlcmd$O \
$(OX)\sqlite3$O \
$(OX)\stash$O \
$(OX)\stat$O \
$(OX)\style$O \
$(OX)\sync$O \
$(OX)\tag$O \
$(OX)\tar$O \
$(OX)\th$O \
$(OX)\th_lang$O \
$(OX)\th_main$O \
$(OX)\timeline$O \
$(OX)\tkt$O \
$(OX)\tktsetup$O \
$(OX)\undo$O \
$(OX)\unicode$O \
$(OX)\update$O \
$(OX)\url$O \
$(OX)\user$O \
$(OX)\utf8$O \
$(OX)\verify$O \
$(OX)\vfile$O \
$(OX)\wiki$O \
$(OX)\wikiformat$O \
$(OX)\winhttp$O \
$(OX)\wysiwyg$O \
$(OX)\xfer$O \
$(OX)\xfersetup$O \
$(OX)\zip$O \
$(OX)\fossil.res
APPNAME = $(OX)\fossil$(E)
all: $(OX) $(APPNAME)
zlib:
@echo Building zlib from "$(ZLIBDIR)"...
@pushd "$(ZLIBDIR)" && nmake /f win32\Makefile.msc $(ZLIB) && popd
$(APPNAME) : translate$E mkindex$E headers $(OBJ) $(OX)\linkopts zlib
cd $(OX)
link /NODEFAULTLIB:msvcrt -OUT:$@ $(LIBDIR) Wsetargv.obj fossil.res @linkopts
$(OX)\linkopts: $B\win\Makefile.msc
echo $(OX)\add.obj > $@
echo $(OX)\allrepo.obj >> $@
echo $(OX)\attach.obj >> $@
echo $(OX)\bag.obj >> $@
echo $(OX)\bisect.obj >> $@
echo $(OX)\blob.obj >> $@
echo $(OX)\branch.obj >> $@
echo $(OX)\browse.obj >> $@
echo $(OX)\captcha.obj >> $@
echo $(OX)\cgi.obj >> $@
echo $(OX)\checkin.obj >> $@
echo $(OX)\checkout.obj >> $@
echo $(OX)\clearsign.obj >> $@
echo $(OX)\clone.obj >> $@
echo $(OX)\comformat.obj >> $@
echo $(OX)\configure.obj >> $@
echo $(OX)\content.obj >> $@
echo $(OX)\cson_amalgamation.obj >> $@
echo $(OX)\db.obj >> $@
echo $(OX)\delta.obj >> $@
echo $(OX)\deltacmd.obj >> $@
echo $(OX)\descendants.obj >> $@
echo $(OX)\diff.obj >> $@
echo $(OX)\diffcmd.obj >> $@
echo $(OX)\doc.obj >> $@
|
| ︙ | ︙ | |||
405 406 407 408 409 410 411 | $(TCC) /Fo$@ -c $** $(OX)\th_lang$O : $(SRCDIR)\th_lang.c $(TCC) /Fo$@ -c $** VERSION.h : mkversion$E $B\manifest.uuid $B\manifest $B\VERSION $** > $@ | < | > > | 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 | $(TCC) /Fo$@ -c $** $(OX)\th_lang$O : $(SRCDIR)\th_lang.c $(TCC) /Fo$@ -c $** VERSION.h : mkversion$E $B\manifest.uuid $B\manifest $B\VERSION $** > $@ $(OX)\cson_amalgamation$O : $(SRCDIR)\cson_amalgamation.c $(TCC) /Fo$@ -c $** page_index.h: mkindex$E $(SRC) $** > $@ clean: -del $(OX)\*.obj -del *.obj -del *_.c -del *.h -del *.map -del *.manifest -del headers -del linkopts -del *.res realclean: clean -del $(APPNAME) -del translate$E -del mkindex$E -del makeheaders$E -del mkversion$E |
| ︙ | ︙ | |||
1068 1069 1070 1071 1072 1073 1074 1075 1076 1077 1078 1079 1080 1081 | $(OX)\zip$O : zip_.c zip.h $(TCC) /Fo$@ -c zip_.c zip_.c : $(SRCDIR)\zip.c translate$E $** > $@ headers: makeheaders$E page_index.h VERSION.h makeheaders$E add_.c:add.h \ allrepo_.c:allrepo.h \ attach_.c:attach.h \ bag_.c:bag.h \ bisect_.c:bisect.h \ blob_.c:blob.h \ | > > | 1089 1090 1091 1092 1093 1094 1095 1096 1097 1098 1099 1100 1101 1102 1103 1104 | $(OX)\zip$O : zip_.c zip.h $(TCC) /Fo$@ -c zip_.c zip_.c : $(SRCDIR)\zip.c translate$E $** > $@ fossil.res : $B\win\fossil.rc $(RCC) -fo $@ $** headers: makeheaders$E page_index.h VERSION.h makeheaders$E add_.c:add.h \ allrepo_.c:allrepo.h \ attach_.c:attach.h \ bag_.c:bag.h \ bisect_.c:bisect.h \ blob_.c:blob.h \ |
| ︙ | ︙ |