Fossil SCM
merge trunk
Commit
013854ae7616e0ae0dd7ab64639c11a492e5ab3d
Parent
9d657c3be50cc77…
23 files changed
+1
+3
+42
-46
+2
-3
+52
-29
+52
-29
+1
-1
+1
-3
+1
-1
+30
-11
+1
+8
+7
-7
+9
+199
-93
+1
-1
+25
-31
+4
-4
+25
-6
+11
-2
+26
+1
-1
+30
-7
~
src/allrepo.c
~
src/cson_amalgamation.h
~
src/db.c
~
src/encode.c
~
src/file.c
~
src/file.c
~
src/info.c
~
src/json.c
~
src/main.mk
~
src/makemake.tcl
~
src/merge.c
~
src/rebuild.c
~
src/regexp.c
~
src/setup.c
~
src/sqlite3.c
~
src/sqlite3.h
~
src/stat.c
~
src/timeline.c
~
src/update.c
~
src/xfer.c
~
test/revert.test
~
win/Makefile.mingw
~
win/Makefile.msc
+1
| --- src/allrepo.c | ||
| +++ src/allrepo.c | ||
| @@ -144,10 +144,11 @@ | ||
| 144 | 144 | collect_argument(&extra, "compress"); |
| 145 | 145 | collect_argument(&extra, "noverify"); |
| 146 | 146 | collect_argument_value(&extra, "pagesize"); |
| 147 | 147 | collect_argument(&extra, "vacuum"); |
| 148 | 148 | collect_argument(&extra, "deanalyze"); |
| 149 | + collect_argument(&extra, "analyze"); | |
| 149 | 150 | collect_argument(&extra, "wal"); |
| 150 | 151 | collect_argument(&extra, "stat"); |
| 151 | 152 | }else if( strncmp(zCmd, "sync", n)==0 ){ |
| 152 | 153 | zCmd = "sync -autourl -R"; |
| 153 | 154 | collect_argument(&extra, "verbose"); |
| 154 | 155 |
| --- src/allrepo.c | |
| +++ src/allrepo.c | |
| @@ -144,10 +144,11 @@ | |
| 144 | collect_argument(&extra, "compress"); |
| 145 | collect_argument(&extra, "noverify"); |
| 146 | collect_argument_value(&extra, "pagesize"); |
| 147 | collect_argument(&extra, "vacuum"); |
| 148 | collect_argument(&extra, "deanalyze"); |
| 149 | collect_argument(&extra, "wal"); |
| 150 | collect_argument(&extra, "stat"); |
| 151 | }else if( strncmp(zCmd, "sync", n)==0 ){ |
| 152 | zCmd = "sync -autourl -R"; |
| 153 | collect_argument(&extra, "verbose"); |
| 154 |
| --- src/allrepo.c | |
| +++ src/allrepo.c | |
| @@ -144,10 +144,11 @@ | |
| 144 | collect_argument(&extra, "compress"); |
| 145 | collect_argument(&extra, "noverify"); |
| 146 | collect_argument_value(&extra, "pagesize"); |
| 147 | collect_argument(&extra, "vacuum"); |
| 148 | collect_argument(&extra, "deanalyze"); |
| 149 | collect_argument(&extra, "analyze"); |
| 150 | collect_argument(&extra, "wal"); |
| 151 | collect_argument(&extra, "stat"); |
| 152 | }else if( strncmp(zCmd, "sync", n)==0 ){ |
| 153 | zCmd = "sync -autourl -R"; |
| 154 | collect_argument(&extra, "verbose"); |
| 155 |
| --- src/cson_amalgamation.h | ||
| +++ src/cson_amalgamation.h | ||
| @@ -1,6 +1,9 @@ | ||
| 1 | 1 | #ifdef FOSSIL_ENABLE_JSON |
| 2 | +#ifndef CSON_FOSSIL_MODE | |
| 3 | +#define CSON_FOSSIL_MODE | |
| 4 | +#endif | |
| 2 | 5 | /* auto-generated! Do not edit! */ |
| 3 | 6 | /* begin file include/wh/cson/cson.h */ |
| 4 | 7 | #if !defined(WANDERINGHORSE_NET_CSON_H_INCLUDED) |
| 5 | 8 | #define WANDERINGHORSE_NET_CSON_H_INCLUDED 1 |
| 6 | 9 | |
| 7 | 10 |
| --- src/cson_amalgamation.h | |
| +++ src/cson_amalgamation.h | |
| @@ -1,6 +1,9 @@ | |
| 1 | #ifdef FOSSIL_ENABLE_JSON |
| 2 | /* auto-generated! Do not edit! */ |
| 3 | /* begin file include/wh/cson/cson.h */ |
| 4 | #if !defined(WANDERINGHORSE_NET_CSON_H_INCLUDED) |
| 5 | #define WANDERINGHORSE_NET_CSON_H_INCLUDED 1 |
| 6 | |
| 7 |
| --- src/cson_amalgamation.h | |
| +++ src/cson_amalgamation.h | |
| @@ -1,6 +1,9 @@ | |
| 1 | #ifdef FOSSIL_ENABLE_JSON |
| 2 | #ifndef CSON_FOSSIL_MODE |
| 3 | #define CSON_FOSSIL_MODE |
| 4 | #endif |
| 5 | /* auto-generated! Do not edit! */ |
| 6 | /* begin file include/wh/cson/cson.h */ |
| 7 | #if !defined(WANDERINGHORSE_NET_CSON_H_INCLUDED) |
| 8 | #define WANDERINGHORSE_NET_CSON_H_INCLUDED 1 |
| 9 | |
| 10 |
M
src/db.c
+42
-46
| --- src/db.c | ||
| +++ src/db.c | ||
| @@ -484,20 +484,30 @@ | ||
| 484 | 484 | /* |
| 485 | 485 | ** Execute multiple SQL statements. |
| 486 | 486 | */ |
| 487 | 487 | int db_multi_exec(const char *zSql, ...){ |
| 488 | 488 | Blob sql; |
| 489 | - int rc; | |
| 489 | + int rc = SQLITE_OK; | |
| 490 | 490 | va_list ap; |
| 491 | - char *zErr = 0; | |
| 491 | + const char *z, *zEnd; | |
| 492 | + sqlite3_stmt *pStmt; | |
| 492 | 493 | blob_init(&sql, 0, 0); |
| 493 | 494 | va_start(ap, zSql); |
| 494 | 495 | blob_vappendf(&sql, zSql, ap); |
| 495 | 496 | va_end(ap); |
| 496 | - rc = sqlite3_exec(g.db, blob_buffer(&sql), 0, 0, &zErr); | |
| 497 | - if( rc!=SQLITE_OK ){ | |
| 498 | - db_err("%s\n%s", zErr, blob_buffer(&sql)); | |
| 497 | + z = blob_str(&sql); | |
| 498 | + while( rc==SQLITE_OK && z[0] ){ | |
| 499 | + pStmt = 0; | |
| 500 | + rc = sqlite3_prepare_v2(g.db, z, -1, &pStmt, &zEnd); | |
| 501 | + if( rc!=SQLITE_OK ) break; | |
| 502 | + if( pStmt ){ | |
| 503 | + db.nPrepare++; | |
| 504 | + while( sqlite3_step(pStmt)==SQLITE_ROW ){} | |
| 505 | + rc = sqlite3_finalize(pStmt); | |
| 506 | + if( rc ) db_err("%s: {%.*s}", sqlite3_errmsg(g.db), (int)(zEnd-z), z); | |
| 507 | + } | |
| 508 | + z = zEnd; | |
| 499 | 509 | } |
| 500 | 510 | blob_reset(&sql); |
| 501 | 511 | return rc; |
| 502 | 512 | } |
| 503 | 513 | |
| @@ -642,15 +652,11 @@ | ||
| 642 | 652 | sqlite3 *db; |
| 643 | 653 | int rc; |
| 644 | 654 | const char *zSql; |
| 645 | 655 | va_list ap; |
| 646 | 656 | |
| 647 | - rc = sqlite3_open(zFileName, &db); | |
| 648 | - if( rc!=SQLITE_OK ){ | |
| 649 | - db_err("[%s] %s", zFileName, sqlite3_errmsg(db)); | |
| 650 | - } | |
| 651 | - sqlite3_busy_timeout(db, 5000); | |
| 657 | + db = db_open(zFileName); | |
| 652 | 658 | sqlite3_exec(db, "BEGIN EXCLUSIVE", 0, 0, 0); |
| 653 | 659 | rc = sqlite3_exec(db, zSchema, 0, 0, 0); |
| 654 | 660 | if( rc!=SQLITE_OK ){ |
| 655 | 661 | db_err(sqlite3_errmsg(db)); |
| 656 | 662 | } |
| @@ -697,11 +703,11 @@ | ||
| 697 | 703 | |
| 698 | 704 | /* |
| 699 | 705 | ** Open a database file. Return a pointer to the new database |
| 700 | 706 | ** connection. An error results in process abort. |
| 701 | 707 | */ |
| 702 | -static sqlite3 *openDatabase(const char *zDbName){ | |
| 708 | +LOCAL sqlite3 *db_open(const char *zDbName){ | |
| 703 | 709 | int rc; |
| 704 | 710 | const char *zVfs; |
| 705 | 711 | sqlite3 *db; |
| 706 | 712 | |
| 707 | 713 | if( g.fSqlTrace ) fossil_trace("-- sqlite3_open: [%s]\n", zDbName); |
| @@ -717,10 +723,23 @@ | ||
| 717 | 723 | sqlite3_busy_timeout(db, 5000); |
| 718 | 724 | sqlite3_wal_autocheckpoint(db, 1); /* Set to checkpoint frequently */ |
| 719 | 725 | sqlite3_create_function(db, "now", 0, SQLITE_ANY, 0, db_now_function, 0, 0); |
| 720 | 726 | sqlite3_create_function(db, "checkin_mtime", 2, SQLITE_ANY, 0, |
| 721 | 727 | db_checkin_mtime_function, 0, 0); |
| 728 | + sqlite3_create_function(db, "user", 0, SQLITE_ANY, 0, db_sql_user, 0, 0); | |
| 729 | + sqlite3_create_function(db, "cgi", 1, SQLITE_ANY, 0, db_sql_cgi, 0, 0); | |
| 730 | + sqlite3_create_function(db, "cgi", 2, SQLITE_ANY, 0, db_sql_cgi, 0, 0); | |
| 731 | + sqlite3_create_function(db, "print", -1, SQLITE_UTF8, 0,db_sql_print,0,0); | |
| 732 | + sqlite3_create_function( | |
| 733 | + db, "is_selected", 1, SQLITE_UTF8, 0, file_is_selected,0,0 | |
| 734 | + ); | |
| 735 | + sqlite3_create_function( | |
| 736 | + db, "if_selected", 3, SQLITE_UTF8, 0, file_is_selected,0,0 | |
| 737 | + ); | |
| 738 | + if( g.fSqlTrace ) sqlite3_trace(db, db_sql_trace, 0); | |
| 739 | + re_add_sql_func(db); | |
| 740 | + sqlite3_exec(db, "PRAGMA foreign_keys=OFF;", 0, 0, 0); | |
| 722 | 741 | return db; |
| 723 | 742 | } |
| 724 | 743 | |
| 725 | 744 | |
| 726 | 745 | /* |
| @@ -748,13 +767,12 @@ | ||
| 748 | 767 | const char *zLabel, |
| 749 | 768 | int *pWasAttached |
| 750 | 769 | ){ |
| 751 | 770 | if( !g.db ){ |
| 752 | 771 | assert( g.zMainDbType==0 ); |
| 753 | - g.db = openDatabase(zDbName); | |
| 772 | + g.db = db_open(zDbName); | |
| 754 | 773 | g.zMainDbType = zLabel; |
| 755 | - db_connection_init(); | |
| 756 | 774 | if ( pWasAttached ) *pWasAttached = 0; |
| 757 | 775 | }else{ |
| 758 | 776 | assert( g.zMainDbType!=0 ); |
| 759 | 777 | db_attach(zDbName, zLabel); |
| 760 | 778 | if ( pWasAttached ) *pWasAttached = 1; |
| @@ -821,11 +839,11 @@ | ||
| 821 | 839 | db_open_or_attach(zDbName, "configdb", &g.useAttach); |
| 822 | 840 | g.dbConfig = 0; |
| 823 | 841 | g.zConfigDbType = 0; |
| 824 | 842 | }else{ |
| 825 | 843 | g.useAttach = 0; |
| 826 | - g.dbConfig = openDatabase(zDbName); | |
| 844 | + g.dbConfig = db_open(zDbName); | |
| 827 | 845 | g.zConfigDbType = "configdb"; |
| 828 | 846 | } |
| 829 | 847 | g.configOpen = 1; |
| 830 | 848 | free(zDbName); |
| 831 | 849 | } |
| @@ -1445,11 +1463,11 @@ | ||
| 1445 | 1463 | ** SQL functions for debugging. |
| 1446 | 1464 | ** |
| 1447 | 1465 | ** The print() function writes its arguments on stdout, but only |
| 1448 | 1466 | ** if the -sqlprint command-line option is turned on. |
| 1449 | 1467 | */ |
| 1450 | -static void db_sql_print( | |
| 1468 | +LOCAL void db_sql_print( | |
| 1451 | 1469 | sqlite3_context *context, |
| 1452 | 1470 | int argc, |
| 1453 | 1471 | sqlite3_value **argv |
| 1454 | 1472 | ){ |
| 1455 | 1473 | int i; |
| @@ -1458,20 +1476,20 @@ | ||
| 1458 | 1476 | char c = i==argc-1 ? '\n' : ' '; |
| 1459 | 1477 | fossil_print("%s%c", sqlite3_value_text(argv[i]), c); |
| 1460 | 1478 | } |
| 1461 | 1479 | } |
| 1462 | 1480 | } |
| 1463 | -static void db_sql_trace(void *notUsed, const char *zSql){ | |
| 1481 | +LOCAL void db_sql_trace(void *notUsed, const char *zSql){ | |
| 1464 | 1482 | int n = strlen(zSql); |
| 1465 | 1483 | fossil_trace("%s%s\n", zSql, (n>0 && zSql[n-1]==';') ? "" : ";"); |
| 1466 | 1484 | } |
| 1467 | 1485 | |
| 1468 | 1486 | /* |
| 1469 | 1487 | ** Implement the user() SQL function. user() takes no arguments and |
| 1470 | 1488 | ** returns the user ID of the current user. |
| 1471 | 1489 | */ |
| 1472 | -static void db_sql_user( | |
| 1490 | +LOCAL void db_sql_user( | |
| 1473 | 1491 | sqlite3_context *context, |
| 1474 | 1492 | int argc, |
| 1475 | 1493 | sqlite3_value **argv |
| 1476 | 1494 | ){ |
| 1477 | 1495 | if( g.zLogin!=0 ){ |
| @@ -1483,11 +1501,11 @@ | ||
| 1483 | 1501 | ** Implement the cgi() SQL function. cgi() takes an argument which is |
| 1484 | 1502 | ** a name of CGI query parameter. The value of that parameter is returned, |
| 1485 | 1503 | ** if available. Optional second argument will be returned if the first |
| 1486 | 1504 | ** doesn't exist as a CGI parameter. |
| 1487 | 1505 | */ |
| 1488 | -static void db_sql_cgi(sqlite3_context *context, int argc, sqlite3_value **argv){ | |
| 1506 | +LOCAL void db_sql_cgi(sqlite3_context *context, int argc, sqlite3_value **argv){ | |
| 1489 | 1507 | const char* zP; |
| 1490 | 1508 | if( argc!=1 && argc!=2 ) return; |
| 1491 | 1509 | zP = P((const char*)sqlite3_value_text(argv[0])); |
| 1492 | 1510 | if( zP ){ |
| 1493 | 1511 | sqlite3_result_text(context, zP, -1, SQLITE_STATIC); |
| @@ -1514,11 +1532,11 @@ | ||
| 1514 | 1532 | ** (meaning that id was named on the command-line). |
| 1515 | 1533 | ** |
| 1516 | 1534 | ** In the second form (3 arguments) return argument X if true and Y |
| 1517 | 1535 | ** if false. Except if Y is NULL then always return X. |
| 1518 | 1536 | */ |
| 1519 | -static void file_is_selected( | |
| 1537 | +LOCAL void file_is_selected( | |
| 1520 | 1538 | sqlite3_context *context, |
| 1521 | 1539 | int argc, |
| 1522 | 1540 | sqlite3_value **argv |
| 1523 | 1541 | ){ |
| 1524 | 1542 | int rc = 0; |
| @@ -1602,32 +1620,10 @@ | ||
| 1602 | 1620 | zOut = mprintf("%s", zKey); |
| 1603 | 1621 | } |
| 1604 | 1622 | return zOut; |
| 1605 | 1623 | } |
| 1606 | 1624 | |
| 1607 | -/* | |
| 1608 | -** This function registers auxiliary functions when the SQLite | |
| 1609 | -** database connection is first established. | |
| 1610 | -*/ | |
| 1611 | -void db_connection_init(void){ | |
| 1612 | - sqlite3_exec(g.db, "PRAGMA foreign_keys=OFF;", 0, 0, 0); | |
| 1613 | - sqlite3_create_function(g.db, "user", 0, SQLITE_ANY, 0, db_sql_user, 0, 0); | |
| 1614 | - sqlite3_create_function(g.db, "cgi", 1, SQLITE_ANY, 0, db_sql_cgi, 0, 0); | |
| 1615 | - sqlite3_create_function(g.db, "cgi", 2, SQLITE_ANY, 0, db_sql_cgi, 0, 0); | |
| 1616 | - sqlite3_create_function(g.db, "print", -1, SQLITE_UTF8, 0,db_sql_print,0,0); | |
| 1617 | - sqlite3_create_function( | |
| 1618 | - g.db, "is_selected", 1, SQLITE_UTF8, 0, file_is_selected,0,0 | |
| 1619 | - ); | |
| 1620 | - sqlite3_create_function( | |
| 1621 | - g.db, "if_selected", 3, SQLITE_UTF8, 0, file_is_selected,0,0 | |
| 1622 | - ); | |
| 1623 | - if( g.fSqlTrace ){ | |
| 1624 | - sqlite3_trace(g.db, db_sql_trace, 0); | |
| 1625 | - } | |
| 1626 | - re_add_sql_func(g.db); | |
| 1627 | -} | |
| 1628 | - | |
| 1629 | 1625 | /* |
| 1630 | 1626 | ** Return true if the string zVal represents "true" (or "false"). |
| 1631 | 1627 | */ |
| 1632 | 1628 | int is_truth(const char *zVal){ |
| 1633 | 1629 | static const char *const azOn[] = { "on", "yes", "true", "1" }; |
| @@ -2176,18 +2172,18 @@ | ||
| 2176 | 2172 | ** diff-command External command to run when performing a diff. |
| 2177 | 2173 | ** If undefined, the internal text diff will be used. |
| 2178 | 2174 | ** |
| 2179 | 2175 | ** dont-push Prevent this repository from pushing from client to |
| 2180 | 2176 | ** server. Useful when setting up a private branch. |
| 2177 | +** | |
| 2178 | +** editor Text editor command used for check-in comments. | |
| 2181 | 2179 | ** |
| 2182 | 2180 | ** empty-dirs A comma or newline-separated list of pathnames. On |
| 2183 | 2181 | ** (versionable) update and checkout commands, if no file or directory |
| 2184 | 2182 | ** exists with that name, an empty directory will be |
| 2185 | 2183 | ** created. |
| 2186 | 2184 | ** |
| 2187 | -** editor Text editor command used for check-in comments. | |
| 2188 | -** | |
| 2189 | 2185 | ** gdiff-command External command to run when performing a graphical |
| 2190 | 2186 | ** diff. If undefined, text diff will be used. |
| 2191 | 2187 | ** |
| 2192 | 2188 | ** gmerge-command A graphical merge conflict resolver command operating |
| 2193 | 2189 | ** on four files. |
| @@ -2245,10 +2241,13 @@ | ||
| 2245 | 2241 | ** |
| 2246 | 2242 | ** self-register Allow users to register themselves through the HTTP UI. |
| 2247 | 2243 | ** This is useful if you want to see other names than |
| 2248 | 2244 | ** "Anonymous" in e.g. ticketing system. On the other hand |
| 2249 | 2245 | ** users can not be deleted. Default: off. |
| 2246 | +** | |
| 2247 | +** ssh-command Command used to talk to a remote machine with | |
| 2248 | +** the "ssh://" protocol. | |
| 2250 | 2249 | ** |
| 2251 | 2250 | ** ssl-ca-location The full pathname to a file containing PEM encoded |
| 2252 | 2251 | ** CA root certificates, or a directory of certificates |
| 2253 | 2252 | ** with filenames formed from the certificate hashes as |
| 2254 | 2253 | ** required by OpenSSL. |
| @@ -2264,13 +2263,10 @@ | ||
| 2264 | 2263 | ** the certificate and private key files. |
| 2265 | 2264 | ** This identity will be presented to SSL servers to |
| 2266 | 2265 | ** authenticate this client, in addition to the normal |
| 2267 | 2266 | ** password authentication. |
| 2268 | 2267 | ** |
| 2269 | -** ssh-command Command used to talk to a remote machine with | |
| 2270 | -** the "ssh://" protocol. | |
| 2271 | -** | |
| 2272 | 2268 | ** tcl If enabled (and Fossil was compiled with Tcl support), |
| 2273 | 2269 | ** Tcl integration commands will be added to the TH1 |
| 2274 | 2270 | ** interpreter, allowing arbitrary Tcl expressions and |
| 2275 | 2271 | ** scripts to be evaluated from TH1. Additionally, the Tcl |
| 2276 | 2272 | ** interpreter will be able to evaluate arbitrary TH1 |
| 2277 | 2273 |
| --- src/db.c | |
| +++ src/db.c | |
| @@ -484,20 +484,30 @@ | |
| 484 | /* |
| 485 | ** Execute multiple SQL statements. |
| 486 | */ |
| 487 | int db_multi_exec(const char *zSql, ...){ |
| 488 | Blob sql; |
| 489 | int rc; |
| 490 | va_list ap; |
| 491 | char *zErr = 0; |
| 492 | blob_init(&sql, 0, 0); |
| 493 | va_start(ap, zSql); |
| 494 | blob_vappendf(&sql, zSql, ap); |
| 495 | va_end(ap); |
| 496 | rc = sqlite3_exec(g.db, blob_buffer(&sql), 0, 0, &zErr); |
| 497 | if( rc!=SQLITE_OK ){ |
| 498 | db_err("%s\n%s", zErr, blob_buffer(&sql)); |
| 499 | } |
| 500 | blob_reset(&sql); |
| 501 | return rc; |
| 502 | } |
| 503 | |
| @@ -642,15 +652,11 @@ | |
| 642 | sqlite3 *db; |
| 643 | int rc; |
| 644 | const char *zSql; |
| 645 | va_list ap; |
| 646 | |
| 647 | rc = sqlite3_open(zFileName, &db); |
| 648 | if( rc!=SQLITE_OK ){ |
| 649 | db_err("[%s] %s", zFileName, sqlite3_errmsg(db)); |
| 650 | } |
| 651 | sqlite3_busy_timeout(db, 5000); |
| 652 | sqlite3_exec(db, "BEGIN EXCLUSIVE", 0, 0, 0); |
| 653 | rc = sqlite3_exec(db, zSchema, 0, 0, 0); |
| 654 | if( rc!=SQLITE_OK ){ |
| 655 | db_err(sqlite3_errmsg(db)); |
| 656 | } |
| @@ -697,11 +703,11 @@ | |
| 697 | |
| 698 | /* |
| 699 | ** Open a database file. Return a pointer to the new database |
| 700 | ** connection. An error results in process abort. |
| 701 | */ |
| 702 | static sqlite3 *openDatabase(const char *zDbName){ |
| 703 | int rc; |
| 704 | const char *zVfs; |
| 705 | sqlite3 *db; |
| 706 | |
| 707 | if( g.fSqlTrace ) fossil_trace("-- sqlite3_open: [%s]\n", zDbName); |
| @@ -717,10 +723,23 @@ | |
| 717 | sqlite3_busy_timeout(db, 5000); |
| 718 | sqlite3_wal_autocheckpoint(db, 1); /* Set to checkpoint frequently */ |
| 719 | sqlite3_create_function(db, "now", 0, SQLITE_ANY, 0, db_now_function, 0, 0); |
| 720 | sqlite3_create_function(db, "checkin_mtime", 2, SQLITE_ANY, 0, |
| 721 | db_checkin_mtime_function, 0, 0); |
| 722 | return db; |
| 723 | } |
| 724 | |
| 725 | |
| 726 | /* |
| @@ -748,13 +767,12 @@ | |
| 748 | const char *zLabel, |
| 749 | int *pWasAttached |
| 750 | ){ |
| 751 | if( !g.db ){ |
| 752 | assert( g.zMainDbType==0 ); |
| 753 | g.db = openDatabase(zDbName); |
| 754 | g.zMainDbType = zLabel; |
| 755 | db_connection_init(); |
| 756 | if ( pWasAttached ) *pWasAttached = 0; |
| 757 | }else{ |
| 758 | assert( g.zMainDbType!=0 ); |
| 759 | db_attach(zDbName, zLabel); |
| 760 | if ( pWasAttached ) *pWasAttached = 1; |
| @@ -821,11 +839,11 @@ | |
| 821 | db_open_or_attach(zDbName, "configdb", &g.useAttach); |
| 822 | g.dbConfig = 0; |
| 823 | g.zConfigDbType = 0; |
| 824 | }else{ |
| 825 | g.useAttach = 0; |
| 826 | g.dbConfig = openDatabase(zDbName); |
| 827 | g.zConfigDbType = "configdb"; |
| 828 | } |
| 829 | g.configOpen = 1; |
| 830 | free(zDbName); |
| 831 | } |
| @@ -1445,11 +1463,11 @@ | |
| 1445 | ** SQL functions for debugging. |
| 1446 | ** |
| 1447 | ** The print() function writes its arguments on stdout, but only |
| 1448 | ** if the -sqlprint command-line option is turned on. |
| 1449 | */ |
| 1450 | static void db_sql_print( |
| 1451 | sqlite3_context *context, |
| 1452 | int argc, |
| 1453 | sqlite3_value **argv |
| 1454 | ){ |
| 1455 | int i; |
| @@ -1458,20 +1476,20 @@ | |
| 1458 | char c = i==argc-1 ? '\n' : ' '; |
| 1459 | fossil_print("%s%c", sqlite3_value_text(argv[i]), c); |
| 1460 | } |
| 1461 | } |
| 1462 | } |
| 1463 | static void db_sql_trace(void *notUsed, const char *zSql){ |
| 1464 | int n = strlen(zSql); |
| 1465 | fossil_trace("%s%s\n", zSql, (n>0 && zSql[n-1]==';') ? "" : ";"); |
| 1466 | } |
| 1467 | |
| 1468 | /* |
| 1469 | ** Implement the user() SQL function. user() takes no arguments and |
| 1470 | ** returns the user ID of the current user. |
| 1471 | */ |
| 1472 | static void db_sql_user( |
| 1473 | sqlite3_context *context, |
| 1474 | int argc, |
| 1475 | sqlite3_value **argv |
| 1476 | ){ |
| 1477 | if( g.zLogin!=0 ){ |
| @@ -1483,11 +1501,11 @@ | |
| 1483 | ** Implement the cgi() SQL function. cgi() takes an argument which is |
| 1484 | ** a name of CGI query parameter. The value of that parameter is returned, |
| 1485 | ** if available. Optional second argument will be returned if the first |
| 1486 | ** doesn't exist as a CGI parameter. |
| 1487 | */ |
| 1488 | static void db_sql_cgi(sqlite3_context *context, int argc, sqlite3_value **argv){ |
| 1489 | const char* zP; |
| 1490 | if( argc!=1 && argc!=2 ) return; |
| 1491 | zP = P((const char*)sqlite3_value_text(argv[0])); |
| 1492 | if( zP ){ |
| 1493 | sqlite3_result_text(context, zP, -1, SQLITE_STATIC); |
| @@ -1514,11 +1532,11 @@ | |
| 1514 | ** (meaning that id was named on the command-line). |
| 1515 | ** |
| 1516 | ** In the second form (3 arguments) return argument X if true and Y |
| 1517 | ** if false. Except if Y is NULL then always return X. |
| 1518 | */ |
| 1519 | static void file_is_selected( |
| 1520 | sqlite3_context *context, |
| 1521 | int argc, |
| 1522 | sqlite3_value **argv |
| 1523 | ){ |
| 1524 | int rc = 0; |
| @@ -1602,32 +1620,10 @@ | |
| 1602 | zOut = mprintf("%s", zKey); |
| 1603 | } |
| 1604 | return zOut; |
| 1605 | } |
| 1606 | |
| 1607 | /* |
| 1608 | ** This function registers auxiliary functions when the SQLite |
| 1609 | ** database connection is first established. |
| 1610 | */ |
| 1611 | void db_connection_init(void){ |
| 1612 | sqlite3_exec(g.db, "PRAGMA foreign_keys=OFF;", 0, 0, 0); |
| 1613 | sqlite3_create_function(g.db, "user", 0, SQLITE_ANY, 0, db_sql_user, 0, 0); |
| 1614 | sqlite3_create_function(g.db, "cgi", 1, SQLITE_ANY, 0, db_sql_cgi, 0, 0); |
| 1615 | sqlite3_create_function(g.db, "cgi", 2, SQLITE_ANY, 0, db_sql_cgi, 0, 0); |
| 1616 | sqlite3_create_function(g.db, "print", -1, SQLITE_UTF8, 0,db_sql_print,0,0); |
| 1617 | sqlite3_create_function( |
| 1618 | g.db, "is_selected", 1, SQLITE_UTF8, 0, file_is_selected,0,0 |
| 1619 | ); |
| 1620 | sqlite3_create_function( |
| 1621 | g.db, "if_selected", 3, SQLITE_UTF8, 0, file_is_selected,0,0 |
| 1622 | ); |
| 1623 | if( g.fSqlTrace ){ |
| 1624 | sqlite3_trace(g.db, db_sql_trace, 0); |
| 1625 | } |
| 1626 | re_add_sql_func(g.db); |
| 1627 | } |
| 1628 | |
| 1629 | /* |
| 1630 | ** Return true if the string zVal represents "true" (or "false"). |
| 1631 | */ |
| 1632 | int is_truth(const char *zVal){ |
| 1633 | static const char *const azOn[] = { "on", "yes", "true", "1" }; |
| @@ -2176,18 +2172,18 @@ | |
| 2176 | ** diff-command External command to run when performing a diff. |
| 2177 | ** If undefined, the internal text diff will be used. |
| 2178 | ** |
| 2179 | ** dont-push Prevent this repository from pushing from client to |
| 2180 | ** server. Useful when setting up a private branch. |
| 2181 | ** |
| 2182 | ** empty-dirs A comma or newline-separated list of pathnames. On |
| 2183 | ** (versionable) update and checkout commands, if no file or directory |
| 2184 | ** exists with that name, an empty directory will be |
| 2185 | ** created. |
| 2186 | ** |
| 2187 | ** editor Text editor command used for check-in comments. |
| 2188 | ** |
| 2189 | ** gdiff-command External command to run when performing a graphical |
| 2190 | ** diff. If undefined, text diff will be used. |
| 2191 | ** |
| 2192 | ** gmerge-command A graphical merge conflict resolver command operating |
| 2193 | ** on four files. |
| @@ -2245,10 +2241,13 @@ | |
| 2245 | ** |
| 2246 | ** self-register Allow users to register themselves through the HTTP UI. |
| 2247 | ** This is useful if you want to see other names than |
| 2248 | ** "Anonymous" in e.g. ticketing system. On the other hand |
| 2249 | ** users can not be deleted. Default: off. |
| 2250 | ** |
| 2251 | ** ssl-ca-location The full pathname to a file containing PEM encoded |
| 2252 | ** CA root certificates, or a directory of certificates |
| 2253 | ** with filenames formed from the certificate hashes as |
| 2254 | ** required by OpenSSL. |
| @@ -2264,13 +2263,10 @@ | |
| 2264 | ** the certificate and private key files. |
| 2265 | ** This identity will be presented to SSL servers to |
| 2266 | ** authenticate this client, in addition to the normal |
| 2267 | ** password authentication. |
| 2268 | ** |
| 2269 | ** ssh-command Command used to talk to a remote machine with |
| 2270 | ** the "ssh://" protocol. |
| 2271 | ** |
| 2272 | ** tcl If enabled (and Fossil was compiled with Tcl support), |
| 2273 | ** Tcl integration commands will be added to the TH1 |
| 2274 | ** interpreter, allowing arbitrary Tcl expressions and |
| 2275 | ** scripts to be evaluated from TH1. Additionally, the Tcl |
| 2276 | ** interpreter will be able to evaluate arbitrary TH1 |
| 2277 |
| --- src/db.c | |
| +++ src/db.c | |
| @@ -484,20 +484,30 @@ | |
| 484 | /* |
| 485 | ** Execute multiple SQL statements. |
| 486 | */ |
| 487 | int db_multi_exec(const char *zSql, ...){ |
| 488 | Blob sql; |
| 489 | int rc = SQLITE_OK; |
| 490 | va_list ap; |
| 491 | const char *z, *zEnd; |
| 492 | sqlite3_stmt *pStmt; |
| 493 | blob_init(&sql, 0, 0); |
| 494 | va_start(ap, zSql); |
| 495 | blob_vappendf(&sql, zSql, ap); |
| 496 | va_end(ap); |
| 497 | z = blob_str(&sql); |
| 498 | while( rc==SQLITE_OK && z[0] ){ |
| 499 | pStmt = 0; |
| 500 | rc = sqlite3_prepare_v2(g.db, z, -1, &pStmt, &zEnd); |
| 501 | if( rc!=SQLITE_OK ) break; |
| 502 | if( pStmt ){ |
| 503 | db.nPrepare++; |
| 504 | while( sqlite3_step(pStmt)==SQLITE_ROW ){} |
| 505 | rc = sqlite3_finalize(pStmt); |
| 506 | if( rc ) db_err("%s: {%.*s}", sqlite3_errmsg(g.db), (int)(zEnd-z), z); |
| 507 | } |
| 508 | z = zEnd; |
| 509 | } |
| 510 | blob_reset(&sql); |
| 511 | return rc; |
| 512 | } |
| 513 | |
| @@ -642,15 +652,11 @@ | |
| 652 | sqlite3 *db; |
| 653 | int rc; |
| 654 | const char *zSql; |
| 655 | va_list ap; |
| 656 | |
| 657 | db = db_open(zFileName); |
| 658 | sqlite3_exec(db, "BEGIN EXCLUSIVE", 0, 0, 0); |
| 659 | rc = sqlite3_exec(db, zSchema, 0, 0, 0); |
| 660 | if( rc!=SQLITE_OK ){ |
| 661 | db_err(sqlite3_errmsg(db)); |
| 662 | } |
| @@ -697,11 +703,11 @@ | |
| 703 | |
| 704 | /* |
| 705 | ** Open a database file. Return a pointer to the new database |
| 706 | ** connection. An error results in process abort. |
| 707 | */ |
| 708 | LOCAL sqlite3 *db_open(const char *zDbName){ |
| 709 | int rc; |
| 710 | const char *zVfs; |
| 711 | sqlite3 *db; |
| 712 | |
| 713 | if( g.fSqlTrace ) fossil_trace("-- sqlite3_open: [%s]\n", zDbName); |
| @@ -717,10 +723,23 @@ | |
| 723 | sqlite3_busy_timeout(db, 5000); |
| 724 | sqlite3_wal_autocheckpoint(db, 1); /* Set to checkpoint frequently */ |
| 725 | sqlite3_create_function(db, "now", 0, SQLITE_ANY, 0, db_now_function, 0, 0); |
| 726 | sqlite3_create_function(db, "checkin_mtime", 2, SQLITE_ANY, 0, |
| 727 | db_checkin_mtime_function, 0, 0); |
| 728 | sqlite3_create_function(db, "user", 0, SQLITE_ANY, 0, db_sql_user, 0, 0); |
| 729 | sqlite3_create_function(db, "cgi", 1, SQLITE_ANY, 0, db_sql_cgi, 0, 0); |
| 730 | sqlite3_create_function(db, "cgi", 2, SQLITE_ANY, 0, db_sql_cgi, 0, 0); |
| 731 | sqlite3_create_function(db, "print", -1, SQLITE_UTF8, 0,db_sql_print,0,0); |
| 732 | sqlite3_create_function( |
| 733 | db, "is_selected", 1, SQLITE_UTF8, 0, file_is_selected,0,0 |
| 734 | ); |
| 735 | sqlite3_create_function( |
| 736 | db, "if_selected", 3, SQLITE_UTF8, 0, file_is_selected,0,0 |
| 737 | ); |
| 738 | if( g.fSqlTrace ) sqlite3_trace(db, db_sql_trace, 0); |
| 739 | re_add_sql_func(db); |
| 740 | sqlite3_exec(db, "PRAGMA foreign_keys=OFF;", 0, 0, 0); |
| 741 | return db; |
| 742 | } |
| 743 | |
| 744 | |
| 745 | /* |
| @@ -748,13 +767,12 @@ | |
| 767 | const char *zLabel, |
| 768 | int *pWasAttached |
| 769 | ){ |
| 770 | if( !g.db ){ |
| 771 | assert( g.zMainDbType==0 ); |
| 772 | g.db = db_open(zDbName); |
| 773 | g.zMainDbType = zLabel; |
| 774 | if ( pWasAttached ) *pWasAttached = 0; |
| 775 | }else{ |
| 776 | assert( g.zMainDbType!=0 ); |
| 777 | db_attach(zDbName, zLabel); |
| 778 | if ( pWasAttached ) *pWasAttached = 1; |
| @@ -821,11 +839,11 @@ | |
| 839 | db_open_or_attach(zDbName, "configdb", &g.useAttach); |
| 840 | g.dbConfig = 0; |
| 841 | g.zConfigDbType = 0; |
| 842 | }else{ |
| 843 | g.useAttach = 0; |
| 844 | g.dbConfig = db_open(zDbName); |
| 845 | g.zConfigDbType = "configdb"; |
| 846 | } |
| 847 | g.configOpen = 1; |
| 848 | free(zDbName); |
| 849 | } |
| @@ -1445,11 +1463,11 @@ | |
| 1463 | ** SQL functions for debugging. |
| 1464 | ** |
| 1465 | ** The print() function writes its arguments on stdout, but only |
| 1466 | ** if the -sqlprint command-line option is turned on. |
| 1467 | */ |
| 1468 | LOCAL void db_sql_print( |
| 1469 | sqlite3_context *context, |
| 1470 | int argc, |
| 1471 | sqlite3_value **argv |
| 1472 | ){ |
| 1473 | int i; |
| @@ -1458,20 +1476,20 @@ | |
| 1476 | char c = i==argc-1 ? '\n' : ' '; |
| 1477 | fossil_print("%s%c", sqlite3_value_text(argv[i]), c); |
| 1478 | } |
| 1479 | } |
| 1480 | } |
| 1481 | LOCAL void db_sql_trace(void *notUsed, const char *zSql){ |
| 1482 | int n = strlen(zSql); |
| 1483 | fossil_trace("%s%s\n", zSql, (n>0 && zSql[n-1]==';') ? "" : ";"); |
| 1484 | } |
| 1485 | |
| 1486 | /* |
| 1487 | ** Implement the user() SQL function. user() takes no arguments and |
| 1488 | ** returns the user ID of the current user. |
| 1489 | */ |
| 1490 | LOCAL void db_sql_user( |
| 1491 | sqlite3_context *context, |
| 1492 | int argc, |
| 1493 | sqlite3_value **argv |
| 1494 | ){ |
| 1495 | if( g.zLogin!=0 ){ |
| @@ -1483,11 +1501,11 @@ | |
| 1501 | ** Implement the cgi() SQL function. cgi() takes an argument which is |
| 1502 | ** a name of CGI query parameter. The value of that parameter is returned, |
| 1503 | ** if available. Optional second argument will be returned if the first |
| 1504 | ** doesn't exist as a CGI parameter. |
| 1505 | */ |
| 1506 | LOCAL void db_sql_cgi(sqlite3_context *context, int argc, sqlite3_value **argv){ |
| 1507 | const char* zP; |
| 1508 | if( argc!=1 && argc!=2 ) return; |
| 1509 | zP = P((const char*)sqlite3_value_text(argv[0])); |
| 1510 | if( zP ){ |
| 1511 | sqlite3_result_text(context, zP, -1, SQLITE_STATIC); |
| @@ -1514,11 +1532,11 @@ | |
| 1532 | ** (meaning that id was named on the command-line). |
| 1533 | ** |
| 1534 | ** In the second form (3 arguments) return argument X if true and Y |
| 1535 | ** if false. Except if Y is NULL then always return X. |
| 1536 | */ |
| 1537 | LOCAL void file_is_selected( |
| 1538 | sqlite3_context *context, |
| 1539 | int argc, |
| 1540 | sqlite3_value **argv |
| 1541 | ){ |
| 1542 | int rc = 0; |
| @@ -1602,32 +1620,10 @@ | |
| 1620 | zOut = mprintf("%s", zKey); |
| 1621 | } |
| 1622 | return zOut; |
| 1623 | } |
| 1624 | |
| 1625 | /* |
| 1626 | ** Return true if the string zVal represents "true" (or "false"). |
| 1627 | */ |
| 1628 | int is_truth(const char *zVal){ |
| 1629 | static const char *const azOn[] = { "on", "yes", "true", "1" }; |
| @@ -2176,18 +2172,18 @@ | |
| 2172 | ** diff-command External command to run when performing a diff. |
| 2173 | ** If undefined, the internal text diff will be used. |
| 2174 | ** |
| 2175 | ** dont-push Prevent this repository from pushing from client to |
| 2176 | ** server. Useful when setting up a private branch. |
| 2177 | ** |
| 2178 | ** editor Text editor command used for check-in comments. |
| 2179 | ** |
| 2180 | ** empty-dirs A comma or newline-separated list of pathnames. On |
| 2181 | ** (versionable) update and checkout commands, if no file or directory |
| 2182 | ** exists with that name, an empty directory will be |
| 2183 | ** created. |
| 2184 | ** |
| 2185 | ** gdiff-command External command to run when performing a graphical |
| 2186 | ** diff. If undefined, text diff will be used. |
| 2187 | ** |
| 2188 | ** gmerge-command A graphical merge conflict resolver command operating |
| 2189 | ** on four files. |
| @@ -2245,10 +2241,13 @@ | |
| 2241 | ** |
| 2242 | ** self-register Allow users to register themselves through the HTTP UI. |
| 2243 | ** This is useful if you want to see other names than |
| 2244 | ** "Anonymous" in e.g. ticketing system. On the other hand |
| 2245 | ** users can not be deleted. Default: off. |
| 2246 | ** |
| 2247 | ** ssh-command Command used to talk to a remote machine with |
| 2248 | ** the "ssh://" protocol. |
| 2249 | ** |
| 2250 | ** ssl-ca-location The full pathname to a file containing PEM encoded |
| 2251 | ** CA root certificates, or a directory of certificates |
| 2252 | ** with filenames formed from the certificate hashes as |
| 2253 | ** required by OpenSSL. |
| @@ -2264,13 +2263,10 @@ | |
| 2263 | ** the certificate and private key files. |
| 2264 | ** This identity will be presented to SSL servers to |
| 2265 | ** authenticate this client, in addition to the normal |
| 2266 | ** password authentication. |
| 2267 | ** |
| 2268 | ** tcl If enabled (and Fossil was compiled with Tcl support), |
| 2269 | ** Tcl integration commands will be added to the TH1 |
| 2270 | ** interpreter, allowing arbitrary Tcl expressions and |
| 2271 | ** scripts to be evaluated from TH1. Additionally, the Tcl |
| 2272 | ** interpreter will be able to evaluate arbitrary TH1 |
| 2273 |
+2
-3
| --- src/encode.c | ||
| +++ src/encode.c | ||
| @@ -131,18 +131,17 @@ | ||
| 131 | 131 | static char *EncodeHttp(const char *zIn, int n, int encodeSlash){ |
| 132 | 132 | int c; |
| 133 | 133 | int i = 0; |
| 134 | 134 | int count = 0; |
| 135 | 135 | char *zOut; |
| 136 | - int other; | |
| 137 | 136 | # define IsSafeChar(X) \ |
| 138 | 137 | (fossil_isalnum(X) || (X)=='.' || (X)=='$' \ |
| 139 | - || (X)=='~' || (X)=='-' || (X)=='_' || (X)==other) | |
| 138 | + || (X)=='~' || (X)=='-' || (X)=='_' \ | |
| 139 | + || (!encodeSlash && ((X)=='/' || (X)==':'))) | |
| 140 | 140 | |
| 141 | 141 | if( zIn==0 ) return 0; |
| 142 | 142 | if( n<0 ) n = strlen(zIn); |
| 143 | - other = encodeSlash ? 'a' : '/'; | |
| 144 | 143 | while( i<n && (c = zIn[i])!=0 ){ |
| 145 | 144 | if( IsSafeChar(c) || c==' ' ){ |
| 146 | 145 | count++; |
| 147 | 146 | }else{ |
| 148 | 147 | count += 3; |
| 149 | 148 |
| --- src/encode.c | |
| +++ src/encode.c | |
| @@ -131,18 +131,17 @@ | |
| 131 | static char *EncodeHttp(const char *zIn, int n, int encodeSlash){ |
| 132 | int c; |
| 133 | int i = 0; |
| 134 | int count = 0; |
| 135 | char *zOut; |
| 136 | int other; |
| 137 | # define IsSafeChar(X) \ |
| 138 | (fossil_isalnum(X) || (X)=='.' || (X)=='$' \ |
| 139 | || (X)=='~' || (X)=='-' || (X)=='_' || (X)==other) |
| 140 | |
| 141 | if( zIn==0 ) return 0; |
| 142 | if( n<0 ) n = strlen(zIn); |
| 143 | other = encodeSlash ? 'a' : '/'; |
| 144 | while( i<n && (c = zIn[i])!=0 ){ |
| 145 | if( IsSafeChar(c) || c==' ' ){ |
| 146 | count++; |
| 147 | }else{ |
| 148 | count += 3; |
| 149 |
| --- src/encode.c | |
| +++ src/encode.c | |
| @@ -131,18 +131,17 @@ | |
| 131 | static char *EncodeHttp(const char *zIn, int n, int encodeSlash){ |
| 132 | int c; |
| 133 | int i = 0; |
| 134 | int count = 0; |
| 135 | char *zOut; |
| 136 | # define IsSafeChar(X) \ |
| 137 | (fossil_isalnum(X) || (X)=='.' || (X)=='$' \ |
| 138 | || (X)=='~' || (X)=='-' || (X)=='_' \ |
| 139 | || (!encodeSlash && ((X)=='/' || (X)==':'))) |
| 140 | |
| 141 | if( zIn==0 ) return 0; |
| 142 | if( n<0 ) n = strlen(zIn); |
| 143 | while( i<n && (c = zIn[i])!=0 ){ |
| 144 | if( IsSafeChar(c) || c==' ' ){ |
| 145 | count++; |
| 146 | }else{ |
| 147 | count += 3; |
| 148 |
+52
-29
| --- src/file.c | ||
| +++ src/file.c | ||
| @@ -483,53 +483,76 @@ | ||
| 483 | 483 | ** a file in a repository. Valid filenames follow all of the |
| 484 | 484 | ** following rules: |
| 485 | 485 | ** |
| 486 | 486 | ** * Does not begin with "/" |
| 487 | 487 | ** * Does not contain any path element named "." or ".." |
| 488 | +** * Does not contain any of these characters in the path: "\" | |
| 488 | 489 | ** * Does not end with "/". |
| 489 | 490 | ** * Does not contain two or more "/" characters in a row. |
| 490 | 491 | ** * Contains at least one character |
| 491 | 492 | ** |
| 492 | -** Invalid UTF8 characters and "\". result in a false return if | |
| 493 | -** bStrictUtf8 is true. If bStrictUtf8 is false, invalid UTF8 | |
| 494 | -** characters and "\" are silently ignored. | |
| 493 | +** Invalid UTF8 characters result in a false return if bStrictUtf8 is | |
| 494 | +** true. If bStrictUtf8 is false, invalid UTF8 characters are silently | |
| 495 | +** ignored. See http://en.wikipedia.org/wiki/UTF-8#Invalid_byte_sequences | |
| 496 | +** and http://en.wikipedia.org/wiki/Unicode (for the noncharacters) | |
| 497 | +** | |
| 498 | +** The bStrictUtf8 flag is true for new inputs, but is false when parsing | |
| 499 | +** legacy manifests, for backwards compatibility. | |
| 495 | 500 | */ |
| 496 | 501 | int file_is_simple_pathname(const char *z, int bStrictUtf8){ |
| 497 | 502 | int i; |
| 498 | - char c = z[0]; | |
| 503 | + unsigned char c = (unsigned char) z[0]; | |
| 504 | + char maskNonAscii = bStrictUtf8 ? 0x80 : 0x00; | |
| 499 | 505 | if( c=='/' || c==0 ) return 0; |
| 500 | 506 | if( c=='.' ){ |
| 501 | 507 | if( z[1]=='/' || z[1]==0 ) return 0; |
| 502 | 508 | if( z[1]=='.' && (z[2]=='/' || z[2]==0) ) return 0; |
| 503 | 509 | } |
| 504 | - for(i=0; (c=z[i])!=0; i++){ | |
| 505 | - if( bStrictUtf8 ){ | |
| 506 | - if( c & 0x80 ){ | |
| 507 | - if( (c & 0xf0) == 0xf0 ) { | |
| 510 | + for(i=0; (c=(unsigned char)z[i])!=0; i++){ | |
| 511 | + if( c & maskNonAscii ){ | |
| 512 | + if( (z[++i]&0xc0)!=0x80 ){ | |
| 513 | + /* Invalid first continuation byte */ | |
| 514 | + return 0; | |
| 515 | + } | |
| 516 | + if( c<0xc2 ){ | |
| 517 | + /* Invalid 1-byte UTF-8 sequence, or 2-byte overlong form. */ | |
| 518 | + return 0; | |
| 519 | + }else if( (c&0xe0)==0xe0 ){ | |
| 520 | + /* 3-byte or more */ | |
| 521 | + int unicode; | |
| 522 | + if( c&0x10 ){ | |
| 508 | 523 | /* Unicode characters > U+FFFF are not supported. |
| 509 | 524 | * Windows XP and earlier cannot handle them. |
| 510 | 525 | */ |
| 511 | 526 | return 0; |
| 512 | 527 | } |
| 513 | - if( (c & 0xf0) == 0xe0 ) { | |
| 514 | - /* This is a 3-byte UTF-8 character */ | |
| 515 | - if ( (c & 0xfe) == 0xee ){ | |
| 516 | - /* Range U+E000 - U+FFFF (Starting with 0xee or 0xef in UTF-8 ) */ | |
| 517 | - if ( !(c & 1) || ((z[i+1] & 0xff) < 0xa4) ){ | |
| 518 | - /* Unicode character in the range U+E000 - U+F8FF are for | |
| 519 | - * private use, they shouldn't occur in filenames. */ | |
| 520 | - return 0; | |
| 521 | - } | |
| 522 | - }else if( ((c & 0xff) == 0xed) && ((z[i+1] & 0xe0) == 0xa0) ){ | |
| 523 | - /* Unicode character in the range U+D800 - U+DFFF are for | |
| 524 | - * surrogate pairs, they shouldn't occur in filenames. */ | |
| 525 | - return 0; | |
| 526 | - } | |
| 527 | - } | |
| 528 | - }else if( c=='\\' ){ | |
| 529 | - return 0; | |
| 530 | - } | |
| 528 | + /* This is a 3-byte UTF-8 character */ | |
| 529 | + unicode = ((c&0x0f)<<12) + ((z[i]&0x3f)<<6) + (z[i+1]&0x3f); | |
| 530 | + if( unicode <= 0x07ff ){ | |
| 531 | + /* overlong form */ | |
| 532 | + return 0; | |
| 533 | + }else if( unicode>=0xe000 ){ | |
| 534 | + /* U+E000..U+FFFF */ | |
| 535 | + if( (unicode<=0xf8ff) || (unicode>=0xfffe) ){ | |
| 536 | + /* U+E000..U+F8FF are for private use. | |
| 537 | + * U+FFFE..U+FFFF are noncharacters. */ | |
| 538 | + return 0; | |
| 539 | + } else if( (unicode>=0xfdd0) && (unicode<=0xfdef) ){ | |
| 540 | + /* U+FDD0..U+FDEF are noncharacters. */ | |
| 541 | + return 0; | |
| 542 | + } | |
| 543 | + }else if( (unicode>=0xd800) && (unicode<=0xdfff) ){ | |
| 544 | + /* U+D800..U+DFFF are for surrogate pairs. */ | |
| 545 | + return 0; | |
| 546 | + } | |
| 547 | + if( (z[++i]&0xc0)!=0x80 ){ | |
| 548 | + /* Invalid second continuation byte */ | |
| 549 | + return 0; | |
| 550 | + } | |
| 551 | + } | |
| 552 | + }else if( bStrictUtf8 && (c=='\\') ){ | |
| 553 | + return 0; | |
| 531 | 554 | } |
| 532 | 555 | if( c=='/' ){ |
| 533 | 556 | if( z[i+1]=='/' ) return 0; |
| 534 | 557 | if( z[i+1]=='.' ){ |
| 535 | 558 | if( z[i+2]=='/' || z[i+2]==0 ) return 0; |
| @@ -578,11 +601,11 @@ | ||
| 578 | 601 | if( z[i]=='\\' ) z[i] = '/'; |
| 579 | 602 | } |
| 580 | 603 | #endif |
| 581 | 604 | |
| 582 | 605 | /* Removing trailing "/" characters */ |
| 583 | - if ( !slash ){ | |
| 606 | + if( !slash ){ | |
| 584 | 607 | while( n>1 && z[n-1]=='/' ){ n--; } |
| 585 | 608 | } |
| 586 | 609 | |
| 587 | 610 | /* Remove duplicate '/' characters. Except, two // at the beginning |
| 588 | 611 | ** of a pathname is allowed since this is important on windows. */ |
| @@ -835,11 +858,11 @@ | ||
| 835 | 858 | if( zPwd[i]==0 ){ |
| 836 | 859 | blob_append(pOut, ".", 1); |
| 837 | 860 | }else{ |
| 838 | 861 | blob_append(pOut, "..", 2); |
| 839 | 862 | for(j=i+1; zPwd[j]; j++){ |
| 840 | - if( zPwd[j]=='/' ) { | |
| 863 | + if( zPwd[j]=='/' ){ | |
| 841 | 864 | blob_append(pOut, "/..", 3); |
| 842 | 865 | } |
| 843 | 866 | } |
| 844 | 867 | } |
| 845 | 868 | return; |
| @@ -852,11 +875,11 @@ | ||
| 852 | 875 | return; |
| 853 | 876 | } |
| 854 | 877 | while( zPath[i-1]!='/' ){ i--; } |
| 855 | 878 | blob_set(&tmp, "../"); |
| 856 | 879 | for(j=i; zPwd[j]; j++){ |
| 857 | - if( zPwd[j]=='/' ) { | |
| 880 | + if( zPwd[j]=='/' ){ | |
| 858 | 881 | blob_append(&tmp, "../", 3); |
| 859 | 882 | } |
| 860 | 883 | } |
| 861 | 884 | blob_append(&tmp, &zPath[i], -1); |
| 862 | 885 | blob_reset(pOut); |
| 863 | 886 |
| --- src/file.c | |
| +++ src/file.c | |
| @@ -483,53 +483,76 @@ | |
| 483 | ** a file in a repository. Valid filenames follow all of the |
| 484 | ** following rules: |
| 485 | ** |
| 486 | ** * Does not begin with "/" |
| 487 | ** * Does not contain any path element named "." or ".." |
| 488 | ** * Does not end with "/". |
| 489 | ** * Does not contain two or more "/" characters in a row. |
| 490 | ** * Contains at least one character |
| 491 | ** |
| 492 | ** Invalid UTF8 characters and "\". result in a false return if |
| 493 | ** bStrictUtf8 is true. If bStrictUtf8 is false, invalid UTF8 |
| 494 | ** characters and "\" are silently ignored. |
| 495 | */ |
| 496 | int file_is_simple_pathname(const char *z, int bStrictUtf8){ |
| 497 | int i; |
| 498 | char c = z[0]; |
| 499 | if( c=='/' || c==0 ) return 0; |
| 500 | if( c=='.' ){ |
| 501 | if( z[1]=='/' || z[1]==0 ) return 0; |
| 502 | if( z[1]=='.' && (z[2]=='/' || z[2]==0) ) return 0; |
| 503 | } |
| 504 | for(i=0; (c=z[i])!=0; i++){ |
| 505 | if( bStrictUtf8 ){ |
| 506 | if( c & 0x80 ){ |
| 507 | if( (c & 0xf0) == 0xf0 ) { |
| 508 | /* Unicode characters > U+FFFF are not supported. |
| 509 | * Windows XP and earlier cannot handle them. |
| 510 | */ |
| 511 | return 0; |
| 512 | } |
| 513 | if( (c & 0xf0) == 0xe0 ) { |
| 514 | /* This is a 3-byte UTF-8 character */ |
| 515 | if ( (c & 0xfe) == 0xee ){ |
| 516 | /* Range U+E000 - U+FFFF (Starting with 0xee or 0xef in UTF-8 ) */ |
| 517 | if ( !(c & 1) || ((z[i+1] & 0xff) < 0xa4) ){ |
| 518 | /* Unicode character in the range U+E000 - U+F8FF are for |
| 519 | * private use, they shouldn't occur in filenames. */ |
| 520 | return 0; |
| 521 | } |
| 522 | }else if( ((c & 0xff) == 0xed) && ((z[i+1] & 0xe0) == 0xa0) ){ |
| 523 | /* Unicode character in the range U+D800 - U+DFFF are for |
| 524 | * surrogate pairs, they shouldn't occur in filenames. */ |
| 525 | return 0; |
| 526 | } |
| 527 | } |
| 528 | }else if( c=='\\' ){ |
| 529 | return 0; |
| 530 | } |
| 531 | } |
| 532 | if( c=='/' ){ |
| 533 | if( z[i+1]=='/' ) return 0; |
| 534 | if( z[i+1]=='.' ){ |
| 535 | if( z[i+2]=='/' || z[i+2]==0 ) return 0; |
| @@ -578,11 +601,11 @@ | |
| 578 | if( z[i]=='\\' ) z[i] = '/'; |
| 579 | } |
| 580 | #endif |
| 581 | |
| 582 | /* Removing trailing "/" characters */ |
| 583 | if ( !slash ){ |
| 584 | while( n>1 && z[n-1]=='/' ){ n--; } |
| 585 | } |
| 586 | |
| 587 | /* Remove duplicate '/' characters. Except, two // at the beginning |
| 588 | ** of a pathname is allowed since this is important on windows. */ |
| @@ -835,11 +858,11 @@ | |
| 835 | if( zPwd[i]==0 ){ |
| 836 | blob_append(pOut, ".", 1); |
| 837 | }else{ |
| 838 | blob_append(pOut, "..", 2); |
| 839 | for(j=i+1; zPwd[j]; j++){ |
| 840 | if( zPwd[j]=='/' ) { |
| 841 | blob_append(pOut, "/..", 3); |
| 842 | } |
| 843 | } |
| 844 | } |
| 845 | return; |
| @@ -852,11 +875,11 @@ | |
| 852 | return; |
| 853 | } |
| 854 | while( zPath[i-1]!='/' ){ i--; } |
| 855 | blob_set(&tmp, "../"); |
| 856 | for(j=i; zPwd[j]; j++){ |
| 857 | if( zPwd[j]=='/' ) { |
| 858 | blob_append(&tmp, "../", 3); |
| 859 | } |
| 860 | } |
| 861 | blob_append(&tmp, &zPath[i], -1); |
| 862 | blob_reset(pOut); |
| 863 |
| --- src/file.c | |
| +++ src/file.c | |
| @@ -483,53 +483,76 @@ | |
| 483 | ** a file in a repository. Valid filenames follow all of the |
| 484 | ** following rules: |
| 485 | ** |
| 486 | ** * Does not begin with "/" |
| 487 | ** * Does not contain any path element named "." or ".." |
| 488 | ** * Does not contain any of these characters in the path: "\" |
| 489 | ** * Does not end with "/". |
| 490 | ** * Does not contain two or more "/" characters in a row. |
| 491 | ** * Contains at least one character |
| 492 | ** |
| 493 | ** Invalid UTF8 characters result in a false return if bStrictUtf8 is |
| 494 | ** true. If bStrictUtf8 is false, invalid UTF8 characters are silently |
| 495 | ** ignored. See http://en.wikipedia.org/wiki/UTF-8#Invalid_byte_sequences |
| 496 | ** and http://en.wikipedia.org/wiki/Unicode (for the noncharacters) |
| 497 | ** |
| 498 | ** The bStrictUtf8 flag is true for new inputs, but is false when parsing |
| 499 | ** legacy manifests, for backwards compatibility. |
| 500 | */ |
| 501 | int file_is_simple_pathname(const char *z, int bStrictUtf8){ |
| 502 | int i; |
| 503 | unsigned char c = (unsigned char) z[0]; |
| 504 | char maskNonAscii = bStrictUtf8 ? 0x80 : 0x00; |
| 505 | if( c=='/' || c==0 ) return 0; |
| 506 | if( c=='.' ){ |
| 507 | if( z[1]=='/' || z[1]==0 ) return 0; |
| 508 | if( z[1]=='.' && (z[2]=='/' || z[2]==0) ) return 0; |
| 509 | } |
| 510 | for(i=0; (c=(unsigned char)z[i])!=0; i++){ |
| 511 | if( c & maskNonAscii ){ |
| 512 | if( (z[++i]&0xc0)!=0x80 ){ |
| 513 | /* Invalid first continuation byte */ |
| 514 | return 0; |
| 515 | } |
| 516 | if( c<0xc2 ){ |
| 517 | /* Invalid 1-byte UTF-8 sequence, or 2-byte overlong form. */ |
| 518 | return 0; |
| 519 | }else if( (c&0xe0)==0xe0 ){ |
| 520 | /* 3-byte or more */ |
| 521 | int unicode; |
| 522 | if( c&0x10 ){ |
| 523 | /* Unicode characters > U+FFFF are not supported. |
| 524 | * Windows XP and earlier cannot handle them. |
| 525 | */ |
| 526 | return 0; |
| 527 | } |
| 528 | /* This is a 3-byte UTF-8 character */ |
| 529 | unicode = ((c&0x0f)<<12) + ((z[i]&0x3f)<<6) + (z[i+1]&0x3f); |
| 530 | if( unicode <= 0x07ff ){ |
| 531 | /* overlong form */ |
| 532 | return 0; |
| 533 | }else if( unicode>=0xe000 ){ |
| 534 | /* U+E000..U+FFFF */ |
| 535 | if( (unicode<=0xf8ff) || (unicode>=0xfffe) ){ |
| 536 | /* U+E000..U+F8FF are for private use. |
| 537 | * U+FFFE..U+FFFF are noncharacters. */ |
| 538 | return 0; |
| 539 | } else if( (unicode>=0xfdd0) && (unicode<=0xfdef) ){ |
| 540 | /* U+FDD0..U+FDEF are noncharacters. */ |
| 541 | return 0; |
| 542 | } |
| 543 | }else if( (unicode>=0xd800) && (unicode<=0xdfff) ){ |
| 544 | /* U+D800..U+DFFF are for surrogate pairs. */ |
| 545 | return 0; |
| 546 | } |
| 547 | if( (z[++i]&0xc0)!=0x80 ){ |
| 548 | /* Invalid second continuation byte */ |
| 549 | return 0; |
| 550 | } |
| 551 | } |
| 552 | }else if( bStrictUtf8 && (c=='\\') ){ |
| 553 | return 0; |
| 554 | } |
| 555 | if( c=='/' ){ |
| 556 | if( z[i+1]=='/' ) return 0; |
| 557 | if( z[i+1]=='.' ){ |
| 558 | if( z[i+2]=='/' || z[i+2]==0 ) return 0; |
| @@ -578,11 +601,11 @@ | |
| 601 | if( z[i]=='\\' ) z[i] = '/'; |
| 602 | } |
| 603 | #endif |
| 604 | |
| 605 | /* Removing trailing "/" characters */ |
| 606 | if( !slash ){ |
| 607 | while( n>1 && z[n-1]=='/' ){ n--; } |
| 608 | } |
| 609 | |
| 610 | /* Remove duplicate '/' characters. Except, two // at the beginning |
| 611 | ** of a pathname is allowed since this is important on windows. */ |
| @@ -835,11 +858,11 @@ | |
| 858 | if( zPwd[i]==0 ){ |
| 859 | blob_append(pOut, ".", 1); |
| 860 | }else{ |
| 861 | blob_append(pOut, "..", 2); |
| 862 | for(j=i+1; zPwd[j]; j++){ |
| 863 | if( zPwd[j]=='/' ){ |
| 864 | blob_append(pOut, "/..", 3); |
| 865 | } |
| 866 | } |
| 867 | } |
| 868 | return; |
| @@ -852,11 +875,11 @@ | |
| 875 | return; |
| 876 | } |
| 877 | while( zPath[i-1]!='/' ){ i--; } |
| 878 | blob_set(&tmp, "../"); |
| 879 | for(j=i; zPwd[j]; j++){ |
| 880 | if( zPwd[j]=='/' ){ |
| 881 | blob_append(&tmp, "../", 3); |
| 882 | } |
| 883 | } |
| 884 | blob_append(&tmp, &zPath[i], -1); |
| 885 | blob_reset(pOut); |
| 886 |
+52
-29
| --- src/file.c | ||
| +++ src/file.c | ||
| @@ -483,53 +483,76 @@ | ||
| 483 | 483 | ** a file in a repository. Valid filenames follow all of the |
| 484 | 484 | ** following rules: |
| 485 | 485 | ** |
| 486 | 486 | ** * Does not begin with "/" |
| 487 | 487 | ** * Does not contain any path element named "." or ".." |
| 488 | +** * Does not contain any of these characters in the path: "\" | |
| 488 | 489 | ** * Does not end with "/". |
| 489 | 490 | ** * Does not contain two or more "/" characters in a row. |
| 490 | 491 | ** * Contains at least one character |
| 491 | 492 | ** |
| 492 | -** Invalid UTF8 characters and "\". result in a false return if | |
| 493 | -** bStrictUtf8 is true. If bStrictUtf8 is false, invalid UTF8 | |
| 494 | -** characters and "\" are silently ignored. | |
| 493 | +** Invalid UTF8 characters result in a false return if bStrictUtf8 is | |
| 494 | +** true. If bStrictUtf8 is false, invalid UTF8 characters are silently | |
| 495 | +** ignored. See http://en.wikipedia.org/wiki/UTF-8#Invalid_byte_sequences | |
| 496 | +** and http://en.wikipedia.org/wiki/Unicode (for the noncharacters) | |
| 497 | +** | |
| 498 | +** The bStrictUtf8 flag is true for new inputs, but is false when parsing | |
| 499 | +** legacy manifests, for backwards compatibility. | |
| 495 | 500 | */ |
| 496 | 501 | int file_is_simple_pathname(const char *z, int bStrictUtf8){ |
| 497 | 502 | int i; |
| 498 | - char c = z[0]; | |
| 503 | + unsigned char c = (unsigned char) z[0]; | |
| 504 | + char maskNonAscii = bStrictUtf8 ? 0x80 : 0x00; | |
| 499 | 505 | if( c=='/' || c==0 ) return 0; |
| 500 | 506 | if( c=='.' ){ |
| 501 | 507 | if( z[1]=='/' || z[1]==0 ) return 0; |
| 502 | 508 | if( z[1]=='.' && (z[2]=='/' || z[2]==0) ) return 0; |
| 503 | 509 | } |
| 504 | - for(i=0; (c=z[i])!=0; i++){ | |
| 505 | - if( bStrictUtf8 ){ | |
| 506 | - if( c & 0x80 ){ | |
| 507 | - if( (c & 0xf0) == 0xf0 ) { | |
| 510 | + for(i=0; (c=(unsigned char)z[i])!=0; i++){ | |
| 511 | + if( c & maskNonAscii ){ | |
| 512 | + if( (z[++i]&0xc0)!=0x80 ){ | |
| 513 | + /* Invalid first continuation byte */ | |
| 514 | + return 0; | |
| 515 | + } | |
| 516 | + if( c<0xc2 ){ | |
| 517 | + /* Invalid 1-byte UTF-8 sequence, or 2-byte overlong form. */ | |
| 518 | + return 0; | |
| 519 | + }else if( (c&0xe0)==0xe0 ){ | |
| 520 | + /* 3-byte or more */ | |
| 521 | + int unicode; | |
| 522 | + if( c&0x10 ){ | |
| 508 | 523 | /* Unicode characters > U+FFFF are not supported. |
| 509 | 524 | * Windows XP and earlier cannot handle them. |
| 510 | 525 | */ |
| 511 | 526 | return 0; |
| 512 | 527 | } |
| 513 | - if( (c & 0xf0) == 0xe0 ) { | |
| 514 | - /* This is a 3-byte UTF-8 character */ | |
| 515 | - if ( (c & 0xfe) == 0xee ){ | |
| 516 | - /* Range U+E000 - U+FFFF (Starting with 0xee or 0xef in UTF-8 ) */ | |
| 517 | - if ( !(c & 1) || ((z[i+1] & 0xff) < 0xa4) ){ | |
| 518 | - /* Unicode character in the range U+E000 - U+F8FF are for | |
| 519 | - * private use, they shouldn't occur in filenames. */ | |
| 520 | - return 0; | |
| 521 | - } | |
| 522 | - }else if( ((c & 0xff) == 0xed) && ((z[i+1] & 0xe0) == 0xa0) ){ | |
| 523 | - /* Unicode character in the range U+D800 - U+DFFF are for | |
| 524 | - * surrogate pairs, they shouldn't occur in filenames. */ | |
| 525 | - return 0; | |
| 526 | - } | |
| 527 | - } | |
| 528 | - }else if( c=='\\' ){ | |
| 529 | - return 0; | |
| 530 | - } | |
| 528 | + /* This is a 3-byte UTF-8 character */ | |
| 529 | + unicode = ((c&0x0f)<<12) + ((z[i]&0x3f)<<6) + (z[i+1]&0x3f); | |
| 530 | + if( unicode <= 0x07ff ){ | |
| 531 | + /* overlong form */ | |
| 532 | + return 0; | |
| 533 | + }else if( unicode>=0xe000 ){ | |
| 534 | + /* U+E000..U+FFFF */ | |
| 535 | + if( (unicode<=0xf8ff) || (unicode>=0xfffe) ){ | |
| 536 | + /* U+E000..U+F8FF are for private use. | |
| 537 | + * U+FFFE..U+FFFF are noncharacters. */ | |
| 538 | + return 0; | |
| 539 | + } else if( (unicode>=0xfdd0) && (unicode<=0xfdef) ){ | |
| 540 | + /* U+FDD0..U+FDEF are noncharacters. */ | |
| 541 | + return 0; | |
| 542 | + } | |
| 543 | + }else if( (unicode>=0xd800) && (unicode<=0xdfff) ){ | |
| 544 | + /* U+D800..U+DFFF are for surrogate pairs. */ | |
| 545 | + return 0; | |
| 546 | + } | |
| 547 | + if( (z[++i]&0xc0)!=0x80 ){ | |
| 548 | + /* Invalid second continuation byte */ | |
| 549 | + return 0; | |
| 550 | + } | |
| 551 | + } | |
| 552 | + }else if( bStrictUtf8 && (c=='\\') ){ | |
| 553 | + return 0; | |
| 531 | 554 | } |
| 532 | 555 | if( c=='/' ){ |
| 533 | 556 | if( z[i+1]=='/' ) return 0; |
| 534 | 557 | if( z[i+1]=='.' ){ |
| 535 | 558 | if( z[i+2]=='/' || z[i+2]==0 ) return 0; |
| @@ -578,11 +601,11 @@ | ||
| 578 | 601 | if( z[i]=='\\' ) z[i] = '/'; |
| 579 | 602 | } |
| 580 | 603 | #endif |
| 581 | 604 | |
| 582 | 605 | /* Removing trailing "/" characters */ |
| 583 | - if ( !slash ){ | |
| 606 | + if( !slash ){ | |
| 584 | 607 | while( n>1 && z[n-1]=='/' ){ n--; } |
| 585 | 608 | } |
| 586 | 609 | |
| 587 | 610 | /* Remove duplicate '/' characters. Except, two // at the beginning |
| 588 | 611 | ** of a pathname is allowed since this is important on windows. */ |
| @@ -835,11 +858,11 @@ | ||
| 835 | 858 | if( zPwd[i]==0 ){ |
| 836 | 859 | blob_append(pOut, ".", 1); |
| 837 | 860 | }else{ |
| 838 | 861 | blob_append(pOut, "..", 2); |
| 839 | 862 | for(j=i+1; zPwd[j]; j++){ |
| 840 | - if( zPwd[j]=='/' ) { | |
| 863 | + if( zPwd[j]=='/' ){ | |
| 841 | 864 | blob_append(pOut, "/..", 3); |
| 842 | 865 | } |
| 843 | 866 | } |
| 844 | 867 | } |
| 845 | 868 | return; |
| @@ -852,11 +875,11 @@ | ||
| 852 | 875 | return; |
| 853 | 876 | } |
| 854 | 877 | while( zPath[i-1]!='/' ){ i--; } |
| 855 | 878 | blob_set(&tmp, "../"); |
| 856 | 879 | for(j=i; zPwd[j]; j++){ |
| 857 | - if( zPwd[j]=='/' ) { | |
| 880 | + if( zPwd[j]=='/' ){ | |
| 858 | 881 | blob_append(&tmp, "../", 3); |
| 859 | 882 | } |
| 860 | 883 | } |
| 861 | 884 | blob_append(&tmp, &zPath[i], -1); |
| 862 | 885 | blob_reset(pOut); |
| 863 | 886 |
| --- src/file.c | |
| +++ src/file.c | |
| @@ -483,53 +483,76 @@ | |
| 483 | ** a file in a repository. Valid filenames follow all of the |
| 484 | ** following rules: |
| 485 | ** |
| 486 | ** * Does not begin with "/" |
| 487 | ** * Does not contain any path element named "." or ".." |
| 488 | ** * Does not end with "/". |
| 489 | ** * Does not contain two or more "/" characters in a row. |
| 490 | ** * Contains at least one character |
| 491 | ** |
| 492 | ** Invalid UTF8 characters and "\". result in a false return if |
| 493 | ** bStrictUtf8 is true. If bStrictUtf8 is false, invalid UTF8 |
| 494 | ** characters and "\" are silently ignored. |
| 495 | */ |
| 496 | int file_is_simple_pathname(const char *z, int bStrictUtf8){ |
| 497 | int i; |
| 498 | char c = z[0]; |
| 499 | if( c=='/' || c==0 ) return 0; |
| 500 | if( c=='.' ){ |
| 501 | if( z[1]=='/' || z[1]==0 ) return 0; |
| 502 | if( z[1]=='.' && (z[2]=='/' || z[2]==0) ) return 0; |
| 503 | } |
| 504 | for(i=0; (c=z[i])!=0; i++){ |
| 505 | if( bStrictUtf8 ){ |
| 506 | if( c & 0x80 ){ |
| 507 | if( (c & 0xf0) == 0xf0 ) { |
| 508 | /* Unicode characters > U+FFFF are not supported. |
| 509 | * Windows XP and earlier cannot handle them. |
| 510 | */ |
| 511 | return 0; |
| 512 | } |
| 513 | if( (c & 0xf0) == 0xe0 ) { |
| 514 | /* This is a 3-byte UTF-8 character */ |
| 515 | if ( (c & 0xfe) == 0xee ){ |
| 516 | /* Range U+E000 - U+FFFF (Starting with 0xee or 0xef in UTF-8 ) */ |
| 517 | if ( !(c & 1) || ((z[i+1] & 0xff) < 0xa4) ){ |
| 518 | /* Unicode character in the range U+E000 - U+F8FF are for |
| 519 | * private use, they shouldn't occur in filenames. */ |
| 520 | return 0; |
| 521 | } |
| 522 | }else if( ((c & 0xff) == 0xed) && ((z[i+1] & 0xe0) == 0xa0) ){ |
| 523 | /* Unicode character in the range U+D800 - U+DFFF are for |
| 524 | * surrogate pairs, they shouldn't occur in filenames. */ |
| 525 | return 0; |
| 526 | } |
| 527 | } |
| 528 | }else if( c=='\\' ){ |
| 529 | return 0; |
| 530 | } |
| 531 | } |
| 532 | if( c=='/' ){ |
| 533 | if( z[i+1]=='/' ) return 0; |
| 534 | if( z[i+1]=='.' ){ |
| 535 | if( z[i+2]=='/' || z[i+2]==0 ) return 0; |
| @@ -578,11 +601,11 @@ | |
| 578 | if( z[i]=='\\' ) z[i] = '/'; |
| 579 | } |
| 580 | #endif |
| 581 | |
| 582 | /* Removing trailing "/" characters */ |
| 583 | if ( !slash ){ |
| 584 | while( n>1 && z[n-1]=='/' ){ n--; } |
| 585 | } |
| 586 | |
| 587 | /* Remove duplicate '/' characters. Except, two // at the beginning |
| 588 | ** of a pathname is allowed since this is important on windows. */ |
| @@ -835,11 +858,11 @@ | |
| 835 | if( zPwd[i]==0 ){ |
| 836 | blob_append(pOut, ".", 1); |
| 837 | }else{ |
| 838 | blob_append(pOut, "..", 2); |
| 839 | for(j=i+1; zPwd[j]; j++){ |
| 840 | if( zPwd[j]=='/' ) { |
| 841 | blob_append(pOut, "/..", 3); |
| 842 | } |
| 843 | } |
| 844 | } |
| 845 | return; |
| @@ -852,11 +875,11 @@ | |
| 852 | return; |
| 853 | } |
| 854 | while( zPath[i-1]!='/' ){ i--; } |
| 855 | blob_set(&tmp, "../"); |
| 856 | for(j=i; zPwd[j]; j++){ |
| 857 | if( zPwd[j]=='/' ) { |
| 858 | blob_append(&tmp, "../", 3); |
| 859 | } |
| 860 | } |
| 861 | blob_append(&tmp, &zPath[i], -1); |
| 862 | blob_reset(pOut); |
| 863 |
| --- src/file.c | |
| +++ src/file.c | |
| @@ -483,53 +483,76 @@ | |
| 483 | ** a file in a repository. Valid filenames follow all of the |
| 484 | ** following rules: |
| 485 | ** |
| 486 | ** * Does not begin with "/" |
| 487 | ** * Does not contain any path element named "." or ".." |
| 488 | ** * Does not contain any of these characters in the path: "\" |
| 489 | ** * Does not end with "/". |
| 490 | ** * Does not contain two or more "/" characters in a row. |
| 491 | ** * Contains at least one character |
| 492 | ** |
| 493 | ** Invalid UTF8 characters result in a false return if bStrictUtf8 is |
| 494 | ** true. If bStrictUtf8 is false, invalid UTF8 characters are silently |
| 495 | ** ignored. See http://en.wikipedia.org/wiki/UTF-8#Invalid_byte_sequences |
| 496 | ** and http://en.wikipedia.org/wiki/Unicode (for the noncharacters) |
| 497 | ** |
| 498 | ** The bStrictUtf8 flag is true for new inputs, but is false when parsing |
| 499 | ** legacy manifests, for backwards compatibility. |
| 500 | */ |
| 501 | int file_is_simple_pathname(const char *z, int bStrictUtf8){ |
| 502 | int i; |
| 503 | unsigned char c = (unsigned char) z[0]; |
| 504 | char maskNonAscii = bStrictUtf8 ? 0x80 : 0x00; |
| 505 | if( c=='/' || c==0 ) return 0; |
| 506 | if( c=='.' ){ |
| 507 | if( z[1]=='/' || z[1]==0 ) return 0; |
| 508 | if( z[1]=='.' && (z[2]=='/' || z[2]==0) ) return 0; |
| 509 | } |
| 510 | for(i=0; (c=(unsigned char)z[i])!=0; i++){ |
| 511 | if( c & maskNonAscii ){ |
| 512 | if( (z[++i]&0xc0)!=0x80 ){ |
| 513 | /* Invalid first continuation byte */ |
| 514 | return 0; |
| 515 | } |
| 516 | if( c<0xc2 ){ |
| 517 | /* Invalid 1-byte UTF-8 sequence, or 2-byte overlong form. */ |
| 518 | return 0; |
| 519 | }else if( (c&0xe0)==0xe0 ){ |
| 520 | /* 3-byte or more */ |
| 521 | int unicode; |
| 522 | if( c&0x10 ){ |
| 523 | /* Unicode characters > U+FFFF are not supported. |
| 524 | * Windows XP and earlier cannot handle them. |
| 525 | */ |
| 526 | return 0; |
| 527 | } |
| 528 | /* This is a 3-byte UTF-8 character */ |
| 529 | unicode = ((c&0x0f)<<12) + ((z[i]&0x3f)<<6) + (z[i+1]&0x3f); |
| 530 | if( unicode <= 0x07ff ){ |
| 531 | /* overlong form */ |
| 532 | return 0; |
| 533 | }else if( unicode>=0xe000 ){ |
| 534 | /* U+E000..U+FFFF */ |
| 535 | if( (unicode<=0xf8ff) || (unicode>=0xfffe) ){ |
| 536 | /* U+E000..U+F8FF are for private use. |
| 537 | * U+FFFE..U+FFFF are noncharacters. */ |
| 538 | return 0; |
| 539 | } else if( (unicode>=0xfdd0) && (unicode<=0xfdef) ){ |
| 540 | /* U+FDD0..U+FDEF are noncharacters. */ |
| 541 | return 0; |
| 542 | } |
| 543 | }else if( (unicode>=0xd800) && (unicode<=0xdfff) ){ |
| 544 | /* U+D800..U+DFFF are for surrogate pairs. */ |
| 545 | return 0; |
| 546 | } |
| 547 | if( (z[++i]&0xc0)!=0x80 ){ |
| 548 | /* Invalid second continuation byte */ |
| 549 | return 0; |
| 550 | } |
| 551 | } |
| 552 | }else if( bStrictUtf8 && (c=='\\') ){ |
| 553 | return 0; |
| 554 | } |
| 555 | if( c=='/' ){ |
| 556 | if( z[i+1]=='/' ) return 0; |
| 557 | if( z[i+1]=='.' ){ |
| 558 | if( z[i+2]=='/' || z[i+2]==0 ) return 0; |
| @@ -578,11 +601,11 @@ | |
| 601 | if( z[i]=='\\' ) z[i] = '/'; |
| 602 | } |
| 603 | #endif |
| 604 | |
| 605 | /* Removing trailing "/" characters */ |
| 606 | if( !slash ){ |
| 607 | while( n>1 && z[n-1]=='/' ){ n--; } |
| 608 | } |
| 609 | |
| 610 | /* Remove duplicate '/' characters. Except, two // at the beginning |
| 611 | ** of a pathname is allowed since this is important on windows. */ |
| @@ -835,11 +858,11 @@ | |
| 858 | if( zPwd[i]==0 ){ |
| 859 | blob_append(pOut, ".", 1); |
| 860 | }else{ |
| 861 | blob_append(pOut, "..", 2); |
| 862 | for(j=i+1; zPwd[j]; j++){ |
| 863 | if( zPwd[j]=='/' ){ |
| 864 | blob_append(pOut, "/..", 3); |
| 865 | } |
| 866 | } |
| 867 | } |
| 868 | return; |
| @@ -852,11 +875,11 @@ | |
| 875 | return; |
| 876 | } |
| 877 | while( zPath[i-1]!='/' ){ i--; } |
| 878 | blob_set(&tmp, "../"); |
| 879 | for(j=i; zPwd[j]; j++){ |
| 880 | if( zPwd[j]=='/' ){ |
| 881 | blob_append(&tmp, "../", 3); |
| 882 | } |
| 883 | } |
| 884 | blob_append(&tmp, &zPath[i], -1); |
| 885 | blob_reset(pOut); |
| 886 |
+1
-1
| --- src/info.c | ||
| +++ src/info.c | ||
| @@ -208,11 +208,11 @@ | ||
| 208 | 208 | fossil_print("project-code: %s\n", db_get("project-code", "")); |
| 209 | 209 | vid = g.localOpen ? db_lget_int("checkout", 0) : 0; |
| 210 | 210 | if( vid ){ |
| 211 | 211 | show_common_info(vid, "checkout:", 1, 1); |
| 212 | 212 | } |
| 213 | - fossil_print("checkin-count: %d\n", | |
| 213 | + fossil_print("checkins: %d\n", | |
| 214 | 214 | db_int(-1, "SELECT count(distinct mid) FROM mlink /*scan*/")); |
| 215 | 215 | }else{ |
| 216 | 216 | int rid; |
| 217 | 217 | rid = name_to_rid(g.argv[2]); |
| 218 | 218 | if( rid==0 ){ |
| 219 | 219 |
| --- src/info.c | |
| +++ src/info.c | |
| @@ -208,11 +208,11 @@ | |
| 208 | fossil_print("project-code: %s\n", db_get("project-code", "")); |
| 209 | vid = g.localOpen ? db_lget_int("checkout", 0) : 0; |
| 210 | if( vid ){ |
| 211 | show_common_info(vid, "checkout:", 1, 1); |
| 212 | } |
| 213 | fossil_print("checkin-count: %d\n", |
| 214 | db_int(-1, "SELECT count(distinct mid) FROM mlink /*scan*/")); |
| 215 | }else{ |
| 216 | int rid; |
| 217 | rid = name_to_rid(g.argv[2]); |
| 218 | if( rid==0 ){ |
| 219 |
| --- src/info.c | |
| +++ src/info.c | |
| @@ -208,11 +208,11 @@ | |
| 208 | fossil_print("project-code: %s\n", db_get("project-code", "")); |
| 209 | vid = g.localOpen ? db_lget_int("checkout", 0) : 0; |
| 210 | if( vid ){ |
| 211 | show_common_info(vid, "checkout:", 1, 1); |
| 212 | } |
| 213 | fossil_print("checkins: %d\n", |
| 214 | db_int(-1, "SELECT count(distinct mid) FROM mlink /*scan*/")); |
| 215 | }else{ |
| 216 | int rid; |
| 217 | rid = name_to_rid(g.argv[2]); |
| 218 | if( rid==0 ){ |
| 219 |
+1
-3
| --- src/json.c | ||
| +++ src/json.c | ||
| @@ -2085,15 +2085,13 @@ | ||
| 2085 | 2085 | cson_object_set(jo, "ticketCount", cson_value_new_integer((cson_int_t)n)); |
| 2086 | 2086 | }/*full*/ |
| 2087 | 2087 | n = db_int(0, "SELECT julianday('now') - (SELECT min(mtime) FROM event)" |
| 2088 | 2088 | " + 0.99"); |
| 2089 | 2089 | cson_object_set(jo, "ageDays", cson_value_new_integer((cson_int_t)n)); |
| 2090 | - cson_object_set(jo, "ageYears", cson_value_new_double(n/365.24)); | |
| 2090 | + cson_object_set(jo, "ageYears", cson_value_new_double(n/365.2425)); | |
| 2091 | 2091 | sqlite3_snprintf(BufLen, zBuf, db_get("project-code","")); |
| 2092 | 2092 | SETBUF(jo, "projectCode"); |
| 2093 | - sqlite3_snprintf(BufLen, zBuf, db_get("server-code","")); | |
| 2094 | - SETBUF(jo, "serverCode"); | |
| 2095 | 2093 | cson_object_set(jo, "compiler", cson_value_new_string(COMPILER_NAME, strlen(COMPILER_NAME))); |
| 2096 | 2094 | |
| 2097 | 2095 | jv2 = cson_value_new_object(); |
| 2098 | 2096 | jo2 = cson_value_get_object(jv2); |
| 2099 | 2097 | cson_object_set(jo, "sqlite", jv2); |
| 2100 | 2098 |
| --- src/json.c | |
| +++ src/json.c | |
| @@ -2085,15 +2085,13 @@ | |
| 2085 | cson_object_set(jo, "ticketCount", cson_value_new_integer((cson_int_t)n)); |
| 2086 | }/*full*/ |
| 2087 | n = db_int(0, "SELECT julianday('now') - (SELECT min(mtime) FROM event)" |
| 2088 | " + 0.99"); |
| 2089 | cson_object_set(jo, "ageDays", cson_value_new_integer((cson_int_t)n)); |
| 2090 | cson_object_set(jo, "ageYears", cson_value_new_double(n/365.24)); |
| 2091 | sqlite3_snprintf(BufLen, zBuf, db_get("project-code","")); |
| 2092 | SETBUF(jo, "projectCode"); |
| 2093 | sqlite3_snprintf(BufLen, zBuf, db_get("server-code","")); |
| 2094 | SETBUF(jo, "serverCode"); |
| 2095 | cson_object_set(jo, "compiler", cson_value_new_string(COMPILER_NAME, strlen(COMPILER_NAME))); |
| 2096 | |
| 2097 | jv2 = cson_value_new_object(); |
| 2098 | jo2 = cson_value_get_object(jv2); |
| 2099 | cson_object_set(jo, "sqlite", jv2); |
| 2100 |
| --- src/json.c | |
| +++ src/json.c | |
| @@ -2085,15 +2085,13 @@ | |
| 2085 | cson_object_set(jo, "ticketCount", cson_value_new_integer((cson_int_t)n)); |
| 2086 | }/*full*/ |
| 2087 | n = db_int(0, "SELECT julianday('now') - (SELECT min(mtime) FROM event)" |
| 2088 | " + 0.99"); |
| 2089 | cson_object_set(jo, "ageDays", cson_value_new_integer((cson_int_t)n)); |
| 2090 | cson_object_set(jo, "ageYears", cson_value_new_double(n/365.2425)); |
| 2091 | sqlite3_snprintf(BufLen, zBuf, db_get("project-code","")); |
| 2092 | SETBUF(jo, "projectCode"); |
| 2093 | cson_object_set(jo, "compiler", cson_value_new_string(COMPILER_NAME, strlen(COMPILER_NAME))); |
| 2094 | |
| 2095 | jv2 = cson_value_new_object(); |
| 2096 | jo2 = cson_value_get_object(jv2); |
| 2097 | cson_object_set(jo, "sqlite", jv2); |
| 2098 |
+1
-1
| --- src/main.mk | ||
| +++ src/main.mk | ||
| @@ -1146,11 +1146,11 @@ | ||
| 1146 | 1146 | $(OBJDIR)/th_tcl.o: $(SRCDIR)/th_tcl.c |
| 1147 | 1147 | $(XTCC) -c $(SRCDIR)/th_tcl.c -o $(OBJDIR)/th_tcl.o |
| 1148 | 1148 | |
| 1149 | 1149 | |
| 1150 | 1150 | $(OBJDIR)/cson_amalgamation.o: $(SRCDIR)/cson_amalgamation.c |
| 1151 | - $(XTCC) -c $(SRCDIR)/cson_amalgamation.c -o $(OBJDIR)/cson_amalgamation.o -DCSON_FOSSIL_MODE | |
| 1151 | + $(XTCC) -c $(SRCDIR)/cson_amalgamation.c -o $(OBJDIR)/cson_amalgamation.o | |
| 1152 | 1152 | |
| 1153 | 1153 | # |
| 1154 | 1154 | # The list of all the targets that do not correspond to real files. This stops |
| 1155 | 1155 | # 'make' from getting confused when someone makes an error in a rule. |
| 1156 | 1156 | # |
| 1157 | 1157 |
| --- src/main.mk | |
| +++ src/main.mk | |
| @@ -1146,11 +1146,11 @@ | |
| 1146 | $(OBJDIR)/th_tcl.o: $(SRCDIR)/th_tcl.c |
| 1147 | $(XTCC) -c $(SRCDIR)/th_tcl.c -o $(OBJDIR)/th_tcl.o |
| 1148 | |
| 1149 | |
| 1150 | $(OBJDIR)/cson_amalgamation.o: $(SRCDIR)/cson_amalgamation.c |
| 1151 | $(XTCC) -c $(SRCDIR)/cson_amalgamation.c -o $(OBJDIR)/cson_amalgamation.o -DCSON_FOSSIL_MODE |
| 1152 | |
| 1153 | # |
| 1154 | # The list of all the targets that do not correspond to real files. This stops |
| 1155 | # 'make' from getting confused when someone makes an error in a rule. |
| 1156 | # |
| 1157 |
| --- src/main.mk | |
| +++ src/main.mk | |
| @@ -1146,11 +1146,11 @@ | |
| 1146 | $(OBJDIR)/th_tcl.o: $(SRCDIR)/th_tcl.c |
| 1147 | $(XTCC) -c $(SRCDIR)/th_tcl.c -o $(OBJDIR)/th_tcl.o |
| 1148 | |
| 1149 | |
| 1150 | $(OBJDIR)/cson_amalgamation.o: $(SRCDIR)/cson_amalgamation.c |
| 1151 | $(XTCC) -c $(SRCDIR)/cson_amalgamation.c -o $(OBJDIR)/cson_amalgamation.o |
| 1152 | |
| 1153 | # |
| 1154 | # The list of all the targets that do not correspond to real files. This stops |
| 1155 | # 'make' from getting confused when someone makes an error in a rule. |
| 1156 | # |
| 1157 |
+30
-11
| --- src/makemake.tcl | ||
| +++ src/makemake.tcl | ||
| @@ -308,11 +308,11 @@ | ||
| 308 | 308 | writeln "\t\$(XTCC) -c \$(SRCDIR)/th_tcl.c -o \$(OBJDIR)/th_tcl.o\n" |
| 309 | 309 | |
| 310 | 310 | set opt {} |
| 311 | 311 | writeln { |
| 312 | 312 | $(OBJDIR)/cson_amalgamation.o: $(SRCDIR)/cson_amalgamation.c |
| 313 | - $(XTCC) -c $(SRCDIR)/cson_amalgamation.c -o $(OBJDIR)/cson_amalgamation.o -DCSON_FOSSIL_MODE | |
| 313 | + $(XTCC) -c $(SRCDIR)/cson_amalgamation.c -o $(OBJDIR)/cson_amalgamation.o | |
| 314 | 314 | |
| 315 | 315 | # |
| 316 | 316 | # The list of all the targets that do not correspond to real files. This stops |
| 317 | 317 | # 'make' from getting confused when someone makes an error in a rule. |
| 318 | 318 | # |
| @@ -751,11 +751,11 @@ | ||
| 751 | 751 | set opt $SQLITE_OPTIONS |
| 752 | 752 | writeln "\t\$(XTCC) $opt -c \$(SRCDIR)/sqlite3.c -o \$(OBJDIR)/sqlite3.o\n" |
| 753 | 753 | |
| 754 | 754 | set opt {} |
| 755 | 755 | writeln "\$(OBJDIR)/cson_amalgamation.o:\t\$(SRCDIR)/cson_amalgamation.c" |
| 756 | -writeln "\t\$(XTCC) $opt -c \$(SRCDIR)/cson_amalgamation.c -o \$(OBJDIR)/cson_amalgamation.o -DCSON_FOSSIL_MODE\n" | |
| 756 | +writeln "\t\$(XTCC) $opt -c \$(SRCDIR)/cson_amalgamation.c -o \$(OBJDIR)/cson_amalgamation.o\n" | |
| 757 | 757 | 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" |
| 758 | 758 | |
| 759 | 759 | writeln "\$(OBJDIR)/shell.o:\t\$(SRCDIR)/shell.c \$(SRCDIR)/sqlite3.h" |
| 760 | 760 | set opt {-Dmain=sqlite3_shell} |
| 761 | 761 | append opt " -DSQLITE_OMIT_LOAD_EXTENSION=1" |
| @@ -959,18 +959,35 @@ | ||
| 959 | 959 | |
| 960 | 960 | # zlib options |
| 961 | 961 | ZINCDIR = $(B)\compat\zlib |
| 962 | 962 | ZLIBDIR = $(B)\compat\zlib |
| 963 | 963 | ZLIB = zlib.lib |
| 964 | + | |
| 965 | +# Uncomment to enable JSON API | |
| 966 | +# FOSSIL_ENABLE_JSON = 1 | |
| 967 | + | |
| 968 | +# Uncomment to enable markdown support | |
| 969 | +# FOSSIL_ENABLE_MARKDOWN = 1 | |
| 964 | 970 | |
| 965 | 971 | INCL = -I. -I$(SRCDIR) -I$B\win\include -I$(ZINCDIR) |
| 966 | 972 | |
| 967 | 973 | CFLAGS = -nologo -MT -O2 |
| 968 | 974 | BCC = $(CC) $(CFLAGS) |
| 969 | 975 | TCC = $(CC) -c $(CFLAGS) $(MSCDEF) $(SSL) $(INCL) |
| 976 | +RCC = rc -D_WIN32 -D_MSC_VER $(INCL) | |
| 970 | 977 | LIBS = $(ZLIB) ws2_32.lib advapi32.lib $(SSLLIB) |
| 971 | 978 | LIBDIR = -LIBPATH:$(ZLIBDIR) |
| 979 | + | |
| 980 | +!ifdef FOSSIL_ENABLE_JSON | |
| 981 | +TCC = $(TCC) -DFOSSIL_ENABLE_JSON | |
| 982 | +RCC = $(RCC) -DFOSSIL_ENABLE_JSON | |
| 983 | +!endif | |
| 984 | + | |
| 985 | +!ifdef FOSSIL_ENABLE_MARKDOWN | |
| 986 | +TCC = $(TCC) -DFOSSIL_ENABLE_MARKDOWN | |
| 987 | +RCC = $(RCC) -DFOSSIL_ENABLE_MARKDOWN | |
| 988 | +!endif | |
| 972 | 989 | } |
| 973 | 990 | regsub -all {[-]D} $SQLITE_OPTIONS {/D} MSC_SQLITE_OPTIONS |
| 974 | 991 | set j " \\\n " |
| 975 | 992 | writeln "SQLITE_OPTIONS = [join $MSC_SQLITE_OPTIONS $j]\n" |
| 976 | 993 | writeln -nonewline "SRC = " |
| @@ -981,24 +998,22 @@ | ||
| 981 | 998 | writeln -nonewline " " |
| 982 | 999 | } |
| 983 | 1000 | writeln -nonewline "${s}_.c"; incr i |
| 984 | 1001 | } |
| 985 | 1002 | writeln "\n" |
| 1003 | +set AdditionalObj [list shell sqlite3 th th_lang cson_amalgamation] | |
| 986 | 1004 | writeln -nonewline "OBJ = " |
| 987 | 1005 | set i 0 |
| 988 | -foreach s [lsort $src] { | |
| 1006 | +foreach s [lsort [concat $src $AdditionalObj]] { | |
| 989 | 1007 | if {$i > 0} { |
| 990 | 1008 | writeln " \\" |
| 991 | 1009 | writeln -nonewline " " |
| 992 | 1010 | } |
| 993 | 1011 | writeln -nonewline "\$(OX)\\$s\$O"; incr i |
| 994 | 1012 | } |
| 995 | 1013 | writeln " \\" |
| 996 | -writeln " \$(OX)\\shell\$O \\" | |
| 997 | -writeln " \$(OX)\\sqlite3\$O \\" | |
| 998 | -writeln " \$(OX)\\th\$O \\" | |
| 999 | -writeln " \$(OX)\\th_lang\$O" | |
| 1014 | +writeln -nonewline " \$(OX)\\fossil.res\n" | |
| 1000 | 1015 | writeln { |
| 1001 | 1016 | APPNAME = $(OX)\fossil$(E) |
| 1002 | 1017 | |
| 1003 | 1018 | all: $(OX) $(APPNAME) |
| 1004 | 1019 | |
| @@ -1006,15 +1021,15 @@ | ||
| 1006 | 1021 | @echo Building zlib from "$(ZLIBDIR)"... |
| 1007 | 1022 | @pushd "$(ZLIBDIR)" && nmake /f win32\Makefile.msc $(ZLIB) && popd |
| 1008 | 1023 | |
| 1009 | 1024 | $(APPNAME) : translate$E mkindex$E headers $(OBJ) $(OX)\linkopts zlib |
| 1010 | 1025 | cd $(OX) |
| 1011 | - link /NODEFAULTLIB:msvcrt -OUT:$@ $(LIBDIR) Wsetargv.obj @linkopts | |
| 1026 | + link /NODEFAULTLIB:msvcrt -OUT:$@ $(LIBDIR) Wsetargv.obj fossil.res @linkopts | |
| 1012 | 1027 | |
| 1013 | 1028 | $(OX)\linkopts: $B\win\Makefile.msc} |
| 1014 | 1029 | set redir {>} |
| 1015 | -foreach s [lsort [concat $src {shell sqlite3 th th_lang}]] { | |
| 1030 | +foreach s [lsort [concat $src $AdditionalObj]] { | |
| 1016 | 1031 | writeln "\techo \$(OX)\\$s.obj $redir \$@" |
| 1017 | 1032 | set redir {>>} |
| 1018 | 1033 | } |
| 1019 | 1034 | writeln "\techo \$(LIBS) >> \$@\n\n" |
| 1020 | 1035 | |
| @@ -1047,12 +1062,12 @@ | ||
| 1047 | 1062 | $(OX)\th_lang$O : $(SRCDIR)\th_lang.c |
| 1048 | 1063 | $(TCC) /Fo$@ -c $** |
| 1049 | 1064 | |
| 1050 | 1065 | VERSION.h : mkversion$E $B\manifest.uuid $B\manifest $B\VERSION |
| 1051 | 1066 | $** > $@ |
| 1052 | -$(OBJDIR)\cson_amalgamation.h : $(SRCDIR)\cson_amalgamation.h | |
| 1053 | - cp $(SRCDIR)\cson_amalgamation.h $@ | |
| 1067 | +$(OX)\cson_amalgamation$O : $(SRCDIR)\cson_amalgamation.c | |
| 1068 | + $(TCC) /Fo$@ -c $** | |
| 1054 | 1069 | |
| 1055 | 1070 | page_index.h: mkindex$E $(SRC) |
| 1056 | 1071 | $** > $@ |
| 1057 | 1072 | |
| 1058 | 1073 | clean: |
| @@ -1062,10 +1077,11 @@ | ||
| 1062 | 1077 | -del *.h |
| 1063 | 1078 | -del *.map |
| 1064 | 1079 | -del *.manifest |
| 1065 | 1080 | -del headers |
| 1066 | 1081 | -del linkopts |
| 1082 | + -del *.res | |
| 1067 | 1083 | |
| 1068 | 1084 | realclean: clean |
| 1069 | 1085 | -del $(APPNAME) |
| 1070 | 1086 | -del translate$E |
| 1071 | 1087 | -del mkindex$E |
| @@ -1092,10 +1108,13 @@ | ||
| 1092 | 1108 | writeln "\$(OX)\\$s\$O : ${s}_.c ${s}.h" |
| 1093 | 1109 | writeln "\t\$(TCC) /Fo\$@ -c ${s}_.c\n" |
| 1094 | 1110 | writeln "${s}_.c : \$(SRCDIR)\\$s.c" |
| 1095 | 1111 | writeln "\ttranslate\$E \$** > \$@\n" |
| 1096 | 1112 | } |
| 1113 | + | |
| 1114 | +writeln "fossil.res : \$B\\win\\fossil.rc" | |
| 1115 | +writeln "\t\$(RCC) -fo \$@ \$**" | |
| 1097 | 1116 | |
| 1098 | 1117 | writeln "headers: makeheaders\$E page_index.h VERSION.h" |
| 1099 | 1118 | writeln -nonewline "\tmakeheaders\$E " |
| 1100 | 1119 | set i 0 |
| 1101 | 1120 | foreach s [lsort $src] { |
| 1102 | 1121 |
| --- src/makemake.tcl | |
| +++ src/makemake.tcl | |
| @@ -308,11 +308,11 @@ | |
| 308 | writeln "\t\$(XTCC) -c \$(SRCDIR)/th_tcl.c -o \$(OBJDIR)/th_tcl.o\n" |
| 309 | |
| 310 | set opt {} |
| 311 | writeln { |
| 312 | $(OBJDIR)/cson_amalgamation.o: $(SRCDIR)/cson_amalgamation.c |
| 313 | $(XTCC) -c $(SRCDIR)/cson_amalgamation.c -o $(OBJDIR)/cson_amalgamation.o -DCSON_FOSSIL_MODE |
| 314 | |
| 315 | # |
| 316 | # The list of all the targets that do not correspond to real files. This stops |
| 317 | # 'make' from getting confused when someone makes an error in a rule. |
| 318 | # |
| @@ -751,11 +751,11 @@ | |
| 751 | set opt $SQLITE_OPTIONS |
| 752 | writeln "\t\$(XTCC) $opt -c \$(SRCDIR)/sqlite3.c -o \$(OBJDIR)/sqlite3.o\n" |
| 753 | |
| 754 | set opt {} |
| 755 | writeln "\$(OBJDIR)/cson_amalgamation.o:\t\$(SRCDIR)/cson_amalgamation.c" |
| 756 | writeln "\t\$(XTCC) $opt -c \$(SRCDIR)/cson_amalgamation.c -o \$(OBJDIR)/cson_amalgamation.o -DCSON_FOSSIL_MODE\n" |
| 757 | 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" |
| 758 | |
| 759 | writeln "\$(OBJDIR)/shell.o:\t\$(SRCDIR)/shell.c \$(SRCDIR)/sqlite3.h" |
| 760 | set opt {-Dmain=sqlite3_shell} |
| 761 | append opt " -DSQLITE_OMIT_LOAD_EXTENSION=1" |
| @@ -959,18 +959,35 @@ | |
| 959 | |
| 960 | # zlib options |
| 961 | ZINCDIR = $(B)\compat\zlib |
| 962 | ZLIBDIR = $(B)\compat\zlib |
| 963 | ZLIB = zlib.lib |
| 964 | |
| 965 | INCL = -I. -I$(SRCDIR) -I$B\win\include -I$(ZINCDIR) |
| 966 | |
| 967 | CFLAGS = -nologo -MT -O2 |
| 968 | BCC = $(CC) $(CFLAGS) |
| 969 | TCC = $(CC) -c $(CFLAGS) $(MSCDEF) $(SSL) $(INCL) |
| 970 | LIBS = $(ZLIB) ws2_32.lib advapi32.lib $(SSLLIB) |
| 971 | LIBDIR = -LIBPATH:$(ZLIBDIR) |
| 972 | } |
| 973 | regsub -all {[-]D} $SQLITE_OPTIONS {/D} MSC_SQLITE_OPTIONS |
| 974 | set j " \\\n " |
| 975 | writeln "SQLITE_OPTIONS = [join $MSC_SQLITE_OPTIONS $j]\n" |
| 976 | writeln -nonewline "SRC = " |
| @@ -981,24 +998,22 @@ | |
| 981 | writeln -nonewline " " |
| 982 | } |
| 983 | writeln -nonewline "${s}_.c"; incr i |
| 984 | } |
| 985 | writeln "\n" |
| 986 | writeln -nonewline "OBJ = " |
| 987 | set i 0 |
| 988 | foreach s [lsort $src] { |
| 989 | if {$i > 0} { |
| 990 | writeln " \\" |
| 991 | writeln -nonewline " " |
| 992 | } |
| 993 | writeln -nonewline "\$(OX)\\$s\$O"; incr i |
| 994 | } |
| 995 | writeln " \\" |
| 996 | writeln " \$(OX)\\shell\$O \\" |
| 997 | writeln " \$(OX)\\sqlite3\$O \\" |
| 998 | writeln " \$(OX)\\th\$O \\" |
| 999 | writeln " \$(OX)\\th_lang\$O" |
| 1000 | writeln { |
| 1001 | APPNAME = $(OX)\fossil$(E) |
| 1002 | |
| 1003 | all: $(OX) $(APPNAME) |
| 1004 | |
| @@ -1006,15 +1021,15 @@ | |
| 1006 | @echo Building zlib from "$(ZLIBDIR)"... |
| 1007 | @pushd "$(ZLIBDIR)" && nmake /f win32\Makefile.msc $(ZLIB) && popd |
| 1008 | |
| 1009 | $(APPNAME) : translate$E mkindex$E headers $(OBJ) $(OX)\linkopts zlib |
| 1010 | cd $(OX) |
| 1011 | link /NODEFAULTLIB:msvcrt -OUT:$@ $(LIBDIR) Wsetargv.obj @linkopts |
| 1012 | |
| 1013 | $(OX)\linkopts: $B\win\Makefile.msc} |
| 1014 | set redir {>} |
| 1015 | foreach s [lsort [concat $src {shell sqlite3 th th_lang}]] { |
| 1016 | writeln "\techo \$(OX)\\$s.obj $redir \$@" |
| 1017 | set redir {>>} |
| 1018 | } |
| 1019 | writeln "\techo \$(LIBS) >> \$@\n\n" |
| 1020 | |
| @@ -1047,12 +1062,12 @@ | |
| 1047 | $(OX)\th_lang$O : $(SRCDIR)\th_lang.c |
| 1048 | $(TCC) /Fo$@ -c $** |
| 1049 | |
| 1050 | VERSION.h : mkversion$E $B\manifest.uuid $B\manifest $B\VERSION |
| 1051 | $** > $@ |
| 1052 | $(OBJDIR)\cson_amalgamation.h : $(SRCDIR)\cson_amalgamation.h |
| 1053 | cp $(SRCDIR)\cson_amalgamation.h $@ |
| 1054 | |
| 1055 | page_index.h: mkindex$E $(SRC) |
| 1056 | $** > $@ |
| 1057 | |
| 1058 | clean: |
| @@ -1062,10 +1077,11 @@ | |
| 1062 | -del *.h |
| 1063 | -del *.map |
| 1064 | -del *.manifest |
| 1065 | -del headers |
| 1066 | -del linkopts |
| 1067 | |
| 1068 | realclean: clean |
| 1069 | -del $(APPNAME) |
| 1070 | -del translate$E |
| 1071 | -del mkindex$E |
| @@ -1092,10 +1108,13 @@ | |
| 1092 | writeln "\$(OX)\\$s\$O : ${s}_.c ${s}.h" |
| 1093 | writeln "\t\$(TCC) /Fo\$@ -c ${s}_.c\n" |
| 1094 | writeln "${s}_.c : \$(SRCDIR)\\$s.c" |
| 1095 | writeln "\ttranslate\$E \$** > \$@\n" |
| 1096 | } |
| 1097 | |
| 1098 | writeln "headers: makeheaders\$E page_index.h VERSION.h" |
| 1099 | writeln -nonewline "\tmakeheaders\$E " |
| 1100 | set i 0 |
| 1101 | foreach s [lsort $src] { |
| 1102 |
| --- src/makemake.tcl | |
| +++ src/makemake.tcl | |
| @@ -308,11 +308,11 @@ | |
| 308 | writeln "\t\$(XTCC) -c \$(SRCDIR)/th_tcl.c -o \$(OBJDIR)/th_tcl.o\n" |
| 309 | |
| 310 | set opt {} |
| 311 | writeln { |
| 312 | $(OBJDIR)/cson_amalgamation.o: $(SRCDIR)/cson_amalgamation.c |
| 313 | $(XTCC) -c $(SRCDIR)/cson_amalgamation.c -o $(OBJDIR)/cson_amalgamation.o |
| 314 | |
| 315 | # |
| 316 | # The list of all the targets that do not correspond to real files. This stops |
| 317 | # 'make' from getting confused when someone makes an error in a rule. |
| 318 | # |
| @@ -751,11 +751,11 @@ | |
| 751 | set opt $SQLITE_OPTIONS |
| 752 | writeln "\t\$(XTCC) $opt -c \$(SRCDIR)/sqlite3.c -o \$(OBJDIR)/sqlite3.o\n" |
| 753 | |
| 754 | set opt {} |
| 755 | writeln "\$(OBJDIR)/cson_amalgamation.o:\t\$(SRCDIR)/cson_amalgamation.c" |
| 756 | writeln "\t\$(XTCC) $opt -c \$(SRCDIR)/cson_amalgamation.c -o \$(OBJDIR)/cson_amalgamation.o\n" |
| 757 | 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" |
| 758 | |
| 759 | writeln "\$(OBJDIR)/shell.o:\t\$(SRCDIR)/shell.c \$(SRCDIR)/sqlite3.h" |
| 760 | set opt {-Dmain=sqlite3_shell} |
| 761 | append opt " -DSQLITE_OMIT_LOAD_EXTENSION=1" |
| @@ -959,18 +959,35 @@ | |
| 959 | |
| 960 | # zlib options |
| 961 | ZINCDIR = $(B)\compat\zlib |
| 962 | ZLIBDIR = $(B)\compat\zlib |
| 963 | ZLIB = zlib.lib |
| 964 | |
| 965 | # Uncomment to enable JSON API |
| 966 | # FOSSIL_ENABLE_JSON = 1 |
| 967 | |
| 968 | # Uncomment to enable markdown support |
| 969 | # FOSSIL_ENABLE_MARKDOWN = 1 |
| 970 | |
| 971 | INCL = -I. -I$(SRCDIR) -I$B\win\include -I$(ZINCDIR) |
| 972 | |
| 973 | CFLAGS = -nologo -MT -O2 |
| 974 | BCC = $(CC) $(CFLAGS) |
| 975 | TCC = $(CC) -c $(CFLAGS) $(MSCDEF) $(SSL) $(INCL) |
| 976 | RCC = rc -D_WIN32 -D_MSC_VER $(INCL) |
| 977 | LIBS = $(ZLIB) ws2_32.lib advapi32.lib $(SSLLIB) |
| 978 | LIBDIR = -LIBPATH:$(ZLIBDIR) |
| 979 | |
| 980 | !ifdef FOSSIL_ENABLE_JSON |
| 981 | TCC = $(TCC) -DFOSSIL_ENABLE_JSON |
| 982 | RCC = $(RCC) -DFOSSIL_ENABLE_JSON |
| 983 | !endif |
| 984 | |
| 985 | !ifdef FOSSIL_ENABLE_MARKDOWN |
| 986 | TCC = $(TCC) -DFOSSIL_ENABLE_MARKDOWN |
| 987 | RCC = $(RCC) -DFOSSIL_ENABLE_MARKDOWN |
| 988 | !endif |
| 989 | } |
| 990 | regsub -all {[-]D} $SQLITE_OPTIONS {/D} MSC_SQLITE_OPTIONS |
| 991 | set j " \\\n " |
| 992 | writeln "SQLITE_OPTIONS = [join $MSC_SQLITE_OPTIONS $j]\n" |
| 993 | writeln -nonewline "SRC = " |
| @@ -981,24 +998,22 @@ | |
| 998 | writeln -nonewline " " |
| 999 | } |
| 1000 | writeln -nonewline "${s}_.c"; incr i |
| 1001 | } |
| 1002 | writeln "\n" |
| 1003 | set AdditionalObj [list shell sqlite3 th th_lang cson_amalgamation] |
| 1004 | writeln -nonewline "OBJ = " |
| 1005 | set i 0 |
| 1006 | foreach s [lsort [concat $src $AdditionalObj]] { |
| 1007 | if {$i > 0} { |
| 1008 | writeln " \\" |
| 1009 | writeln -nonewline " " |
| 1010 | } |
| 1011 | writeln -nonewline "\$(OX)\\$s\$O"; incr i |
| 1012 | } |
| 1013 | writeln " \\" |
| 1014 | writeln -nonewline " \$(OX)\\fossil.res\n" |
| 1015 | writeln { |
| 1016 | APPNAME = $(OX)\fossil$(E) |
| 1017 | |
| 1018 | all: $(OX) $(APPNAME) |
| 1019 | |
| @@ -1006,15 +1021,15 @@ | |
| 1021 | @echo Building zlib from "$(ZLIBDIR)"... |
| 1022 | @pushd "$(ZLIBDIR)" && nmake /f win32\Makefile.msc $(ZLIB) && popd |
| 1023 | |
| 1024 | $(APPNAME) : translate$E mkindex$E headers $(OBJ) $(OX)\linkopts zlib |
| 1025 | cd $(OX) |
| 1026 | link /NODEFAULTLIB:msvcrt -OUT:$@ $(LIBDIR) Wsetargv.obj fossil.res @linkopts |
| 1027 | |
| 1028 | $(OX)\linkopts: $B\win\Makefile.msc} |
| 1029 | set redir {>} |
| 1030 | foreach s [lsort [concat $src $AdditionalObj]] { |
| 1031 | writeln "\techo \$(OX)\\$s.obj $redir \$@" |
| 1032 | set redir {>>} |
| 1033 | } |
| 1034 | writeln "\techo \$(LIBS) >> \$@\n\n" |
| 1035 | |
| @@ -1047,12 +1062,12 @@ | |
| 1062 | $(OX)\th_lang$O : $(SRCDIR)\th_lang.c |
| 1063 | $(TCC) /Fo$@ -c $** |
| 1064 | |
| 1065 | VERSION.h : mkversion$E $B\manifest.uuid $B\manifest $B\VERSION |
| 1066 | $** > $@ |
| 1067 | $(OX)\cson_amalgamation$O : $(SRCDIR)\cson_amalgamation.c |
| 1068 | $(TCC) /Fo$@ -c $** |
| 1069 | |
| 1070 | page_index.h: mkindex$E $(SRC) |
| 1071 | $** > $@ |
| 1072 | |
| 1073 | clean: |
| @@ -1062,10 +1077,11 @@ | |
| 1077 | -del *.h |
| 1078 | -del *.map |
| 1079 | -del *.manifest |
| 1080 | -del headers |
| 1081 | -del linkopts |
| 1082 | -del *.res |
| 1083 | |
| 1084 | realclean: clean |
| 1085 | -del $(APPNAME) |
| 1086 | -del translate$E |
| 1087 | -del mkindex$E |
| @@ -1092,10 +1108,13 @@ | |
| 1108 | writeln "\$(OX)\\$s\$O : ${s}_.c ${s}.h" |
| 1109 | writeln "\t\$(TCC) /Fo\$@ -c ${s}_.c\n" |
| 1110 | writeln "${s}_.c : \$(SRCDIR)\\$s.c" |
| 1111 | writeln "\ttranslate\$E \$** > \$@\n" |
| 1112 | } |
| 1113 | |
| 1114 | writeln "fossil.res : \$B\\win\\fossil.rc" |
| 1115 | writeln "\t\$(RCC) -fo \$@ \$**" |
| 1116 | |
| 1117 | writeln "headers: makeheaders\$E page_index.h VERSION.h" |
| 1118 | writeln -nonewline "\tmakeheaders\$E " |
| 1119 | set i 0 |
| 1120 | foreach s [lsort $src] { |
| 1121 |
+1
| --- src/merge.c | ||
| +++ src/merge.c | ||
| @@ -205,10 +205,11 @@ | ||
| 205 | 205 | fossil_free(zCom); |
| 206 | 206 | } |
| 207 | 207 | db_finalize(&q); |
| 208 | 208 | }else{ |
| 209 | 209 | usage("?OPTIONS? ?VERSION?"); |
| 210 | + return; | |
| 210 | 211 | } |
| 211 | 212 | |
| 212 | 213 | if( zPivot ){ |
| 213 | 214 | pid = name_to_typed_rid(zPivot, "ci"); |
| 214 | 215 | if( pid==0 || !is_a_version(pid) ){ |
| 215 | 216 |
| --- src/merge.c | |
| +++ src/merge.c | |
| @@ -205,10 +205,11 @@ | |
| 205 | fossil_free(zCom); |
| 206 | } |
| 207 | db_finalize(&q); |
| 208 | }else{ |
| 209 | usage("?OPTIONS? ?VERSION?"); |
| 210 | } |
| 211 | |
| 212 | if( zPivot ){ |
| 213 | pid = name_to_typed_rid(zPivot, "ci"); |
| 214 | if( pid==0 || !is_a_version(pid) ){ |
| 215 |
| --- src/merge.c | |
| +++ src/merge.c | |
| @@ -205,10 +205,11 @@ | |
| 205 | fossil_free(zCom); |
| 206 | } |
| 207 | db_finalize(&q); |
| 208 | }else{ |
| 209 | usage("?OPTIONS? ?VERSION?"); |
| 210 | return; |
| 211 | } |
| 212 | |
| 213 | if( zPivot ){ |
| 214 | pid = name_to_typed_rid(zPivot, "ci"); |
| 215 | if( pid==0 || !is_a_version(pid) ){ |
| 216 |
+8
| --- src/rebuild.c | ||
| +++ src/rebuild.c | ||
| @@ -524,10 +524,11 @@ | ||
| 524 | 524 | ** --noverify Skip the verification of changes to the BLOB table |
| 525 | 525 | ** --pagesize N Set the database pagesize to N. (512..65536 and power of 2) |
| 526 | 526 | ** --randomize Scan artifacts in a random order |
| 527 | 527 | ** --vacuum Run VACUUM on the database after rebuilding |
| 528 | 528 | ** --deanalyze Remove ANALYZE tables from the database |
| 529 | +** --analyze Run ANALYZE on the database after rebuilding | |
| 529 | 530 | ** --wal Set Write-Ahead-Log journalling mode on the database |
| 530 | 531 | ** --stats Show artifact statistics after rebuilding |
| 531 | 532 | ** |
| 532 | 533 | ** See also: deconstruct, reconstruct |
| 533 | 534 | */ |
| @@ -540,19 +541,21 @@ | ||
| 540 | 541 | const char *zPagesize; |
| 541 | 542 | int newPagesize = 0; |
| 542 | 543 | int activateWal; |
| 543 | 544 | int runVacuum; |
| 544 | 545 | int runDeanalyze; |
| 546 | + int runAnalyze; | |
| 545 | 547 | int runCompress; |
| 546 | 548 | int showStats; |
| 547 | 549 | |
| 548 | 550 | omitVerify = find_option("noverify",0,0)!=0; |
| 549 | 551 | forceFlag = find_option("force","f",0)!=0; |
| 550 | 552 | randomizeFlag = find_option("randomize", 0, 0)!=0; |
| 551 | 553 | doClustering = find_option("cluster", 0, 0)!=0; |
| 552 | 554 | runVacuum = find_option("vacuum",0,0)!=0; |
| 553 | 555 | runDeanalyze = find_option("deanalyze",0,0)!=0; |
| 556 | + runAnalyze = find_option("analyze",0,0)!=0; | |
| 554 | 557 | runCompress = find_option("compress",0,0)!=0; |
| 555 | 558 | zPagesize = find_option("pagesize",0,1); |
| 556 | 559 | showStats = find_option("stats",0,0)!=0; |
| 557 | 560 | if( zPagesize ){ |
| 558 | 561 | newPagesize = atoi(zPagesize); |
| @@ -605,10 +608,15 @@ | ||
| 605 | 608 | } |
| 606 | 609 | if( runDeanalyze ){ |
| 607 | 610 | db_multi_exec("DROP TABLE IF EXISTS sqlite_stat1;" |
| 608 | 611 | "DROP TABLE IF EXISTS sqlite_stat3;"); |
| 609 | 612 | } |
| 613 | + if( runAnalyze ){ | |
| 614 | + fossil_print("Analyzing the database... "); fflush(stdout); | |
| 615 | + db_multi_exec("ANALYZE;"); | |
| 616 | + fossil_print("done\n"); | |
| 617 | + } | |
| 610 | 618 | if( runVacuum ){ |
| 611 | 619 | fossil_print("Vacuuming the database... "); fflush(stdout); |
| 612 | 620 | db_multi_exec("VACUUM"); |
| 613 | 621 | fossil_print("done\n"); |
| 614 | 622 | } |
| 615 | 623 |
| --- src/rebuild.c | |
| +++ src/rebuild.c | |
| @@ -524,10 +524,11 @@ | |
| 524 | ** --noverify Skip the verification of changes to the BLOB table |
| 525 | ** --pagesize N Set the database pagesize to N. (512..65536 and power of 2) |
| 526 | ** --randomize Scan artifacts in a random order |
| 527 | ** --vacuum Run VACUUM on the database after rebuilding |
| 528 | ** --deanalyze Remove ANALYZE tables from the database |
| 529 | ** --wal Set Write-Ahead-Log journalling mode on the database |
| 530 | ** --stats Show artifact statistics after rebuilding |
| 531 | ** |
| 532 | ** See also: deconstruct, reconstruct |
| 533 | */ |
| @@ -540,19 +541,21 @@ | |
| 540 | const char *zPagesize; |
| 541 | int newPagesize = 0; |
| 542 | int activateWal; |
| 543 | int runVacuum; |
| 544 | int runDeanalyze; |
| 545 | int runCompress; |
| 546 | int showStats; |
| 547 | |
| 548 | omitVerify = find_option("noverify",0,0)!=0; |
| 549 | forceFlag = find_option("force","f",0)!=0; |
| 550 | randomizeFlag = find_option("randomize", 0, 0)!=0; |
| 551 | doClustering = find_option("cluster", 0, 0)!=0; |
| 552 | runVacuum = find_option("vacuum",0,0)!=0; |
| 553 | runDeanalyze = find_option("deanalyze",0,0)!=0; |
| 554 | runCompress = find_option("compress",0,0)!=0; |
| 555 | zPagesize = find_option("pagesize",0,1); |
| 556 | showStats = find_option("stats",0,0)!=0; |
| 557 | if( zPagesize ){ |
| 558 | newPagesize = atoi(zPagesize); |
| @@ -605,10 +608,15 @@ | |
| 605 | } |
| 606 | if( runDeanalyze ){ |
| 607 | db_multi_exec("DROP TABLE IF EXISTS sqlite_stat1;" |
| 608 | "DROP TABLE IF EXISTS sqlite_stat3;"); |
| 609 | } |
| 610 | if( runVacuum ){ |
| 611 | fossil_print("Vacuuming the database... "); fflush(stdout); |
| 612 | db_multi_exec("VACUUM"); |
| 613 | fossil_print("done\n"); |
| 614 | } |
| 615 |
| --- src/rebuild.c | |
| +++ src/rebuild.c | |
| @@ -524,10 +524,11 @@ | |
| 524 | ** --noverify Skip the verification of changes to the BLOB table |
| 525 | ** --pagesize N Set the database pagesize to N. (512..65536 and power of 2) |
| 526 | ** --randomize Scan artifacts in a random order |
| 527 | ** --vacuum Run VACUUM on the database after rebuilding |
| 528 | ** --deanalyze Remove ANALYZE tables from the database |
| 529 | ** --analyze Run ANALYZE on the database after rebuilding |
| 530 | ** --wal Set Write-Ahead-Log journalling mode on the database |
| 531 | ** --stats Show artifact statistics after rebuilding |
| 532 | ** |
| 533 | ** See also: deconstruct, reconstruct |
| 534 | */ |
| @@ -540,19 +541,21 @@ | |
| 541 | const char *zPagesize; |
| 542 | int newPagesize = 0; |
| 543 | int activateWal; |
| 544 | int runVacuum; |
| 545 | int runDeanalyze; |
| 546 | int runAnalyze; |
| 547 | int runCompress; |
| 548 | int showStats; |
| 549 | |
| 550 | omitVerify = find_option("noverify",0,0)!=0; |
| 551 | forceFlag = find_option("force","f",0)!=0; |
| 552 | randomizeFlag = find_option("randomize", 0, 0)!=0; |
| 553 | doClustering = find_option("cluster", 0, 0)!=0; |
| 554 | runVacuum = find_option("vacuum",0,0)!=0; |
| 555 | runDeanalyze = find_option("deanalyze",0,0)!=0; |
| 556 | runAnalyze = find_option("analyze",0,0)!=0; |
| 557 | runCompress = find_option("compress",0,0)!=0; |
| 558 | zPagesize = find_option("pagesize",0,1); |
| 559 | showStats = find_option("stats",0,0)!=0; |
| 560 | if( zPagesize ){ |
| 561 | newPagesize = atoi(zPagesize); |
| @@ -605,10 +608,15 @@ | |
| 608 | } |
| 609 | if( runDeanalyze ){ |
| 610 | db_multi_exec("DROP TABLE IF EXISTS sqlite_stat1;" |
| 611 | "DROP TABLE IF EXISTS sqlite_stat3;"); |
| 612 | } |
| 613 | if( runAnalyze ){ |
| 614 | fossil_print("Analyzing the database... "); fflush(stdout); |
| 615 | db_multi_exec("ANALYZE;"); |
| 616 | fossil_print("done\n"); |
| 617 | } |
| 618 | if( runVacuum ){ |
| 619 | fossil_print("Vacuuming the database... "); fflush(stdout); |
| 620 | db_multi_exec("VACUUM"); |
| 621 | fossil_print("done\n"); |
| 622 | } |
| 623 |
+7
-7
| --- src/regexp.c | ||
| +++ src/regexp.c | ||
| @@ -172,11 +172,11 @@ | ||
| 172 | 172 | return (c>='0' && c<='9'); |
| 173 | 173 | } |
| 174 | 174 | |
| 175 | 175 | /* Return true if c is a perl "space" character: [ \t\r\n\v\f] */ |
| 176 | 176 | static int re_space_char(int c){ |
| 177 | - return c==' ' || c=='\t' || c=='\n' || c=='\r' || c=='\v' || c=='\f' ; | |
| 177 | + return c==' ' || c=='\t' || c=='\n' || c=='\r' || c=='\v' || c=='\f'; | |
| 178 | 178 | } |
| 179 | 179 | |
| 180 | 180 | /* Run a compiled regular expression on the zero-terminated input |
| 181 | 181 | ** string zIn[]. Return true on a match and false if there is no match. |
| 182 | 182 | */ |
| @@ -401,17 +401,17 @@ | ||
| 401 | 401 | ){ |
| 402 | 402 | p->sIn.i += 5; |
| 403 | 403 | return v; |
| 404 | 404 | } |
| 405 | 405 | } |
| 406 | - if( c=='x' ){ | |
| 406 | + if( c=='x' && p->sIn.i+2<p->sIn.mx ){ | |
| 407 | 407 | const unsigned char *zIn = p->sIn.z + p->sIn.i; |
| 408 | - if( p->sIn.i+2<p->sIn.mx ){ | |
| 409 | - if( re_hex(zIn[1],&v) && re_hex(zIn[2],&v) ){ | |
| 410 | - p->sIn.i += 3; | |
| 411 | - return v; | |
| 412 | - } | |
| 408 | + if( re_hex(zIn[1],&v) | |
| 409 | + && re_hex(zIn[2],&v) | |
| 410 | + ){ | |
| 411 | + p->sIn.i += 3; | |
| 412 | + return v; | |
| 413 | 413 | } |
| 414 | 414 | } |
| 415 | 415 | for(i=0; zEsc[i] && zEsc[i]!=c; i++){} |
| 416 | 416 | if( zEsc[i] ){ |
| 417 | 417 | if( i<6 ) c = zTrans[i]; |
| 418 | 418 |
| --- src/regexp.c | |
| +++ src/regexp.c | |
| @@ -172,11 +172,11 @@ | |
| 172 | return (c>='0' && c<='9'); |
| 173 | } |
| 174 | |
| 175 | /* Return true if c is a perl "space" character: [ \t\r\n\v\f] */ |
| 176 | static int re_space_char(int c){ |
| 177 | return c==' ' || c=='\t' || c=='\n' || c=='\r' || c=='\v' || c=='\f' ; |
| 178 | } |
| 179 | |
| 180 | /* Run a compiled regular expression on the zero-terminated input |
| 181 | ** string zIn[]. Return true on a match and false if there is no match. |
| 182 | */ |
| @@ -401,17 +401,17 @@ | |
| 401 | ){ |
| 402 | p->sIn.i += 5; |
| 403 | return v; |
| 404 | } |
| 405 | } |
| 406 | if( c=='x' ){ |
| 407 | const unsigned char *zIn = p->sIn.z + p->sIn.i; |
| 408 | if( p->sIn.i+2<p->sIn.mx ){ |
| 409 | if( re_hex(zIn[1],&v) && re_hex(zIn[2],&v) ){ |
| 410 | p->sIn.i += 3; |
| 411 | return v; |
| 412 | } |
| 413 | } |
| 414 | } |
| 415 | for(i=0; zEsc[i] && zEsc[i]!=c; i++){} |
| 416 | if( zEsc[i] ){ |
| 417 | if( i<6 ) c = zTrans[i]; |
| 418 |
| --- src/regexp.c | |
| +++ src/regexp.c | |
| @@ -172,11 +172,11 @@ | |
| 172 | return (c>='0' && c<='9'); |
| 173 | } |
| 174 | |
| 175 | /* Return true if c is a perl "space" character: [ \t\r\n\v\f] */ |
| 176 | static int re_space_char(int c){ |
| 177 | return c==' ' || c=='\t' || c=='\n' || c=='\r' || c=='\v' || c=='\f'; |
| 178 | } |
| 179 | |
| 180 | /* Run a compiled regular expression on the zero-terminated input |
| 181 | ** string zIn[]. Return true on a match and false if there is no match. |
| 182 | */ |
| @@ -401,17 +401,17 @@ | |
| 401 | ){ |
| 402 | p->sIn.i += 5; |
| 403 | return v; |
| 404 | } |
| 405 | } |
| 406 | if( c=='x' && p->sIn.i+2<p->sIn.mx ){ |
| 407 | const unsigned char *zIn = p->sIn.z + p->sIn.i; |
| 408 | if( re_hex(zIn[1],&v) |
| 409 | && re_hex(zIn[2],&v) |
| 410 | ){ |
| 411 | p->sIn.i += 3; |
| 412 | return v; |
| 413 | } |
| 414 | } |
| 415 | for(i=0; zEsc[i] && zEsc[i]!=c; i++){} |
| 416 | if( zEsc[i] ){ |
| 417 | if( i<6 ) c = zTrans[i]; |
| 418 |
+9
| --- src/setup.c | ||
| +++ src/setup.c | ||
| @@ -917,10 +917,19 @@ | ||
| 917 | 917 | @ to this many bytes, uncompressed. If the client requires more data |
| 918 | 918 | @ than this, then the client will issue multiple HTTP requests. |
| 919 | 919 | @ Values below 1 million are not recommended. 5 million is a |
| 920 | 920 | @ reasonable number.</p> |
| 921 | 921 | |
| 922 | + @ <hr /> | |
| 923 | + entry_attribute("Download time limit", 11, "max-download-time", "mxdwnt", | |
| 924 | + "30"); | |
| 925 | + | |
| 926 | + @ <p>Fossil tries to spend less than this many seconds gathering | |
| 927 | + @ the out-bound data of sync, clone, and pull packets. | |
| 928 | + @ If the client request takes longer, a partial reply is given similar | |
| 929 | + @ to the download packet limit. 30s is a reasonable default.</p> | |
| 930 | + | |
| 922 | 931 | @ <hr /> |
| 923 | 932 | onoff_attribute( |
| 924 | 933 | "Enable hyperlinks for \"nobody\" based on User-Agent and Javascript", |
| 925 | 934 | "auto-hyperlink", "autohyperlink", 1); |
| 926 | 935 | @ <p>Enable hyperlinks (the equivalent of the "h" permission) for all users |
| 927 | 936 |
| --- src/setup.c | |
| +++ src/setup.c | |
| @@ -917,10 +917,19 @@ | |
| 917 | @ to this many bytes, uncompressed. If the client requires more data |
| 918 | @ than this, then the client will issue multiple HTTP requests. |
| 919 | @ Values below 1 million are not recommended. 5 million is a |
| 920 | @ reasonable number.</p> |
| 921 | |
| 922 | @ <hr /> |
| 923 | onoff_attribute( |
| 924 | "Enable hyperlinks for \"nobody\" based on User-Agent and Javascript", |
| 925 | "auto-hyperlink", "autohyperlink", 1); |
| 926 | @ <p>Enable hyperlinks (the equivalent of the "h" permission) for all users |
| 927 |
| --- src/setup.c | |
| +++ src/setup.c | |
| @@ -917,10 +917,19 @@ | |
| 917 | @ to this many bytes, uncompressed. If the client requires more data |
| 918 | @ than this, then the client will issue multiple HTTP requests. |
| 919 | @ Values below 1 million are not recommended. 5 million is a |
| 920 | @ reasonable number.</p> |
| 921 | |
| 922 | @ <hr /> |
| 923 | entry_attribute("Download time limit", 11, "max-download-time", "mxdwnt", |
| 924 | "30"); |
| 925 | |
| 926 | @ <p>Fossil tries to spend less than this many seconds gathering |
| 927 | @ the out-bound data of sync, clone, and pull packets. |
| 928 | @ If the client request takes longer, a partial reply is given similar |
| 929 | @ to the download packet limit. 30s is a reasonable default.</p> |
| 930 | |
| 931 | @ <hr /> |
| 932 | onoff_attribute( |
| 933 | "Enable hyperlinks for \"nobody\" based on User-Agent and Javascript", |
| 934 | "auto-hyperlink", "autohyperlink", 1); |
| 935 | @ <p>Enable hyperlinks (the equivalent of the "h" permission) for all users |
| 936 |
+199
-93
| --- src/sqlite3.c | ||
| +++ src/sqlite3.c | ||
| @@ -673,11 +673,11 @@ | ||
| 673 | 673 | ** [sqlite3_libversion_number()], [sqlite3_sourceid()], |
| 674 | 674 | ** [sqlite_version()] and [sqlite_source_id()]. |
| 675 | 675 | */ |
| 676 | 676 | #define SQLITE_VERSION "3.7.16" |
| 677 | 677 | #define SQLITE_VERSION_NUMBER 3007016 |
| 678 | -#define SQLITE_SOURCE_ID "2013-01-09 11:31:17 5774f2175ce621dfc4b6b93f7ee13fd66f3ec2b9" | |
| 678 | +#define SQLITE_SOURCE_ID "2013-01-17 17:20:49 38852f158ab20bb4d7b264af987ec1538052bec3" | |
| 679 | 679 | |
| 680 | 680 | /* |
| 681 | 681 | ** CAPI3REF: Run-Time Library Version Numbers |
| 682 | 682 | ** KEYWORDS: sqlite3_version, sqlite3_sourceid |
| 683 | 683 | ** |
| @@ -8238,10 +8238,15 @@ | ||
| 8238 | 8238 | ** A convenience macro that returns the number of elements in |
| 8239 | 8239 | ** an array. |
| 8240 | 8240 | */ |
| 8241 | 8241 | #define ArraySize(X) ((int)(sizeof(X)/sizeof(X[0]))) |
| 8242 | 8242 | |
| 8243 | +/* | |
| 8244 | +** Determine if the argument is a power of two | |
| 8245 | +*/ | |
| 8246 | +#define IsPowerOfTwo(X) (((X)&((X)-1))==0) | |
| 8247 | + | |
| 8243 | 8248 | /* |
| 8244 | 8249 | ** The following value as a destructor means to use sqlite3DbFree(). |
| 8245 | 8250 | ** The sqlite3DbFree() routine requires two parameters instead of the |
| 8246 | 8251 | ** one parameter that destructors normally want. So we have to introduce |
| 8247 | 8252 | ** this magic value that the code knows to handle differently. Any |
| @@ -10042,10 +10047,11 @@ | ||
| 10042 | 10047 | #define SQLITE_IdxRealAsInt 0x0010 /* Store REAL as INT in indices */ |
| 10043 | 10048 | #define SQLITE_DistinctOpt 0x0020 /* DISTINCT using indexes */ |
| 10044 | 10049 | #define SQLITE_CoverIdxScan 0x0040 /* Covering index scans */ |
| 10045 | 10050 | #define SQLITE_OrderByIdxJoin 0x0080 /* ORDER BY of joins via index */ |
| 10046 | 10051 | #define SQLITE_SubqCoroutine 0x0100 /* Evaluate subqueries as coroutines */ |
| 10052 | +#define SQLITE_Transitive 0x0200 /* Transitive constraints */ | |
| 10047 | 10053 | #define SQLITE_AllOpts 0xffff /* All optimizations */ |
| 10048 | 10054 | |
| 10049 | 10055 | /* |
| 10050 | 10056 | ** Macros for testing whether or not optimizations are enabled or disabled. |
| 10051 | 10057 | */ |
| @@ -93581,11 +93587,11 @@ | ||
| 93581 | 93587 | sqlite3_rekey(db, zKey, i/2); |
| 93582 | 93588 | } |
| 93583 | 93589 | }else |
| 93584 | 93590 | #endif |
| 93585 | 93591 | #if defined(SQLITE_HAS_CODEC) || defined(SQLITE_ENABLE_CEROD) |
| 93586 | - if( sqlite3StrICmp(zLeft, "activate_extensions")==0 ){ | |
| 93592 | + if( sqlite3StrICmp(zLeft, "activate_extensions")==0 && zRight ){ | |
| 93587 | 93593 | #ifdef SQLITE_HAS_CODEC |
| 93588 | 93594 | if( sqlite3StrNICmp(zRight, "see-", 4)==0 ){ |
| 93589 | 93595 | sqlite3_activate_see(&zRight[4]); |
| 93590 | 93596 | } |
| 93591 | 93597 | #endif |
| @@ -102802,12 +102808,12 @@ | ||
| 102802 | 102808 | Expr *pExpr; /* Pointer to the subexpression that is this term */ |
| 102803 | 102809 | int iParent; /* Disable pWC->a[iParent] when this term disabled */ |
| 102804 | 102810 | int leftCursor; /* Cursor number of X in "X <op> <expr>" */ |
| 102805 | 102811 | union { |
| 102806 | 102812 | int leftColumn; /* Column number of X in "X <op> <expr>" */ |
| 102807 | - WhereOrInfo *pOrInfo; /* Extra information if eOperator==WO_OR */ | |
| 102808 | - WhereAndInfo *pAndInfo; /* Extra information if eOperator==WO_AND */ | |
| 102813 | + WhereOrInfo *pOrInfo; /* Extra information if (eOperator & WO_OR)!=0 */ | |
| 102814 | + WhereAndInfo *pAndInfo; /* Extra information if (eOperator& WO_AND)!=0 */ | |
| 102809 | 102815 | } u; |
| 102810 | 102816 | u16 eOperator; /* A WO_xx value describing <op> */ |
| 102811 | 102817 | u8 wtFlags; /* TERM_xxx bit flags. See below */ |
| 102812 | 102818 | u8 nChild; /* Number of children that must disable us */ |
| 102813 | 102819 | WhereClause *pWC; /* The clause this term is part of */ |
| @@ -102931,10 +102937,11 @@ | ||
| 102931 | 102937 | #define WO_GE (WO_EQ<<(TK_GE-TK_EQ)) |
| 102932 | 102938 | #define WO_MATCH 0x040 |
| 102933 | 102939 | #define WO_ISNULL 0x080 |
| 102934 | 102940 | #define WO_OR 0x100 /* Two or more OR-connected terms */ |
| 102935 | 102941 | #define WO_AND 0x200 /* Two or more AND-connected terms */ |
| 102942 | +#define WO_EQUIV 0x400 /* Of the form A==B, both columns */ | |
| 102936 | 102943 | #define WO_NOOP 0x800 /* This term does not restrict search space */ |
| 102937 | 102944 | |
| 102938 | 102945 | #define WO_ALL 0xfff /* Mask of all possible WO_* values */ |
| 102939 | 102946 | #define WO_SINGLE 0x0ff /* Mask of all non-compound WO_* values */ |
| 102940 | 102947 | |
| @@ -103333,58 +103340,112 @@ | ||
| 103333 | 103340 | /* |
| 103334 | 103341 | ** Search for a term in the WHERE clause that is of the form "X <op> <expr>" |
| 103335 | 103342 | ** where X is a reference to the iColumn of table iCur and <op> is one of |
| 103336 | 103343 | ** the WO_xx operator codes specified by the op parameter. |
| 103337 | 103344 | ** Return a pointer to the term. Return 0 if not found. |
| 103345 | +** | |
| 103346 | +** The term returned might by Y=<expr> if there is another constraint in | |
| 103347 | +** the WHERE clause that specifies that X=Y. Any such constraints will be | |
| 103348 | +** identified by the WO_EQUIV bit in the pTerm->eOperator field. The | |
| 103349 | +** aEquiv[] array holds X and all its equivalents, with each SQL variable | |
| 103350 | +** taking up two slots in aEquiv[]. The first slot is for the cursor number | |
| 103351 | +** and the second is for the column number. There are 22 slots in aEquiv[] | |
| 103352 | +** so that means we can look for X plus up to 10 other equivalent values. | |
| 103353 | +** Hence a search for X will return <expr> if X=A1 and A1=A2 and A2=A3 | |
| 103354 | +** and ... and A9=A10 and A10=<expr>. | |
| 103355 | +** | |
| 103356 | +** If there are multiple terms in the WHERE clause of the form "X <op> <expr>" | |
| 103357 | +** then try for the one with no dependencies on <expr> - in other words where | |
| 103358 | +** <expr> is a constant expression of some kind. Only return entries of | |
| 103359 | +** the form "X <op> Y" where Y is a column in another table if no terms of | |
| 103360 | +** the form "X <op> <const-expr>" exist. Other than this priority, if there | |
| 103361 | +** are two or more terms that match, then the choice of which term to return | |
| 103362 | +** is arbitrary. | |
| 103338 | 103363 | */ |
| 103339 | 103364 | static WhereTerm *findTerm( |
| 103340 | 103365 | WhereClause *pWC, /* The WHERE clause to be searched */ |
| 103341 | 103366 | int iCur, /* Cursor number of LHS */ |
| 103342 | 103367 | int iColumn, /* Column number of LHS */ |
| 103343 | 103368 | Bitmask notReady, /* RHS must not overlap with this mask */ |
| 103344 | 103369 | u32 op, /* Mask of WO_xx values describing operator */ |
| 103345 | 103370 | Index *pIdx /* Must be compatible with this index, if not NULL */ |
| 103346 | 103371 | ){ |
| 103347 | - WhereTerm *pTerm; | |
| 103348 | - int k; | |
| 103372 | + WhereTerm *pTerm; /* Term being examined as possible result */ | |
| 103373 | + WhereTerm *pResult = 0; /* The answer to return */ | |
| 103374 | + WhereClause *pWCOrig = pWC; /* Original pWC value */ | |
| 103375 | + int j, k; /* Loop counters */ | |
| 103376 | + Expr *pX; /* Pointer to an expression */ | |
| 103377 | + Parse *pParse; /* Parsing context */ | |
| 103378 | + int iOrigCol = iColumn; /* Original value of iColumn */ | |
| 103379 | + int nEquiv = 2; /* Number of entires in aEquiv[] */ | |
| 103380 | + int iEquiv = 2; /* Number of entries of aEquiv[] processed so far */ | |
| 103381 | + int aEquiv[22]; /* iCur,iColumn and up to 10 other equivalents */ | |
| 103382 | + | |
| 103349 | 103383 | assert( iCur>=0 ); |
| 103350 | - op &= WO_ALL; | |
| 103351 | - for(; pWC; pWC=pWC->pOuter){ | |
| 103352 | - for(pTerm=pWC->a, k=pWC->nTerm; k; k--, pTerm++){ | |
| 103353 | - if( pTerm->leftCursor==iCur | |
| 103354 | - && (pTerm->prereqRight & notReady)==0 | |
| 103355 | - && pTerm->u.leftColumn==iColumn | |
| 103356 | - && (pTerm->eOperator & op)!=0 | |
| 103357 | - ){ | |
| 103358 | - if( iColumn>=0 && pIdx && pTerm->eOperator!=WO_ISNULL ){ | |
| 103359 | - Expr *pX = pTerm->pExpr; | |
| 103360 | - CollSeq *pColl; | |
| 103361 | - char idxaff; | |
| 103362 | - int j; | |
| 103363 | - Parse *pParse = pWC->pParse; | |
| 103364 | - | |
| 103365 | - idxaff = pIdx->pTable->aCol[iColumn].affinity; | |
| 103366 | - if( !sqlite3IndexAffinityOk(pX, idxaff) ) continue; | |
| 103367 | - | |
| 103368 | - /* Figure out the collation sequence required from an index for | |
| 103369 | - ** it to be useful for optimising expression pX. Store this | |
| 103370 | - ** value in variable pColl. | |
| 103371 | - */ | |
| 103372 | - assert(pX->pLeft); | |
| 103373 | - pColl = sqlite3BinaryCompareCollSeq(pParse, pX->pLeft, pX->pRight); | |
| 103374 | - if( pColl==0 ) pColl = pParse->db->pDfltColl; | |
| 103375 | - | |
| 103376 | - for(j=0; pIdx->aiColumn[j]!=iColumn; j++){ | |
| 103377 | - if( NEVER(j>=pIdx->nColumn) ) return 0; | |
| 103378 | - } | |
| 103379 | - if( sqlite3StrICmp(pColl->zName, pIdx->azColl[j]) ) continue; | |
| 103380 | - } | |
| 103381 | - return pTerm; | |
| 103382 | - } | |
| 103383 | - } | |
| 103384 | - } | |
| 103385 | - return 0; | |
| 103384 | + aEquiv[0] = iCur; | |
| 103385 | + aEquiv[1] = iColumn; | |
| 103386 | + for(;;){ | |
| 103387 | + for(pWC=pWCOrig; pWC; pWC=pWC->pOuter){ | |
| 103388 | + for(pTerm=pWC->a, k=pWC->nTerm; k; k--, pTerm++){ | |
| 103389 | + if( pTerm->leftCursor==iCur | |
| 103390 | + && pTerm->u.leftColumn==iColumn | |
| 103391 | + ){ | |
| 103392 | + if( (pTerm->prereqRight & notReady)==0 | |
| 103393 | + && (pTerm->eOperator & op & WO_ALL)!=0 | |
| 103394 | + ){ | |
| 103395 | + if( iOrigCol>=0 && pIdx && (pTerm->eOperator & WO_ISNULL)==0 ){ | |
| 103396 | + CollSeq *pColl; | |
| 103397 | + char idxaff; | |
| 103398 | + | |
| 103399 | + pX = pTerm->pExpr; | |
| 103400 | + pParse = pWC->pParse; | |
| 103401 | + idxaff = pIdx->pTable->aCol[iOrigCol].affinity; | |
| 103402 | + if( !sqlite3IndexAffinityOk(pX, idxaff) ){ | |
| 103403 | + continue; | |
| 103404 | + } | |
| 103405 | + | |
| 103406 | + /* Figure out the collation sequence required from an index for | |
| 103407 | + ** it to be useful for optimising expression pX. Store this | |
| 103408 | + ** value in variable pColl. | |
| 103409 | + */ | |
| 103410 | + assert(pX->pLeft); | |
| 103411 | + pColl = sqlite3BinaryCompareCollSeq(pParse,pX->pLeft,pX->pRight); | |
| 103412 | + if( pColl==0 ) pColl = pParse->db->pDfltColl; | |
| 103413 | + | |
| 103414 | + for(j=0; pIdx->aiColumn[j]!=iOrigCol; j++){ | |
| 103415 | + if( NEVER(j>=pIdx->nColumn) ) return 0; | |
| 103416 | + } | |
| 103417 | + if( sqlite3StrICmp(pColl->zName, pIdx->azColl[j]) ){ | |
| 103418 | + continue; | |
| 103419 | + } | |
| 103420 | + } | |
| 103421 | + pResult = pTerm; | |
| 103422 | + if( pTerm->prereqRight==0 ) goto findTerm_success; | |
| 103423 | + } | |
| 103424 | + if( (pTerm->eOperator & WO_EQUIV)!=0 | |
| 103425 | + && nEquiv<ArraySize(aEquiv) | |
| 103426 | + ){ | |
| 103427 | + pX = sqlite3ExprSkipCollate(pTerm->pExpr->pRight); | |
| 103428 | + assert( pX->op==TK_COLUMN ); | |
| 103429 | + for(j=0; j<nEquiv; j+=2){ | |
| 103430 | + if( aEquiv[j]==pX->iTable && aEquiv[j+1]==pX->iColumn ) break; | |
| 103431 | + } | |
| 103432 | + if( j==nEquiv ){ | |
| 103433 | + aEquiv[j] = pX->iTable; | |
| 103434 | + aEquiv[j+1] = pX->iColumn; | |
| 103435 | + nEquiv += 2; | |
| 103436 | + } | |
| 103437 | + } | |
| 103438 | + } | |
| 103439 | + } | |
| 103440 | + } | |
| 103441 | + if( iEquiv>=nEquiv ) break; | |
| 103442 | + iCur = aEquiv[iEquiv++]; | |
| 103443 | + iColumn = aEquiv[iEquiv++]; | |
| 103444 | + } | |
| 103445 | +findTerm_success: | |
| 103446 | + return pResult; | |
| 103386 | 103447 | } |
| 103387 | 103448 | |
| 103388 | 103449 | /* Forward reference */ |
| 103389 | 103450 | static void exprAnalyze(SrcList*, WhereClause*, int); |
| 103390 | 103451 | |
| @@ -103658,11 +103719,10 @@ | ||
| 103658 | 103719 | indexable = ~(Bitmask)0; |
| 103659 | 103720 | chngToIN = ~(pWC->vmask); |
| 103660 | 103721 | for(i=pOrWc->nTerm-1, pOrTerm=pOrWc->a; i>=0 && indexable; i--, pOrTerm++){ |
| 103661 | 103722 | if( (pOrTerm->eOperator & WO_SINGLE)==0 ){ |
| 103662 | 103723 | WhereAndInfo *pAndInfo; |
| 103663 | - assert( pOrTerm->eOperator==0 ); | |
| 103664 | 103724 | assert( (pOrTerm->wtFlags & (TERM_ANDINFO|TERM_ORINFO))==0 ); |
| 103665 | 103725 | chngToIN = 0; |
| 103666 | 103726 | pAndInfo = sqlite3DbMallocRaw(db, sizeof(*pAndInfo)); |
| 103667 | 103727 | if( pAndInfo ){ |
| 103668 | 103728 | WhereClause *pAndWC; |
| @@ -103697,11 +103757,11 @@ | ||
| 103697 | 103757 | if( pOrTerm->wtFlags & TERM_VIRTUAL ){ |
| 103698 | 103758 | WhereTerm *pOther = &pOrWc->a[pOrTerm->iParent]; |
| 103699 | 103759 | b |= getMask(pMaskSet, pOther->leftCursor); |
| 103700 | 103760 | } |
| 103701 | 103761 | indexable &= b; |
| 103702 | - if( pOrTerm->eOperator!=WO_EQ ){ | |
| 103762 | + if( (pOrTerm->eOperator & WO_EQ)==0 ){ | |
| 103703 | 103763 | chngToIN = 0; |
| 103704 | 103764 | }else{ |
| 103705 | 103765 | chngToIN &= b; |
| 103706 | 103766 | } |
| 103707 | 103767 | } |
| @@ -103748,11 +103808,11 @@ | ||
| 103748 | 103808 | ** and column is found but leave okToChngToIN false if not found. |
| 103749 | 103809 | */ |
| 103750 | 103810 | for(j=0; j<2 && !okToChngToIN; j++){ |
| 103751 | 103811 | pOrTerm = pOrWc->a; |
| 103752 | 103812 | for(i=pOrWc->nTerm-1; i>=0; i--, pOrTerm++){ |
| 103753 | - assert( pOrTerm->eOperator==WO_EQ ); | |
| 103813 | + assert( pOrTerm->eOperator & WO_EQ ); | |
| 103754 | 103814 | pOrTerm->wtFlags &= ~TERM_OR_OK; |
| 103755 | 103815 | if( pOrTerm->leftCursor==iCursor ){ |
| 103756 | 103816 | /* This is the 2-bit case and we are on the second iteration and |
| 103757 | 103817 | ** current term is from the first iteration. So skip this term. */ |
| 103758 | 103818 | assert( j==1 ); |
| @@ -103774,21 +103834,21 @@ | ||
| 103774 | 103834 | } |
| 103775 | 103835 | if( i<0 ){ |
| 103776 | 103836 | /* No candidate table+column was found. This can only occur |
| 103777 | 103837 | ** on the second iteration */ |
| 103778 | 103838 | assert( j==1 ); |
| 103779 | - assert( (chngToIN&(chngToIN-1))==0 ); | |
| 103839 | + assert( IsPowerOfTwo(chngToIN) ); | |
| 103780 | 103840 | assert( chngToIN==getMask(pMaskSet, iCursor) ); |
| 103781 | 103841 | break; |
| 103782 | 103842 | } |
| 103783 | 103843 | testcase( j==1 ); |
| 103784 | 103844 | |
| 103785 | 103845 | /* We have found a candidate table and column. Check to see if that |
| 103786 | 103846 | ** table and column is common to every term in the OR clause */ |
| 103787 | 103847 | okToChngToIN = 1; |
| 103788 | 103848 | for(; i>=0 && okToChngToIN; i--, pOrTerm++){ |
| 103789 | - assert( pOrTerm->eOperator==WO_EQ ); | |
| 103849 | + assert( pOrTerm->eOperator & WO_EQ ); | |
| 103790 | 103850 | if( pOrTerm->leftCursor!=iCursor ){ |
| 103791 | 103851 | pOrTerm->wtFlags &= ~TERM_OR_OK; |
| 103792 | 103852 | }else if( pOrTerm->u.leftColumn!=iColumn ){ |
| 103793 | 103853 | okToChngToIN = 0; |
| 103794 | 103854 | }else{ |
| @@ -103820,11 +103880,11 @@ | ||
| 103820 | 103880 | Expr *pLeft = 0; /* The LHS of the IN operator */ |
| 103821 | 103881 | Expr *pNew; /* The complete IN operator */ |
| 103822 | 103882 | |
| 103823 | 103883 | for(i=pOrWc->nTerm-1, pOrTerm=pOrWc->a; i>=0; i--, pOrTerm++){ |
| 103824 | 103884 | if( (pOrTerm->wtFlags & TERM_OR_OK)==0 ) continue; |
| 103825 | - assert( pOrTerm->eOperator==WO_EQ ); | |
| 103885 | + assert( pOrTerm->eOperator & WO_EQ ); | |
| 103826 | 103886 | assert( pOrTerm->leftCursor==iCursor ); |
| 103827 | 103887 | assert( pOrTerm->u.leftColumn==iColumn ); |
| 103828 | 103888 | pDup = sqlite3ExprDup(db, pOrTerm->pExpr->pRight, 0); |
| 103829 | 103889 | pList = sqlite3ExprListAppend(pWC->pParse, pList, pDup); |
| 103830 | 103890 | pLeft = pOrTerm->pExpr->pLeft; |
| @@ -103849,11 +103909,10 @@ | ||
| 103849 | 103909 | pTerm->eOperator = WO_NOOP; /* case 1 trumps case 2 */ |
| 103850 | 103910 | } |
| 103851 | 103911 | } |
| 103852 | 103912 | } |
| 103853 | 103913 | #endif /* !SQLITE_OMIT_OR_OPTIMIZATION && !SQLITE_OMIT_SUBQUERY */ |
| 103854 | - | |
| 103855 | 103914 | |
| 103856 | 103915 | /* |
| 103857 | 103916 | ** The input to this routine is an WhereTerm structure with only the |
| 103858 | 103917 | ** "pExpr" field filled in. The job of this routine is to analyze the |
| 103859 | 103918 | ** subexpression and populate all the other fields of the WhereTerm |
| @@ -103919,21 +103978,23 @@ | ||
| 103919 | 103978 | } |
| 103920 | 103979 | pTerm->prereqAll = prereqAll; |
| 103921 | 103980 | pTerm->leftCursor = -1; |
| 103922 | 103981 | pTerm->iParent = -1; |
| 103923 | 103982 | pTerm->eOperator = 0; |
| 103924 | - if( allowedOp(op) && (pTerm->prereqRight & prereqLeft)==0 ){ | |
| 103983 | + if( allowedOp(op) ){ | |
| 103925 | 103984 | Expr *pLeft = sqlite3ExprSkipCollate(pExpr->pLeft); |
| 103926 | 103985 | Expr *pRight = sqlite3ExprSkipCollate(pExpr->pRight); |
| 103986 | + u16 opMask = (pTerm->prereqRight & prereqLeft)==0 ? WO_ALL : WO_EQUIV; | |
| 103927 | 103987 | if( pLeft->op==TK_COLUMN ){ |
| 103928 | 103988 | pTerm->leftCursor = pLeft->iTable; |
| 103929 | 103989 | pTerm->u.leftColumn = pLeft->iColumn; |
| 103930 | - pTerm->eOperator = operatorMask(op); | |
| 103990 | + pTerm->eOperator = operatorMask(op) & opMask; | |
| 103931 | 103991 | } |
| 103932 | 103992 | if( pRight && pRight->op==TK_COLUMN ){ |
| 103933 | 103993 | WhereTerm *pNew; |
| 103934 | 103994 | Expr *pDup; |
| 103995 | + u16 eExtraOp = 0; /* Extra bits for pNew->eOperator */ | |
| 103935 | 103996 | if( pTerm->leftCursor>=0 ){ |
| 103936 | 103997 | int idxNew; |
| 103937 | 103998 | pDup = sqlite3ExprDup(db, pExpr, 0); |
| 103938 | 103999 | if( db->mallocFailed ){ |
| 103939 | 104000 | sqlite3ExprDelete(db, pDup); |
| @@ -103944,10 +104005,17 @@ | ||
| 103944 | 104005 | pNew = &pWC->a[idxNew]; |
| 103945 | 104006 | pNew->iParent = idxTerm; |
| 103946 | 104007 | pTerm = &pWC->a[idxTerm]; |
| 103947 | 104008 | pTerm->nChild = 1; |
| 103948 | 104009 | pTerm->wtFlags |= TERM_COPIED; |
| 104010 | + if( pExpr->op==TK_EQ | |
| 104011 | + && !ExprHasProperty(pExpr, EP_FromJoin) | |
| 104012 | + && OptimizationEnabled(db, SQLITE_Transitive) | |
| 104013 | + ){ | |
| 104014 | + pTerm->eOperator |= WO_EQUIV; | |
| 104015 | + eExtraOp = WO_EQUIV; | |
| 104016 | + } | |
| 103949 | 104017 | }else{ |
| 103950 | 104018 | pDup = pExpr; |
| 103951 | 104019 | pNew = pTerm; |
| 103952 | 104020 | } |
| 103953 | 104021 | exprCommute(pParse, pDup); |
| @@ -103955,11 +104023,11 @@ | ||
| 103955 | 104023 | pNew->leftCursor = pLeft->iTable; |
| 103956 | 104024 | pNew->u.leftColumn = pLeft->iColumn; |
| 103957 | 104025 | testcase( (prereqLeft | extraRight) != prereqLeft ); |
| 103958 | 104026 | pNew->prereqRight = prereqLeft | extraRight; |
| 103959 | 104027 | pNew->prereqAll = prereqAll; |
| 103960 | - pNew->eOperator = operatorMask(pDup->op); | |
| 104028 | + pNew->eOperator = (operatorMask(pDup->op) + eExtraOp) & opMask; | |
| 103961 | 104029 | } |
| 103962 | 104030 | } |
| 103963 | 104031 | |
| 103964 | 104032 | #ifndef SQLITE_OMIT_BETWEEN_OPTIMIZATION |
| 103965 | 104033 | /* If a term is the BETWEEN operator, create two new virtual terms |
| @@ -104414,11 +104482,11 @@ | ||
| 104414 | 104482 | return; |
| 104415 | 104483 | } |
| 104416 | 104484 | |
| 104417 | 104485 | /* Search the WHERE clause terms for a usable WO_OR term. */ |
| 104418 | 104486 | for(pTerm=pWC->a; pTerm<pWCEnd; pTerm++){ |
| 104419 | - if( pTerm->eOperator==WO_OR | |
| 104487 | + if( (pTerm->eOperator & WO_OR)!=0 | |
| 104420 | 104488 | && ((pTerm->prereqAll & ~maskSrc) & p->notReady)==0 |
| 104421 | 104489 | && (pTerm->u.pOrInfo->indexable & maskSrc)!=0 |
| 104422 | 104490 | ){ |
| 104423 | 104491 | WhereClause * const pOrWC = &pTerm->u.pOrInfo->wc; |
| 104424 | 104492 | WhereTerm * const pOrWCEnd = &pOrWC->a[pOrWC->nTerm]; |
| @@ -104435,11 +104503,11 @@ | ||
| 104435 | 104503 | sBOI.ppIdxInfo = 0; |
| 104436 | 104504 | for(pOrTerm=pOrWC->a; pOrTerm<pOrWCEnd; pOrTerm++){ |
| 104437 | 104505 | WHERETRACE(("... Multi-index OR testing for term %d of %d....\n", |
| 104438 | 104506 | (pOrTerm - pOrWC->a), (pTerm - pWC->a) |
| 104439 | 104507 | )); |
| 104440 | - if( pOrTerm->eOperator==WO_AND ){ | |
| 104508 | + if( (pOrTerm->eOperator& WO_AND)!=0 ){ | |
| 104441 | 104509 | sBOI.pWC = &pOrTerm->u.pAndInfo->wc; |
| 104442 | 104510 | bestIndex(&sBOI); |
| 104443 | 104511 | }else if( pOrTerm->leftCursor==iCur ){ |
| 104444 | 104512 | WhereClause tempWC; |
| 104445 | 104513 | tempWC.pParse = pWC->pParse; |
| @@ -104496,11 +104564,11 @@ | ||
| 104496 | 104564 | struct SrcList_item *pSrc, /* Table we are trying to access */ |
| 104497 | 104565 | Bitmask notReady /* Tables in outer loops of the join */ |
| 104498 | 104566 | ){ |
| 104499 | 104567 | char aff; |
| 104500 | 104568 | if( pTerm->leftCursor!=pSrc->iCursor ) return 0; |
| 104501 | - if( pTerm->eOperator!=WO_EQ ) return 0; | |
| 104569 | + if( (pTerm->eOperator & WO_EQ)==0 ) return 0; | |
| 104502 | 104570 | if( (pTerm->prereqRight & notReady)!=0 ) return 0; |
| 104503 | 104571 | aff = pSrc->pTab->aCol[pTerm->u.leftColumn].affinity; |
| 104504 | 104572 | if( !sqlite3IndexAffinityOk(pTerm->pExpr, aff) ) return 0; |
| 104505 | 104573 | return 1; |
| 104506 | 104574 | } |
| @@ -104758,13 +104826,13 @@ | ||
| 104758 | 104826 | |
| 104759 | 104827 | /* Count the number of possible WHERE clause constraints referring |
| 104760 | 104828 | ** to this virtual table */ |
| 104761 | 104829 | for(i=nTerm=0, pTerm=pWC->a; i<pWC->nTerm; i++, pTerm++){ |
| 104762 | 104830 | if( pTerm->leftCursor != pSrc->iCursor ) continue; |
| 104763 | - assert( (pTerm->eOperator&(pTerm->eOperator-1))==0 ); | |
| 104764 | - testcase( pTerm->eOperator==WO_IN ); | |
| 104765 | - testcase( pTerm->eOperator==WO_ISNULL ); | |
| 104831 | + assert( IsPowerOfTwo(pTerm->eOperator & ~WO_EQUIV) ); | |
| 104832 | + testcase( pTerm->eOperator & WO_IN ); | |
| 104833 | + testcase( pTerm->eOperator & WO_ISNULL ); | |
| 104766 | 104834 | if( pTerm->eOperator & (WO_ISNULL) ) continue; |
| 104767 | 104835 | if( pTerm->wtFlags & TERM_VNULL ) continue; |
| 104768 | 104836 | nTerm++; |
| 104769 | 104837 | } |
| 104770 | 104838 | |
| @@ -104811,18 +104879,18 @@ | ||
| 104811 | 104879 | pUsage; |
| 104812 | 104880 | |
| 104813 | 104881 | for(i=j=0, pTerm=pWC->a; i<pWC->nTerm; i++, pTerm++){ |
| 104814 | 104882 | u8 op; |
| 104815 | 104883 | if( pTerm->leftCursor != pSrc->iCursor ) continue; |
| 104816 | - assert( (pTerm->eOperator&(pTerm->eOperator-1))==0 ); | |
| 104817 | - testcase( pTerm->eOperator==WO_IN ); | |
| 104818 | - testcase( pTerm->eOperator==WO_ISNULL ); | |
| 104884 | + assert( IsPowerOfTwo(pTerm->eOperator & ~WO_EQUIV) ); | |
| 104885 | + testcase( pTerm->eOperator & WO_IN ); | |
| 104886 | + testcase( pTerm->eOperator & WO_ISNULL ); | |
| 104819 | 104887 | if( pTerm->eOperator & (WO_ISNULL) ) continue; |
| 104820 | 104888 | if( pTerm->wtFlags & TERM_VNULL ) continue; |
| 104821 | 104889 | pIdxCons[j].iColumn = pTerm->u.leftColumn; |
| 104822 | 104890 | pIdxCons[j].iTermOffset = i; |
| 104823 | - op = (u8)pTerm->eOperator; | |
| 104891 | + op = (u8)pTerm->eOperator & WO_ALL; | |
| 104824 | 104892 | if( op==WO_IN ) op = WO_EQ; |
| 104825 | 104893 | pIdxCons[j].op = op; |
| 104826 | 104894 | /* The direct assignment in the previous line is possible only because |
| 104827 | 104895 | ** the WO_ and SQLITE_INDEX_CONSTRAINT_ codes are identical. The |
| 104828 | 104896 | ** following asserts verify this fact. */ |
| @@ -104988,11 +105056,11 @@ | ||
| 104988 | 105056 | pUsage = pIdxInfo->aConstraintUsage; |
| 104989 | 105057 | for(i=0; i<pIdxInfo->nConstraint; i++, pIdxCons++){ |
| 104990 | 105058 | j = pIdxCons->iTermOffset; |
| 104991 | 105059 | pTerm = &pWC->a[j]; |
| 104992 | 105060 | if( (pTerm->prereqRight&p->notReady)==0 |
| 104993 | - && (bAllowIN || pTerm->eOperator!=WO_IN) | |
| 105061 | + && (bAllowIN || (pTerm->eOperator & WO_IN)==0) | |
| 104994 | 105062 | ){ |
| 104995 | 105063 | pIdxCons->usable = 1; |
| 104996 | 105064 | }else{ |
| 104997 | 105065 | pIdxCons->usable = 0; |
| 104998 | 105066 | } |
| @@ -105020,11 +105088,11 @@ | ||
| 105020 | 105088 | for(i=0; i<pIdxInfo->nConstraint; i++, pIdxCons++){ |
| 105021 | 105089 | if( pUsage[i].argvIndex>0 ){ |
| 105022 | 105090 | j = pIdxCons->iTermOffset; |
| 105023 | 105091 | pTerm = &pWC->a[j]; |
| 105024 | 105092 | p->cost.used |= pTerm->prereqRight; |
| 105025 | - if( pTerm->eOperator==WO_IN && pUsage[i].omit==0 ){ | |
| 105093 | + if( (pTerm->eOperator & WO_IN)!=0 && pUsage[i].omit==0 ){ | |
| 105026 | 105094 | /* Do not attempt to use an IN constraint if the virtual table |
| 105027 | 105095 | ** says that the equivalent EQ constraint cannot be safely omitted. |
| 105028 | 105096 | ** If we do attempt to use such a constraint, some rows might be |
| 105029 | 105097 | ** repeated in the output. */ |
| 105030 | 105098 | break; |
| @@ -105326,28 +105394,28 @@ | ||
| 105326 | 105394 | u8 aff = p->pTable->aCol[p->aiColumn[0]].affinity; |
| 105327 | 105395 | |
| 105328 | 105396 | if( pLower ){ |
| 105329 | 105397 | Expr *pExpr = pLower->pExpr->pRight; |
| 105330 | 105398 | rc = valueFromExpr(pParse, pExpr, aff, &pRangeVal); |
| 105331 | - assert( pLower->eOperator==WO_GT || pLower->eOperator==WO_GE ); | |
| 105399 | + assert( (pLower->eOperator & (WO_GT|WO_GE))!=0 ); | |
| 105332 | 105400 | if( rc==SQLITE_OK |
| 105333 | 105401 | && whereKeyStats(pParse, p, pRangeVal, 0, a)==SQLITE_OK |
| 105334 | 105402 | ){ |
| 105335 | 105403 | iLower = a[0]; |
| 105336 | - if( pLower->eOperator==WO_GT ) iLower += a[1]; | |
| 105404 | + if( (pLower->eOperator & WO_GT)!=0 ) iLower += a[1]; | |
| 105337 | 105405 | } |
| 105338 | 105406 | sqlite3ValueFree(pRangeVal); |
| 105339 | 105407 | } |
| 105340 | 105408 | if( rc==SQLITE_OK && pUpper ){ |
| 105341 | 105409 | Expr *pExpr = pUpper->pExpr->pRight; |
| 105342 | 105410 | rc = valueFromExpr(pParse, pExpr, aff, &pRangeVal); |
| 105343 | - assert( pUpper->eOperator==WO_LT || pUpper->eOperator==WO_LE ); | |
| 105411 | + assert( (pUpper->eOperator & (WO_LT|WO_LE))!=0 ); | |
| 105344 | 105412 | if( rc==SQLITE_OK |
| 105345 | 105413 | && whereKeyStats(pParse, p, pRangeVal, 1, a)==SQLITE_OK |
| 105346 | 105414 | ){ |
| 105347 | 105415 | iUpper = a[0]; |
| 105348 | - if( pUpper->eOperator==WO_LE ) iUpper += a[1]; | |
| 105416 | + if( (pUpper->eOperator & WO_LE)!=0 ) iUpper += a[1]; | |
| 105349 | 105417 | } |
| 105350 | 105418 | sqlite3ValueFree(pRangeVal); |
| 105351 | 105419 | } |
| 105352 | 105420 | if( rc==SQLITE_OK ){ |
| 105353 | 105421 | if( iUpper<=iLower ){ |
| @@ -105651,16 +105719,16 @@ | ||
| 105651 | 105719 | ** if there are any X= or X IS NULL constraints in the WHERE clause. */ |
| 105652 | 105720 | pConstraint = findTerm(p->pWC, base, iColumn, p->notReady, |
| 105653 | 105721 | WO_EQ|WO_ISNULL|WO_IN, pIdx); |
| 105654 | 105722 | if( pConstraint==0 ){ |
| 105655 | 105723 | isEq = 0; |
| 105656 | - }else if( pConstraint->eOperator==WO_IN ){ | |
| 105724 | + }else if( (pConstraint->eOperator & WO_IN)!=0 ){ | |
| 105657 | 105725 | /* Constraints of the form: "X IN ..." cannot be used with an ORDER BY |
| 105658 | 105726 | ** because we do not know in what order the values on the RHS of the IN |
| 105659 | 105727 | ** operator will occur. */ |
| 105660 | 105728 | break; |
| 105661 | - }else if( pConstraint->eOperator==WO_ISNULL ){ | |
| 105729 | + }else if( (pConstraint->eOperator & WO_ISNULL)!=0 ){ | |
| 105662 | 105730 | uniqueNotNull = 0; |
| 105663 | 105731 | isEq = 1; /* "X IS NULL" means X has only a single value */ |
| 105664 | 105732 | }else if( pConstraint->prereqRight==0 ){ |
| 105665 | 105733 | isEq = 1; /* Constraint "X=constant" means X has only a single value */ |
| 105666 | 105734 | }else{ |
| @@ -106069,16 +106137,17 @@ | ||
| 106069 | 106137 | */ |
| 106070 | 106138 | if( pc.plan.nRow>(double)1 && pc.plan.nEq==1 |
| 106071 | 106139 | && pFirstTerm!=0 && aiRowEst[1]>1 ){ |
| 106072 | 106140 | assert( (pFirstTerm->eOperator & (WO_EQ|WO_ISNULL|WO_IN))!=0 ); |
| 106073 | 106141 | if( pFirstTerm->eOperator & (WO_EQ|WO_ISNULL) ){ |
| 106074 | - testcase( pFirstTerm->eOperator==WO_EQ ); | |
| 106075 | - testcase( pFirstTerm->eOperator==WO_ISNULL ); | |
| 106142 | + testcase( pFirstTerm->eOperator & WO_EQ ); | |
| 106143 | + testcase( pFirstTerm->eOperator & WO_EQUIV ); | |
| 106144 | + testcase( pFirstTerm->eOperator & WO_ISNULL ); | |
| 106076 | 106145 | whereEqualScanEst(pParse, pProbe, pFirstTerm->pExpr->pRight, |
| 106077 | 106146 | &pc.plan.nRow); |
| 106078 | 106147 | }else if( bInEst==0 ){ |
| 106079 | - assert( pFirstTerm->eOperator==WO_IN ); | |
| 106148 | + assert( pFirstTerm->eOperator & WO_IN ); | |
| 106080 | 106149 | whereInScanEst(pParse, pProbe, pFirstTerm->pExpr->x.pList, |
| 106081 | 106150 | &pc.plan.nRow); |
| 106082 | 106151 | } |
| 106083 | 106152 | } |
| 106084 | 106153 | #endif /* SQLITE_ENABLE_STAT3 */ |
| @@ -106221,11 +106290,11 @@ | ||
| 106221 | 106290 | ** more selective intentionally because of the subjective |
| 106222 | 106291 | ** observation that indexed range constraints really are more |
| 106223 | 106292 | ** selective in practice, on average. */ |
| 106224 | 106293 | pc.plan.nRow /= 3; |
| 106225 | 106294 | } |
| 106226 | - }else if( pTerm->eOperator!=WO_NOOP ){ | |
| 106295 | + }else if( (pTerm->eOperator & WO_NOOP)==0 ){ | |
| 106227 | 106296 | /* Any other expression lowers the output row count by half */ |
| 106228 | 106297 | pc.plan.nRow /= 2; |
| 106229 | 106298 | } |
| 106230 | 106299 | } |
| 106231 | 106300 | if( pc.plan.nRow<2 ) pc.plan.nRow = 2; |
| @@ -106273,12 +106342,13 @@ | ||
| 106273 | 106342 | assert( pSrc->pIndex==0 |
| 106274 | 106343 | || p->cost.plan.u.pIdx==0 |
| 106275 | 106344 | || p->cost.plan.u.pIdx==pSrc->pIndex |
| 106276 | 106345 | ); |
| 106277 | 106346 | |
| 106278 | - WHERETRACE((" best index is: %s\n", | |
| 106279 | - p->cost.plan.u.pIdx ? p->cost.plan.u.pIdx->zName : "ipk")); | |
| 106347 | + WHERETRACE((" best index is %s cost=%.1f\n", | |
| 106348 | + p->cost.plan.u.pIdx ? p->cost.plan.u.pIdx->zName : "ipk", | |
| 106349 | + p->cost.rCost)); | |
| 106280 | 106350 | |
| 106281 | 106351 | bestOrClauseIndex(p); |
| 106282 | 106352 | bestAutomaticIndex(p); |
| 106283 | 106353 | p->cost.plan.wsFlags |= eqTermMask; |
| 106284 | 106354 | } |
| @@ -106856,11 +106926,10 @@ | ||
| 106856 | 106926 | */ |
| 106857 | 106927 | iReleaseReg = sqlite3GetTempReg(pParse); |
| 106858 | 106928 | pTerm = findTerm(pWC, iCur, -1, notReady, WO_EQ|WO_IN, 0); |
| 106859 | 106929 | assert( pTerm!=0 ); |
| 106860 | 106930 | assert( pTerm->pExpr!=0 ); |
| 106861 | - assert( pTerm->leftCursor==iCur ); | |
| 106862 | 106931 | assert( omitTable==0 ); |
| 106863 | 106932 | testcase( pTerm->wtFlags & TERM_VIRTUAL ); /* EV: R-30575-11662 */ |
| 106864 | 106933 | iRowidReg = codeEqualityTerm(pParse, pTerm, pLevel, iReleaseReg); |
| 106865 | 106934 | addrNxt = pLevel->addrNxt; |
| 106866 | 106935 | sqlite3VdbeAddOp2(v, OP_MustBeInt, iRowidReg, addrNxt); |
| @@ -107247,11 +107316,11 @@ | ||
| 107247 | 107316 | int ii; /* Loop counter */ |
| 107248 | 107317 | Expr *pAndExpr = 0; /* An ".. AND (...)" expression */ |
| 107249 | 107318 | |
| 107250 | 107319 | pTerm = pLevel->plan.u.pTerm; |
| 107251 | 107320 | assert( pTerm!=0 ); |
| 107252 | - assert( pTerm->eOperator==WO_OR ); | |
| 107321 | + assert( pTerm->eOperator & WO_OR ); | |
| 107253 | 107322 | assert( (pTerm->wtFlags & TERM_ORINFO)!=0 ); |
| 107254 | 107323 | pOrWc = &pTerm->u.pOrInfo->wc; |
| 107255 | 107324 | pLevel->op = OP_Return; |
| 107256 | 107325 | pLevel->p1 = regReturn; |
| 107257 | 107326 | |
| @@ -107320,11 +107389,11 @@ | ||
| 107320 | 107389 | } |
| 107321 | 107390 | } |
| 107322 | 107391 | |
| 107323 | 107392 | for(ii=0; ii<pOrWc->nTerm; ii++){ |
| 107324 | 107393 | WhereTerm *pOrTerm = &pOrWc->a[ii]; |
| 107325 | - if( pOrTerm->leftCursor==iCur || pOrTerm->eOperator==WO_AND ){ | |
| 107394 | + if( pOrTerm->leftCursor==iCur || (pOrTerm->eOperator & WO_AND)!=0 ){ | |
| 107326 | 107395 | WhereInfo *pSubWInfo; /* Info for single OR-term scan */ |
| 107327 | 107396 | Expr *pOrExpr = pOrTerm->pExpr; |
| 107328 | 107397 | if( pAndExpr ){ |
| 107329 | 107398 | pAndExpr->pLeft = pOrExpr; |
| 107330 | 107399 | pOrExpr = pAndExpr; |
| @@ -107775,10 +107844,11 @@ | ||
| 107775 | 107844 | Index *pIdx; /* Index for FROM table at pTabItem */ |
| 107776 | 107845 | int j; /* For looping over FROM tables */ |
| 107777 | 107846 | int bestJ = -1; /* The value of j */ |
| 107778 | 107847 | Bitmask m; /* Bitmask value for j or bestJ */ |
| 107779 | 107848 | int isOptimal; /* Iterator for optimal/non-optimal search */ |
| 107849 | + int ckOptimal; /* Do the optimal scan check */ | |
| 107780 | 107850 | int nUnconstrained; /* Number tables without INDEXED BY */ |
| 107781 | 107851 | Bitmask notIndexed; /* Mask of tables that cannot use an index */ |
| 107782 | 107852 | |
| 107783 | 107853 | memset(&bestPlan, 0, sizeof(bestPlan)); |
| 107784 | 107854 | bestPlan.rCost = SQLITE_BIG_DBL; |
| @@ -107809,14 +107879,12 @@ | ||
| 107809 | 107879 | ** |
| 107810 | 107880 | ** The second loop iteration is only performed if no optimal scan |
| 107811 | 107881 | ** strategies were found by the first iteration. This second iteration |
| 107812 | 107882 | ** is used to search for the lowest cost scan overall. |
| 107813 | 107883 | ** |
| 107814 | - ** Previous versions of SQLite performed only the second iteration - | |
| 107815 | - ** the next outermost loop was always that with the lowest overall | |
| 107816 | - ** cost. However, this meant that SQLite could select the wrong plan | |
| 107817 | - ** for scripts such as the following: | |
| 107884 | + ** Without the optimal scan step (the first iteration) a suboptimal | |
| 107885 | + ** plan might be chosen for queries like this: | |
| 107818 | 107886 | ** |
| 107819 | 107887 | ** CREATE TABLE t1(a, b); |
| 107820 | 107888 | ** CREATE TABLE t2(c, d); |
| 107821 | 107889 | ** SELECT * FROM t2, t1 WHERE t2.rowid = t1.a; |
| 107822 | 107890 | ** |
| @@ -107827,20 +107895,44 @@ | ||
| 107827 | 107895 | ** algorithm may choose to use t2 for the outer loop, which is a much |
| 107828 | 107896 | ** costlier approach. |
| 107829 | 107897 | */ |
| 107830 | 107898 | nUnconstrained = 0; |
| 107831 | 107899 | notIndexed = 0; |
| 107832 | - for(isOptimal=(iFrom<nTabList-1); isOptimal>=0 && bestJ<0; isOptimal--){ | |
| 107900 | + | |
| 107901 | + /* The optimal scan check only occurs if there are two or more tables | |
| 107902 | + ** available to be reordered */ | |
| 107903 | + if( iFrom==nTabList-1 ){ | |
| 107904 | + ckOptimal = 0; /* Common case of just one table in the FROM clause */ | |
| 107905 | + }else{ | |
| 107906 | + ckOptimal = -1; | |
| 107833 | 107907 | for(j=iFrom, sWBI.pSrc=&pTabList->a[j]; j<nTabList; j++, sWBI.pSrc++){ |
| 107834 | - int doNotReorder; /* True if this table should not be reordered */ | |
| 107835 | - | |
| 107836 | - doNotReorder = (sWBI.pSrc->jointype & (JT_LEFT|JT_CROSS))!=0; | |
| 107837 | - if( j!=iFrom && doNotReorder ) break; | |
| 107838 | 107908 | m = getMask(pMaskSet, sWBI.pSrc->iCursor); |
| 107839 | 107909 | if( (m & sWBI.notValid)==0 ){ |
| 107840 | 107910 | if( j==iFrom ) iFrom++; |
| 107841 | 107911 | continue; |
| 107912 | + } | |
| 107913 | + if( j>iFrom && (sWBI.pSrc->jointype & (JT_LEFT|JT_CROSS))!=0 ) break; | |
| 107914 | + if( ++ckOptimal ) break; | |
| 107915 | + if( (sWBI.pSrc->jointype & JT_LEFT)!=0 ) break; | |
| 107916 | + } | |
| 107917 | + } | |
| 107918 | + assert( ckOptimal==0 || ckOptimal==1 ); | |
| 107919 | + | |
| 107920 | + for(isOptimal=ckOptimal; isOptimal>=0 && bestJ<0; isOptimal--){ | |
| 107921 | + for(j=iFrom, sWBI.pSrc=&pTabList->a[j]; j<nTabList; j++, sWBI.pSrc++){ | |
| 107922 | + if( j>iFrom && (sWBI.pSrc->jointype & (JT_LEFT|JT_CROSS))!=0 ){ | |
| 107923 | + /* This break and one like it in the ckOptimal computation loop | |
| 107924 | + ** above prevent table reordering across LEFT and CROSS JOINs. | |
| 107925 | + ** The LEFT JOIN case is necessary for correctness. The prohibition | |
| 107926 | + ** against reordering across a CROSS JOIN is an SQLite feature that | |
| 107927 | + ** allows the developer to control table reordering */ | |
| 107928 | + break; | |
| 107929 | + } | |
| 107930 | + m = getMask(pMaskSet, sWBI.pSrc->iCursor); | |
| 107931 | + if( (m & sWBI.notValid)==0 ){ | |
| 107932 | + assert( j>iFrom ); | |
| 107933 | + continue; | |
| 107842 | 107934 | } |
| 107843 | 107935 | sWBI.notReady = (isOptimal ? m : sWBI.notValid); |
| 107844 | 107936 | if( sWBI.pSrc->pIndex==0 ) nUnconstrained++; |
| 107845 | 107937 | |
| 107846 | 107938 | WHERETRACE((" === trying table %d (%s) with isOptimal=%d ===\n", |
| @@ -107866,12 +107958,12 @@ | ||
| 107866 | 107958 | if( isOptimal && (sWBI.cost.plan.wsFlags & WHERE_NOT_FULLSCAN)==0 ){ |
| 107867 | 107959 | notIndexed |= m; |
| 107868 | 107960 | } |
| 107869 | 107961 | if( isOptimal ){ |
| 107870 | 107962 | pWInfo->a[j].rOptCost = sWBI.cost.rCost; |
| 107871 | - }else if( iFrom<nTabList-1 ){ | |
| 107872 | - /* If two or more tables have nearly the same outer loop cost, | |
| 107963 | + }else if( ckOptimal ){ | |
| 107964 | + /* If two or more tables have nearly the same outer loop cost, but | |
| 107873 | 107965 | ** very different inner loop (optimal) cost, we want to choose |
| 107874 | 107966 | ** for the outer loop that table which benefits the least from |
| 107875 | 107967 | ** being in the inner loop. The following code scales the |
| 107876 | 107968 | ** outer loop cost estimate to accomplish that. */ |
| 107877 | 107969 | WHERETRACE((" scaling cost from %.1f to %.1f\n", |
| @@ -107912,15 +108004,23 @@ | ||
| 107912 | 108004 | sWBI.cost.rCost, sWBI.cost.plan.nRow, |
| 107913 | 108005 | sWBI.cost.plan.nOBSat, sWBI.cost.plan.wsFlags)); |
| 107914 | 108006 | bestPlan = sWBI.cost; |
| 107915 | 108007 | bestJ = j; |
| 107916 | 108008 | } |
| 107917 | - if( doNotReorder ) break; | |
| 108009 | + | |
| 108010 | + /* In a join like "w JOIN x LEFT JOIN y JOIN z" make sure that | |
| 108011 | + ** table y (and not table z) is always the next inner loop inside | |
| 108012 | + ** of table x. */ | |
| 108013 | + if( (sWBI.pSrc->jointype & JT_LEFT)!=0 ) break; | |
| 107918 | 108014 | } |
| 107919 | 108015 | } |
| 107920 | 108016 | assert( bestJ>=0 ); |
| 107921 | 108017 | assert( sWBI.notValid & getMask(pMaskSet, pTabList->a[bestJ].iCursor) ); |
| 108018 | + assert( bestJ==iFrom || (pTabList->a[iFrom].jointype & JT_LEFT)==0 ); | |
| 108019 | + testcase( bestJ>iFrom && (pTabList->a[iFrom].jointype & JT_CROSS)!=0 ); | |
| 108020 | + testcase( bestJ>iFrom && bestJ<nTabList-1 | |
| 108021 | + && (pTabList->a[bestJ+1].jointype & JT_LEFT)!=0 ); | |
| 107922 | 108022 | WHERETRACE(("*** Optimizer selects table %d (%s) for loop %d with:\n" |
| 107923 | 108023 | " cost=%.1f, nRow=%.1f, nOBSat=%d, wsFlags=0x%08x\n", |
| 107924 | 108024 | bestJ, pTabList->a[bestJ].pTab->zName, |
| 107925 | 108025 | pLevel-pWInfo->a, bestPlan.rCost, bestPlan.plan.nRow, |
| 107926 | 108026 | bestPlan.plan.nOBSat, bestPlan.plan.wsFlags)); |
| @@ -136646,11 +136746,12 @@ | ||
| 136646 | 136746 | ** would fit in a single node, use a smaller node-size. |
| 136647 | 136747 | */ |
| 136648 | 136748 | static int getNodeSize( |
| 136649 | 136749 | sqlite3 *db, /* Database handle */ |
| 136650 | 136750 | Rtree *pRtree, /* Rtree handle */ |
| 136651 | - int isCreate /* True for xCreate, false for xConnect */ | |
| 136751 | + int isCreate, /* True for xCreate, false for xConnect */ | |
| 136752 | + char **pzErr /* OUT: Error message, if any */ | |
| 136652 | 136753 | ){ |
| 136653 | 136754 | int rc; |
| 136654 | 136755 | char *zSql; |
| 136655 | 136756 | if( isCreate ){ |
| 136656 | 136757 | int iPageSize = 0; |
| @@ -136659,17 +136760,22 @@ | ||
| 136659 | 136760 | if( rc==SQLITE_OK ){ |
| 136660 | 136761 | pRtree->iNodeSize = iPageSize-64; |
| 136661 | 136762 | if( (4+pRtree->nBytesPerCell*RTREE_MAXCELLS)<pRtree->iNodeSize ){ |
| 136662 | 136763 | pRtree->iNodeSize = 4+pRtree->nBytesPerCell*RTREE_MAXCELLS; |
| 136663 | 136764 | } |
| 136765 | + }else{ | |
| 136766 | + *pzErr = sqlite3_mprintf("%s", sqlite3_errmsg(db)); | |
| 136664 | 136767 | } |
| 136665 | 136768 | }else{ |
| 136666 | 136769 | zSql = sqlite3_mprintf( |
| 136667 | 136770 | "SELECT length(data) FROM '%q'.'%q_node' WHERE nodeno = 1", |
| 136668 | 136771 | pRtree->zDb, pRtree->zName |
| 136669 | 136772 | ); |
| 136670 | 136773 | rc = getIntFromStmt(db, zSql, &pRtree->iNodeSize); |
| 136774 | + if( rc!=SQLITE_OK ){ | |
| 136775 | + *pzErr = sqlite3_mprintf("%s", sqlite3_errmsg(db)); | |
| 136776 | + } | |
| 136671 | 136777 | } |
| 136672 | 136778 | |
| 136673 | 136779 | sqlite3_free(zSql); |
| 136674 | 136780 | return rc; |
| 136675 | 136781 | } |
| @@ -136729,11 +136835,11 @@ | ||
| 136729 | 136835 | pRtree->eCoordType = eCoordType; |
| 136730 | 136836 | memcpy(pRtree->zDb, argv[1], nDb); |
| 136731 | 136837 | memcpy(pRtree->zName, argv[2], nName); |
| 136732 | 136838 | |
| 136733 | 136839 | /* Figure out the node size to use. */ |
| 136734 | - rc = getNodeSize(db, pRtree, isCreate); | |
| 136840 | + rc = getNodeSize(db, pRtree, isCreate, pzErr); | |
| 136735 | 136841 | |
| 136736 | 136842 | /* Create/Connect to the underlying relational database schema. If |
| 136737 | 136843 | ** that is successful, call sqlite3_declare_vtab() to configure |
| 136738 | 136844 | ** the r-tree table schema. |
| 136739 | 136845 | */ |
| 136740 | 136846 |
| --- src/sqlite3.c | |
| +++ src/sqlite3.c | |
| @@ -673,11 +673,11 @@ | |
| 673 | ** [sqlite3_libversion_number()], [sqlite3_sourceid()], |
| 674 | ** [sqlite_version()] and [sqlite_source_id()]. |
| 675 | */ |
| 676 | #define SQLITE_VERSION "3.7.16" |
| 677 | #define SQLITE_VERSION_NUMBER 3007016 |
| 678 | #define SQLITE_SOURCE_ID "2013-01-09 11:31:17 5774f2175ce621dfc4b6b93f7ee13fd66f3ec2b9" |
| 679 | |
| 680 | /* |
| 681 | ** CAPI3REF: Run-Time Library Version Numbers |
| 682 | ** KEYWORDS: sqlite3_version, sqlite3_sourceid |
| 683 | ** |
| @@ -8238,10 +8238,15 @@ | |
| 8238 | ** A convenience macro that returns the number of elements in |
| 8239 | ** an array. |
| 8240 | */ |
| 8241 | #define ArraySize(X) ((int)(sizeof(X)/sizeof(X[0]))) |
| 8242 | |
| 8243 | /* |
| 8244 | ** The following value as a destructor means to use sqlite3DbFree(). |
| 8245 | ** The sqlite3DbFree() routine requires two parameters instead of the |
| 8246 | ** one parameter that destructors normally want. So we have to introduce |
| 8247 | ** this magic value that the code knows to handle differently. Any |
| @@ -10042,10 +10047,11 @@ | |
| 10042 | #define SQLITE_IdxRealAsInt 0x0010 /* Store REAL as INT in indices */ |
| 10043 | #define SQLITE_DistinctOpt 0x0020 /* DISTINCT using indexes */ |
| 10044 | #define SQLITE_CoverIdxScan 0x0040 /* Covering index scans */ |
| 10045 | #define SQLITE_OrderByIdxJoin 0x0080 /* ORDER BY of joins via index */ |
| 10046 | #define SQLITE_SubqCoroutine 0x0100 /* Evaluate subqueries as coroutines */ |
| 10047 | #define SQLITE_AllOpts 0xffff /* All optimizations */ |
| 10048 | |
| 10049 | /* |
| 10050 | ** Macros for testing whether or not optimizations are enabled or disabled. |
| 10051 | */ |
| @@ -93581,11 +93587,11 @@ | |
| 93581 | sqlite3_rekey(db, zKey, i/2); |
| 93582 | } |
| 93583 | }else |
| 93584 | #endif |
| 93585 | #if defined(SQLITE_HAS_CODEC) || defined(SQLITE_ENABLE_CEROD) |
| 93586 | if( sqlite3StrICmp(zLeft, "activate_extensions")==0 ){ |
| 93587 | #ifdef SQLITE_HAS_CODEC |
| 93588 | if( sqlite3StrNICmp(zRight, "see-", 4)==0 ){ |
| 93589 | sqlite3_activate_see(&zRight[4]); |
| 93590 | } |
| 93591 | #endif |
| @@ -102802,12 +102808,12 @@ | |
| 102802 | Expr *pExpr; /* Pointer to the subexpression that is this term */ |
| 102803 | int iParent; /* Disable pWC->a[iParent] when this term disabled */ |
| 102804 | int leftCursor; /* Cursor number of X in "X <op> <expr>" */ |
| 102805 | union { |
| 102806 | int leftColumn; /* Column number of X in "X <op> <expr>" */ |
| 102807 | WhereOrInfo *pOrInfo; /* Extra information if eOperator==WO_OR */ |
| 102808 | WhereAndInfo *pAndInfo; /* Extra information if eOperator==WO_AND */ |
| 102809 | } u; |
| 102810 | u16 eOperator; /* A WO_xx value describing <op> */ |
| 102811 | u8 wtFlags; /* TERM_xxx bit flags. See below */ |
| 102812 | u8 nChild; /* Number of children that must disable us */ |
| 102813 | WhereClause *pWC; /* The clause this term is part of */ |
| @@ -102931,10 +102937,11 @@ | |
| 102931 | #define WO_GE (WO_EQ<<(TK_GE-TK_EQ)) |
| 102932 | #define WO_MATCH 0x040 |
| 102933 | #define WO_ISNULL 0x080 |
| 102934 | #define WO_OR 0x100 /* Two or more OR-connected terms */ |
| 102935 | #define WO_AND 0x200 /* Two or more AND-connected terms */ |
| 102936 | #define WO_NOOP 0x800 /* This term does not restrict search space */ |
| 102937 | |
| 102938 | #define WO_ALL 0xfff /* Mask of all possible WO_* values */ |
| 102939 | #define WO_SINGLE 0x0ff /* Mask of all non-compound WO_* values */ |
| 102940 | |
| @@ -103333,58 +103340,112 @@ | |
| 103333 | /* |
| 103334 | ** Search for a term in the WHERE clause that is of the form "X <op> <expr>" |
| 103335 | ** where X is a reference to the iColumn of table iCur and <op> is one of |
| 103336 | ** the WO_xx operator codes specified by the op parameter. |
| 103337 | ** Return a pointer to the term. Return 0 if not found. |
| 103338 | */ |
| 103339 | static WhereTerm *findTerm( |
| 103340 | WhereClause *pWC, /* The WHERE clause to be searched */ |
| 103341 | int iCur, /* Cursor number of LHS */ |
| 103342 | int iColumn, /* Column number of LHS */ |
| 103343 | Bitmask notReady, /* RHS must not overlap with this mask */ |
| 103344 | u32 op, /* Mask of WO_xx values describing operator */ |
| 103345 | Index *pIdx /* Must be compatible with this index, if not NULL */ |
| 103346 | ){ |
| 103347 | WhereTerm *pTerm; |
| 103348 | int k; |
| 103349 | assert( iCur>=0 ); |
| 103350 | op &= WO_ALL; |
| 103351 | for(; pWC; pWC=pWC->pOuter){ |
| 103352 | for(pTerm=pWC->a, k=pWC->nTerm; k; k--, pTerm++){ |
| 103353 | if( pTerm->leftCursor==iCur |
| 103354 | && (pTerm->prereqRight & notReady)==0 |
| 103355 | && pTerm->u.leftColumn==iColumn |
| 103356 | && (pTerm->eOperator & op)!=0 |
| 103357 | ){ |
| 103358 | if( iColumn>=0 && pIdx && pTerm->eOperator!=WO_ISNULL ){ |
| 103359 | Expr *pX = pTerm->pExpr; |
| 103360 | CollSeq *pColl; |
| 103361 | char idxaff; |
| 103362 | int j; |
| 103363 | Parse *pParse = pWC->pParse; |
| 103364 | |
| 103365 | idxaff = pIdx->pTable->aCol[iColumn].affinity; |
| 103366 | if( !sqlite3IndexAffinityOk(pX, idxaff) ) continue; |
| 103367 | |
| 103368 | /* Figure out the collation sequence required from an index for |
| 103369 | ** it to be useful for optimising expression pX. Store this |
| 103370 | ** value in variable pColl. |
| 103371 | */ |
| 103372 | assert(pX->pLeft); |
| 103373 | pColl = sqlite3BinaryCompareCollSeq(pParse, pX->pLeft, pX->pRight); |
| 103374 | if( pColl==0 ) pColl = pParse->db->pDfltColl; |
| 103375 | |
| 103376 | for(j=0; pIdx->aiColumn[j]!=iColumn; j++){ |
| 103377 | if( NEVER(j>=pIdx->nColumn) ) return 0; |
| 103378 | } |
| 103379 | if( sqlite3StrICmp(pColl->zName, pIdx->azColl[j]) ) continue; |
| 103380 | } |
| 103381 | return pTerm; |
| 103382 | } |
| 103383 | } |
| 103384 | } |
| 103385 | return 0; |
| 103386 | } |
| 103387 | |
| 103388 | /* Forward reference */ |
| 103389 | static void exprAnalyze(SrcList*, WhereClause*, int); |
| 103390 | |
| @@ -103658,11 +103719,10 @@ | |
| 103658 | indexable = ~(Bitmask)0; |
| 103659 | chngToIN = ~(pWC->vmask); |
| 103660 | for(i=pOrWc->nTerm-1, pOrTerm=pOrWc->a; i>=0 && indexable; i--, pOrTerm++){ |
| 103661 | if( (pOrTerm->eOperator & WO_SINGLE)==0 ){ |
| 103662 | WhereAndInfo *pAndInfo; |
| 103663 | assert( pOrTerm->eOperator==0 ); |
| 103664 | assert( (pOrTerm->wtFlags & (TERM_ANDINFO|TERM_ORINFO))==0 ); |
| 103665 | chngToIN = 0; |
| 103666 | pAndInfo = sqlite3DbMallocRaw(db, sizeof(*pAndInfo)); |
| 103667 | if( pAndInfo ){ |
| 103668 | WhereClause *pAndWC; |
| @@ -103697,11 +103757,11 @@ | |
| 103697 | if( pOrTerm->wtFlags & TERM_VIRTUAL ){ |
| 103698 | WhereTerm *pOther = &pOrWc->a[pOrTerm->iParent]; |
| 103699 | b |= getMask(pMaskSet, pOther->leftCursor); |
| 103700 | } |
| 103701 | indexable &= b; |
| 103702 | if( pOrTerm->eOperator!=WO_EQ ){ |
| 103703 | chngToIN = 0; |
| 103704 | }else{ |
| 103705 | chngToIN &= b; |
| 103706 | } |
| 103707 | } |
| @@ -103748,11 +103808,11 @@ | |
| 103748 | ** and column is found but leave okToChngToIN false if not found. |
| 103749 | */ |
| 103750 | for(j=0; j<2 && !okToChngToIN; j++){ |
| 103751 | pOrTerm = pOrWc->a; |
| 103752 | for(i=pOrWc->nTerm-1; i>=0; i--, pOrTerm++){ |
| 103753 | assert( pOrTerm->eOperator==WO_EQ ); |
| 103754 | pOrTerm->wtFlags &= ~TERM_OR_OK; |
| 103755 | if( pOrTerm->leftCursor==iCursor ){ |
| 103756 | /* This is the 2-bit case and we are on the second iteration and |
| 103757 | ** current term is from the first iteration. So skip this term. */ |
| 103758 | assert( j==1 ); |
| @@ -103774,21 +103834,21 @@ | |
| 103774 | } |
| 103775 | if( i<0 ){ |
| 103776 | /* No candidate table+column was found. This can only occur |
| 103777 | ** on the second iteration */ |
| 103778 | assert( j==1 ); |
| 103779 | assert( (chngToIN&(chngToIN-1))==0 ); |
| 103780 | assert( chngToIN==getMask(pMaskSet, iCursor) ); |
| 103781 | break; |
| 103782 | } |
| 103783 | testcase( j==1 ); |
| 103784 | |
| 103785 | /* We have found a candidate table and column. Check to see if that |
| 103786 | ** table and column is common to every term in the OR clause */ |
| 103787 | okToChngToIN = 1; |
| 103788 | for(; i>=0 && okToChngToIN; i--, pOrTerm++){ |
| 103789 | assert( pOrTerm->eOperator==WO_EQ ); |
| 103790 | if( pOrTerm->leftCursor!=iCursor ){ |
| 103791 | pOrTerm->wtFlags &= ~TERM_OR_OK; |
| 103792 | }else if( pOrTerm->u.leftColumn!=iColumn ){ |
| 103793 | okToChngToIN = 0; |
| 103794 | }else{ |
| @@ -103820,11 +103880,11 @@ | |
| 103820 | Expr *pLeft = 0; /* The LHS of the IN operator */ |
| 103821 | Expr *pNew; /* The complete IN operator */ |
| 103822 | |
| 103823 | for(i=pOrWc->nTerm-1, pOrTerm=pOrWc->a; i>=0; i--, pOrTerm++){ |
| 103824 | if( (pOrTerm->wtFlags & TERM_OR_OK)==0 ) continue; |
| 103825 | assert( pOrTerm->eOperator==WO_EQ ); |
| 103826 | assert( pOrTerm->leftCursor==iCursor ); |
| 103827 | assert( pOrTerm->u.leftColumn==iColumn ); |
| 103828 | pDup = sqlite3ExprDup(db, pOrTerm->pExpr->pRight, 0); |
| 103829 | pList = sqlite3ExprListAppend(pWC->pParse, pList, pDup); |
| 103830 | pLeft = pOrTerm->pExpr->pLeft; |
| @@ -103849,11 +103909,10 @@ | |
| 103849 | pTerm->eOperator = WO_NOOP; /* case 1 trumps case 2 */ |
| 103850 | } |
| 103851 | } |
| 103852 | } |
| 103853 | #endif /* !SQLITE_OMIT_OR_OPTIMIZATION && !SQLITE_OMIT_SUBQUERY */ |
| 103854 | |
| 103855 | |
| 103856 | /* |
| 103857 | ** The input to this routine is an WhereTerm structure with only the |
| 103858 | ** "pExpr" field filled in. The job of this routine is to analyze the |
| 103859 | ** subexpression and populate all the other fields of the WhereTerm |
| @@ -103919,21 +103978,23 @@ | |
| 103919 | } |
| 103920 | pTerm->prereqAll = prereqAll; |
| 103921 | pTerm->leftCursor = -1; |
| 103922 | pTerm->iParent = -1; |
| 103923 | pTerm->eOperator = 0; |
| 103924 | if( allowedOp(op) && (pTerm->prereqRight & prereqLeft)==0 ){ |
| 103925 | Expr *pLeft = sqlite3ExprSkipCollate(pExpr->pLeft); |
| 103926 | Expr *pRight = sqlite3ExprSkipCollate(pExpr->pRight); |
| 103927 | if( pLeft->op==TK_COLUMN ){ |
| 103928 | pTerm->leftCursor = pLeft->iTable; |
| 103929 | pTerm->u.leftColumn = pLeft->iColumn; |
| 103930 | pTerm->eOperator = operatorMask(op); |
| 103931 | } |
| 103932 | if( pRight && pRight->op==TK_COLUMN ){ |
| 103933 | WhereTerm *pNew; |
| 103934 | Expr *pDup; |
| 103935 | if( pTerm->leftCursor>=0 ){ |
| 103936 | int idxNew; |
| 103937 | pDup = sqlite3ExprDup(db, pExpr, 0); |
| 103938 | if( db->mallocFailed ){ |
| 103939 | sqlite3ExprDelete(db, pDup); |
| @@ -103944,10 +104005,17 @@ | |
| 103944 | pNew = &pWC->a[idxNew]; |
| 103945 | pNew->iParent = idxTerm; |
| 103946 | pTerm = &pWC->a[idxTerm]; |
| 103947 | pTerm->nChild = 1; |
| 103948 | pTerm->wtFlags |= TERM_COPIED; |
| 103949 | }else{ |
| 103950 | pDup = pExpr; |
| 103951 | pNew = pTerm; |
| 103952 | } |
| 103953 | exprCommute(pParse, pDup); |
| @@ -103955,11 +104023,11 @@ | |
| 103955 | pNew->leftCursor = pLeft->iTable; |
| 103956 | pNew->u.leftColumn = pLeft->iColumn; |
| 103957 | testcase( (prereqLeft | extraRight) != prereqLeft ); |
| 103958 | pNew->prereqRight = prereqLeft | extraRight; |
| 103959 | pNew->prereqAll = prereqAll; |
| 103960 | pNew->eOperator = operatorMask(pDup->op); |
| 103961 | } |
| 103962 | } |
| 103963 | |
| 103964 | #ifndef SQLITE_OMIT_BETWEEN_OPTIMIZATION |
| 103965 | /* If a term is the BETWEEN operator, create two new virtual terms |
| @@ -104414,11 +104482,11 @@ | |
| 104414 | return; |
| 104415 | } |
| 104416 | |
| 104417 | /* Search the WHERE clause terms for a usable WO_OR term. */ |
| 104418 | for(pTerm=pWC->a; pTerm<pWCEnd; pTerm++){ |
| 104419 | if( pTerm->eOperator==WO_OR |
| 104420 | && ((pTerm->prereqAll & ~maskSrc) & p->notReady)==0 |
| 104421 | && (pTerm->u.pOrInfo->indexable & maskSrc)!=0 |
| 104422 | ){ |
| 104423 | WhereClause * const pOrWC = &pTerm->u.pOrInfo->wc; |
| 104424 | WhereTerm * const pOrWCEnd = &pOrWC->a[pOrWC->nTerm]; |
| @@ -104435,11 +104503,11 @@ | |
| 104435 | sBOI.ppIdxInfo = 0; |
| 104436 | for(pOrTerm=pOrWC->a; pOrTerm<pOrWCEnd; pOrTerm++){ |
| 104437 | WHERETRACE(("... Multi-index OR testing for term %d of %d....\n", |
| 104438 | (pOrTerm - pOrWC->a), (pTerm - pWC->a) |
| 104439 | )); |
| 104440 | if( pOrTerm->eOperator==WO_AND ){ |
| 104441 | sBOI.pWC = &pOrTerm->u.pAndInfo->wc; |
| 104442 | bestIndex(&sBOI); |
| 104443 | }else if( pOrTerm->leftCursor==iCur ){ |
| 104444 | WhereClause tempWC; |
| 104445 | tempWC.pParse = pWC->pParse; |
| @@ -104496,11 +104564,11 @@ | |
| 104496 | struct SrcList_item *pSrc, /* Table we are trying to access */ |
| 104497 | Bitmask notReady /* Tables in outer loops of the join */ |
| 104498 | ){ |
| 104499 | char aff; |
| 104500 | if( pTerm->leftCursor!=pSrc->iCursor ) return 0; |
| 104501 | if( pTerm->eOperator!=WO_EQ ) return 0; |
| 104502 | if( (pTerm->prereqRight & notReady)!=0 ) return 0; |
| 104503 | aff = pSrc->pTab->aCol[pTerm->u.leftColumn].affinity; |
| 104504 | if( !sqlite3IndexAffinityOk(pTerm->pExpr, aff) ) return 0; |
| 104505 | return 1; |
| 104506 | } |
| @@ -104758,13 +104826,13 @@ | |
| 104758 | |
| 104759 | /* Count the number of possible WHERE clause constraints referring |
| 104760 | ** to this virtual table */ |
| 104761 | for(i=nTerm=0, pTerm=pWC->a; i<pWC->nTerm; i++, pTerm++){ |
| 104762 | if( pTerm->leftCursor != pSrc->iCursor ) continue; |
| 104763 | assert( (pTerm->eOperator&(pTerm->eOperator-1))==0 ); |
| 104764 | testcase( pTerm->eOperator==WO_IN ); |
| 104765 | testcase( pTerm->eOperator==WO_ISNULL ); |
| 104766 | if( pTerm->eOperator & (WO_ISNULL) ) continue; |
| 104767 | if( pTerm->wtFlags & TERM_VNULL ) continue; |
| 104768 | nTerm++; |
| 104769 | } |
| 104770 | |
| @@ -104811,18 +104879,18 @@ | |
| 104811 | pUsage; |
| 104812 | |
| 104813 | for(i=j=0, pTerm=pWC->a; i<pWC->nTerm; i++, pTerm++){ |
| 104814 | u8 op; |
| 104815 | if( pTerm->leftCursor != pSrc->iCursor ) continue; |
| 104816 | assert( (pTerm->eOperator&(pTerm->eOperator-1))==0 ); |
| 104817 | testcase( pTerm->eOperator==WO_IN ); |
| 104818 | testcase( pTerm->eOperator==WO_ISNULL ); |
| 104819 | if( pTerm->eOperator & (WO_ISNULL) ) continue; |
| 104820 | if( pTerm->wtFlags & TERM_VNULL ) continue; |
| 104821 | pIdxCons[j].iColumn = pTerm->u.leftColumn; |
| 104822 | pIdxCons[j].iTermOffset = i; |
| 104823 | op = (u8)pTerm->eOperator; |
| 104824 | if( op==WO_IN ) op = WO_EQ; |
| 104825 | pIdxCons[j].op = op; |
| 104826 | /* The direct assignment in the previous line is possible only because |
| 104827 | ** the WO_ and SQLITE_INDEX_CONSTRAINT_ codes are identical. The |
| 104828 | ** following asserts verify this fact. */ |
| @@ -104988,11 +105056,11 @@ | |
| 104988 | pUsage = pIdxInfo->aConstraintUsage; |
| 104989 | for(i=0; i<pIdxInfo->nConstraint; i++, pIdxCons++){ |
| 104990 | j = pIdxCons->iTermOffset; |
| 104991 | pTerm = &pWC->a[j]; |
| 104992 | if( (pTerm->prereqRight&p->notReady)==0 |
| 104993 | && (bAllowIN || pTerm->eOperator!=WO_IN) |
| 104994 | ){ |
| 104995 | pIdxCons->usable = 1; |
| 104996 | }else{ |
| 104997 | pIdxCons->usable = 0; |
| 104998 | } |
| @@ -105020,11 +105088,11 @@ | |
| 105020 | for(i=0; i<pIdxInfo->nConstraint; i++, pIdxCons++){ |
| 105021 | if( pUsage[i].argvIndex>0 ){ |
| 105022 | j = pIdxCons->iTermOffset; |
| 105023 | pTerm = &pWC->a[j]; |
| 105024 | p->cost.used |= pTerm->prereqRight; |
| 105025 | if( pTerm->eOperator==WO_IN && pUsage[i].omit==0 ){ |
| 105026 | /* Do not attempt to use an IN constraint if the virtual table |
| 105027 | ** says that the equivalent EQ constraint cannot be safely omitted. |
| 105028 | ** If we do attempt to use such a constraint, some rows might be |
| 105029 | ** repeated in the output. */ |
| 105030 | break; |
| @@ -105326,28 +105394,28 @@ | |
| 105326 | u8 aff = p->pTable->aCol[p->aiColumn[0]].affinity; |
| 105327 | |
| 105328 | if( pLower ){ |
| 105329 | Expr *pExpr = pLower->pExpr->pRight; |
| 105330 | rc = valueFromExpr(pParse, pExpr, aff, &pRangeVal); |
| 105331 | assert( pLower->eOperator==WO_GT || pLower->eOperator==WO_GE ); |
| 105332 | if( rc==SQLITE_OK |
| 105333 | && whereKeyStats(pParse, p, pRangeVal, 0, a)==SQLITE_OK |
| 105334 | ){ |
| 105335 | iLower = a[0]; |
| 105336 | if( pLower->eOperator==WO_GT ) iLower += a[1]; |
| 105337 | } |
| 105338 | sqlite3ValueFree(pRangeVal); |
| 105339 | } |
| 105340 | if( rc==SQLITE_OK && pUpper ){ |
| 105341 | Expr *pExpr = pUpper->pExpr->pRight; |
| 105342 | rc = valueFromExpr(pParse, pExpr, aff, &pRangeVal); |
| 105343 | assert( pUpper->eOperator==WO_LT || pUpper->eOperator==WO_LE ); |
| 105344 | if( rc==SQLITE_OK |
| 105345 | && whereKeyStats(pParse, p, pRangeVal, 1, a)==SQLITE_OK |
| 105346 | ){ |
| 105347 | iUpper = a[0]; |
| 105348 | if( pUpper->eOperator==WO_LE ) iUpper += a[1]; |
| 105349 | } |
| 105350 | sqlite3ValueFree(pRangeVal); |
| 105351 | } |
| 105352 | if( rc==SQLITE_OK ){ |
| 105353 | if( iUpper<=iLower ){ |
| @@ -105651,16 +105719,16 @@ | |
| 105651 | ** if there are any X= or X IS NULL constraints in the WHERE clause. */ |
| 105652 | pConstraint = findTerm(p->pWC, base, iColumn, p->notReady, |
| 105653 | WO_EQ|WO_ISNULL|WO_IN, pIdx); |
| 105654 | if( pConstraint==0 ){ |
| 105655 | isEq = 0; |
| 105656 | }else if( pConstraint->eOperator==WO_IN ){ |
| 105657 | /* Constraints of the form: "X IN ..." cannot be used with an ORDER BY |
| 105658 | ** because we do not know in what order the values on the RHS of the IN |
| 105659 | ** operator will occur. */ |
| 105660 | break; |
| 105661 | }else if( pConstraint->eOperator==WO_ISNULL ){ |
| 105662 | uniqueNotNull = 0; |
| 105663 | isEq = 1; /* "X IS NULL" means X has only a single value */ |
| 105664 | }else if( pConstraint->prereqRight==0 ){ |
| 105665 | isEq = 1; /* Constraint "X=constant" means X has only a single value */ |
| 105666 | }else{ |
| @@ -106069,16 +106137,17 @@ | |
| 106069 | */ |
| 106070 | if( pc.plan.nRow>(double)1 && pc.plan.nEq==1 |
| 106071 | && pFirstTerm!=0 && aiRowEst[1]>1 ){ |
| 106072 | assert( (pFirstTerm->eOperator & (WO_EQ|WO_ISNULL|WO_IN))!=0 ); |
| 106073 | if( pFirstTerm->eOperator & (WO_EQ|WO_ISNULL) ){ |
| 106074 | testcase( pFirstTerm->eOperator==WO_EQ ); |
| 106075 | testcase( pFirstTerm->eOperator==WO_ISNULL ); |
| 106076 | whereEqualScanEst(pParse, pProbe, pFirstTerm->pExpr->pRight, |
| 106077 | &pc.plan.nRow); |
| 106078 | }else if( bInEst==0 ){ |
| 106079 | assert( pFirstTerm->eOperator==WO_IN ); |
| 106080 | whereInScanEst(pParse, pProbe, pFirstTerm->pExpr->x.pList, |
| 106081 | &pc.plan.nRow); |
| 106082 | } |
| 106083 | } |
| 106084 | #endif /* SQLITE_ENABLE_STAT3 */ |
| @@ -106221,11 +106290,11 @@ | |
| 106221 | ** more selective intentionally because of the subjective |
| 106222 | ** observation that indexed range constraints really are more |
| 106223 | ** selective in practice, on average. */ |
| 106224 | pc.plan.nRow /= 3; |
| 106225 | } |
| 106226 | }else if( pTerm->eOperator!=WO_NOOP ){ |
| 106227 | /* Any other expression lowers the output row count by half */ |
| 106228 | pc.plan.nRow /= 2; |
| 106229 | } |
| 106230 | } |
| 106231 | if( pc.plan.nRow<2 ) pc.plan.nRow = 2; |
| @@ -106273,12 +106342,13 @@ | |
| 106273 | assert( pSrc->pIndex==0 |
| 106274 | || p->cost.plan.u.pIdx==0 |
| 106275 | || p->cost.plan.u.pIdx==pSrc->pIndex |
| 106276 | ); |
| 106277 | |
| 106278 | WHERETRACE((" best index is: %s\n", |
| 106279 | p->cost.plan.u.pIdx ? p->cost.plan.u.pIdx->zName : "ipk")); |
| 106280 | |
| 106281 | bestOrClauseIndex(p); |
| 106282 | bestAutomaticIndex(p); |
| 106283 | p->cost.plan.wsFlags |= eqTermMask; |
| 106284 | } |
| @@ -106856,11 +106926,10 @@ | |
| 106856 | */ |
| 106857 | iReleaseReg = sqlite3GetTempReg(pParse); |
| 106858 | pTerm = findTerm(pWC, iCur, -1, notReady, WO_EQ|WO_IN, 0); |
| 106859 | assert( pTerm!=0 ); |
| 106860 | assert( pTerm->pExpr!=0 ); |
| 106861 | assert( pTerm->leftCursor==iCur ); |
| 106862 | assert( omitTable==0 ); |
| 106863 | testcase( pTerm->wtFlags & TERM_VIRTUAL ); /* EV: R-30575-11662 */ |
| 106864 | iRowidReg = codeEqualityTerm(pParse, pTerm, pLevel, iReleaseReg); |
| 106865 | addrNxt = pLevel->addrNxt; |
| 106866 | sqlite3VdbeAddOp2(v, OP_MustBeInt, iRowidReg, addrNxt); |
| @@ -107247,11 +107316,11 @@ | |
| 107247 | int ii; /* Loop counter */ |
| 107248 | Expr *pAndExpr = 0; /* An ".. AND (...)" expression */ |
| 107249 | |
| 107250 | pTerm = pLevel->plan.u.pTerm; |
| 107251 | assert( pTerm!=0 ); |
| 107252 | assert( pTerm->eOperator==WO_OR ); |
| 107253 | assert( (pTerm->wtFlags & TERM_ORINFO)!=0 ); |
| 107254 | pOrWc = &pTerm->u.pOrInfo->wc; |
| 107255 | pLevel->op = OP_Return; |
| 107256 | pLevel->p1 = regReturn; |
| 107257 | |
| @@ -107320,11 +107389,11 @@ | |
| 107320 | } |
| 107321 | } |
| 107322 | |
| 107323 | for(ii=0; ii<pOrWc->nTerm; ii++){ |
| 107324 | WhereTerm *pOrTerm = &pOrWc->a[ii]; |
| 107325 | if( pOrTerm->leftCursor==iCur || pOrTerm->eOperator==WO_AND ){ |
| 107326 | WhereInfo *pSubWInfo; /* Info for single OR-term scan */ |
| 107327 | Expr *pOrExpr = pOrTerm->pExpr; |
| 107328 | if( pAndExpr ){ |
| 107329 | pAndExpr->pLeft = pOrExpr; |
| 107330 | pOrExpr = pAndExpr; |
| @@ -107775,10 +107844,11 @@ | |
| 107775 | Index *pIdx; /* Index for FROM table at pTabItem */ |
| 107776 | int j; /* For looping over FROM tables */ |
| 107777 | int bestJ = -1; /* The value of j */ |
| 107778 | Bitmask m; /* Bitmask value for j or bestJ */ |
| 107779 | int isOptimal; /* Iterator for optimal/non-optimal search */ |
| 107780 | int nUnconstrained; /* Number tables without INDEXED BY */ |
| 107781 | Bitmask notIndexed; /* Mask of tables that cannot use an index */ |
| 107782 | |
| 107783 | memset(&bestPlan, 0, sizeof(bestPlan)); |
| 107784 | bestPlan.rCost = SQLITE_BIG_DBL; |
| @@ -107809,14 +107879,12 @@ | |
| 107809 | ** |
| 107810 | ** The second loop iteration is only performed if no optimal scan |
| 107811 | ** strategies were found by the first iteration. This second iteration |
| 107812 | ** is used to search for the lowest cost scan overall. |
| 107813 | ** |
| 107814 | ** Previous versions of SQLite performed only the second iteration - |
| 107815 | ** the next outermost loop was always that with the lowest overall |
| 107816 | ** cost. However, this meant that SQLite could select the wrong plan |
| 107817 | ** for scripts such as the following: |
| 107818 | ** |
| 107819 | ** CREATE TABLE t1(a, b); |
| 107820 | ** CREATE TABLE t2(c, d); |
| 107821 | ** SELECT * FROM t2, t1 WHERE t2.rowid = t1.a; |
| 107822 | ** |
| @@ -107827,20 +107895,44 @@ | |
| 107827 | ** algorithm may choose to use t2 for the outer loop, which is a much |
| 107828 | ** costlier approach. |
| 107829 | */ |
| 107830 | nUnconstrained = 0; |
| 107831 | notIndexed = 0; |
| 107832 | for(isOptimal=(iFrom<nTabList-1); isOptimal>=0 && bestJ<0; isOptimal--){ |
| 107833 | for(j=iFrom, sWBI.pSrc=&pTabList->a[j]; j<nTabList; j++, sWBI.pSrc++){ |
| 107834 | int doNotReorder; /* True if this table should not be reordered */ |
| 107835 | |
| 107836 | doNotReorder = (sWBI.pSrc->jointype & (JT_LEFT|JT_CROSS))!=0; |
| 107837 | if( j!=iFrom && doNotReorder ) break; |
| 107838 | m = getMask(pMaskSet, sWBI.pSrc->iCursor); |
| 107839 | if( (m & sWBI.notValid)==0 ){ |
| 107840 | if( j==iFrom ) iFrom++; |
| 107841 | continue; |
| 107842 | } |
| 107843 | sWBI.notReady = (isOptimal ? m : sWBI.notValid); |
| 107844 | if( sWBI.pSrc->pIndex==0 ) nUnconstrained++; |
| 107845 | |
| 107846 | WHERETRACE((" === trying table %d (%s) with isOptimal=%d ===\n", |
| @@ -107866,12 +107958,12 @@ | |
| 107866 | if( isOptimal && (sWBI.cost.plan.wsFlags & WHERE_NOT_FULLSCAN)==0 ){ |
| 107867 | notIndexed |= m; |
| 107868 | } |
| 107869 | if( isOptimal ){ |
| 107870 | pWInfo->a[j].rOptCost = sWBI.cost.rCost; |
| 107871 | }else if( iFrom<nTabList-1 ){ |
| 107872 | /* If two or more tables have nearly the same outer loop cost, |
| 107873 | ** very different inner loop (optimal) cost, we want to choose |
| 107874 | ** for the outer loop that table which benefits the least from |
| 107875 | ** being in the inner loop. The following code scales the |
| 107876 | ** outer loop cost estimate to accomplish that. */ |
| 107877 | WHERETRACE((" scaling cost from %.1f to %.1f\n", |
| @@ -107912,15 +108004,23 @@ | |
| 107912 | sWBI.cost.rCost, sWBI.cost.plan.nRow, |
| 107913 | sWBI.cost.plan.nOBSat, sWBI.cost.plan.wsFlags)); |
| 107914 | bestPlan = sWBI.cost; |
| 107915 | bestJ = j; |
| 107916 | } |
| 107917 | if( doNotReorder ) break; |
| 107918 | } |
| 107919 | } |
| 107920 | assert( bestJ>=0 ); |
| 107921 | assert( sWBI.notValid & getMask(pMaskSet, pTabList->a[bestJ].iCursor) ); |
| 107922 | WHERETRACE(("*** Optimizer selects table %d (%s) for loop %d with:\n" |
| 107923 | " cost=%.1f, nRow=%.1f, nOBSat=%d, wsFlags=0x%08x\n", |
| 107924 | bestJ, pTabList->a[bestJ].pTab->zName, |
| 107925 | pLevel-pWInfo->a, bestPlan.rCost, bestPlan.plan.nRow, |
| 107926 | bestPlan.plan.nOBSat, bestPlan.plan.wsFlags)); |
| @@ -136646,11 +136746,12 @@ | |
| 136646 | ** would fit in a single node, use a smaller node-size. |
| 136647 | */ |
| 136648 | static int getNodeSize( |
| 136649 | sqlite3 *db, /* Database handle */ |
| 136650 | Rtree *pRtree, /* Rtree handle */ |
| 136651 | int isCreate /* True for xCreate, false for xConnect */ |
| 136652 | ){ |
| 136653 | int rc; |
| 136654 | char *zSql; |
| 136655 | if( isCreate ){ |
| 136656 | int iPageSize = 0; |
| @@ -136659,17 +136760,22 @@ | |
| 136659 | if( rc==SQLITE_OK ){ |
| 136660 | pRtree->iNodeSize = iPageSize-64; |
| 136661 | if( (4+pRtree->nBytesPerCell*RTREE_MAXCELLS)<pRtree->iNodeSize ){ |
| 136662 | pRtree->iNodeSize = 4+pRtree->nBytesPerCell*RTREE_MAXCELLS; |
| 136663 | } |
| 136664 | } |
| 136665 | }else{ |
| 136666 | zSql = sqlite3_mprintf( |
| 136667 | "SELECT length(data) FROM '%q'.'%q_node' WHERE nodeno = 1", |
| 136668 | pRtree->zDb, pRtree->zName |
| 136669 | ); |
| 136670 | rc = getIntFromStmt(db, zSql, &pRtree->iNodeSize); |
| 136671 | } |
| 136672 | |
| 136673 | sqlite3_free(zSql); |
| 136674 | return rc; |
| 136675 | } |
| @@ -136729,11 +136835,11 @@ | |
| 136729 | pRtree->eCoordType = eCoordType; |
| 136730 | memcpy(pRtree->zDb, argv[1], nDb); |
| 136731 | memcpy(pRtree->zName, argv[2], nName); |
| 136732 | |
| 136733 | /* Figure out the node size to use. */ |
| 136734 | rc = getNodeSize(db, pRtree, isCreate); |
| 136735 | |
| 136736 | /* Create/Connect to the underlying relational database schema. If |
| 136737 | ** that is successful, call sqlite3_declare_vtab() to configure |
| 136738 | ** the r-tree table schema. |
| 136739 | */ |
| 136740 |
| --- src/sqlite3.c | |
| +++ src/sqlite3.c | |
| @@ -673,11 +673,11 @@ | |
| 673 | ** [sqlite3_libversion_number()], [sqlite3_sourceid()], |
| 674 | ** [sqlite_version()] and [sqlite_source_id()]. |
| 675 | */ |
| 676 | #define SQLITE_VERSION "3.7.16" |
| 677 | #define SQLITE_VERSION_NUMBER 3007016 |
| 678 | #define SQLITE_SOURCE_ID "2013-01-17 17:20:49 38852f158ab20bb4d7b264af987ec1538052bec3" |
| 679 | |
| 680 | /* |
| 681 | ** CAPI3REF: Run-Time Library Version Numbers |
| 682 | ** KEYWORDS: sqlite3_version, sqlite3_sourceid |
| 683 | ** |
| @@ -8238,10 +8238,15 @@ | |
| 8238 | ** A convenience macro that returns the number of elements in |
| 8239 | ** an array. |
| 8240 | */ |
| 8241 | #define ArraySize(X) ((int)(sizeof(X)/sizeof(X[0]))) |
| 8242 | |
| 8243 | /* |
| 8244 | ** Determine if the argument is a power of two |
| 8245 | */ |
| 8246 | #define IsPowerOfTwo(X) (((X)&((X)-1))==0) |
| 8247 | |
| 8248 | /* |
| 8249 | ** The following value as a destructor means to use sqlite3DbFree(). |
| 8250 | ** The sqlite3DbFree() routine requires two parameters instead of the |
| 8251 | ** one parameter that destructors normally want. So we have to introduce |
| 8252 | ** this magic value that the code knows to handle differently. Any |
| @@ -10042,10 +10047,11 @@ | |
| 10047 | #define SQLITE_IdxRealAsInt 0x0010 /* Store REAL as INT in indices */ |
| 10048 | #define SQLITE_DistinctOpt 0x0020 /* DISTINCT using indexes */ |
| 10049 | #define SQLITE_CoverIdxScan 0x0040 /* Covering index scans */ |
| 10050 | #define SQLITE_OrderByIdxJoin 0x0080 /* ORDER BY of joins via index */ |
| 10051 | #define SQLITE_SubqCoroutine 0x0100 /* Evaluate subqueries as coroutines */ |
| 10052 | #define SQLITE_Transitive 0x0200 /* Transitive constraints */ |
| 10053 | #define SQLITE_AllOpts 0xffff /* All optimizations */ |
| 10054 | |
| 10055 | /* |
| 10056 | ** Macros for testing whether or not optimizations are enabled or disabled. |
| 10057 | */ |
| @@ -93581,11 +93587,11 @@ | |
| 93587 | sqlite3_rekey(db, zKey, i/2); |
| 93588 | } |
| 93589 | }else |
| 93590 | #endif |
| 93591 | #if defined(SQLITE_HAS_CODEC) || defined(SQLITE_ENABLE_CEROD) |
| 93592 | if( sqlite3StrICmp(zLeft, "activate_extensions")==0 && zRight ){ |
| 93593 | #ifdef SQLITE_HAS_CODEC |
| 93594 | if( sqlite3StrNICmp(zRight, "see-", 4)==0 ){ |
| 93595 | sqlite3_activate_see(&zRight[4]); |
| 93596 | } |
| 93597 | #endif |
| @@ -102802,12 +102808,12 @@ | |
| 102808 | Expr *pExpr; /* Pointer to the subexpression that is this term */ |
| 102809 | int iParent; /* Disable pWC->a[iParent] when this term disabled */ |
| 102810 | int leftCursor; /* Cursor number of X in "X <op> <expr>" */ |
| 102811 | union { |
| 102812 | int leftColumn; /* Column number of X in "X <op> <expr>" */ |
| 102813 | WhereOrInfo *pOrInfo; /* Extra information if (eOperator & WO_OR)!=0 */ |
| 102814 | WhereAndInfo *pAndInfo; /* Extra information if (eOperator& WO_AND)!=0 */ |
| 102815 | } u; |
| 102816 | u16 eOperator; /* A WO_xx value describing <op> */ |
| 102817 | u8 wtFlags; /* TERM_xxx bit flags. See below */ |
| 102818 | u8 nChild; /* Number of children that must disable us */ |
| 102819 | WhereClause *pWC; /* The clause this term is part of */ |
| @@ -102931,10 +102937,11 @@ | |
| 102937 | #define WO_GE (WO_EQ<<(TK_GE-TK_EQ)) |
| 102938 | #define WO_MATCH 0x040 |
| 102939 | #define WO_ISNULL 0x080 |
| 102940 | #define WO_OR 0x100 /* Two or more OR-connected terms */ |
| 102941 | #define WO_AND 0x200 /* Two or more AND-connected terms */ |
| 102942 | #define WO_EQUIV 0x400 /* Of the form A==B, both columns */ |
| 102943 | #define WO_NOOP 0x800 /* This term does not restrict search space */ |
| 102944 | |
| 102945 | #define WO_ALL 0xfff /* Mask of all possible WO_* values */ |
| 102946 | #define WO_SINGLE 0x0ff /* Mask of all non-compound WO_* values */ |
| 102947 | |
| @@ -103333,58 +103340,112 @@ | |
| 103340 | /* |
| 103341 | ** Search for a term in the WHERE clause that is of the form "X <op> <expr>" |
| 103342 | ** where X is a reference to the iColumn of table iCur and <op> is one of |
| 103343 | ** the WO_xx operator codes specified by the op parameter. |
| 103344 | ** Return a pointer to the term. Return 0 if not found. |
| 103345 | ** |
| 103346 | ** The term returned might by Y=<expr> if there is another constraint in |
| 103347 | ** the WHERE clause that specifies that X=Y. Any such constraints will be |
| 103348 | ** identified by the WO_EQUIV bit in the pTerm->eOperator field. The |
| 103349 | ** aEquiv[] array holds X and all its equivalents, with each SQL variable |
| 103350 | ** taking up two slots in aEquiv[]. The first slot is for the cursor number |
| 103351 | ** and the second is for the column number. There are 22 slots in aEquiv[] |
| 103352 | ** so that means we can look for X plus up to 10 other equivalent values. |
| 103353 | ** Hence a search for X will return <expr> if X=A1 and A1=A2 and A2=A3 |
| 103354 | ** and ... and A9=A10 and A10=<expr>. |
| 103355 | ** |
| 103356 | ** If there are multiple terms in the WHERE clause of the form "X <op> <expr>" |
| 103357 | ** then try for the one with no dependencies on <expr> - in other words where |
| 103358 | ** <expr> is a constant expression of some kind. Only return entries of |
| 103359 | ** the form "X <op> Y" where Y is a column in another table if no terms of |
| 103360 | ** the form "X <op> <const-expr>" exist. Other than this priority, if there |
| 103361 | ** are two or more terms that match, then the choice of which term to return |
| 103362 | ** is arbitrary. |
| 103363 | */ |
| 103364 | static WhereTerm *findTerm( |
| 103365 | WhereClause *pWC, /* The WHERE clause to be searched */ |
| 103366 | int iCur, /* Cursor number of LHS */ |
| 103367 | int iColumn, /* Column number of LHS */ |
| 103368 | Bitmask notReady, /* RHS must not overlap with this mask */ |
| 103369 | u32 op, /* Mask of WO_xx values describing operator */ |
| 103370 | Index *pIdx /* Must be compatible with this index, if not NULL */ |
| 103371 | ){ |
| 103372 | WhereTerm *pTerm; /* Term being examined as possible result */ |
| 103373 | WhereTerm *pResult = 0; /* The answer to return */ |
| 103374 | WhereClause *pWCOrig = pWC; /* Original pWC value */ |
| 103375 | int j, k; /* Loop counters */ |
| 103376 | Expr *pX; /* Pointer to an expression */ |
| 103377 | Parse *pParse; /* Parsing context */ |
| 103378 | int iOrigCol = iColumn; /* Original value of iColumn */ |
| 103379 | int nEquiv = 2; /* Number of entires in aEquiv[] */ |
| 103380 | int iEquiv = 2; /* Number of entries of aEquiv[] processed so far */ |
| 103381 | int aEquiv[22]; /* iCur,iColumn and up to 10 other equivalents */ |
| 103382 | |
| 103383 | assert( iCur>=0 ); |
| 103384 | aEquiv[0] = iCur; |
| 103385 | aEquiv[1] = iColumn; |
| 103386 | for(;;){ |
| 103387 | for(pWC=pWCOrig; pWC; pWC=pWC->pOuter){ |
| 103388 | for(pTerm=pWC->a, k=pWC->nTerm; k; k--, pTerm++){ |
| 103389 | if( pTerm->leftCursor==iCur |
| 103390 | && pTerm->u.leftColumn==iColumn |
| 103391 | ){ |
| 103392 | if( (pTerm->prereqRight & notReady)==0 |
| 103393 | && (pTerm->eOperator & op & WO_ALL)!=0 |
| 103394 | ){ |
| 103395 | if( iOrigCol>=0 && pIdx && (pTerm->eOperator & WO_ISNULL)==0 ){ |
| 103396 | CollSeq *pColl; |
| 103397 | char idxaff; |
| 103398 | |
| 103399 | pX = pTerm->pExpr; |
| 103400 | pParse = pWC->pParse; |
| 103401 | idxaff = pIdx->pTable->aCol[iOrigCol].affinity; |
| 103402 | if( !sqlite3IndexAffinityOk(pX, idxaff) ){ |
| 103403 | continue; |
| 103404 | } |
| 103405 | |
| 103406 | /* Figure out the collation sequence required from an index for |
| 103407 | ** it to be useful for optimising expression pX. Store this |
| 103408 | ** value in variable pColl. |
| 103409 | */ |
| 103410 | assert(pX->pLeft); |
| 103411 | pColl = sqlite3BinaryCompareCollSeq(pParse,pX->pLeft,pX->pRight); |
| 103412 | if( pColl==0 ) pColl = pParse->db->pDfltColl; |
| 103413 | |
| 103414 | for(j=0; pIdx->aiColumn[j]!=iOrigCol; j++){ |
| 103415 | if( NEVER(j>=pIdx->nColumn) ) return 0; |
| 103416 | } |
| 103417 | if( sqlite3StrICmp(pColl->zName, pIdx->azColl[j]) ){ |
| 103418 | continue; |
| 103419 | } |
| 103420 | } |
| 103421 | pResult = pTerm; |
| 103422 | if( pTerm->prereqRight==0 ) goto findTerm_success; |
| 103423 | } |
| 103424 | if( (pTerm->eOperator & WO_EQUIV)!=0 |
| 103425 | && nEquiv<ArraySize(aEquiv) |
| 103426 | ){ |
| 103427 | pX = sqlite3ExprSkipCollate(pTerm->pExpr->pRight); |
| 103428 | assert( pX->op==TK_COLUMN ); |
| 103429 | for(j=0; j<nEquiv; j+=2){ |
| 103430 | if( aEquiv[j]==pX->iTable && aEquiv[j+1]==pX->iColumn ) break; |
| 103431 | } |
| 103432 | if( j==nEquiv ){ |
| 103433 | aEquiv[j] = pX->iTable; |
| 103434 | aEquiv[j+1] = pX->iColumn; |
| 103435 | nEquiv += 2; |
| 103436 | } |
| 103437 | } |
| 103438 | } |
| 103439 | } |
| 103440 | } |
| 103441 | if( iEquiv>=nEquiv ) break; |
| 103442 | iCur = aEquiv[iEquiv++]; |
| 103443 | iColumn = aEquiv[iEquiv++]; |
| 103444 | } |
| 103445 | findTerm_success: |
| 103446 | return pResult; |
| 103447 | } |
| 103448 | |
| 103449 | /* Forward reference */ |
| 103450 | static void exprAnalyze(SrcList*, WhereClause*, int); |
| 103451 | |
| @@ -103658,11 +103719,10 @@ | |
| 103719 | indexable = ~(Bitmask)0; |
| 103720 | chngToIN = ~(pWC->vmask); |
| 103721 | for(i=pOrWc->nTerm-1, pOrTerm=pOrWc->a; i>=0 && indexable; i--, pOrTerm++){ |
| 103722 | if( (pOrTerm->eOperator & WO_SINGLE)==0 ){ |
| 103723 | WhereAndInfo *pAndInfo; |
| 103724 | assert( (pOrTerm->wtFlags & (TERM_ANDINFO|TERM_ORINFO))==0 ); |
| 103725 | chngToIN = 0; |
| 103726 | pAndInfo = sqlite3DbMallocRaw(db, sizeof(*pAndInfo)); |
| 103727 | if( pAndInfo ){ |
| 103728 | WhereClause *pAndWC; |
| @@ -103697,11 +103757,11 @@ | |
| 103757 | if( pOrTerm->wtFlags & TERM_VIRTUAL ){ |
| 103758 | WhereTerm *pOther = &pOrWc->a[pOrTerm->iParent]; |
| 103759 | b |= getMask(pMaskSet, pOther->leftCursor); |
| 103760 | } |
| 103761 | indexable &= b; |
| 103762 | if( (pOrTerm->eOperator & WO_EQ)==0 ){ |
| 103763 | chngToIN = 0; |
| 103764 | }else{ |
| 103765 | chngToIN &= b; |
| 103766 | } |
| 103767 | } |
| @@ -103748,11 +103808,11 @@ | |
| 103808 | ** and column is found but leave okToChngToIN false if not found. |
| 103809 | */ |
| 103810 | for(j=0; j<2 && !okToChngToIN; j++){ |
| 103811 | pOrTerm = pOrWc->a; |
| 103812 | for(i=pOrWc->nTerm-1; i>=0; i--, pOrTerm++){ |
| 103813 | assert( pOrTerm->eOperator & WO_EQ ); |
| 103814 | pOrTerm->wtFlags &= ~TERM_OR_OK; |
| 103815 | if( pOrTerm->leftCursor==iCursor ){ |
| 103816 | /* This is the 2-bit case and we are on the second iteration and |
| 103817 | ** current term is from the first iteration. So skip this term. */ |
| 103818 | assert( j==1 ); |
| @@ -103774,21 +103834,21 @@ | |
| 103834 | } |
| 103835 | if( i<0 ){ |
| 103836 | /* No candidate table+column was found. This can only occur |
| 103837 | ** on the second iteration */ |
| 103838 | assert( j==1 ); |
| 103839 | assert( IsPowerOfTwo(chngToIN) ); |
| 103840 | assert( chngToIN==getMask(pMaskSet, iCursor) ); |
| 103841 | break; |
| 103842 | } |
| 103843 | testcase( j==1 ); |
| 103844 | |
| 103845 | /* We have found a candidate table and column. Check to see if that |
| 103846 | ** table and column is common to every term in the OR clause */ |
| 103847 | okToChngToIN = 1; |
| 103848 | for(; i>=0 && okToChngToIN; i--, pOrTerm++){ |
| 103849 | assert( pOrTerm->eOperator & WO_EQ ); |
| 103850 | if( pOrTerm->leftCursor!=iCursor ){ |
| 103851 | pOrTerm->wtFlags &= ~TERM_OR_OK; |
| 103852 | }else if( pOrTerm->u.leftColumn!=iColumn ){ |
| 103853 | okToChngToIN = 0; |
| 103854 | }else{ |
| @@ -103820,11 +103880,11 @@ | |
| 103880 | Expr *pLeft = 0; /* The LHS of the IN operator */ |
| 103881 | Expr *pNew; /* The complete IN operator */ |
| 103882 | |
| 103883 | for(i=pOrWc->nTerm-1, pOrTerm=pOrWc->a; i>=0; i--, pOrTerm++){ |
| 103884 | if( (pOrTerm->wtFlags & TERM_OR_OK)==0 ) continue; |
| 103885 | assert( pOrTerm->eOperator & WO_EQ ); |
| 103886 | assert( pOrTerm->leftCursor==iCursor ); |
| 103887 | assert( pOrTerm->u.leftColumn==iColumn ); |
| 103888 | pDup = sqlite3ExprDup(db, pOrTerm->pExpr->pRight, 0); |
| 103889 | pList = sqlite3ExprListAppend(pWC->pParse, pList, pDup); |
| 103890 | pLeft = pOrTerm->pExpr->pLeft; |
| @@ -103849,11 +103909,10 @@ | |
| 103909 | pTerm->eOperator = WO_NOOP; /* case 1 trumps case 2 */ |
| 103910 | } |
| 103911 | } |
| 103912 | } |
| 103913 | #endif /* !SQLITE_OMIT_OR_OPTIMIZATION && !SQLITE_OMIT_SUBQUERY */ |
| 103914 | |
| 103915 | /* |
| 103916 | ** The input to this routine is an WhereTerm structure with only the |
| 103917 | ** "pExpr" field filled in. The job of this routine is to analyze the |
| 103918 | ** subexpression and populate all the other fields of the WhereTerm |
| @@ -103919,21 +103978,23 @@ | |
| 103978 | } |
| 103979 | pTerm->prereqAll = prereqAll; |
| 103980 | pTerm->leftCursor = -1; |
| 103981 | pTerm->iParent = -1; |
| 103982 | pTerm->eOperator = 0; |
| 103983 | if( allowedOp(op) ){ |
| 103984 | Expr *pLeft = sqlite3ExprSkipCollate(pExpr->pLeft); |
| 103985 | Expr *pRight = sqlite3ExprSkipCollate(pExpr->pRight); |
| 103986 | u16 opMask = (pTerm->prereqRight & prereqLeft)==0 ? WO_ALL : WO_EQUIV; |
| 103987 | if( pLeft->op==TK_COLUMN ){ |
| 103988 | pTerm->leftCursor = pLeft->iTable; |
| 103989 | pTerm->u.leftColumn = pLeft->iColumn; |
| 103990 | pTerm->eOperator = operatorMask(op) & opMask; |
| 103991 | } |
| 103992 | if( pRight && pRight->op==TK_COLUMN ){ |
| 103993 | WhereTerm *pNew; |
| 103994 | Expr *pDup; |
| 103995 | u16 eExtraOp = 0; /* Extra bits for pNew->eOperator */ |
| 103996 | if( pTerm->leftCursor>=0 ){ |
| 103997 | int idxNew; |
| 103998 | pDup = sqlite3ExprDup(db, pExpr, 0); |
| 103999 | if( db->mallocFailed ){ |
| 104000 | sqlite3ExprDelete(db, pDup); |
| @@ -103944,10 +104005,17 @@ | |
| 104005 | pNew = &pWC->a[idxNew]; |
| 104006 | pNew->iParent = idxTerm; |
| 104007 | pTerm = &pWC->a[idxTerm]; |
| 104008 | pTerm->nChild = 1; |
| 104009 | pTerm->wtFlags |= TERM_COPIED; |
| 104010 | if( pExpr->op==TK_EQ |
| 104011 | && !ExprHasProperty(pExpr, EP_FromJoin) |
| 104012 | && OptimizationEnabled(db, SQLITE_Transitive) |
| 104013 | ){ |
| 104014 | pTerm->eOperator |= WO_EQUIV; |
| 104015 | eExtraOp = WO_EQUIV; |
| 104016 | } |
| 104017 | }else{ |
| 104018 | pDup = pExpr; |
| 104019 | pNew = pTerm; |
| 104020 | } |
| 104021 | exprCommute(pParse, pDup); |
| @@ -103955,11 +104023,11 @@ | |
| 104023 | pNew->leftCursor = pLeft->iTable; |
| 104024 | pNew->u.leftColumn = pLeft->iColumn; |
| 104025 | testcase( (prereqLeft | extraRight) != prereqLeft ); |
| 104026 | pNew->prereqRight = prereqLeft | extraRight; |
| 104027 | pNew->prereqAll = prereqAll; |
| 104028 | pNew->eOperator = (operatorMask(pDup->op) + eExtraOp) & opMask; |
| 104029 | } |
| 104030 | } |
| 104031 | |
| 104032 | #ifndef SQLITE_OMIT_BETWEEN_OPTIMIZATION |
| 104033 | /* If a term is the BETWEEN operator, create two new virtual terms |
| @@ -104414,11 +104482,11 @@ | |
| 104482 | return; |
| 104483 | } |
| 104484 | |
| 104485 | /* Search the WHERE clause terms for a usable WO_OR term. */ |
| 104486 | for(pTerm=pWC->a; pTerm<pWCEnd; pTerm++){ |
| 104487 | if( (pTerm->eOperator & WO_OR)!=0 |
| 104488 | && ((pTerm->prereqAll & ~maskSrc) & p->notReady)==0 |
| 104489 | && (pTerm->u.pOrInfo->indexable & maskSrc)!=0 |
| 104490 | ){ |
| 104491 | WhereClause * const pOrWC = &pTerm->u.pOrInfo->wc; |
| 104492 | WhereTerm * const pOrWCEnd = &pOrWC->a[pOrWC->nTerm]; |
| @@ -104435,11 +104503,11 @@ | |
| 104503 | sBOI.ppIdxInfo = 0; |
| 104504 | for(pOrTerm=pOrWC->a; pOrTerm<pOrWCEnd; pOrTerm++){ |
| 104505 | WHERETRACE(("... Multi-index OR testing for term %d of %d....\n", |
| 104506 | (pOrTerm - pOrWC->a), (pTerm - pWC->a) |
| 104507 | )); |
| 104508 | if( (pOrTerm->eOperator& WO_AND)!=0 ){ |
| 104509 | sBOI.pWC = &pOrTerm->u.pAndInfo->wc; |
| 104510 | bestIndex(&sBOI); |
| 104511 | }else if( pOrTerm->leftCursor==iCur ){ |
| 104512 | WhereClause tempWC; |
| 104513 | tempWC.pParse = pWC->pParse; |
| @@ -104496,11 +104564,11 @@ | |
| 104564 | struct SrcList_item *pSrc, /* Table we are trying to access */ |
| 104565 | Bitmask notReady /* Tables in outer loops of the join */ |
| 104566 | ){ |
| 104567 | char aff; |
| 104568 | if( pTerm->leftCursor!=pSrc->iCursor ) return 0; |
| 104569 | if( (pTerm->eOperator & WO_EQ)==0 ) return 0; |
| 104570 | if( (pTerm->prereqRight & notReady)!=0 ) return 0; |
| 104571 | aff = pSrc->pTab->aCol[pTerm->u.leftColumn].affinity; |
| 104572 | if( !sqlite3IndexAffinityOk(pTerm->pExpr, aff) ) return 0; |
| 104573 | return 1; |
| 104574 | } |
| @@ -104758,13 +104826,13 @@ | |
| 104826 | |
| 104827 | /* Count the number of possible WHERE clause constraints referring |
| 104828 | ** to this virtual table */ |
| 104829 | for(i=nTerm=0, pTerm=pWC->a; i<pWC->nTerm; i++, pTerm++){ |
| 104830 | if( pTerm->leftCursor != pSrc->iCursor ) continue; |
| 104831 | assert( IsPowerOfTwo(pTerm->eOperator & ~WO_EQUIV) ); |
| 104832 | testcase( pTerm->eOperator & WO_IN ); |
| 104833 | testcase( pTerm->eOperator & WO_ISNULL ); |
| 104834 | if( pTerm->eOperator & (WO_ISNULL) ) continue; |
| 104835 | if( pTerm->wtFlags & TERM_VNULL ) continue; |
| 104836 | nTerm++; |
| 104837 | } |
| 104838 | |
| @@ -104811,18 +104879,18 @@ | |
| 104879 | pUsage; |
| 104880 | |
| 104881 | for(i=j=0, pTerm=pWC->a; i<pWC->nTerm; i++, pTerm++){ |
| 104882 | u8 op; |
| 104883 | if( pTerm->leftCursor != pSrc->iCursor ) continue; |
| 104884 | assert( IsPowerOfTwo(pTerm->eOperator & ~WO_EQUIV) ); |
| 104885 | testcase( pTerm->eOperator & WO_IN ); |
| 104886 | testcase( pTerm->eOperator & WO_ISNULL ); |
| 104887 | if( pTerm->eOperator & (WO_ISNULL) ) continue; |
| 104888 | if( pTerm->wtFlags & TERM_VNULL ) continue; |
| 104889 | pIdxCons[j].iColumn = pTerm->u.leftColumn; |
| 104890 | pIdxCons[j].iTermOffset = i; |
| 104891 | op = (u8)pTerm->eOperator & WO_ALL; |
| 104892 | if( op==WO_IN ) op = WO_EQ; |
| 104893 | pIdxCons[j].op = op; |
| 104894 | /* The direct assignment in the previous line is possible only because |
| 104895 | ** the WO_ and SQLITE_INDEX_CONSTRAINT_ codes are identical. The |
| 104896 | ** following asserts verify this fact. */ |
| @@ -104988,11 +105056,11 @@ | |
| 105056 | pUsage = pIdxInfo->aConstraintUsage; |
| 105057 | for(i=0; i<pIdxInfo->nConstraint; i++, pIdxCons++){ |
| 105058 | j = pIdxCons->iTermOffset; |
| 105059 | pTerm = &pWC->a[j]; |
| 105060 | if( (pTerm->prereqRight&p->notReady)==0 |
| 105061 | && (bAllowIN || (pTerm->eOperator & WO_IN)==0) |
| 105062 | ){ |
| 105063 | pIdxCons->usable = 1; |
| 105064 | }else{ |
| 105065 | pIdxCons->usable = 0; |
| 105066 | } |
| @@ -105020,11 +105088,11 @@ | |
| 105088 | for(i=0; i<pIdxInfo->nConstraint; i++, pIdxCons++){ |
| 105089 | if( pUsage[i].argvIndex>0 ){ |
| 105090 | j = pIdxCons->iTermOffset; |
| 105091 | pTerm = &pWC->a[j]; |
| 105092 | p->cost.used |= pTerm->prereqRight; |
| 105093 | if( (pTerm->eOperator & WO_IN)!=0 && pUsage[i].omit==0 ){ |
| 105094 | /* Do not attempt to use an IN constraint if the virtual table |
| 105095 | ** says that the equivalent EQ constraint cannot be safely omitted. |
| 105096 | ** If we do attempt to use such a constraint, some rows might be |
| 105097 | ** repeated in the output. */ |
| 105098 | break; |
| @@ -105326,28 +105394,28 @@ | |
| 105394 | u8 aff = p->pTable->aCol[p->aiColumn[0]].affinity; |
| 105395 | |
| 105396 | if( pLower ){ |
| 105397 | Expr *pExpr = pLower->pExpr->pRight; |
| 105398 | rc = valueFromExpr(pParse, pExpr, aff, &pRangeVal); |
| 105399 | assert( (pLower->eOperator & (WO_GT|WO_GE))!=0 ); |
| 105400 | if( rc==SQLITE_OK |
| 105401 | && whereKeyStats(pParse, p, pRangeVal, 0, a)==SQLITE_OK |
| 105402 | ){ |
| 105403 | iLower = a[0]; |
| 105404 | if( (pLower->eOperator & WO_GT)!=0 ) iLower += a[1]; |
| 105405 | } |
| 105406 | sqlite3ValueFree(pRangeVal); |
| 105407 | } |
| 105408 | if( rc==SQLITE_OK && pUpper ){ |
| 105409 | Expr *pExpr = pUpper->pExpr->pRight; |
| 105410 | rc = valueFromExpr(pParse, pExpr, aff, &pRangeVal); |
| 105411 | assert( (pUpper->eOperator & (WO_LT|WO_LE))!=0 ); |
| 105412 | if( rc==SQLITE_OK |
| 105413 | && whereKeyStats(pParse, p, pRangeVal, 1, a)==SQLITE_OK |
| 105414 | ){ |
| 105415 | iUpper = a[0]; |
| 105416 | if( (pUpper->eOperator & WO_LE)!=0 ) iUpper += a[1]; |
| 105417 | } |
| 105418 | sqlite3ValueFree(pRangeVal); |
| 105419 | } |
| 105420 | if( rc==SQLITE_OK ){ |
| 105421 | if( iUpper<=iLower ){ |
| @@ -105651,16 +105719,16 @@ | |
| 105719 | ** if there are any X= or X IS NULL constraints in the WHERE clause. */ |
| 105720 | pConstraint = findTerm(p->pWC, base, iColumn, p->notReady, |
| 105721 | WO_EQ|WO_ISNULL|WO_IN, pIdx); |
| 105722 | if( pConstraint==0 ){ |
| 105723 | isEq = 0; |
| 105724 | }else if( (pConstraint->eOperator & WO_IN)!=0 ){ |
| 105725 | /* Constraints of the form: "X IN ..." cannot be used with an ORDER BY |
| 105726 | ** because we do not know in what order the values on the RHS of the IN |
| 105727 | ** operator will occur. */ |
| 105728 | break; |
| 105729 | }else if( (pConstraint->eOperator & WO_ISNULL)!=0 ){ |
| 105730 | uniqueNotNull = 0; |
| 105731 | isEq = 1; /* "X IS NULL" means X has only a single value */ |
| 105732 | }else if( pConstraint->prereqRight==0 ){ |
| 105733 | isEq = 1; /* Constraint "X=constant" means X has only a single value */ |
| 105734 | }else{ |
| @@ -106069,16 +106137,17 @@ | |
| 106137 | */ |
| 106138 | if( pc.plan.nRow>(double)1 && pc.plan.nEq==1 |
| 106139 | && pFirstTerm!=0 && aiRowEst[1]>1 ){ |
| 106140 | assert( (pFirstTerm->eOperator & (WO_EQ|WO_ISNULL|WO_IN))!=0 ); |
| 106141 | if( pFirstTerm->eOperator & (WO_EQ|WO_ISNULL) ){ |
| 106142 | testcase( pFirstTerm->eOperator & WO_EQ ); |
| 106143 | testcase( pFirstTerm->eOperator & WO_EQUIV ); |
| 106144 | testcase( pFirstTerm->eOperator & WO_ISNULL ); |
| 106145 | whereEqualScanEst(pParse, pProbe, pFirstTerm->pExpr->pRight, |
| 106146 | &pc.plan.nRow); |
| 106147 | }else if( bInEst==0 ){ |
| 106148 | assert( pFirstTerm->eOperator & WO_IN ); |
| 106149 | whereInScanEst(pParse, pProbe, pFirstTerm->pExpr->x.pList, |
| 106150 | &pc.plan.nRow); |
| 106151 | } |
| 106152 | } |
| 106153 | #endif /* SQLITE_ENABLE_STAT3 */ |
| @@ -106221,11 +106290,11 @@ | |
| 106290 | ** more selective intentionally because of the subjective |
| 106291 | ** observation that indexed range constraints really are more |
| 106292 | ** selective in practice, on average. */ |
| 106293 | pc.plan.nRow /= 3; |
| 106294 | } |
| 106295 | }else if( (pTerm->eOperator & WO_NOOP)==0 ){ |
| 106296 | /* Any other expression lowers the output row count by half */ |
| 106297 | pc.plan.nRow /= 2; |
| 106298 | } |
| 106299 | } |
| 106300 | if( pc.plan.nRow<2 ) pc.plan.nRow = 2; |
| @@ -106273,12 +106342,13 @@ | |
| 106342 | assert( pSrc->pIndex==0 |
| 106343 | || p->cost.plan.u.pIdx==0 |
| 106344 | || p->cost.plan.u.pIdx==pSrc->pIndex |
| 106345 | ); |
| 106346 | |
| 106347 | WHERETRACE((" best index is %s cost=%.1f\n", |
| 106348 | p->cost.plan.u.pIdx ? p->cost.plan.u.pIdx->zName : "ipk", |
| 106349 | p->cost.rCost)); |
| 106350 | |
| 106351 | bestOrClauseIndex(p); |
| 106352 | bestAutomaticIndex(p); |
| 106353 | p->cost.plan.wsFlags |= eqTermMask; |
| 106354 | } |
| @@ -106856,11 +106926,10 @@ | |
| 106926 | */ |
| 106927 | iReleaseReg = sqlite3GetTempReg(pParse); |
| 106928 | pTerm = findTerm(pWC, iCur, -1, notReady, WO_EQ|WO_IN, 0); |
| 106929 | assert( pTerm!=0 ); |
| 106930 | assert( pTerm->pExpr!=0 ); |
| 106931 | assert( omitTable==0 ); |
| 106932 | testcase( pTerm->wtFlags & TERM_VIRTUAL ); /* EV: R-30575-11662 */ |
| 106933 | iRowidReg = codeEqualityTerm(pParse, pTerm, pLevel, iReleaseReg); |
| 106934 | addrNxt = pLevel->addrNxt; |
| 106935 | sqlite3VdbeAddOp2(v, OP_MustBeInt, iRowidReg, addrNxt); |
| @@ -107247,11 +107316,11 @@ | |
| 107316 | int ii; /* Loop counter */ |
| 107317 | Expr *pAndExpr = 0; /* An ".. AND (...)" expression */ |
| 107318 | |
| 107319 | pTerm = pLevel->plan.u.pTerm; |
| 107320 | assert( pTerm!=0 ); |
| 107321 | assert( pTerm->eOperator & WO_OR ); |
| 107322 | assert( (pTerm->wtFlags & TERM_ORINFO)!=0 ); |
| 107323 | pOrWc = &pTerm->u.pOrInfo->wc; |
| 107324 | pLevel->op = OP_Return; |
| 107325 | pLevel->p1 = regReturn; |
| 107326 | |
| @@ -107320,11 +107389,11 @@ | |
| 107389 | } |
| 107390 | } |
| 107391 | |
| 107392 | for(ii=0; ii<pOrWc->nTerm; ii++){ |
| 107393 | WhereTerm *pOrTerm = &pOrWc->a[ii]; |
| 107394 | if( pOrTerm->leftCursor==iCur || (pOrTerm->eOperator & WO_AND)!=0 ){ |
| 107395 | WhereInfo *pSubWInfo; /* Info for single OR-term scan */ |
| 107396 | Expr *pOrExpr = pOrTerm->pExpr; |
| 107397 | if( pAndExpr ){ |
| 107398 | pAndExpr->pLeft = pOrExpr; |
| 107399 | pOrExpr = pAndExpr; |
| @@ -107775,10 +107844,11 @@ | |
| 107844 | Index *pIdx; /* Index for FROM table at pTabItem */ |
| 107845 | int j; /* For looping over FROM tables */ |
| 107846 | int bestJ = -1; /* The value of j */ |
| 107847 | Bitmask m; /* Bitmask value for j or bestJ */ |
| 107848 | int isOptimal; /* Iterator for optimal/non-optimal search */ |
| 107849 | int ckOptimal; /* Do the optimal scan check */ |
| 107850 | int nUnconstrained; /* Number tables without INDEXED BY */ |
| 107851 | Bitmask notIndexed; /* Mask of tables that cannot use an index */ |
| 107852 | |
| 107853 | memset(&bestPlan, 0, sizeof(bestPlan)); |
| 107854 | bestPlan.rCost = SQLITE_BIG_DBL; |
| @@ -107809,14 +107879,12 @@ | |
| 107879 | ** |
| 107880 | ** The second loop iteration is only performed if no optimal scan |
| 107881 | ** strategies were found by the first iteration. This second iteration |
| 107882 | ** is used to search for the lowest cost scan overall. |
| 107883 | ** |
| 107884 | ** Without the optimal scan step (the first iteration) a suboptimal |
| 107885 | ** plan might be chosen for queries like this: |
| 107886 | ** |
| 107887 | ** CREATE TABLE t1(a, b); |
| 107888 | ** CREATE TABLE t2(c, d); |
| 107889 | ** SELECT * FROM t2, t1 WHERE t2.rowid = t1.a; |
| 107890 | ** |
| @@ -107827,20 +107895,44 @@ | |
| 107895 | ** algorithm may choose to use t2 for the outer loop, which is a much |
| 107896 | ** costlier approach. |
| 107897 | */ |
| 107898 | nUnconstrained = 0; |
| 107899 | notIndexed = 0; |
| 107900 | |
| 107901 | /* The optimal scan check only occurs if there are two or more tables |
| 107902 | ** available to be reordered */ |
| 107903 | if( iFrom==nTabList-1 ){ |
| 107904 | ckOptimal = 0; /* Common case of just one table in the FROM clause */ |
| 107905 | }else{ |
| 107906 | ckOptimal = -1; |
| 107907 | for(j=iFrom, sWBI.pSrc=&pTabList->a[j]; j<nTabList; j++, sWBI.pSrc++){ |
| 107908 | m = getMask(pMaskSet, sWBI.pSrc->iCursor); |
| 107909 | if( (m & sWBI.notValid)==0 ){ |
| 107910 | if( j==iFrom ) iFrom++; |
| 107911 | continue; |
| 107912 | } |
| 107913 | if( j>iFrom && (sWBI.pSrc->jointype & (JT_LEFT|JT_CROSS))!=0 ) break; |
| 107914 | if( ++ckOptimal ) break; |
| 107915 | if( (sWBI.pSrc->jointype & JT_LEFT)!=0 ) break; |
| 107916 | } |
| 107917 | } |
| 107918 | assert( ckOptimal==0 || ckOptimal==1 ); |
| 107919 | |
| 107920 | for(isOptimal=ckOptimal; isOptimal>=0 && bestJ<0; isOptimal--){ |
| 107921 | for(j=iFrom, sWBI.pSrc=&pTabList->a[j]; j<nTabList; j++, sWBI.pSrc++){ |
| 107922 | if( j>iFrom && (sWBI.pSrc->jointype & (JT_LEFT|JT_CROSS))!=0 ){ |
| 107923 | /* This break and one like it in the ckOptimal computation loop |
| 107924 | ** above prevent table reordering across LEFT and CROSS JOINs. |
| 107925 | ** The LEFT JOIN case is necessary for correctness. The prohibition |
| 107926 | ** against reordering across a CROSS JOIN is an SQLite feature that |
| 107927 | ** allows the developer to control table reordering */ |
| 107928 | break; |
| 107929 | } |
| 107930 | m = getMask(pMaskSet, sWBI.pSrc->iCursor); |
| 107931 | if( (m & sWBI.notValid)==0 ){ |
| 107932 | assert( j>iFrom ); |
| 107933 | continue; |
| 107934 | } |
| 107935 | sWBI.notReady = (isOptimal ? m : sWBI.notValid); |
| 107936 | if( sWBI.pSrc->pIndex==0 ) nUnconstrained++; |
| 107937 | |
| 107938 | WHERETRACE((" === trying table %d (%s) with isOptimal=%d ===\n", |
| @@ -107866,12 +107958,12 @@ | |
| 107958 | if( isOptimal && (sWBI.cost.plan.wsFlags & WHERE_NOT_FULLSCAN)==0 ){ |
| 107959 | notIndexed |= m; |
| 107960 | } |
| 107961 | if( isOptimal ){ |
| 107962 | pWInfo->a[j].rOptCost = sWBI.cost.rCost; |
| 107963 | }else if( ckOptimal ){ |
| 107964 | /* If two or more tables have nearly the same outer loop cost, but |
| 107965 | ** very different inner loop (optimal) cost, we want to choose |
| 107966 | ** for the outer loop that table which benefits the least from |
| 107967 | ** being in the inner loop. The following code scales the |
| 107968 | ** outer loop cost estimate to accomplish that. */ |
| 107969 | WHERETRACE((" scaling cost from %.1f to %.1f\n", |
| @@ -107912,15 +108004,23 @@ | |
| 108004 | sWBI.cost.rCost, sWBI.cost.plan.nRow, |
| 108005 | sWBI.cost.plan.nOBSat, sWBI.cost.plan.wsFlags)); |
| 108006 | bestPlan = sWBI.cost; |
| 108007 | bestJ = j; |
| 108008 | } |
| 108009 | |
| 108010 | /* In a join like "w JOIN x LEFT JOIN y JOIN z" make sure that |
| 108011 | ** table y (and not table z) is always the next inner loop inside |
| 108012 | ** of table x. */ |
| 108013 | if( (sWBI.pSrc->jointype & JT_LEFT)!=0 ) break; |
| 108014 | } |
| 108015 | } |
| 108016 | assert( bestJ>=0 ); |
| 108017 | assert( sWBI.notValid & getMask(pMaskSet, pTabList->a[bestJ].iCursor) ); |
| 108018 | assert( bestJ==iFrom || (pTabList->a[iFrom].jointype & JT_LEFT)==0 ); |
| 108019 | testcase( bestJ>iFrom && (pTabList->a[iFrom].jointype & JT_CROSS)!=0 ); |
| 108020 | testcase( bestJ>iFrom && bestJ<nTabList-1 |
| 108021 | && (pTabList->a[bestJ+1].jointype & JT_LEFT)!=0 ); |
| 108022 | WHERETRACE(("*** Optimizer selects table %d (%s) for loop %d with:\n" |
| 108023 | " cost=%.1f, nRow=%.1f, nOBSat=%d, wsFlags=0x%08x\n", |
| 108024 | bestJ, pTabList->a[bestJ].pTab->zName, |
| 108025 | pLevel-pWInfo->a, bestPlan.rCost, bestPlan.plan.nRow, |
| 108026 | bestPlan.plan.nOBSat, bestPlan.plan.wsFlags)); |
| @@ -136646,11 +136746,12 @@ | |
| 136746 | ** would fit in a single node, use a smaller node-size. |
| 136747 | */ |
| 136748 | static int getNodeSize( |
| 136749 | sqlite3 *db, /* Database handle */ |
| 136750 | Rtree *pRtree, /* Rtree handle */ |
| 136751 | int isCreate, /* True for xCreate, false for xConnect */ |
| 136752 | char **pzErr /* OUT: Error message, if any */ |
| 136753 | ){ |
| 136754 | int rc; |
| 136755 | char *zSql; |
| 136756 | if( isCreate ){ |
| 136757 | int iPageSize = 0; |
| @@ -136659,17 +136760,22 @@ | |
| 136760 | if( rc==SQLITE_OK ){ |
| 136761 | pRtree->iNodeSize = iPageSize-64; |
| 136762 | if( (4+pRtree->nBytesPerCell*RTREE_MAXCELLS)<pRtree->iNodeSize ){ |
| 136763 | pRtree->iNodeSize = 4+pRtree->nBytesPerCell*RTREE_MAXCELLS; |
| 136764 | } |
| 136765 | }else{ |
| 136766 | *pzErr = sqlite3_mprintf("%s", sqlite3_errmsg(db)); |
| 136767 | } |
| 136768 | }else{ |
| 136769 | zSql = sqlite3_mprintf( |
| 136770 | "SELECT length(data) FROM '%q'.'%q_node' WHERE nodeno = 1", |
| 136771 | pRtree->zDb, pRtree->zName |
| 136772 | ); |
| 136773 | rc = getIntFromStmt(db, zSql, &pRtree->iNodeSize); |
| 136774 | if( rc!=SQLITE_OK ){ |
| 136775 | *pzErr = sqlite3_mprintf("%s", sqlite3_errmsg(db)); |
| 136776 | } |
| 136777 | } |
| 136778 | |
| 136779 | sqlite3_free(zSql); |
| 136780 | return rc; |
| 136781 | } |
| @@ -136729,11 +136835,11 @@ | |
| 136835 | pRtree->eCoordType = eCoordType; |
| 136836 | memcpy(pRtree->zDb, argv[1], nDb); |
| 136837 | memcpy(pRtree->zName, argv[2], nName); |
| 136838 | |
| 136839 | /* Figure out the node size to use. */ |
| 136840 | rc = getNodeSize(db, pRtree, isCreate, pzErr); |
| 136841 | |
| 136842 | /* Create/Connect to the underlying relational database schema. If |
| 136843 | ** that is successful, call sqlite3_declare_vtab() to configure |
| 136844 | ** the r-tree table schema. |
| 136845 | */ |
| 136846 |
+1
-1
| --- src/sqlite3.h | ||
| +++ src/sqlite3.h | ||
| @@ -107,11 +107,11 @@ | ||
| 107 | 107 | ** [sqlite3_libversion_number()], [sqlite3_sourceid()], |
| 108 | 108 | ** [sqlite_version()] and [sqlite_source_id()]. |
| 109 | 109 | */ |
| 110 | 110 | #define SQLITE_VERSION "3.7.16" |
| 111 | 111 | #define SQLITE_VERSION_NUMBER 3007016 |
| 112 | -#define SQLITE_SOURCE_ID "2013-01-09 11:31:17 5774f2175ce621dfc4b6b93f7ee13fd66f3ec2b9" | |
| 112 | +#define SQLITE_SOURCE_ID "2013-01-17 17:20:49 38852f158ab20bb4d7b264af987ec1538052bec3" | |
| 113 | 113 | |
| 114 | 114 | /* |
| 115 | 115 | ** CAPI3REF: Run-Time Library Version Numbers |
| 116 | 116 | ** KEYWORDS: sqlite3_version, sqlite3_sourceid |
| 117 | 117 | ** |
| 118 | 118 |
| --- src/sqlite3.h | |
| +++ src/sqlite3.h | |
| @@ -107,11 +107,11 @@ | |
| 107 | ** [sqlite3_libversion_number()], [sqlite3_sourceid()], |
| 108 | ** [sqlite_version()] and [sqlite_source_id()]. |
| 109 | */ |
| 110 | #define SQLITE_VERSION "3.7.16" |
| 111 | #define SQLITE_VERSION_NUMBER 3007016 |
| 112 | #define SQLITE_SOURCE_ID "2013-01-09 11:31:17 5774f2175ce621dfc4b6b93f7ee13fd66f3ec2b9" |
| 113 | |
| 114 | /* |
| 115 | ** CAPI3REF: Run-Time Library Version Numbers |
| 116 | ** KEYWORDS: sqlite3_version, sqlite3_sourceid |
| 117 | ** |
| 118 |
| --- src/sqlite3.h | |
| +++ src/sqlite3.h | |
| @@ -107,11 +107,11 @@ | |
| 107 | ** [sqlite3_libversion_number()], [sqlite3_sourceid()], |
| 108 | ** [sqlite_version()] and [sqlite_source_id()]. |
| 109 | */ |
| 110 | #define SQLITE_VERSION "3.7.16" |
| 111 | #define SQLITE_VERSION_NUMBER 3007016 |
| 112 | #define SQLITE_SOURCE_ID "2013-01-17 17:20:49 38852f158ab20bb4d7b264af987ec1538052bec3" |
| 113 | |
| 114 | /* |
| 115 | ** CAPI3REF: Run-Time Library Version Numbers |
| 116 | ** KEYWORDS: sqlite3_version, sqlite3_sourceid |
| 117 | ** |
| 118 |
+25
-31
| --- src/stat.c | ||
| +++ src/stat.c | ||
| @@ -66,11 +66,11 @@ | ||
| 66 | 66 | @ </td></tr> |
| 67 | 67 | if( !brief ){ |
| 68 | 68 | @ <tr><th>Number Of Artifacts:</th><td> |
| 69 | 69 | n = db_int(0, "SELECT count(*) FROM blob"); |
| 70 | 70 | m = db_int(0, "SELECT count(*) FROM delta"); |
| 71 | - @ %d(n) (stored as %d(n-m) full text and %d(m) delta blobs) | |
| 71 | + @ %d(n) (%d(n-m) fulltext and %d(m) deltas) | |
| 72 | 72 | @ </td></tr> |
| 73 | 73 | if( n>0 ){ |
| 74 | 74 | int a, b; |
| 75 | 75 | Stmt q; |
| 76 | 76 | @ <tr><th>Uncompressed Artifact Size:</th><td> |
| @@ -94,11 +94,11 @@ | ||
| 94 | 94 | a = t/fsize; |
| 95 | 95 | @ %d(a):%d(b) |
| 96 | 96 | @ </td></tr> |
| 97 | 97 | } |
| 98 | 98 | @ <tr><th>Number Of Check-ins:</th><td> |
| 99 | - n = db_int(0, "SELECT count(distinct mid) FROM mlink /*scan*/"); | |
| 99 | + n = db_int(0, "SELECT count(*) FROM event WHERE type='ci' /*scan*/"); | |
| 100 | 100 | @ %d(n) |
| 101 | 101 | @ </td></tr> |
| 102 | 102 | @ <tr><th>Number Of Files:</th><td> |
| 103 | 103 | n = db_int(0, "SELECT count(*) FROM filename /*scan*/"); |
| 104 | 104 | @ %d(n) |
| @@ -115,17 +115,16 @@ | ||
| 115 | 115 | @ </td></tr> |
| 116 | 116 | } |
| 117 | 117 | @ <tr><th>Duration Of Project:</th><td> |
| 118 | 118 | n = db_int(0, "SELECT julianday('now') - (SELECT min(mtime) FROM event)" |
| 119 | 119 | " + 0.99"); |
| 120 | - @ %d(n) days or approximately %.2f(n/365.24) years. | |
| 120 | + @ %d(n) days or approximately %.2f(n/365.2425) years. | |
| 121 | 121 | @ </td></tr> |
| 122 | 122 | @ <tr><th>Project ID:</th><td>%h(db_get("project-code",""))</td></tr> |
| 123 | - @ <tr><th>Server ID:</th><td>%h(db_get("server-code",""))</td></tr> | |
| 124 | 123 | @ <tr><th>Fossil Version:</th><td> |
| 125 | - @ %h(RELEASE_VERSION) %h(MANIFEST_DATE) %h(MANIFEST_VERSION) | |
| 126 | - @ (%h(COMPILER_NAME)) | |
| 124 | + @ %h(MANIFEST_DATE) %h(MANIFEST_VERSION) | |
| 125 | + @ (%h(RELEASE_VERSION)) [compiled using %h(COMPILER_NAME)] | |
| 127 | 126 | @ </td></tr> |
| 128 | 127 | @ <tr><th>SQLite Version:</th><td>%.19s(SQLITE_SOURCE_ID) |
| 129 | 128 | @ [%.10s(&SQLITE_SOURCE_ID[20])] (%s(SQLITE_VERSION))</td></tr> |
| 130 | 129 | @ <tr><th>Database Stats:</th><td> |
| 131 | 130 | zDb = db_name("repository"); |
| @@ -139,13 +138,20 @@ | ||
| 139 | 138 | @ </table> |
| 140 | 139 | style_footer(); |
| 141 | 140 | } |
| 142 | 141 | |
| 143 | 142 | /* |
| 144 | -** COMMAND: dbstat | |
| 143 | +** COMMAND: dbstat* | |
| 144 | +** | |
| 145 | +** Usage: %fossil dbstat ?-brief | -b? | |
| 146 | +** | |
| 147 | +** Shows statistics and global information about the repository. | |
| 145 | 148 | ** |
| 146 | -** Show statistics and global information about the repository. | |
| 149 | +** The (-brief|-b) option removes any "long-running" statistics, namely | |
| 150 | +** those whose calculations are known to slow down as the repository | |
| 151 | +** grows. | |
| 152 | +** | |
| 147 | 153 | */ |
| 148 | 154 | void dbstat_cmd(void){ |
| 149 | 155 | i64 t, fsize; |
| 150 | 156 | int n, m; |
| 151 | 157 | int szMax, szAvg; |
| @@ -187,43 +193,31 @@ | ||
| 187 | 193 | } |
| 188 | 194 | a = t/fsize; |
| 189 | 195 | fossil_print("%*s%d:%d\n", colWidth, "compression-ratio:", a, b); |
| 190 | 196 | } |
| 191 | 197 | n = db_int(0, "SELECT COUNT(*) FROM event e WHERE e.type='ci'"); |
| 192 | - fossil_print("%*s%d\n", colWidth, "checkin-count:", n); | |
| 198 | + fossil_print("%*s%d\n", colWidth, "checkins:", n); | |
| 193 | 199 | n = db_int(0, "SELECT count(*) FROM filename /*scan*/"); |
| 194 | - /* FIXME/TODO: add the change-count-per-type to each event type, | |
| 195 | - ** plus add 'Event' count | |
| 196 | - */ | |
| 197 | -#if 0 | |
| 198 | - m = db_int(0, "SELECT count(distinct mid) FROM mlink /*scan*/"); | |
| 199 | -#endif | |
| 200 | - fossil_print("%*s%d"/* (%d changes) */"\n", colWidth, "file-count:", | |
| 201 | - n/*, m */); | |
| 200 | + fossil_print("%*s%d across all branches\n", colWidth, "files:", n); | |
| 202 | 201 | n = db_int(0, "SELECT count(*) FROM tag /*scan*/" |
| 203 | 202 | " WHERE tagname GLOB 'wiki-*'"); |
| 204 | -#if 0 | |
| 205 | - m = db_int(0, "SELECT COUNT(*) FROM blob b JOIN event e WHERE " | |
| 206 | - "b.rid=e.objid AND e.type='w'"); | |
| 207 | -#endif | |
| 208 | - fossil_print("%*s%d"/* (%d changes) */"\n", colWidth, "wikipage-count:", | |
| 209 | - n/*, m */); | |
| 203 | + m = db_int(0, "SELECT COUNT(*) FROM event WHERE type='w'"); | |
| 204 | + fossil_print("%*s%d (%d changes)\n", colWidth, "wikipages:", n, m); | |
| 210 | 205 | n = db_int(0, "SELECT count(*) FROM tag /*scan*/" |
| 211 | 206 | " WHERE tagname GLOB 'tkt-*'"); |
| 212 | -#if 0 | |
| 213 | - m = db_int(0, "SELECT COUNT(*) FROM blob b JOIN event e WHERE " | |
| 214 | - "b.rid=e.objid AND e.type='t'"); | |
| 215 | -#endif | |
| 216 | - fossil_print("%*s%d"/* (%d changes)*/"\n", colWidth, "ticket-count:", | |
| 217 | - n/* , m */); | |
| 207 | + m = db_int(0, "SELECT COUNT(*) FROM event WHERE type='t'"); | |
| 208 | + fossil_print("%*s%d (%d changes)\n", colWidth, "tickets:", n, m); | |
| 209 | + n = db_int(0, "SELECT COUNT(*) FROM event WHERE type='e'"); | |
| 210 | + fossil_print("%*s%d\n", colWidth, "events:", n); | |
| 211 | + n = db_int(0, "SELECT COUNT(*) FROM event WHERE type='g'"); | |
| 212 | + fossil_print("%*s%d\n", colWidth, "tagchanges:", n); | |
| 218 | 213 | } |
| 219 | 214 | n = db_int(0, "SELECT julianday('now') - (SELECT min(mtime) FROM event)" |
| 220 | 215 | " + 0.99"); |
| 221 | 216 | fossil_print("%*s%d days or approximately %.2f years.\n", |
| 222 | - colWidth, "project-age:", n, n/365.24); | |
| 217 | + colWidth, "project-age:", n, n/365.2425); | |
| 223 | 218 | fossil_print("%*s%s\n", colWidth, "project-id:", db_get("project-code","")); |
| 224 | - fossil_print("%*s%s\n", colWidth, "server-id:", db_get("server-code","")); | |
| 225 | 219 | fossil_print("%*s%s %s %s (%s)\n", |
| 226 | 220 | colWidth, "fossil-version:", |
| 227 | 221 | RELEASE_VERSION, MANIFEST_DATE, MANIFEST_VERSION, |
| 228 | 222 | COMPILER_NAME); |
| 229 | 223 | fossil_print("%*s%.19s [%.10s] (%s)\n", |
| 230 | 224 |
| --- src/stat.c | |
| +++ src/stat.c | |
| @@ -66,11 +66,11 @@ | |
| 66 | @ </td></tr> |
| 67 | if( !brief ){ |
| 68 | @ <tr><th>Number Of Artifacts:</th><td> |
| 69 | n = db_int(0, "SELECT count(*) FROM blob"); |
| 70 | m = db_int(0, "SELECT count(*) FROM delta"); |
| 71 | @ %d(n) (stored as %d(n-m) full text and %d(m) delta blobs) |
| 72 | @ </td></tr> |
| 73 | if( n>0 ){ |
| 74 | int a, b; |
| 75 | Stmt q; |
| 76 | @ <tr><th>Uncompressed Artifact Size:</th><td> |
| @@ -94,11 +94,11 @@ | |
| 94 | a = t/fsize; |
| 95 | @ %d(a):%d(b) |
| 96 | @ </td></tr> |
| 97 | } |
| 98 | @ <tr><th>Number Of Check-ins:</th><td> |
| 99 | n = db_int(0, "SELECT count(distinct mid) FROM mlink /*scan*/"); |
| 100 | @ %d(n) |
| 101 | @ </td></tr> |
| 102 | @ <tr><th>Number Of Files:</th><td> |
| 103 | n = db_int(0, "SELECT count(*) FROM filename /*scan*/"); |
| 104 | @ %d(n) |
| @@ -115,17 +115,16 @@ | |
| 115 | @ </td></tr> |
| 116 | } |
| 117 | @ <tr><th>Duration Of Project:</th><td> |
| 118 | n = db_int(0, "SELECT julianday('now') - (SELECT min(mtime) FROM event)" |
| 119 | " + 0.99"); |
| 120 | @ %d(n) days or approximately %.2f(n/365.24) years. |
| 121 | @ </td></tr> |
| 122 | @ <tr><th>Project ID:</th><td>%h(db_get("project-code",""))</td></tr> |
| 123 | @ <tr><th>Server ID:</th><td>%h(db_get("server-code",""))</td></tr> |
| 124 | @ <tr><th>Fossil Version:</th><td> |
| 125 | @ %h(RELEASE_VERSION) %h(MANIFEST_DATE) %h(MANIFEST_VERSION) |
| 126 | @ (%h(COMPILER_NAME)) |
| 127 | @ </td></tr> |
| 128 | @ <tr><th>SQLite Version:</th><td>%.19s(SQLITE_SOURCE_ID) |
| 129 | @ [%.10s(&SQLITE_SOURCE_ID[20])] (%s(SQLITE_VERSION))</td></tr> |
| 130 | @ <tr><th>Database Stats:</th><td> |
| 131 | zDb = db_name("repository"); |
| @@ -139,13 +138,20 @@ | |
| 139 | @ </table> |
| 140 | style_footer(); |
| 141 | } |
| 142 | |
| 143 | /* |
| 144 | ** COMMAND: dbstat |
| 145 | ** |
| 146 | ** Show statistics and global information about the repository. |
| 147 | */ |
| 148 | void dbstat_cmd(void){ |
| 149 | i64 t, fsize; |
| 150 | int n, m; |
| 151 | int szMax, szAvg; |
| @@ -187,43 +193,31 @@ | |
| 187 | } |
| 188 | a = t/fsize; |
| 189 | fossil_print("%*s%d:%d\n", colWidth, "compression-ratio:", a, b); |
| 190 | } |
| 191 | n = db_int(0, "SELECT COUNT(*) FROM event e WHERE e.type='ci'"); |
| 192 | fossil_print("%*s%d\n", colWidth, "checkin-count:", n); |
| 193 | n = db_int(0, "SELECT count(*) FROM filename /*scan*/"); |
| 194 | /* FIXME/TODO: add the change-count-per-type to each event type, |
| 195 | ** plus add 'Event' count |
| 196 | */ |
| 197 | #if 0 |
| 198 | m = db_int(0, "SELECT count(distinct mid) FROM mlink /*scan*/"); |
| 199 | #endif |
| 200 | fossil_print("%*s%d"/* (%d changes) */"\n", colWidth, "file-count:", |
| 201 | n/*, m */); |
| 202 | n = db_int(0, "SELECT count(*) FROM tag /*scan*/" |
| 203 | " WHERE tagname GLOB 'wiki-*'"); |
| 204 | #if 0 |
| 205 | m = db_int(0, "SELECT COUNT(*) FROM blob b JOIN event e WHERE " |
| 206 | "b.rid=e.objid AND e.type='w'"); |
| 207 | #endif |
| 208 | fossil_print("%*s%d"/* (%d changes) */"\n", colWidth, "wikipage-count:", |
| 209 | n/*, m */); |
| 210 | n = db_int(0, "SELECT count(*) FROM tag /*scan*/" |
| 211 | " WHERE tagname GLOB 'tkt-*'"); |
| 212 | #if 0 |
| 213 | m = db_int(0, "SELECT COUNT(*) FROM blob b JOIN event e WHERE " |
| 214 | "b.rid=e.objid AND e.type='t'"); |
| 215 | #endif |
| 216 | fossil_print("%*s%d"/* (%d changes)*/"\n", colWidth, "ticket-count:", |
| 217 | n/* , m */); |
| 218 | } |
| 219 | n = db_int(0, "SELECT julianday('now') - (SELECT min(mtime) FROM event)" |
| 220 | " + 0.99"); |
| 221 | fossil_print("%*s%d days or approximately %.2f years.\n", |
| 222 | colWidth, "project-age:", n, n/365.24); |
| 223 | fossil_print("%*s%s\n", colWidth, "project-id:", db_get("project-code","")); |
| 224 | fossil_print("%*s%s\n", colWidth, "server-id:", db_get("server-code","")); |
| 225 | fossil_print("%*s%s %s %s (%s)\n", |
| 226 | colWidth, "fossil-version:", |
| 227 | RELEASE_VERSION, MANIFEST_DATE, MANIFEST_VERSION, |
| 228 | COMPILER_NAME); |
| 229 | fossil_print("%*s%.19s [%.10s] (%s)\n", |
| 230 |
| --- src/stat.c | |
| +++ src/stat.c | |
| @@ -66,11 +66,11 @@ | |
| 66 | @ </td></tr> |
| 67 | if( !brief ){ |
| 68 | @ <tr><th>Number Of Artifacts:</th><td> |
| 69 | n = db_int(0, "SELECT count(*) FROM blob"); |
| 70 | m = db_int(0, "SELECT count(*) FROM delta"); |
| 71 | @ %d(n) (%d(n-m) fulltext and %d(m) deltas) |
| 72 | @ </td></tr> |
| 73 | if( n>0 ){ |
| 74 | int a, b; |
| 75 | Stmt q; |
| 76 | @ <tr><th>Uncompressed Artifact Size:</th><td> |
| @@ -94,11 +94,11 @@ | |
| 94 | a = t/fsize; |
| 95 | @ %d(a):%d(b) |
| 96 | @ </td></tr> |
| 97 | } |
| 98 | @ <tr><th>Number Of Check-ins:</th><td> |
| 99 | n = db_int(0, "SELECT count(*) FROM event WHERE type='ci' /*scan*/"); |
| 100 | @ %d(n) |
| 101 | @ </td></tr> |
| 102 | @ <tr><th>Number Of Files:</th><td> |
| 103 | n = db_int(0, "SELECT count(*) FROM filename /*scan*/"); |
| 104 | @ %d(n) |
| @@ -115,17 +115,16 @@ | |
| 115 | @ </td></tr> |
| 116 | } |
| 117 | @ <tr><th>Duration Of Project:</th><td> |
| 118 | n = db_int(0, "SELECT julianday('now') - (SELECT min(mtime) FROM event)" |
| 119 | " + 0.99"); |
| 120 | @ %d(n) days or approximately %.2f(n/365.2425) years. |
| 121 | @ </td></tr> |
| 122 | @ <tr><th>Project ID:</th><td>%h(db_get("project-code",""))</td></tr> |
| 123 | @ <tr><th>Fossil Version:</th><td> |
| 124 | @ %h(MANIFEST_DATE) %h(MANIFEST_VERSION) |
| 125 | @ (%h(RELEASE_VERSION)) [compiled using %h(COMPILER_NAME)] |
| 126 | @ </td></tr> |
| 127 | @ <tr><th>SQLite Version:</th><td>%.19s(SQLITE_SOURCE_ID) |
| 128 | @ [%.10s(&SQLITE_SOURCE_ID[20])] (%s(SQLITE_VERSION))</td></tr> |
| 129 | @ <tr><th>Database Stats:</th><td> |
| 130 | zDb = db_name("repository"); |
| @@ -139,13 +138,20 @@ | |
| 138 | @ </table> |
| 139 | style_footer(); |
| 140 | } |
| 141 | |
| 142 | /* |
| 143 | ** COMMAND: dbstat* |
| 144 | ** |
| 145 | ** Usage: %fossil dbstat ?-brief | -b? |
| 146 | ** |
| 147 | ** Shows statistics and global information about the repository. |
| 148 | ** |
| 149 | ** The (-brief|-b) option removes any "long-running" statistics, namely |
| 150 | ** those whose calculations are known to slow down as the repository |
| 151 | ** grows. |
| 152 | ** |
| 153 | */ |
| 154 | void dbstat_cmd(void){ |
| 155 | i64 t, fsize; |
| 156 | int n, m; |
| 157 | int szMax, szAvg; |
| @@ -187,43 +193,31 @@ | |
| 193 | } |
| 194 | a = t/fsize; |
| 195 | fossil_print("%*s%d:%d\n", colWidth, "compression-ratio:", a, b); |
| 196 | } |
| 197 | n = db_int(0, "SELECT COUNT(*) FROM event e WHERE e.type='ci'"); |
| 198 | fossil_print("%*s%d\n", colWidth, "checkins:", n); |
| 199 | n = db_int(0, "SELECT count(*) FROM filename /*scan*/"); |
| 200 | fossil_print("%*s%d across all branches\n", colWidth, "files:", n); |
| 201 | n = db_int(0, "SELECT count(*) FROM tag /*scan*/" |
| 202 | " WHERE tagname GLOB 'wiki-*'"); |
| 203 | m = db_int(0, "SELECT COUNT(*) FROM event WHERE type='w'"); |
| 204 | fossil_print("%*s%d (%d changes)\n", colWidth, "wikipages:", n, m); |
| 205 | n = db_int(0, "SELECT count(*) FROM tag /*scan*/" |
| 206 | " WHERE tagname GLOB 'tkt-*'"); |
| 207 | m = db_int(0, "SELECT COUNT(*) FROM event WHERE type='t'"); |
| 208 | fossil_print("%*s%d (%d changes)\n", colWidth, "tickets:", n, m); |
| 209 | n = db_int(0, "SELECT COUNT(*) FROM event WHERE type='e'"); |
| 210 | fossil_print("%*s%d\n", colWidth, "events:", n); |
| 211 | n = db_int(0, "SELECT COUNT(*) FROM event WHERE type='g'"); |
| 212 | fossil_print("%*s%d\n", colWidth, "tagchanges:", n); |
| 213 | } |
| 214 | n = db_int(0, "SELECT julianday('now') - (SELECT min(mtime) FROM event)" |
| 215 | " + 0.99"); |
| 216 | fossil_print("%*s%d days or approximately %.2f years.\n", |
| 217 | colWidth, "project-age:", n, n/365.2425); |
| 218 | fossil_print("%*s%s\n", colWidth, "project-id:", db_get("project-code","")); |
| 219 | fossil_print("%*s%s %s %s (%s)\n", |
| 220 | colWidth, "fossil-version:", |
| 221 | RELEASE_VERSION, MANIFEST_DATE, MANIFEST_VERSION, |
| 222 | COMPILER_NAME); |
| 223 | fossil_print("%*s%.19s [%.10s] (%s)\n", |
| 224 |
+4
-4
| --- src/timeline.c | ||
| +++ src/timeline.c | ||
| @@ -1507,16 +1507,16 @@ | ||
| 1507 | 1507 | @ (SELECT count(*) FROM plink WHERE pid=blob.rid AND isprim) |
| 1508 | 1508 | @ AS primPlinkCount, |
| 1509 | 1509 | @ (SELECT count(*) FROM plink WHERE cid=blob.rid) AS plinkCount, |
| 1510 | 1510 | @ event.mtime AS mtime, |
| 1511 | 1511 | @ tagxref.value AS branch |
| 1512 | - @ FROM tag CROSS JOIN event CROSS JOIN blob CROSS JOIN tagxref | |
| 1512 | + @ FROM tag CROSS JOIN event CROSS JOIN blob | |
| 1513 | + @ LEFT JOIN tagxref ON tagxref.tagid=tag.tagid | |
| 1514 | + @ AND tagxref.tagtype>0 | |
| 1515 | + @ AND tagxref.rid=blob.rid | |
| 1513 | 1516 | @ WHERE blob.rid=event.objid |
| 1514 | 1517 | @ AND tag.tagname='branch' |
| 1515 | - @ AND tagxref.tagid=tag.tagid | |
| 1516 | - @ AND tagxref.tagtype>0 | |
| 1517 | - @ AND tagxref.rid=blob.rid | |
| 1518 | 1518 | ; |
| 1519 | 1519 | return zBaseSql; |
| 1520 | 1520 | } |
| 1521 | 1521 | |
| 1522 | 1522 | /* |
| 1523 | 1523 |
| --- src/timeline.c | |
| +++ src/timeline.c | |
| @@ -1507,16 +1507,16 @@ | |
| 1507 | @ (SELECT count(*) FROM plink WHERE pid=blob.rid AND isprim) |
| 1508 | @ AS primPlinkCount, |
| 1509 | @ (SELECT count(*) FROM plink WHERE cid=blob.rid) AS plinkCount, |
| 1510 | @ event.mtime AS mtime, |
| 1511 | @ tagxref.value AS branch |
| 1512 | @ FROM tag CROSS JOIN event CROSS JOIN blob CROSS JOIN tagxref |
| 1513 | @ WHERE blob.rid=event.objid |
| 1514 | @ AND tag.tagname='branch' |
| 1515 | @ AND tagxref.tagid=tag.tagid |
| 1516 | @ AND tagxref.tagtype>0 |
| 1517 | @ AND tagxref.rid=blob.rid |
| 1518 | ; |
| 1519 | return zBaseSql; |
| 1520 | } |
| 1521 | |
| 1522 | /* |
| 1523 |
| --- src/timeline.c | |
| +++ src/timeline.c | |
| @@ -1507,16 +1507,16 @@ | |
| 1507 | @ (SELECT count(*) FROM plink WHERE pid=blob.rid AND isprim) |
| 1508 | @ AS primPlinkCount, |
| 1509 | @ (SELECT count(*) FROM plink WHERE cid=blob.rid) AS plinkCount, |
| 1510 | @ event.mtime AS mtime, |
| 1511 | @ tagxref.value AS branch |
| 1512 | @ FROM tag CROSS JOIN event CROSS JOIN blob |
| 1513 | @ LEFT JOIN tagxref ON tagxref.tagid=tag.tagid |
| 1514 | @ AND tagxref.tagtype>0 |
| 1515 | @ AND tagxref.rid=blob.rid |
| 1516 | @ WHERE blob.rid=event.objid |
| 1517 | @ AND tag.tagname='branch' |
| 1518 | ; |
| 1519 | return zBaseSql; |
| 1520 | } |
| 1521 | |
| 1522 | /* |
| 1523 |
+25
-6
| --- src/update.c | ||
| +++ src/update.c | ||
| @@ -667,10 +667,13 @@ | ||
| 667 | 667 | ** Usage: %fossil revert ?-r REVISION? ?FILE ...? |
| 668 | 668 | ** |
| 669 | 669 | ** Revert to the current repository version of FILE, or to |
| 670 | 670 | ** the version associated with baseline REVISION if the -r flag |
| 671 | 671 | ** appears. |
| 672 | +** | |
| 673 | +** If FILE was part of a rename operation, both the original file | |
| 674 | +** and the renamed file are reverted. | |
| 672 | 675 | ** |
| 673 | 676 | ** Revert all files if no file name is provided. |
| 674 | 677 | ** |
| 675 | 678 | ** If a file is reverted accidently, it can be restored using |
| 676 | 679 | ** the "fossil undo" command. |
| @@ -706,11 +709,22 @@ | ||
| 706 | 709 | if( g.argc>2 ){ |
| 707 | 710 | for(i=2; i<g.argc; i++){ |
| 708 | 711 | Blob fname; |
| 709 | 712 | zFile = mprintf("%/", g.argv[i]); |
| 710 | 713 | file_tree_name(zFile, &fname, 1); |
| 711 | - db_multi_exec("REPLACE INTO torevert VALUES(%B)", &fname); | |
| 714 | + db_multi_exec( | |
| 715 | + "REPLACE INTO torevert VALUES(%B);" | |
| 716 | + "INSERT OR IGNORE INTO torevert" | |
| 717 | + " SELECT pathname" | |
| 718 | + " FROM vfile" | |
| 719 | + " WHERE origname IN(%B)" | |
| 720 | + " UNION ALL" | |
| 721 | + " SELECT origname" | |
| 722 | + " FROM vfile" | |
| 723 | + " WHERE pathname IN(%B) AND origname IS NOT NULL;", | |
| 724 | + &fname, &fname, &fname | |
| 725 | + ); | |
| 712 | 726 | blob_reset(&fname); |
| 713 | 727 | } |
| 714 | 728 | }else{ |
| 715 | 729 | int vid; |
| 716 | 730 | vid = db_lget_int("checkout", 0); |
| @@ -748,11 +762,17 @@ | ||
| 748 | 762 | }else{ |
| 749 | 763 | undo_save(zFile); |
| 750 | 764 | file_delete(zFull); |
| 751 | 765 | fossil_print("DELETE: %s\n", zFile); |
| 752 | 766 | } |
| 753 | - db_multi_exec("DELETE FROM vfile WHERE pathname=%Q", zFile); | |
| 767 | + db_multi_exec( | |
| 768 | + "UPDATE vfile" | |
| 769 | + " SET pathname=origname, origname=NULL" | |
| 770 | + " WHERE pathname=%Q AND origname!=pathname AND origname IS NOT NULL;" | |
| 771 | + "DELETE FROM vfile WHERE pathname=%Q", | |
| 772 | + zFile, zFile | |
| 773 | + ); | |
| 754 | 774 | }else{ |
| 755 | 775 | sqlite3_int64 mtime; |
| 756 | 776 | undo_save(zFile); |
| 757 | 777 | if( file_wd_size(zFull)>=0 && (isLink || file_wd_islink(zFull)) ){ |
| 758 | 778 | file_delete(zFull); |
| @@ -765,18 +785,17 @@ | ||
| 765 | 785 | file_wd_setexe(zFull, isExe); |
| 766 | 786 | fossil_print("REVERTED: %s\n", zFile); |
| 767 | 787 | mtime = file_wd_mtime(zFull); |
| 768 | 788 | db_multi_exec( |
| 769 | 789 | "UPDATE vfile" |
| 770 | - " SET mtime=%lld, chnged=0, deleted=0, isexe=%d, islink=%d,mrid=rid," | |
| 771 | - " pathname=coalesce(origname,pathname), origname=NULL" | |
| 772 | - " WHERE pathname=%Q", | |
| 773 | - mtime, isExe, isLink, zFile | |
| 790 | + " SET mtime=%lld, chnged=0, deleted=0, isexe=%d, islink=%d,mrid=rid" | |
| 791 | + " WHERE pathname=%Q OR origname=%Q", | |
| 792 | + mtime, isExe, isLink, zFile, zFile | |
| 774 | 793 | ); |
| 775 | 794 | } |
| 776 | 795 | blob_reset(&record); |
| 777 | 796 | free(zFull); |
| 778 | 797 | } |
| 779 | 798 | db_finalize(&q); |
| 780 | 799 | undo_finish(); |
| 781 | 800 | db_end_transaction(0); |
| 782 | 801 | } |
| 783 | 802 |
| --- src/update.c | |
| +++ src/update.c | |
| @@ -667,10 +667,13 @@ | |
| 667 | ** Usage: %fossil revert ?-r REVISION? ?FILE ...? |
| 668 | ** |
| 669 | ** Revert to the current repository version of FILE, or to |
| 670 | ** the version associated with baseline REVISION if the -r flag |
| 671 | ** appears. |
| 672 | ** |
| 673 | ** Revert all files if no file name is provided. |
| 674 | ** |
| 675 | ** If a file is reverted accidently, it can be restored using |
| 676 | ** the "fossil undo" command. |
| @@ -706,11 +709,22 @@ | |
| 706 | if( g.argc>2 ){ |
| 707 | for(i=2; i<g.argc; i++){ |
| 708 | Blob fname; |
| 709 | zFile = mprintf("%/", g.argv[i]); |
| 710 | file_tree_name(zFile, &fname, 1); |
| 711 | db_multi_exec("REPLACE INTO torevert VALUES(%B)", &fname); |
| 712 | blob_reset(&fname); |
| 713 | } |
| 714 | }else{ |
| 715 | int vid; |
| 716 | vid = db_lget_int("checkout", 0); |
| @@ -748,11 +762,17 @@ | |
| 748 | }else{ |
| 749 | undo_save(zFile); |
| 750 | file_delete(zFull); |
| 751 | fossil_print("DELETE: %s\n", zFile); |
| 752 | } |
| 753 | db_multi_exec("DELETE FROM vfile WHERE pathname=%Q", zFile); |
| 754 | }else{ |
| 755 | sqlite3_int64 mtime; |
| 756 | undo_save(zFile); |
| 757 | if( file_wd_size(zFull)>=0 && (isLink || file_wd_islink(zFull)) ){ |
| 758 | file_delete(zFull); |
| @@ -765,18 +785,17 @@ | |
| 765 | file_wd_setexe(zFull, isExe); |
| 766 | fossil_print("REVERTED: %s\n", zFile); |
| 767 | mtime = file_wd_mtime(zFull); |
| 768 | db_multi_exec( |
| 769 | "UPDATE vfile" |
| 770 | " SET mtime=%lld, chnged=0, deleted=0, isexe=%d, islink=%d,mrid=rid," |
| 771 | " pathname=coalesce(origname,pathname), origname=NULL" |
| 772 | " WHERE pathname=%Q", |
| 773 | mtime, isExe, isLink, zFile |
| 774 | ); |
| 775 | } |
| 776 | blob_reset(&record); |
| 777 | free(zFull); |
| 778 | } |
| 779 | db_finalize(&q); |
| 780 | undo_finish(); |
| 781 | db_end_transaction(0); |
| 782 | } |
| 783 |
| --- src/update.c | |
| +++ src/update.c | |
| @@ -667,10 +667,13 @@ | |
| 667 | ** Usage: %fossil revert ?-r REVISION? ?FILE ...? |
| 668 | ** |
| 669 | ** Revert to the current repository version of FILE, or to |
| 670 | ** the version associated with baseline REVISION if the -r flag |
| 671 | ** appears. |
| 672 | ** |
| 673 | ** If FILE was part of a rename operation, both the original file |
| 674 | ** and the renamed file are reverted. |
| 675 | ** |
| 676 | ** Revert all files if no file name is provided. |
| 677 | ** |
| 678 | ** If a file is reverted accidently, it can be restored using |
| 679 | ** the "fossil undo" command. |
| @@ -706,11 +709,22 @@ | |
| 709 | if( g.argc>2 ){ |
| 710 | for(i=2; i<g.argc; i++){ |
| 711 | Blob fname; |
| 712 | zFile = mprintf("%/", g.argv[i]); |
| 713 | file_tree_name(zFile, &fname, 1); |
| 714 | db_multi_exec( |
| 715 | "REPLACE INTO torevert VALUES(%B);" |
| 716 | "INSERT OR IGNORE INTO torevert" |
| 717 | " SELECT pathname" |
| 718 | " FROM vfile" |
| 719 | " WHERE origname IN(%B)" |
| 720 | " UNION ALL" |
| 721 | " SELECT origname" |
| 722 | " FROM vfile" |
| 723 | " WHERE pathname IN(%B) AND origname IS NOT NULL;", |
| 724 | &fname, &fname, &fname |
| 725 | ); |
| 726 | blob_reset(&fname); |
| 727 | } |
| 728 | }else{ |
| 729 | int vid; |
| 730 | vid = db_lget_int("checkout", 0); |
| @@ -748,11 +762,17 @@ | |
| 762 | }else{ |
| 763 | undo_save(zFile); |
| 764 | file_delete(zFull); |
| 765 | fossil_print("DELETE: %s\n", zFile); |
| 766 | } |
| 767 | db_multi_exec( |
| 768 | "UPDATE vfile" |
| 769 | " SET pathname=origname, origname=NULL" |
| 770 | " WHERE pathname=%Q AND origname!=pathname AND origname IS NOT NULL;" |
| 771 | "DELETE FROM vfile WHERE pathname=%Q", |
| 772 | zFile, zFile |
| 773 | ); |
| 774 | }else{ |
| 775 | sqlite3_int64 mtime; |
| 776 | undo_save(zFile); |
| 777 | if( file_wd_size(zFull)>=0 && (isLink || file_wd_islink(zFull)) ){ |
| 778 | file_delete(zFull); |
| @@ -765,18 +785,17 @@ | |
| 785 | file_wd_setexe(zFull, isExe); |
| 786 | fossil_print("REVERTED: %s\n", zFile); |
| 787 | mtime = file_wd_mtime(zFull); |
| 788 | db_multi_exec( |
| 789 | "UPDATE vfile" |
| 790 | " SET mtime=%lld, chnged=0, deleted=0, isexe=%d, islink=%d,mrid=rid" |
| 791 | " WHERE pathname=%Q OR origname=%Q", |
| 792 | mtime, isExe, isLink, zFile, zFile |
| 793 | ); |
| 794 | } |
| 795 | blob_reset(&record); |
| 796 | free(zFull); |
| 797 | } |
| 798 | db_finalize(&q); |
| 799 | undo_finish(); |
| 800 | db_end_transaction(0); |
| 801 | } |
| 802 |
+11
-2
| --- src/xfer.c | ||
| +++ src/xfer.c | ||
| @@ -18,10 +18,12 @@ | ||
| 18 | 18 | ** This file contains code to implement the file transfer protocol. |
| 19 | 19 | */ |
| 20 | 20 | #include "config.h" |
| 21 | 21 | #include "xfer.h" |
| 22 | 22 | |
| 23 | +#include <time.h> | |
| 24 | + | |
| 23 | 25 | /* |
| 24 | 26 | ** This structure holds information about the current state of either |
| 25 | 27 | ** a client or a server that is participating in xfer. |
| 26 | 28 | */ |
| 27 | 29 | typedef struct Xfer Xfer; |
| @@ -40,10 +42,11 @@ | ||
| 40 | 42 | int nDeltaRcvd; /* Number of deltas received */ |
| 41 | 43 | int nDanglingFile; /* Number of dangling deltas received */ |
| 42 | 44 | int mxSend; /* Stop sending "file" with pOut reaches this size */ |
| 43 | 45 | u8 syncPrivate; /* True to enable syncing private content */ |
| 44 | 46 | u8 nextIsPrivate; /* If true, next "file" received is a private */ |
| 47 | + time_t maxTime; /* Time when this transfer should be finished */ | |
| 45 | 48 | }; |
| 46 | 49 | |
| 47 | 50 | |
| 48 | 51 | /* |
| 49 | 52 | ** The input blob contains a UUID. Convert it into a record ID. |
| @@ -393,11 +396,12 @@ | ||
| 393 | 396 | } |
| 394 | 397 | if( uuid_is_shunned(blob_str(pUuid)) ){ |
| 395 | 398 | blob_reset(&uuid); |
| 396 | 399 | return; |
| 397 | 400 | } |
| 398 | - if( pXfer->mxSend<=blob_size(pXfer->pOut) ){ | |
| 401 | + if( (pXfer->maxTime != -1 && time(NULL) >= pXfer->maxTime) || | |
| 402 | + pXfer->mxSend<=blob_size(pXfer->pOut) ){ | |
| 399 | 403 | const char *zFormat = isPriv ? "igot %b 1\n" : "igot %b\n"; |
| 400 | 404 | blob_appendf(pXfer->pOut, zFormat, pUuid); |
| 401 | 405 | pXfer->nIGotSent++; |
| 402 | 406 | blob_reset(&uuid); |
| 403 | 407 | return; |
| @@ -867,10 +871,13 @@ | ||
| 867 | 871 | } |
| 868 | 872 | blob_zero(&xfer.err); |
| 869 | 873 | xfer.pIn = &g.cgiIn; |
| 870 | 874 | xfer.pOut = cgi_output_blob(); |
| 871 | 875 | xfer.mxSend = db_get_int("max-download", 5000000); |
| 876 | + xfer.maxTime = db_get_int("max-download-time", 30); | |
| 877 | + if( xfer.maxTime<1 ) xfer.maxTime = 1; | |
| 878 | + xfer.maxTime += time(NULL); | |
| 872 | 879 | g.xferPanic = 1; |
| 873 | 880 | |
| 874 | 881 | db_begin_transaction(); |
| 875 | 882 | db_multi_exec( |
| 876 | 883 | "CREATE TEMP TABLE onremote(rid INTEGER PRIMARY KEY);" |
| @@ -1034,11 +1041,12 @@ | ||
| 1034 | 1041 | if( iVers>=3 ){ |
| 1035 | 1042 | cgi_set_content_type("application/x-fossil-uncompressed"); |
| 1036 | 1043 | } |
| 1037 | 1044 | blob_is_int(&xfer.aToken[2], &seqno); |
| 1038 | 1045 | max = db_int(0, "SELECT max(rid) FROM blob"); |
| 1039 | - while( xfer.mxSend>blob_size(xfer.pOut) && seqno<=max ){ | |
| 1046 | + while( xfer.mxSend>blob_size(xfer.pOut) && seqno<=max){ | |
| 1047 | + if( time(NULL) >= xfer.maxTime ) break; | |
| 1040 | 1048 | if( iVers>=3 ){ |
| 1041 | 1049 | send_compressed_file(&xfer, seqno); |
| 1042 | 1050 | }else{ |
| 1043 | 1051 | send_file(&xfer, seqno, 0, 1); |
| 1044 | 1052 | } |
| @@ -1328,10 +1336,11 @@ | ||
| 1328 | 1336 | socket_global_init(); |
| 1329 | 1337 | memset(&xfer, 0, sizeof(xfer)); |
| 1330 | 1338 | xfer.pIn = &recv; |
| 1331 | 1339 | xfer.pOut = &send; |
| 1332 | 1340 | xfer.mxSend = db_get_int("max-upload", 250000); |
| 1341 | + xfer.maxTime = -1; | |
| 1333 | 1342 | if( syncFlags & SYNC_PRIVATE ){ |
| 1334 | 1343 | g.perm.Private = 1; |
| 1335 | 1344 | xfer.syncPrivate = 1; |
| 1336 | 1345 | } |
| 1337 | 1346 | |
| 1338 | 1347 | |
| 1339 | 1348 | ADDED test/revert.test |
| --- src/xfer.c | |
| +++ src/xfer.c | |
| @@ -18,10 +18,12 @@ | |
| 18 | ** This file contains code to implement the file transfer protocol. |
| 19 | */ |
| 20 | #include "config.h" |
| 21 | #include "xfer.h" |
| 22 | |
| 23 | /* |
| 24 | ** This structure holds information about the current state of either |
| 25 | ** a client or a server that is participating in xfer. |
| 26 | */ |
| 27 | typedef struct Xfer Xfer; |
| @@ -40,10 +42,11 @@ | |
| 40 | int nDeltaRcvd; /* Number of deltas received */ |
| 41 | int nDanglingFile; /* Number of dangling deltas received */ |
| 42 | int mxSend; /* Stop sending "file" with pOut reaches this size */ |
| 43 | u8 syncPrivate; /* True to enable syncing private content */ |
| 44 | u8 nextIsPrivate; /* If true, next "file" received is a private */ |
| 45 | }; |
| 46 | |
| 47 | |
| 48 | /* |
| 49 | ** The input blob contains a UUID. Convert it into a record ID. |
| @@ -393,11 +396,12 @@ | |
| 393 | } |
| 394 | if( uuid_is_shunned(blob_str(pUuid)) ){ |
| 395 | blob_reset(&uuid); |
| 396 | return; |
| 397 | } |
| 398 | if( pXfer->mxSend<=blob_size(pXfer->pOut) ){ |
| 399 | const char *zFormat = isPriv ? "igot %b 1\n" : "igot %b\n"; |
| 400 | blob_appendf(pXfer->pOut, zFormat, pUuid); |
| 401 | pXfer->nIGotSent++; |
| 402 | blob_reset(&uuid); |
| 403 | return; |
| @@ -867,10 +871,13 @@ | |
| 867 | } |
| 868 | blob_zero(&xfer.err); |
| 869 | xfer.pIn = &g.cgiIn; |
| 870 | xfer.pOut = cgi_output_blob(); |
| 871 | xfer.mxSend = db_get_int("max-download", 5000000); |
| 872 | g.xferPanic = 1; |
| 873 | |
| 874 | db_begin_transaction(); |
| 875 | db_multi_exec( |
| 876 | "CREATE TEMP TABLE onremote(rid INTEGER PRIMARY KEY);" |
| @@ -1034,11 +1041,12 @@ | |
| 1034 | if( iVers>=3 ){ |
| 1035 | cgi_set_content_type("application/x-fossil-uncompressed"); |
| 1036 | } |
| 1037 | blob_is_int(&xfer.aToken[2], &seqno); |
| 1038 | max = db_int(0, "SELECT max(rid) FROM blob"); |
| 1039 | while( xfer.mxSend>blob_size(xfer.pOut) && seqno<=max ){ |
| 1040 | if( iVers>=3 ){ |
| 1041 | send_compressed_file(&xfer, seqno); |
| 1042 | }else{ |
| 1043 | send_file(&xfer, seqno, 0, 1); |
| 1044 | } |
| @@ -1328,10 +1336,11 @@ | |
| 1328 | socket_global_init(); |
| 1329 | memset(&xfer, 0, sizeof(xfer)); |
| 1330 | xfer.pIn = &recv; |
| 1331 | xfer.pOut = &send; |
| 1332 | xfer.mxSend = db_get_int("max-upload", 250000); |
| 1333 | if( syncFlags & SYNC_PRIVATE ){ |
| 1334 | g.perm.Private = 1; |
| 1335 | xfer.syncPrivate = 1; |
| 1336 | } |
| 1337 | |
| 1338 | |
| 1339 | DDED test/revert.test |
| --- src/xfer.c | |
| +++ src/xfer.c | |
| @@ -18,10 +18,12 @@ | |
| 18 | ** This file contains code to implement the file transfer protocol. |
| 19 | */ |
| 20 | #include "config.h" |
| 21 | #include "xfer.h" |
| 22 | |
| 23 | #include <time.h> |
| 24 | |
| 25 | /* |
| 26 | ** This structure holds information about the current state of either |
| 27 | ** a client or a server that is participating in xfer. |
| 28 | */ |
| 29 | typedef struct Xfer Xfer; |
| @@ -40,10 +42,11 @@ | |
| 42 | int nDeltaRcvd; /* Number of deltas received */ |
| 43 | int nDanglingFile; /* Number of dangling deltas received */ |
| 44 | int mxSend; /* Stop sending "file" with pOut reaches this size */ |
| 45 | u8 syncPrivate; /* True to enable syncing private content */ |
| 46 | u8 nextIsPrivate; /* If true, next "file" received is a private */ |
| 47 | time_t maxTime; /* Time when this transfer should be finished */ |
| 48 | }; |
| 49 | |
| 50 | |
| 51 | /* |
| 52 | ** The input blob contains a UUID. Convert it into a record ID. |
| @@ -393,11 +396,12 @@ | |
| 396 | } |
| 397 | if( uuid_is_shunned(blob_str(pUuid)) ){ |
| 398 | blob_reset(&uuid); |
| 399 | return; |
| 400 | } |
| 401 | if( (pXfer->maxTime != -1 && time(NULL) >= pXfer->maxTime) || |
| 402 | pXfer->mxSend<=blob_size(pXfer->pOut) ){ |
| 403 | const char *zFormat = isPriv ? "igot %b 1\n" : "igot %b\n"; |
| 404 | blob_appendf(pXfer->pOut, zFormat, pUuid); |
| 405 | pXfer->nIGotSent++; |
| 406 | blob_reset(&uuid); |
| 407 | return; |
| @@ -867,10 +871,13 @@ | |
| 871 | } |
| 872 | blob_zero(&xfer.err); |
| 873 | xfer.pIn = &g.cgiIn; |
| 874 | xfer.pOut = cgi_output_blob(); |
| 875 | xfer.mxSend = db_get_int("max-download", 5000000); |
| 876 | xfer.maxTime = db_get_int("max-download-time", 30); |
| 877 | if( xfer.maxTime<1 ) xfer.maxTime = 1; |
| 878 | xfer.maxTime += time(NULL); |
| 879 | g.xferPanic = 1; |
| 880 | |
| 881 | db_begin_transaction(); |
| 882 | db_multi_exec( |
| 883 | "CREATE TEMP TABLE onremote(rid INTEGER PRIMARY KEY);" |
| @@ -1034,11 +1041,12 @@ | |
| 1041 | if( iVers>=3 ){ |
| 1042 | cgi_set_content_type("application/x-fossil-uncompressed"); |
| 1043 | } |
| 1044 | blob_is_int(&xfer.aToken[2], &seqno); |
| 1045 | max = db_int(0, "SELECT max(rid) FROM blob"); |
| 1046 | while( xfer.mxSend>blob_size(xfer.pOut) && seqno<=max){ |
| 1047 | if( time(NULL) >= xfer.maxTime ) break; |
| 1048 | if( iVers>=3 ){ |
| 1049 | send_compressed_file(&xfer, seqno); |
| 1050 | }else{ |
| 1051 | send_file(&xfer, seqno, 0, 1); |
| 1052 | } |
| @@ -1328,10 +1336,11 @@ | |
| 1336 | socket_global_init(); |
| 1337 | memset(&xfer, 0, sizeof(xfer)); |
| 1338 | xfer.pIn = &recv; |
| 1339 | xfer.pOut = &send; |
| 1340 | xfer.mxSend = db_get_int("max-upload", 250000); |
| 1341 | xfer.maxTime = -1; |
| 1342 | if( syncFlags & SYNC_PRIVATE ){ |
| 1343 | g.perm.Private = 1; |
| 1344 | xfer.syncPrivate = 1; |
| 1345 | } |
| 1346 | |
| 1347 | |
| 1348 | DDED test/revert.test |
+26
| --- a/test/revert.test | ||
| +++ b/test/revert.test | ||
| @@ -0,0 +1,26 @@ | ||
| 1 | +puts res=$# Fossil will write dexec rm f1po_init# Copytcatch {exec $::exec mvitory} $res]} { | |
| 2 | + puts stderr "Cannot ris test within anif {[llength $args] % 2} { | |
| 3 | + set revertArgs [lindex $args 0] | |
| 4 | + set args [lrange $args 1 end] | |
| 5 | + } else { | |
| 6 | + set revertArgs {} | |
| 7 | + }VERTED: f3 | |
| 8 | + DELETE:j@Uy,1X@Se,4:: f0G@YW,21@UT,3:ED:1w@WW,3:ED:3t@YT,G:ED: f3 | |
| 9 | + DELETE:K@hF,O@YG:ED: f3 | |
| 10 | + DELETE:K@hF,O@Yl,1 | |
| 11 | + DELETE:a@nd,1WvoIq;-testnormalize-status-list] | |
| 12 | + set r] | |
| 13 | + if {$result ne $expected} { | |
| 14 | + set passed 0 | |
| 15 | + protOut " Expected:\n [join $expected "\n "]" | |
| 16 | + protOut " Got:\n [joh {exec $::fossilexe info} res | |
| 17 | +if {![regexp {use --repository} $res]} { | |
| 18 | + puts stderr "Cannot run this test within an open checkout" | |
| 19 | + return | |
| 20 | +} | |
| 21 | +repo_init# Copytcatch {exec $::fossilexe info} res | |
| 22 | +if {![regexp {use --repository} $res]} { | |
| 23 | + puts stderr "Cannot run this test witt# Copytcatch {exec $::fossilexe info} res | |
| 24 | +if {![regexp {use --repository} $res]} { | |
| 25 | + put12 f03 f1 {exec $::fossilexe info}t# Copytcatch {exec $::foss4 f2 {exec $::ft# Copytcatch {exec $::foss5 f3 {exec $::fossilexe info} res | |
| 26 | +if {![re |
| --- a/test/revert.test | |
| +++ b/test/revert.test | |
| @@ -0,0 +1,26 @@ | |
| --- a/test/revert.test | |
| +++ b/test/revert.test | |
| @@ -0,0 +1,26 @@ | |
| 1 | puts res=$# Fossil will write dexec rm f1po_init# Copytcatch {exec $::exec mvitory} $res]} { |
| 2 | puts stderr "Cannot ris test within anif {[llength $args] % 2} { |
| 3 | set revertArgs [lindex $args 0] |
| 4 | set args [lrange $args 1 end] |
| 5 | } else { |
| 6 | set revertArgs {} |
| 7 | }VERTED: f3 |
| 8 | DELETE:j@Uy,1X@Se,4:: f0G@YW,21@UT,3:ED:1w@WW,3:ED:3t@YT,G:ED: f3 |
| 9 | DELETE:K@hF,O@YG:ED: f3 |
| 10 | DELETE:K@hF,O@Yl,1 |
| 11 | DELETE:a@nd,1WvoIq;-testnormalize-status-list] |
| 12 | set r] |
| 13 | if {$result ne $expected} { |
| 14 | set passed 0 |
| 15 | protOut " Expected:\n [join $expected "\n "]" |
| 16 | protOut " Got:\n [joh {exec $::fossilexe info} res |
| 17 | if {![regexp {use --repository} $res]} { |
| 18 | puts stderr "Cannot run this test within an open checkout" |
| 19 | return |
| 20 | } |
| 21 | repo_init# Copytcatch {exec $::fossilexe info} res |
| 22 | if {![regexp {use --repository} $res]} { |
| 23 | puts stderr "Cannot run this test witt# Copytcatch {exec $::fossilexe info} res |
| 24 | if {![regexp {use --repository} $res]} { |
| 25 | put12 f03 f1 {exec $::fossilexe info}t# Copytcatch {exec $::foss4 f2 {exec $::ft# Copytcatch {exec $::foss5 f3 {exec $::fossilexe info} res |
| 26 | if {![re |
+1
-1
| --- win/Makefile.mingw | ||
| +++ win/Makefile.mingw | ||
| @@ -1633,11 +1633,11 @@ | ||
| 1633 | 1633 | |
| 1634 | 1634 | $(OBJDIR)/sqlite3.o: $(SRCDIR)/sqlite3.c |
| 1635 | 1635 | $(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 |
| 1636 | 1636 | |
| 1637 | 1637 | $(OBJDIR)/cson_amalgamation.o: $(SRCDIR)/cson_amalgamation.c |
| 1638 | - $(XTCC) -c $(SRCDIR)/cson_amalgamation.c -o $(OBJDIR)/cson_amalgamation.o -DCSON_FOSSIL_MODE | |
| 1638 | + $(XTCC) -c $(SRCDIR)/cson_amalgamation.c -o $(OBJDIR)/cson_amalgamation.o | |
| 1639 | 1639 | |
| 1640 | 1640 | $(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 |
| 1641 | 1641 | |
| 1642 | 1642 | $(OBJDIR)/shell.o: $(SRCDIR)/shell.c $(SRCDIR)/sqlite3.h |
| 1643 | 1643 | $(XTCC) -Dmain=sqlite3_shell -DSQLITE_OMIT_LOAD_EXTENSION=1 -c $(SRCDIR)/shell.c -o $(OBJDIR)/shell.o |
| 1644 | 1644 |
| --- win/Makefile.mingw | |
| +++ win/Makefile.mingw | |
| @@ -1633,11 +1633,11 @@ | |
| 1633 | |
| 1634 | $(OBJDIR)/sqlite3.o: $(SRCDIR)/sqlite3.c |
| 1635 | $(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 |
| 1636 | |
| 1637 | $(OBJDIR)/cson_amalgamation.o: $(SRCDIR)/cson_amalgamation.c |
| 1638 | $(XTCC) -c $(SRCDIR)/cson_amalgamation.c -o $(OBJDIR)/cson_amalgamation.o -DCSON_FOSSIL_MODE |
| 1639 | |
| 1640 | $(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 |
| 1641 | |
| 1642 | $(OBJDIR)/shell.o: $(SRCDIR)/shell.c $(SRCDIR)/sqlite3.h |
| 1643 | $(XTCC) -Dmain=sqlite3_shell -DSQLITE_OMIT_LOAD_EXTENSION=1 -c $(SRCDIR)/shell.c -o $(OBJDIR)/shell.o |
| 1644 |
| --- win/Makefile.mingw | |
| +++ win/Makefile.mingw | |
| @@ -1633,11 +1633,11 @@ | |
| 1633 | |
| 1634 | $(OBJDIR)/sqlite3.o: $(SRCDIR)/sqlite3.c |
| 1635 | $(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 |
| 1636 | |
| 1637 | $(OBJDIR)/cson_amalgamation.o: $(SRCDIR)/cson_amalgamation.c |
| 1638 | $(XTCC) -c $(SRCDIR)/cson_amalgamation.c -o $(OBJDIR)/cson_amalgamation.o |
| 1639 | |
| 1640 | $(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 |
| 1641 | |
| 1642 | $(OBJDIR)/shell.o: $(SRCDIR)/shell.c $(SRCDIR)/sqlite3.h |
| 1643 | $(XTCC) -Dmain=sqlite3_shell -DSQLITE_OMIT_LOAD_EXTENSION=1 -c $(SRCDIR)/shell.c -o $(OBJDIR)/shell.o |
| 1644 |
+30
-7
| --- win/Makefile.msc | ||
| +++ win/Makefile.msc | ||
| @@ -22,18 +22,35 @@ | ||
| 22 | 22 | |
| 23 | 23 | # zlib options |
| 24 | 24 | ZINCDIR = $(B)\compat\zlib |
| 25 | 25 | ZLIBDIR = $(B)\compat\zlib |
| 26 | 26 | ZLIB = zlib.lib |
| 27 | + | |
| 28 | +# Uncomment to enable JSON API | |
| 29 | +# FOSSIL_ENABLE_JSON = 1 | |
| 30 | + | |
| 31 | +# Uncomment to enable markdown support | |
| 32 | +# FOSSIL_ENABLE_MARKDOWN = 1 | |
| 27 | 33 | |
| 28 | 34 | INCL = -I. -I$(SRCDIR) -I$B\win\include -I$(ZINCDIR) |
| 29 | 35 | |
| 30 | 36 | CFLAGS = -nologo -MT -O2 |
| 31 | 37 | BCC = $(CC) $(CFLAGS) |
| 32 | 38 | TCC = $(CC) -c $(CFLAGS) $(MSCDEF) $(SSL) $(INCL) |
| 39 | +RCC = rc -D_WIN32 -D_MSC_VER $(INCL) | |
| 33 | 40 | LIBS = $(ZLIB) ws2_32.lib advapi32.lib $(SSLLIB) |
| 34 | 41 | LIBDIR = -LIBPATH:$(ZLIBDIR) |
| 42 | + | |
| 43 | +!ifdef FOSSIL_ENABLE_JSON | |
| 44 | +TCC = $(TCC) -DFOSSIL_ENABLE_JSON | |
| 45 | +RCC = $(RCC) -DFOSSIL_ENABLE_JSON | |
| 46 | +!endif | |
| 47 | + | |
| 48 | +!ifdef FOSSIL_ENABLE_MARKDOWN | |
| 49 | +TCC = $(TCC) -DFOSSIL_ENABLE_MARKDOWN | |
| 50 | +RCC = $(RCC) -DFOSSIL_ENABLE_MARKDOWN | |
| 51 | +!endif | |
| 35 | 52 | |
| 36 | 53 | SQLITE_OPTIONS = /DSQLITE_OMIT_LOAD_EXTENSION=1 \ |
| 37 | 54 | /DSQLITE_THREADSAFE=0 \ |
| 38 | 55 | /DSQLITE_DEFAULT_FILE_FORMAT=4 \ |
| 39 | 56 | /DSQLITE_ENABLE_STAT3 \ |
| @@ -160,10 +177,11 @@ | ||
| 160 | 177 | $(OX)\clearsign$O \ |
| 161 | 178 | $(OX)\clone$O \ |
| 162 | 179 | $(OX)\comformat$O \ |
| 163 | 180 | $(OX)\configure$O \ |
| 164 | 181 | $(OX)\content$O \ |
| 182 | + $(OX)\cson_amalgamation$O \ | |
| 165 | 183 | $(OX)\db$O \ |
| 166 | 184 | $(OX)\delta$O \ |
| 167 | 185 | $(OX)\deltacmd$O \ |
| 168 | 186 | $(OX)\descendants$O \ |
| 169 | 187 | $(OX)\diff$O \ |
| @@ -219,19 +237,23 @@ | ||
| 219 | 237 | $(OX)\rss$O \ |
| 220 | 238 | $(OX)\schema$O \ |
| 221 | 239 | $(OX)\search$O \ |
| 222 | 240 | $(OX)\setup$O \ |
| 223 | 241 | $(OX)\sha1$O \ |
| 242 | + $(OX)\shell$O \ | |
| 224 | 243 | $(OX)\shun$O \ |
| 225 | 244 | $(OX)\skins$O \ |
| 226 | 245 | $(OX)\sqlcmd$O \ |
| 246 | + $(OX)\sqlite3$O \ | |
| 227 | 247 | $(OX)\stash$O \ |
| 228 | 248 | $(OX)\stat$O \ |
| 229 | 249 | $(OX)\style$O \ |
| 230 | 250 | $(OX)\sync$O \ |
| 231 | 251 | $(OX)\tag$O \ |
| 232 | 252 | $(OX)\tar$O \ |
| 253 | + $(OX)\th$O \ | |
| 254 | + $(OX)\th_lang$O \ | |
| 233 | 255 | $(OX)\th_main$O \ |
| 234 | 256 | $(OX)\timeline$O \ |
| 235 | 257 | $(OX)\tkt$O \ |
| 236 | 258 | $(OX)\tktsetup$O \ |
| 237 | 259 | $(OX)\undo$O \ |
| @@ -247,14 +269,11 @@ | ||
| 247 | 269 | $(OX)\winhttp$O \ |
| 248 | 270 | $(OX)\wysiwyg$O \ |
| 249 | 271 | $(OX)\xfer$O \ |
| 250 | 272 | $(OX)\xfersetup$O \ |
| 251 | 273 | $(OX)\zip$O \ |
| 252 | - $(OX)\shell$O \ | |
| 253 | - $(OX)\sqlite3$O \ | |
| 254 | - $(OX)\th$O \ | |
| 255 | - $(OX)\th_lang$O | |
| 274 | + $(OX)\fossil.res | |
| 256 | 275 | |
| 257 | 276 | APPNAME = $(OX)\fossil$(E) |
| 258 | 277 | |
| 259 | 278 | all: $(OX) $(APPNAME) |
| 260 | 279 | |
| @@ -262,11 +281,11 @@ | ||
| 262 | 281 | @echo Building zlib from "$(ZLIBDIR)"... |
| 263 | 282 | @pushd "$(ZLIBDIR)" && nmake /f win32\Makefile.msc $(ZLIB) && popd |
| 264 | 283 | |
| 265 | 284 | $(APPNAME) : translate$E mkindex$E headers $(OBJ) $(OX)\linkopts zlib |
| 266 | 285 | cd $(OX) |
| 267 | - link /NODEFAULTLIB:msvcrt -OUT:$@ $(LIBDIR) Wsetargv.obj @linkopts | |
| 286 | + link /NODEFAULTLIB:msvcrt -OUT:$@ $(LIBDIR) Wsetargv.obj fossil.res @linkopts | |
| 268 | 287 | |
| 269 | 288 | $(OX)\linkopts: $B\win\Makefile.msc |
| 270 | 289 | echo $(OX)\add.obj > $@ |
| 271 | 290 | echo $(OX)\allrepo.obj >> $@ |
| 272 | 291 | echo $(OX)\attach.obj >> $@ |
| @@ -282,10 +301,11 @@ | ||
| 282 | 301 | echo $(OX)\clearsign.obj >> $@ |
| 283 | 302 | echo $(OX)\clone.obj >> $@ |
| 284 | 303 | echo $(OX)\comformat.obj >> $@ |
| 285 | 304 | echo $(OX)\configure.obj >> $@ |
| 286 | 305 | echo $(OX)\content.obj >> $@ |
| 306 | + echo $(OX)\cson_amalgamation.obj >> $@ | |
| 287 | 307 | echo $(OX)\db.obj >> $@ |
| 288 | 308 | echo $(OX)\delta.obj >> $@ |
| 289 | 309 | echo $(OX)\deltacmd.obj >> $@ |
| 290 | 310 | echo $(OX)\descendants.obj >> $@ |
| 291 | 311 | echo $(OX)\diff.obj >> $@ |
| @@ -407,12 +427,12 @@ | ||
| 407 | 427 | $(OX)\th_lang$O : $(SRCDIR)\th_lang.c |
| 408 | 428 | $(TCC) /Fo$@ -c $** |
| 409 | 429 | |
| 410 | 430 | VERSION.h : mkversion$E $B\manifest.uuid $B\manifest $B\VERSION |
| 411 | 431 | $** > $@ |
| 412 | -$(OBJDIR)\cson_amalgamation.h : $(SRCDIR)\cson_amalgamation.h | |
| 413 | - cp $(SRCDIR)\cson_amalgamation.h $@ | |
| 432 | +$(OX)\cson_amalgamation$O : $(SRCDIR)\cson_amalgamation.c | |
| 433 | + $(TCC) /Fo$@ -c $** | |
| 414 | 434 | |
| 415 | 435 | page_index.h: mkindex$E $(SRC) |
| 416 | 436 | $** > $@ |
| 417 | 437 | |
| 418 | 438 | clean: |
| @@ -422,10 +442,11 @@ | ||
| 422 | 442 | -del *.h |
| 423 | 443 | -del *.map |
| 424 | 444 | -del *.manifest |
| 425 | 445 | -del headers |
| 426 | 446 | -del linkopts |
| 447 | + -del *.res | |
| 427 | 448 | |
| 428 | 449 | realclean: clean |
| 429 | 450 | -del $(APPNAME) |
| 430 | 451 | -del translate$E |
| 431 | 452 | -del mkindex$E |
| @@ -1070,10 +1091,12 @@ | ||
| 1070 | 1091 | $(TCC) /Fo$@ -c zip_.c |
| 1071 | 1092 | |
| 1072 | 1093 | zip_.c : $(SRCDIR)\zip.c |
| 1073 | 1094 | translate$E $** > $@ |
| 1074 | 1095 | |
| 1096 | +fossil.res : $B\win\fossil.rc | |
| 1097 | + $(RCC) -fo $@ $** | |
| 1075 | 1098 | headers: makeheaders$E page_index.h VERSION.h |
| 1076 | 1099 | makeheaders$E add_.c:add.h \ |
| 1077 | 1100 | allrepo_.c:allrepo.h \ |
| 1078 | 1101 | attach_.c:attach.h \ |
| 1079 | 1102 | bag_.c:bag.h \ |
| 1080 | 1103 |
| --- win/Makefile.msc | |
| +++ win/Makefile.msc | |
| @@ -22,18 +22,35 @@ | |
| 22 | |
| 23 | # zlib options |
| 24 | ZINCDIR = $(B)\compat\zlib |
| 25 | ZLIBDIR = $(B)\compat\zlib |
| 26 | ZLIB = zlib.lib |
| 27 | |
| 28 | INCL = -I. -I$(SRCDIR) -I$B\win\include -I$(ZINCDIR) |
| 29 | |
| 30 | CFLAGS = -nologo -MT -O2 |
| 31 | BCC = $(CC) $(CFLAGS) |
| 32 | TCC = $(CC) -c $(CFLAGS) $(MSCDEF) $(SSL) $(INCL) |
| 33 | LIBS = $(ZLIB) ws2_32.lib advapi32.lib $(SSLLIB) |
| 34 | LIBDIR = -LIBPATH:$(ZLIBDIR) |
| 35 | |
| 36 | SQLITE_OPTIONS = /DSQLITE_OMIT_LOAD_EXTENSION=1 \ |
| 37 | /DSQLITE_THREADSAFE=0 \ |
| 38 | /DSQLITE_DEFAULT_FILE_FORMAT=4 \ |
| 39 | /DSQLITE_ENABLE_STAT3 \ |
| @@ -160,10 +177,11 @@ | |
| 160 | $(OX)\clearsign$O \ |
| 161 | $(OX)\clone$O \ |
| 162 | $(OX)\comformat$O \ |
| 163 | $(OX)\configure$O \ |
| 164 | $(OX)\content$O \ |
| 165 | $(OX)\db$O \ |
| 166 | $(OX)\delta$O \ |
| 167 | $(OX)\deltacmd$O \ |
| 168 | $(OX)\descendants$O \ |
| 169 | $(OX)\diff$O \ |
| @@ -219,19 +237,23 @@ | |
| 219 | $(OX)\rss$O \ |
| 220 | $(OX)\schema$O \ |
| 221 | $(OX)\search$O \ |
| 222 | $(OX)\setup$O \ |
| 223 | $(OX)\sha1$O \ |
| 224 | $(OX)\shun$O \ |
| 225 | $(OX)\skins$O \ |
| 226 | $(OX)\sqlcmd$O \ |
| 227 | $(OX)\stash$O \ |
| 228 | $(OX)\stat$O \ |
| 229 | $(OX)\style$O \ |
| 230 | $(OX)\sync$O \ |
| 231 | $(OX)\tag$O \ |
| 232 | $(OX)\tar$O \ |
| 233 | $(OX)\th_main$O \ |
| 234 | $(OX)\timeline$O \ |
| 235 | $(OX)\tkt$O \ |
| 236 | $(OX)\tktsetup$O \ |
| 237 | $(OX)\undo$O \ |
| @@ -247,14 +269,11 @@ | |
| 247 | $(OX)\winhttp$O \ |
| 248 | $(OX)\wysiwyg$O \ |
| 249 | $(OX)\xfer$O \ |
| 250 | $(OX)\xfersetup$O \ |
| 251 | $(OX)\zip$O \ |
| 252 | $(OX)\shell$O \ |
| 253 | $(OX)\sqlite3$O \ |
| 254 | $(OX)\th$O \ |
| 255 | $(OX)\th_lang$O |
| 256 | |
| 257 | APPNAME = $(OX)\fossil$(E) |
| 258 | |
| 259 | all: $(OX) $(APPNAME) |
| 260 | |
| @@ -262,11 +281,11 @@ | |
| 262 | @echo Building zlib from "$(ZLIBDIR)"... |
| 263 | @pushd "$(ZLIBDIR)" && nmake /f win32\Makefile.msc $(ZLIB) && popd |
| 264 | |
| 265 | $(APPNAME) : translate$E mkindex$E headers $(OBJ) $(OX)\linkopts zlib |
| 266 | cd $(OX) |
| 267 | link /NODEFAULTLIB:msvcrt -OUT:$@ $(LIBDIR) Wsetargv.obj @linkopts |
| 268 | |
| 269 | $(OX)\linkopts: $B\win\Makefile.msc |
| 270 | echo $(OX)\add.obj > $@ |
| 271 | echo $(OX)\allrepo.obj >> $@ |
| 272 | echo $(OX)\attach.obj >> $@ |
| @@ -282,10 +301,11 @@ | |
| 282 | echo $(OX)\clearsign.obj >> $@ |
| 283 | echo $(OX)\clone.obj >> $@ |
| 284 | echo $(OX)\comformat.obj >> $@ |
| 285 | echo $(OX)\configure.obj >> $@ |
| 286 | echo $(OX)\content.obj >> $@ |
| 287 | echo $(OX)\db.obj >> $@ |
| 288 | echo $(OX)\delta.obj >> $@ |
| 289 | echo $(OX)\deltacmd.obj >> $@ |
| 290 | echo $(OX)\descendants.obj >> $@ |
| 291 | echo $(OX)\diff.obj >> $@ |
| @@ -407,12 +427,12 @@ | |
| 407 | $(OX)\th_lang$O : $(SRCDIR)\th_lang.c |
| 408 | $(TCC) /Fo$@ -c $** |
| 409 | |
| 410 | VERSION.h : mkversion$E $B\manifest.uuid $B\manifest $B\VERSION |
| 411 | $** > $@ |
| 412 | $(OBJDIR)\cson_amalgamation.h : $(SRCDIR)\cson_amalgamation.h |
| 413 | cp $(SRCDIR)\cson_amalgamation.h $@ |
| 414 | |
| 415 | page_index.h: mkindex$E $(SRC) |
| 416 | $** > $@ |
| 417 | |
| 418 | clean: |
| @@ -422,10 +442,11 @@ | |
| 422 | -del *.h |
| 423 | -del *.map |
| 424 | -del *.manifest |
| 425 | -del headers |
| 426 | -del linkopts |
| 427 | |
| 428 | realclean: clean |
| 429 | -del $(APPNAME) |
| 430 | -del translate$E |
| 431 | -del mkindex$E |
| @@ -1070,10 +1091,12 @@ | |
| 1070 | $(TCC) /Fo$@ -c zip_.c |
| 1071 | |
| 1072 | zip_.c : $(SRCDIR)\zip.c |
| 1073 | translate$E $** > $@ |
| 1074 | |
| 1075 | headers: makeheaders$E page_index.h VERSION.h |
| 1076 | makeheaders$E add_.c:add.h \ |
| 1077 | allrepo_.c:allrepo.h \ |
| 1078 | attach_.c:attach.h \ |
| 1079 | bag_.c:bag.h \ |
| 1080 |
| --- win/Makefile.msc | |
| +++ win/Makefile.msc | |
| @@ -22,18 +22,35 @@ | |
| 22 | |
| 23 | # zlib options |
| 24 | ZINCDIR = $(B)\compat\zlib |
| 25 | ZLIBDIR = $(B)\compat\zlib |
| 26 | ZLIB = zlib.lib |
| 27 | |
| 28 | # Uncomment to enable JSON API |
| 29 | # FOSSIL_ENABLE_JSON = 1 |
| 30 | |
| 31 | # Uncomment to enable markdown support |
| 32 | # FOSSIL_ENABLE_MARKDOWN = 1 |
| 33 | |
| 34 | INCL = -I. -I$(SRCDIR) -I$B\win\include -I$(ZINCDIR) |
| 35 | |
| 36 | CFLAGS = -nologo -MT -O2 |
| 37 | BCC = $(CC) $(CFLAGS) |
| 38 | TCC = $(CC) -c $(CFLAGS) $(MSCDEF) $(SSL) $(INCL) |
| 39 | RCC = rc -D_WIN32 -D_MSC_VER $(INCL) |
| 40 | LIBS = $(ZLIB) ws2_32.lib advapi32.lib $(SSLLIB) |
| 41 | LIBDIR = -LIBPATH:$(ZLIBDIR) |
| 42 | |
| 43 | !ifdef FOSSIL_ENABLE_JSON |
| 44 | TCC = $(TCC) -DFOSSIL_ENABLE_JSON |
| 45 | RCC = $(RCC) -DFOSSIL_ENABLE_JSON |
| 46 | !endif |
| 47 | |
| 48 | !ifdef FOSSIL_ENABLE_MARKDOWN |
| 49 | TCC = $(TCC) -DFOSSIL_ENABLE_MARKDOWN |
| 50 | RCC = $(RCC) -DFOSSIL_ENABLE_MARKDOWN |
| 51 | !endif |
| 52 | |
| 53 | SQLITE_OPTIONS = /DSQLITE_OMIT_LOAD_EXTENSION=1 \ |
| 54 | /DSQLITE_THREADSAFE=0 \ |
| 55 | /DSQLITE_DEFAULT_FILE_FORMAT=4 \ |
| 56 | /DSQLITE_ENABLE_STAT3 \ |
| @@ -160,10 +177,11 @@ | |
| 177 | $(OX)\clearsign$O \ |
| 178 | $(OX)\clone$O \ |
| 179 | $(OX)\comformat$O \ |
| 180 | $(OX)\configure$O \ |
| 181 | $(OX)\content$O \ |
| 182 | $(OX)\cson_amalgamation$O \ |
| 183 | $(OX)\db$O \ |
| 184 | $(OX)\delta$O \ |
| 185 | $(OX)\deltacmd$O \ |
| 186 | $(OX)\descendants$O \ |
| 187 | $(OX)\diff$O \ |
| @@ -219,19 +237,23 @@ | |
| 237 | $(OX)\rss$O \ |
| 238 | $(OX)\schema$O \ |
| 239 | $(OX)\search$O \ |
| 240 | $(OX)\setup$O \ |
| 241 | $(OX)\sha1$O \ |
| 242 | $(OX)\shell$O \ |
| 243 | $(OX)\shun$O \ |
| 244 | $(OX)\skins$O \ |
| 245 | $(OX)\sqlcmd$O \ |
| 246 | $(OX)\sqlite3$O \ |
| 247 | $(OX)\stash$O \ |
| 248 | $(OX)\stat$O \ |
| 249 | $(OX)\style$O \ |
| 250 | $(OX)\sync$O \ |
| 251 | $(OX)\tag$O \ |
| 252 | $(OX)\tar$O \ |
| 253 | $(OX)\th$O \ |
| 254 | $(OX)\th_lang$O \ |
| 255 | $(OX)\th_main$O \ |
| 256 | $(OX)\timeline$O \ |
| 257 | $(OX)\tkt$O \ |
| 258 | $(OX)\tktsetup$O \ |
| 259 | $(OX)\undo$O \ |
| @@ -247,14 +269,11 @@ | |
| 269 | $(OX)\winhttp$O \ |
| 270 | $(OX)\wysiwyg$O \ |
| 271 | $(OX)\xfer$O \ |
| 272 | $(OX)\xfersetup$O \ |
| 273 | $(OX)\zip$O \ |
| 274 | $(OX)\fossil.res |
| 275 | |
| 276 | APPNAME = $(OX)\fossil$(E) |
| 277 | |
| 278 | all: $(OX) $(APPNAME) |
| 279 | |
| @@ -262,11 +281,11 @@ | |
| 281 | @echo Building zlib from "$(ZLIBDIR)"... |
| 282 | @pushd "$(ZLIBDIR)" && nmake /f win32\Makefile.msc $(ZLIB) && popd |
| 283 | |
| 284 | $(APPNAME) : translate$E mkindex$E headers $(OBJ) $(OX)\linkopts zlib |
| 285 | cd $(OX) |
| 286 | link /NODEFAULTLIB:msvcrt -OUT:$@ $(LIBDIR) Wsetargv.obj fossil.res @linkopts |
| 287 | |
| 288 | $(OX)\linkopts: $B\win\Makefile.msc |
| 289 | echo $(OX)\add.obj > $@ |
| 290 | echo $(OX)\allrepo.obj >> $@ |
| 291 | echo $(OX)\attach.obj >> $@ |
| @@ -282,10 +301,11 @@ | |
| 301 | echo $(OX)\clearsign.obj >> $@ |
| 302 | echo $(OX)\clone.obj >> $@ |
| 303 | echo $(OX)\comformat.obj >> $@ |
| 304 | echo $(OX)\configure.obj >> $@ |
| 305 | echo $(OX)\content.obj >> $@ |
| 306 | echo $(OX)\cson_amalgamation.obj >> $@ |
| 307 | echo $(OX)\db.obj >> $@ |
| 308 | echo $(OX)\delta.obj >> $@ |
| 309 | echo $(OX)\deltacmd.obj >> $@ |
| 310 | echo $(OX)\descendants.obj >> $@ |
| 311 | echo $(OX)\diff.obj >> $@ |
| @@ -407,12 +427,12 @@ | |
| 427 | $(OX)\th_lang$O : $(SRCDIR)\th_lang.c |
| 428 | $(TCC) /Fo$@ -c $** |
| 429 | |
| 430 | VERSION.h : mkversion$E $B\manifest.uuid $B\manifest $B\VERSION |
| 431 | $** > $@ |
| 432 | $(OX)\cson_amalgamation$O : $(SRCDIR)\cson_amalgamation.c |
| 433 | $(TCC) /Fo$@ -c $** |
| 434 | |
| 435 | page_index.h: mkindex$E $(SRC) |
| 436 | $** > $@ |
| 437 | |
| 438 | clean: |
| @@ -422,10 +442,11 @@ | |
| 442 | -del *.h |
| 443 | -del *.map |
| 444 | -del *.manifest |
| 445 | -del headers |
| 446 | -del linkopts |
| 447 | -del *.res |
| 448 | |
| 449 | realclean: clean |
| 450 | -del $(APPNAME) |
| 451 | -del translate$E |
| 452 | -del mkindex$E |
| @@ -1070,10 +1091,12 @@ | |
| 1091 | $(TCC) /Fo$@ -c zip_.c |
| 1092 | |
| 1093 | zip_.c : $(SRCDIR)\zip.c |
| 1094 | translate$E $** > $@ |
| 1095 | |
| 1096 | fossil.res : $B\win\fossil.rc |
| 1097 | $(RCC) -fo $@ $** |
| 1098 | headers: makeheaders$E page_index.h VERSION.h |
| 1099 | makeheaders$E add_.c:add.h \ |
| 1100 | allrepo_.c:allrepo.h \ |
| 1101 | attach_.c:attach.h \ |
| 1102 | bag_.c:bag.h \ |
| 1103 |