Fossil SCM
Bring branch up-to-date with respect to trunk
Commit
bbc5264066b88f772da23732c2315e9eaeff2691490285a38e66a8d2352fe548
Parent
20b8a0ea53a0953…
42 files changed
+2
-2
+17
-1
+1
-1
+8
-6
+2
-2
+29
-13
+3
+5
-1
+11
-5
+1
-1
+4
+1
-1
+1
-1
+4
-1
+5
-4
+8
-37
+1
-1
+39
-16
+93
-13
+37
-14
+215
-74
+5
-4
+4
-2
+126
-24
+1
+1
-1
+1
-1
+1
-1
+2
-2
+18
+26
-38
+1
-1
+6
-6
+18
-10
+1
+1
+28
-45
+28
-45
+1
-1
+29
-8
+12
+1
-1
~
skins/default/header.txt
~
src/allrepo.c
~
src/backlink.c
~
src/builtin.c
~
src/checkout.c
~
src/clone.c
~
src/config.h
~
src/db.c
~
src/dispatch.c
~
src/doc.c
~
src/export.c
~
src/fileedit.c
~
src/forum.c
~
src/info.c
~
src/main.c
~
src/manifest.c
~
src/pikchrshow.c
~
src/shell.c
~
src/sitemap.c
~
src/sqlcmd.c
~
src/sqlite3.c
~
src/sqlite3.h
~
src/style.c
~
src/timeline.c
~
src/tkt.c
~
src/tktsetup.c
~
src/update.c
~
src/wiki.c
~
www/backup.md
~
www/changes.wiki
~
www/ckout-workflows.md
~
www/embeddeddoc.wiki
~
www/fossil-v-git.wiki
~
www/gitusers.md
~
www/mkindex.tcl
~
www/permutedindex.html
~
www/rebaseharm.md
~
www/rebaseharm.md
~
www/server/any/althttpd.md
~
www/server/debian/nginx.md
~
www/server/openbsd/fastcgi.md
~
www/sync.wiki
+2
-2
| --- skins/default/header.txt | ||
| +++ skins/default/header.txt | ||
| @@ -27,15 +27,15 @@ | ||
| 27 | 27 | if {[hascap oh]} { |
| 28 | 28 | if {![info exists current_checkin]} {set current_checkin tip} |
| 29 | 29 | menulink /dir?ci=$current_checkin Files desktoponly |
| 30 | 30 | } |
| 31 | 31 | if {[hascap o]} { |
| 32 | - menulink /brlist Branches desktoponly | |
| 32 | + menulink /brlist Branches wideonly | |
| 33 | 33 | menulink /taglist Tags wideonly |
| 34 | 34 | } |
| 35 | 35 | if {[anycap 23456] || [anoncap 2] || [anoncap 3]} { |
| 36 | - menulink /forum Forum wideonly | |
| 36 | + menulink /forum Forum desktoponly | |
| 37 | 37 | } |
| 38 | 38 | if {[hascap r]} { |
| 39 | 39 | menulink /ticket Tickets wideonly |
| 40 | 40 | } |
| 41 | 41 | if {[hascap j]} { |
| 42 | 42 |
| --- skins/default/header.txt | |
| +++ skins/default/header.txt | |
| @@ -27,15 +27,15 @@ | |
| 27 | if {[hascap oh]} { |
| 28 | if {![info exists current_checkin]} {set current_checkin tip} |
| 29 | menulink /dir?ci=$current_checkin Files desktoponly |
| 30 | } |
| 31 | if {[hascap o]} { |
| 32 | menulink /brlist Branches desktoponly |
| 33 | menulink /taglist Tags wideonly |
| 34 | } |
| 35 | if {[anycap 23456] || [anoncap 2] || [anoncap 3]} { |
| 36 | menulink /forum Forum wideonly |
| 37 | } |
| 38 | if {[hascap r]} { |
| 39 | menulink /ticket Tickets wideonly |
| 40 | } |
| 41 | if {[hascap j]} { |
| 42 |
| --- skins/default/header.txt | |
| +++ skins/default/header.txt | |
| @@ -27,15 +27,15 @@ | |
| 27 | if {[hascap oh]} { |
| 28 | if {![info exists current_checkin]} {set current_checkin tip} |
| 29 | menulink /dir?ci=$current_checkin Files desktoponly |
| 30 | } |
| 31 | if {[hascap o]} { |
| 32 | menulink /brlist Branches wideonly |
| 33 | menulink /taglist Tags wideonly |
| 34 | } |
| 35 | if {[anycap 23456] || [anoncap 2] || [anoncap 3]} { |
| 36 | menulink /forum Forum desktoponly |
| 37 | } |
| 38 | if {[hascap r]} { |
| 39 | menulink /ticket Tickets wideonly |
| 40 | } |
| 41 | if {[hascap j]} { |
| 42 |
+17
-1
| --- src/allrepo.c | ||
| +++ src/allrepo.c | ||
| @@ -91,10 +91,13 @@ | ||
| 91 | 91 | ** line options supported by the extra command itself, if any |
| 92 | 92 | ** are present, are passed along verbatim. |
| 93 | 93 | ** |
| 94 | 94 | ** fts-config Run the "fts-config" command on all repositories. |
| 95 | 95 | ** |
| 96 | +** git export Do the "git export" command on all repositories for which | |
| 97 | +** a Git mirror has been previously established. | |
| 98 | +** | |
| 96 | 99 | ** info Run the "info" command on all repositories. |
| 97 | 100 | ** |
| 98 | 101 | ** pull Run a "pull" operation on all repositories. Only the |
| 99 | 102 | ** --verbose option is supported. |
| 100 | 103 | ** |
| @@ -236,10 +239,23 @@ | ||
| 236 | 239 | collect_argument_value(&extra, "ignore"); |
| 237 | 240 | collect_argument(&extra, "rel-paths",0); |
| 238 | 241 | useCheckouts = 1; |
| 239 | 242 | stopOnError = 0; |
| 240 | 243 | quiet = 1; |
| 244 | + }else if( strncmp(zCmd, "git", n)==0 ){ | |
| 245 | + if( g.argc<4 ){ | |
| 246 | + usage("git (export|status)"); | |
| 247 | + }else{ | |
| 248 | + int n3 = (int)strlen(g.argv[3]); | |
| 249 | + if( strncmp(g.argv[3], "export", n3)==0 ){ | |
| 250 | + zCmd = "git export --if-mirrored -R"; | |
| 251 | + }else if( strncmp(g.argv[3], "status", n3)==0 ){ | |
| 252 | + zCmd = "git status -R"; | |
| 253 | + }else{ | |
| 254 | + usage("git (export|status)"); | |
| 255 | + } | |
| 256 | + } | |
| 241 | 257 | }else if( strncmp(zCmd, "push", n)==0 ){ |
| 242 | 258 | zCmd = "push -autourl -R"; |
| 243 | 259 | collect_argument(&extra, "verbose","v"); |
| 244 | 260 | }else if( strncmp(zCmd, "pull", n)==0 ){ |
| 245 | 261 | zCmd = "pull -autourl -R"; |
| @@ -358,11 +374,11 @@ | ||
| 358 | 374 | zCmd = "cache -R"; |
| 359 | 375 | showLabel = 1; |
| 360 | 376 | collect_argv(&extra, 3); |
| 361 | 377 | }else{ |
| 362 | 378 | fossil_fatal("\"all\" subcommand should be one of: " |
| 363 | - "add cache changes clean dbstat extras fts-config ignore " | |
| 379 | + "add cache changes clean dbstat extras fts-config git ignore " | |
| 364 | 380 | "info list ls pull push rebuild server setting sync ui unset"); |
| 365 | 381 | } |
| 366 | 382 | verify_all_options(); |
| 367 | 383 | db_multi_exec("CREATE TEMP TABLE repolist(name,tag);"); |
| 368 | 384 | if( useCheckouts ){ |
| 369 | 385 |
| --- src/allrepo.c | |
| +++ src/allrepo.c | |
| @@ -91,10 +91,13 @@ | |
| 91 | ** line options supported by the extra command itself, if any |
| 92 | ** are present, are passed along verbatim. |
| 93 | ** |
| 94 | ** fts-config Run the "fts-config" command on all repositories. |
| 95 | ** |
| 96 | ** info Run the "info" command on all repositories. |
| 97 | ** |
| 98 | ** pull Run a "pull" operation on all repositories. Only the |
| 99 | ** --verbose option is supported. |
| 100 | ** |
| @@ -236,10 +239,23 @@ | |
| 236 | collect_argument_value(&extra, "ignore"); |
| 237 | collect_argument(&extra, "rel-paths",0); |
| 238 | useCheckouts = 1; |
| 239 | stopOnError = 0; |
| 240 | quiet = 1; |
| 241 | }else if( strncmp(zCmd, "push", n)==0 ){ |
| 242 | zCmd = "push -autourl -R"; |
| 243 | collect_argument(&extra, "verbose","v"); |
| 244 | }else if( strncmp(zCmd, "pull", n)==0 ){ |
| 245 | zCmd = "pull -autourl -R"; |
| @@ -358,11 +374,11 @@ | |
| 358 | zCmd = "cache -R"; |
| 359 | showLabel = 1; |
| 360 | collect_argv(&extra, 3); |
| 361 | }else{ |
| 362 | fossil_fatal("\"all\" subcommand should be one of: " |
| 363 | "add cache changes clean dbstat extras fts-config ignore " |
| 364 | "info list ls pull push rebuild server setting sync ui unset"); |
| 365 | } |
| 366 | verify_all_options(); |
| 367 | db_multi_exec("CREATE TEMP TABLE repolist(name,tag);"); |
| 368 | if( useCheckouts ){ |
| 369 |
| --- src/allrepo.c | |
| +++ src/allrepo.c | |
| @@ -91,10 +91,13 @@ | |
| 91 | ** line options supported by the extra command itself, if any |
| 92 | ** are present, are passed along verbatim. |
| 93 | ** |
| 94 | ** fts-config Run the "fts-config" command on all repositories. |
| 95 | ** |
| 96 | ** git export Do the "git export" command on all repositories for which |
| 97 | ** a Git mirror has been previously established. |
| 98 | ** |
| 99 | ** info Run the "info" command on all repositories. |
| 100 | ** |
| 101 | ** pull Run a "pull" operation on all repositories. Only the |
| 102 | ** --verbose option is supported. |
| 103 | ** |
| @@ -236,10 +239,23 @@ | |
| 239 | collect_argument_value(&extra, "ignore"); |
| 240 | collect_argument(&extra, "rel-paths",0); |
| 241 | useCheckouts = 1; |
| 242 | stopOnError = 0; |
| 243 | quiet = 1; |
| 244 | }else if( strncmp(zCmd, "git", n)==0 ){ |
| 245 | if( g.argc<4 ){ |
| 246 | usage("git (export|status)"); |
| 247 | }else{ |
| 248 | int n3 = (int)strlen(g.argv[3]); |
| 249 | if( strncmp(g.argv[3], "export", n3)==0 ){ |
| 250 | zCmd = "git export --if-mirrored -R"; |
| 251 | }else if( strncmp(g.argv[3], "status", n3)==0 ){ |
| 252 | zCmd = "git status -R"; |
| 253 | }else{ |
| 254 | usage("git (export|status)"); |
| 255 | } |
| 256 | } |
| 257 | }else if( strncmp(zCmd, "push", n)==0 ){ |
| 258 | zCmd = "push -autourl -R"; |
| 259 | collect_argument(&extra, "verbose","v"); |
| 260 | }else if( strncmp(zCmd, "pull", n)==0 ){ |
| 261 | zCmd = "pull -autourl -R"; |
| @@ -358,11 +374,11 @@ | |
| 374 | zCmd = "cache -R"; |
| 375 | showLabel = 1; |
| 376 | collect_argv(&extra, 3); |
| 377 | }else{ |
| 378 | fossil_fatal("\"all\" subcommand should be one of: " |
| 379 | "add cache changes clean dbstat extras fts-config git ignore " |
| 380 | "info list ls pull push rebuild server setting sync ui unset"); |
| 381 | } |
| 382 | verify_all_options(); |
| 383 | db_multi_exec("CREATE TEMP TABLE repolist(name,tag);"); |
| 384 | if( useCheckouts ){ |
| 385 |
+1
-1
| --- src/backlink.c | ||
| +++ src/backlink.c | ||
| @@ -130,11 +130,11 @@ | ||
| 130 | 130 | int srcid = db_column_int(&q, 2); |
| 131 | 131 | const char *zMtime = db_column_text(&q, 3); |
| 132 | 132 | @ <tr><td><a href="%R/info/%h(zTarget)">%h(zTarget)</a> |
| 133 | 133 | switch( srctype ){ |
| 134 | 134 | case BKLNK_COMMENT: { |
| 135 | - @ <td><a href="%R/info?name=rid:%d(srcid)">comment-%d(srcid)</a> | |
| 135 | + @ <td><a href="%R/info?name=rid:%d(srcid)">checkin-%d(srcid)</a> | |
| 136 | 136 | break; |
| 137 | 137 | } |
| 138 | 138 | case BKLNK_TICKET: { |
| 139 | 139 | @ <td><a href="%R/info?name=rid:%d(srcid)">ticket-%d(srcid)</a> |
| 140 | 140 | break; |
| 141 | 141 |
| --- src/backlink.c | |
| +++ src/backlink.c | |
| @@ -130,11 +130,11 @@ | |
| 130 | int srcid = db_column_int(&q, 2); |
| 131 | const char *zMtime = db_column_text(&q, 3); |
| 132 | @ <tr><td><a href="%R/info/%h(zTarget)">%h(zTarget)</a> |
| 133 | switch( srctype ){ |
| 134 | case BKLNK_COMMENT: { |
| 135 | @ <td><a href="%R/info?name=rid:%d(srcid)">comment-%d(srcid)</a> |
| 136 | break; |
| 137 | } |
| 138 | case BKLNK_TICKET: { |
| 139 | @ <td><a href="%R/info?name=rid:%d(srcid)">ticket-%d(srcid)</a> |
| 140 | break; |
| 141 |
| --- src/backlink.c | |
| +++ src/backlink.c | |
| @@ -130,11 +130,11 @@ | |
| 130 | int srcid = db_column_int(&q, 2); |
| 131 | const char *zMtime = db_column_text(&q, 3); |
| 132 | @ <tr><td><a href="%R/info/%h(zTarget)">%h(zTarget)</a> |
| 133 | switch( srctype ){ |
| 134 | case BKLNK_COMMENT: { |
| 135 | @ <td><a href="%R/info?name=rid:%d(srcid)">checkin-%d(srcid)</a> |
| 136 | break; |
| 137 | } |
| 138 | case BKLNK_TICKET: { |
| 139 | @ <td><a href="%R/info?name=rid:%d(srcid)">ticket-%d(srcid)</a> |
| 140 | break; |
| 141 |
+8
-6
| --- src/builtin.c | ||
| +++ src/builtin.c | ||
| @@ -775,13 +775,15 @@ | ||
| 775 | 775 | ** initialize the window.fossil JS API. The first argument is the NAME |
| 776 | 776 | ** part of the first API to emit. All subsequent arguments must be |
| 777 | 777 | ** strings of the NAME part of additional fossil.NAME.js files, |
| 778 | 778 | ** followed by a NULL argument to terminate the list. |
| 779 | 779 | ** |
| 780 | -** e.g. pass it ("fetch", "dom", "tabs", 0) to load those 3 APIs (or | |
| 781 | -** pass it ("fetch","tabs",0), as "dom" is a dependency of "tabs", so | |
| 782 | -** it will be automatically loaded). Do not forget the trailing 0! | |
| 780 | +** e.g. pass it ("fetch", "dom", "tabs", NULL) to load those 3 APIs (or | |
| 781 | +** pass it ("fetch","tabs",NULL), as "dom" is a dependency of "tabs", so | |
| 782 | +** it will be automatically loaded). Do not forget the trailing NULL, | |
| 783 | +** and do not pass 0 instead, since that isn't always equivalent to NULL | |
| 784 | +** in this context. | |
| 783 | 785 | ** |
| 784 | 786 | ** If it is JS_BUNDLED then this routine queues up an emit of ALL of |
| 785 | 787 | ** the JS fossil.XYZ.js APIs which are not strictly specific to a |
| 786 | 788 | ** single page, and then calls builtin_fulfill_js_requests(). The idea |
| 787 | 789 | ** is that we can get better bundle caching and reduced HTTP requests |
| @@ -843,18 +845,18 @@ | ||
| 843 | 845 | ** builtin_request_js()) with any other app-/page-specific JS it may |
| 844 | 846 | ** need. |
| 845 | 847 | ** |
| 846 | 848 | ** Example usage: |
| 847 | 849 | ** |
| 848 | -** builtin_fossil_js_bundle_or("dom", "fetch", 0); | |
| 850 | +** builtin_fossil_js_bundle_or("dom", "fetch", NULL); | |
| 849 | 851 | ** |
| 850 | 852 | ** In bundled mode, that will (the first time it is called) emit all |
| 851 | 853 | ** builtin fossil JS APIs and "fulfill" the queue immediately. In |
| 852 | 854 | ** non-bundled mode it will queue up the "dom" and "fetch" APIs to be |
| 853 | 855 | ** emitted the next time builtin_fulfill_js_requests() is called. |
| 854 | 856 | */ |
| 855 | -void builtin_fossil_js_bundle_or( const char * zApi, ... ) { | |
| 857 | +NULL_SENTINEL void builtin_fossil_js_bundle_or( const char * zApi, ... ) { | |
| 856 | 858 | static int bundled = 0; |
| 857 | 859 | const char *zArg; |
| 858 | 860 | va_list vargs; |
| 859 | 861 | |
| 860 | 862 | if(JS_BUNDLED == builtin_get_js_delivery_mode()){ |
| @@ -864,12 +866,12 @@ | ||
| 864 | 866 | builtin_fulfill_js_requests(); |
| 865 | 867 | } |
| 866 | 868 | return; |
| 867 | 869 | } |
| 868 | 870 | va_start(vargs,zApi); |
| 869 | - for( zArg = zApi; zArg!=0; (zArg = va_arg (vargs, const char *))){ | |
| 871 | + for( zArg = zApi; zArg!=NULL; (zArg = va_arg (vargs, const char *))){ | |
| 870 | 872 | if(0==builtin_emit_fossil_js_once(zArg)){ |
| 871 | 873 | fossil_fatal("Unknown fossil JS module: %s\n", zArg); |
| 872 | 874 | } |
| 873 | 875 | } |
| 874 | 876 | va_end(vargs); |
| 875 | 877 | } |
| 876 | 878 |
| --- src/builtin.c | |
| +++ src/builtin.c | |
| @@ -775,13 +775,15 @@ | |
| 775 | ** initialize the window.fossil JS API. The first argument is the NAME |
| 776 | ** part of the first API to emit. All subsequent arguments must be |
| 777 | ** strings of the NAME part of additional fossil.NAME.js files, |
| 778 | ** followed by a NULL argument to terminate the list. |
| 779 | ** |
| 780 | ** e.g. pass it ("fetch", "dom", "tabs", 0) to load those 3 APIs (or |
| 781 | ** pass it ("fetch","tabs",0), as "dom" is a dependency of "tabs", so |
| 782 | ** it will be automatically loaded). Do not forget the trailing 0! |
| 783 | ** |
| 784 | ** If it is JS_BUNDLED then this routine queues up an emit of ALL of |
| 785 | ** the JS fossil.XYZ.js APIs which are not strictly specific to a |
| 786 | ** single page, and then calls builtin_fulfill_js_requests(). The idea |
| 787 | ** is that we can get better bundle caching and reduced HTTP requests |
| @@ -843,18 +845,18 @@ | |
| 843 | ** builtin_request_js()) with any other app-/page-specific JS it may |
| 844 | ** need. |
| 845 | ** |
| 846 | ** Example usage: |
| 847 | ** |
| 848 | ** builtin_fossil_js_bundle_or("dom", "fetch", 0); |
| 849 | ** |
| 850 | ** In bundled mode, that will (the first time it is called) emit all |
| 851 | ** builtin fossil JS APIs and "fulfill" the queue immediately. In |
| 852 | ** non-bundled mode it will queue up the "dom" and "fetch" APIs to be |
| 853 | ** emitted the next time builtin_fulfill_js_requests() is called. |
| 854 | */ |
| 855 | void builtin_fossil_js_bundle_or( const char * zApi, ... ) { |
| 856 | static int bundled = 0; |
| 857 | const char *zArg; |
| 858 | va_list vargs; |
| 859 | |
| 860 | if(JS_BUNDLED == builtin_get_js_delivery_mode()){ |
| @@ -864,12 +866,12 @@ | |
| 864 | builtin_fulfill_js_requests(); |
| 865 | } |
| 866 | return; |
| 867 | } |
| 868 | va_start(vargs,zApi); |
| 869 | for( zArg = zApi; zArg!=0; (zArg = va_arg (vargs, const char *))){ |
| 870 | if(0==builtin_emit_fossil_js_once(zArg)){ |
| 871 | fossil_fatal("Unknown fossil JS module: %s\n", zArg); |
| 872 | } |
| 873 | } |
| 874 | va_end(vargs); |
| 875 | } |
| 876 |
| --- src/builtin.c | |
| +++ src/builtin.c | |
| @@ -775,13 +775,15 @@ | |
| 775 | ** initialize the window.fossil JS API. The first argument is the NAME |
| 776 | ** part of the first API to emit. All subsequent arguments must be |
| 777 | ** strings of the NAME part of additional fossil.NAME.js files, |
| 778 | ** followed by a NULL argument to terminate the list. |
| 779 | ** |
| 780 | ** e.g. pass it ("fetch", "dom", "tabs", NULL) to load those 3 APIs (or |
| 781 | ** pass it ("fetch","tabs",NULL), as "dom" is a dependency of "tabs", so |
| 782 | ** it will be automatically loaded). Do not forget the trailing NULL, |
| 783 | ** and do not pass 0 instead, since that isn't always equivalent to NULL |
| 784 | ** in this context. |
| 785 | ** |
| 786 | ** If it is JS_BUNDLED then this routine queues up an emit of ALL of |
| 787 | ** the JS fossil.XYZ.js APIs which are not strictly specific to a |
| 788 | ** single page, and then calls builtin_fulfill_js_requests(). The idea |
| 789 | ** is that we can get better bundle caching and reduced HTTP requests |
| @@ -843,18 +845,18 @@ | |
| 845 | ** builtin_request_js()) with any other app-/page-specific JS it may |
| 846 | ** need. |
| 847 | ** |
| 848 | ** Example usage: |
| 849 | ** |
| 850 | ** builtin_fossil_js_bundle_or("dom", "fetch", NULL); |
| 851 | ** |
| 852 | ** In bundled mode, that will (the first time it is called) emit all |
| 853 | ** builtin fossil JS APIs and "fulfill" the queue immediately. In |
| 854 | ** non-bundled mode it will queue up the "dom" and "fetch" APIs to be |
| 855 | ** emitted the next time builtin_fulfill_js_requests() is called. |
| 856 | */ |
| 857 | NULL_SENTINEL void builtin_fossil_js_bundle_or( const char * zApi, ... ) { |
| 858 | static int bundled = 0; |
| 859 | const char *zArg; |
| 860 | va_list vargs; |
| 861 | |
| 862 | if(JS_BUNDLED == builtin_get_js_delivery_mode()){ |
| @@ -864,12 +866,12 @@ | |
| 866 | builtin_fulfill_js_requests(); |
| 867 | } |
| 868 | return; |
| 869 | } |
| 870 | va_start(vargs,zApi); |
| 871 | for( zArg = zApi; zArg!=NULL; (zArg = va_arg (vargs, const char *))){ |
| 872 | if(0==builtin_emit_fossil_js_once(zArg)){ |
| 873 | fossil_fatal("Unknown fossil JS module: %s\n", zArg); |
| 874 | } |
| 875 | } |
| 876 | va_end(vargs); |
| 877 | } |
| 878 |
+2
-2
| --- src/checkout.c | ||
| +++ src/checkout.c | ||
| @@ -49,13 +49,13 @@ | ||
| 49 | 49 | void uncheckout(int vid){ |
| 50 | 50 | char *zPwd; |
| 51 | 51 | if( vid<=0 ) return; |
| 52 | 52 | sqlite3_create_function(g.db, "dirname",1,SQLITE_UTF8,0, |
| 53 | 53 | file_dirname_sql_function, 0, 0); |
| 54 | - sqlite3_create_function(g.db, "unlink",1,SQLITE_UTF8,0, | |
| 54 | + sqlite3_create_function(g.db, "unlink",1,SQLITE_UTF8|SQLITE_DIRECTONLY,0, | |
| 55 | 55 | file_delete_sql_function, 0, 0); |
| 56 | - sqlite3_create_function(g.db, "rmdir", 1, SQLITE_UTF8, 0, | |
| 56 | + sqlite3_create_function(g.db, "rmdir", 1, SQLITE_UTF8|SQLITE_DIRECTONLY, 0, | |
| 57 | 57 | file_rmdir_sql_function, 0, 0); |
| 58 | 58 | db_multi_exec( |
| 59 | 59 | "CREATE TEMP TABLE dir_to_delete(name TEXT %s PRIMARY KEY)WITHOUT ROWID", |
| 60 | 60 | filename_collation() |
| 61 | 61 | ); |
| 62 | 62 |
| --- src/checkout.c | |
| +++ src/checkout.c | |
| @@ -49,13 +49,13 @@ | |
| 49 | void uncheckout(int vid){ |
| 50 | char *zPwd; |
| 51 | if( vid<=0 ) return; |
| 52 | sqlite3_create_function(g.db, "dirname",1,SQLITE_UTF8,0, |
| 53 | file_dirname_sql_function, 0, 0); |
| 54 | sqlite3_create_function(g.db, "unlink",1,SQLITE_UTF8,0, |
| 55 | file_delete_sql_function, 0, 0); |
| 56 | sqlite3_create_function(g.db, "rmdir", 1, SQLITE_UTF8, 0, |
| 57 | file_rmdir_sql_function, 0, 0); |
| 58 | db_multi_exec( |
| 59 | "CREATE TEMP TABLE dir_to_delete(name TEXT %s PRIMARY KEY)WITHOUT ROWID", |
| 60 | filename_collation() |
| 61 | ); |
| 62 |
| --- src/checkout.c | |
| +++ src/checkout.c | |
| @@ -49,13 +49,13 @@ | |
| 49 | void uncheckout(int vid){ |
| 50 | char *zPwd; |
| 51 | if( vid<=0 ) return; |
| 52 | sqlite3_create_function(g.db, "dirname",1,SQLITE_UTF8,0, |
| 53 | file_dirname_sql_function, 0, 0); |
| 54 | sqlite3_create_function(g.db, "unlink",1,SQLITE_UTF8|SQLITE_DIRECTONLY,0, |
| 55 | file_delete_sql_function, 0, 0); |
| 56 | sqlite3_create_function(g.db, "rmdir", 1, SQLITE_UTF8|SQLITE_DIRECTONLY, 0, |
| 57 | file_rmdir_sql_function, 0, 0); |
| 58 | db_multi_exec( |
| 59 | "CREATE TEMP TABLE dir_to_delete(name TEXT %s PRIMARY KEY)WITHOUT ROWID", |
| 60 | filename_collation() |
| 61 | ); |
| 62 |
+29
-13
| --- src/clone.c | ||
| +++ src/clone.c | ||
| @@ -123,10 +123,12 @@ | ||
| 123 | 123 | ** the -A|--admin-user parameter. |
| 124 | 124 | ** |
| 125 | 125 | ** Options: |
| 126 | 126 | ** --admin-user|-A USERNAME Make USERNAME the administrator |
| 127 | 127 | ** --httpauth|-B USER:PASS Add HTTP Basic Authorization to requests |
| 128 | +** --nested Allow opening a repository inside an opened | |
| 129 | +** checkout | |
| 128 | 130 | ** --nocompress Omit extra delta compression |
| 129 | 131 | ** --no-open Clone only. Do not open a check-out. |
| 130 | 132 | ** --once Don't remember the URI. |
| 131 | 133 | ** --private Also clone private branches |
| 132 | 134 | ** --save-http-password Remember the HTTP password without asking |
| @@ -145,12 +147,13 @@ | ||
| 145 | 147 | int nErr = 0; |
| 146 | 148 | int urlFlags = URL_PROMPT_PW | URL_REMEMBER; |
| 147 | 149 | int syncFlags = SYNC_CLONE; |
| 148 | 150 | int noCompress = find_option("nocompress",0,0)!=0; |
| 149 | 151 | int noOpen = find_option("no-open",0,0)!=0; |
| 152 | + int allowNested = find_option("nested",0,0)!=0; /* Used by open */ | |
| 150 | 153 | const char *zRepo = 0; /* Name of the new local repository file */ |
| 151 | - const char *zWorkDir = 0; /* Open in this director, if not zero */ | |
| 154 | + const char *zWorkDir = 0; /* Open in this directory, if not zero */ | |
| 152 | 155 | |
| 153 | 156 | |
| 154 | 157 | /* Also clone private branches */ |
| 155 | 158 | if( find_option("private",0,0)!=0 ) syncFlags |= SYNC_PRIVATE; |
| 156 | 159 | if( find_option("once",0,0)!=0) urlFlags &= ~URL_REMEMBER; |
| @@ -189,10 +192,16 @@ | ||
| 189 | 192 | } |
| 190 | 193 | fossil_free(zBase); |
| 191 | 194 | } |
| 192 | 195 | if( -1 != file_size(zRepo, ExtFILE) ){ |
| 193 | 196 | fossil_fatal("file already exists: %s", zRepo); |
| 197 | + } | |
| 198 | + /* Fail before clone if open will fail because inside an open checkout */ | |
| 199 | + if( zWorkDir!=0 && zWorkDir[0]!=0 && !noOpen ){ | |
| 200 | + if( db_open_local_v2(0, allowNested) ){ | |
| 201 | + fossil_fatal("there is already an open tree at %s", g.zLocalRoot); | |
| 202 | + } | |
| 194 | 203 | } |
| 195 | 204 | url_parse(g.argv[2], urlFlags); |
| 196 | 205 | if( zDefaultUser==0 && g.url.user!=0 ) zDefaultUser = g.url.user; |
| 197 | 206 | if( g.url.isFile ){ |
| 198 | 207 | file_copy(g.url.name, zRepo); |
| @@ -277,22 +286,29 @@ | ||
| 277 | 286 | fossil_print("\nproject-id: %s\n", db_get("project-code", 0)); |
| 278 | 287 | fossil_print("server-id: %s\n", db_get("server-code", 0)); |
| 279 | 288 | zPassword = db_text(0, "SELECT pw FROM user WHERE login=%Q", g.zLogin); |
| 280 | 289 | fossil_print("admin-user: %s (password is \"%s\")\n", g.zLogin, zPassword); |
| 281 | 290 | if( zWorkDir!=0 && zWorkDir[0]!=0 && !noOpen ){ |
| 282 | - char *azNew[6]; | |
| 283 | - fossil_print("opening the new %s repository in directory %s...\n", | |
| 284 | - zRepo, zWorkDir); | |
| 285 | - azNew[0] = g.argv[0]; | |
| 286 | - azNew[1] = "open"; | |
| 287 | - azNew[2] = (char*)zRepo; | |
| 288 | - azNew[3] = "--workdir"; | |
| 289 | - azNew[4] = (char*)zWorkDir; | |
| 290 | - azNew[5] = 0; | |
| 291 | - g.argv = azNew; | |
| 292 | - g.argc = 5; | |
| 293 | - cmd_open(); | |
| 291 | + char *azNew[7]; | |
| 292 | + int nargs = 5; | |
| 293 | + fossil_print("opening the new %s repository in directory %s...\n", | |
| 294 | + zRepo, zWorkDir); | |
| 295 | + azNew[0] = g.argv[0]; | |
| 296 | + azNew[1] = "open"; | |
| 297 | + azNew[2] = (char*)zRepo; | |
| 298 | + azNew[3] = "--workdir"; | |
| 299 | + azNew[4] = (char*)zWorkDir; | |
| 300 | + if( allowNested ){ | |
| 301 | + azNew[5] = "--nested"; | |
| 302 | + nargs++; | |
| 303 | + }else{ | |
| 304 | + azNew[5] = 0; | |
| 305 | + } | |
| 306 | + azNew[6] = 0; | |
| 307 | + g.argv = azNew; | |
| 308 | + g.argc = nargs; | |
| 309 | + cmd_open(); | |
| 294 | 310 | } |
| 295 | 311 | } |
| 296 | 312 | |
| 297 | 313 | /* |
| 298 | 314 | ** If user chooses to use HTTP Authentication over unencrypted HTTP, |
| 299 | 315 |
| --- src/clone.c | |
| +++ src/clone.c | |
| @@ -123,10 +123,12 @@ | |
| 123 | ** the -A|--admin-user parameter. |
| 124 | ** |
| 125 | ** Options: |
| 126 | ** --admin-user|-A USERNAME Make USERNAME the administrator |
| 127 | ** --httpauth|-B USER:PASS Add HTTP Basic Authorization to requests |
| 128 | ** --nocompress Omit extra delta compression |
| 129 | ** --no-open Clone only. Do not open a check-out. |
| 130 | ** --once Don't remember the URI. |
| 131 | ** --private Also clone private branches |
| 132 | ** --save-http-password Remember the HTTP password without asking |
| @@ -145,12 +147,13 @@ | |
| 145 | int nErr = 0; |
| 146 | int urlFlags = URL_PROMPT_PW | URL_REMEMBER; |
| 147 | int syncFlags = SYNC_CLONE; |
| 148 | int noCompress = find_option("nocompress",0,0)!=0; |
| 149 | int noOpen = find_option("no-open",0,0)!=0; |
| 150 | const char *zRepo = 0; /* Name of the new local repository file */ |
| 151 | const char *zWorkDir = 0; /* Open in this director, if not zero */ |
| 152 | |
| 153 | |
| 154 | /* Also clone private branches */ |
| 155 | if( find_option("private",0,0)!=0 ) syncFlags |= SYNC_PRIVATE; |
| 156 | if( find_option("once",0,0)!=0) urlFlags &= ~URL_REMEMBER; |
| @@ -189,10 +192,16 @@ | |
| 189 | } |
| 190 | fossil_free(zBase); |
| 191 | } |
| 192 | if( -1 != file_size(zRepo, ExtFILE) ){ |
| 193 | fossil_fatal("file already exists: %s", zRepo); |
| 194 | } |
| 195 | url_parse(g.argv[2], urlFlags); |
| 196 | if( zDefaultUser==0 && g.url.user!=0 ) zDefaultUser = g.url.user; |
| 197 | if( g.url.isFile ){ |
| 198 | file_copy(g.url.name, zRepo); |
| @@ -277,22 +286,29 @@ | |
| 277 | fossil_print("\nproject-id: %s\n", db_get("project-code", 0)); |
| 278 | fossil_print("server-id: %s\n", db_get("server-code", 0)); |
| 279 | zPassword = db_text(0, "SELECT pw FROM user WHERE login=%Q", g.zLogin); |
| 280 | fossil_print("admin-user: %s (password is \"%s\")\n", g.zLogin, zPassword); |
| 281 | if( zWorkDir!=0 && zWorkDir[0]!=0 && !noOpen ){ |
| 282 | char *azNew[6]; |
| 283 | fossil_print("opening the new %s repository in directory %s...\n", |
| 284 | zRepo, zWorkDir); |
| 285 | azNew[0] = g.argv[0]; |
| 286 | azNew[1] = "open"; |
| 287 | azNew[2] = (char*)zRepo; |
| 288 | azNew[3] = "--workdir"; |
| 289 | azNew[4] = (char*)zWorkDir; |
| 290 | azNew[5] = 0; |
| 291 | g.argv = azNew; |
| 292 | g.argc = 5; |
| 293 | cmd_open(); |
| 294 | } |
| 295 | } |
| 296 | |
| 297 | /* |
| 298 | ** If user chooses to use HTTP Authentication over unencrypted HTTP, |
| 299 |
| --- src/clone.c | |
| +++ src/clone.c | |
| @@ -123,10 +123,12 @@ | |
| 123 | ** the -A|--admin-user parameter. |
| 124 | ** |
| 125 | ** Options: |
| 126 | ** --admin-user|-A USERNAME Make USERNAME the administrator |
| 127 | ** --httpauth|-B USER:PASS Add HTTP Basic Authorization to requests |
| 128 | ** --nested Allow opening a repository inside an opened |
| 129 | ** checkout |
| 130 | ** --nocompress Omit extra delta compression |
| 131 | ** --no-open Clone only. Do not open a check-out. |
| 132 | ** --once Don't remember the URI. |
| 133 | ** --private Also clone private branches |
| 134 | ** --save-http-password Remember the HTTP password without asking |
| @@ -145,12 +147,13 @@ | |
| 147 | int nErr = 0; |
| 148 | int urlFlags = URL_PROMPT_PW | URL_REMEMBER; |
| 149 | int syncFlags = SYNC_CLONE; |
| 150 | int noCompress = find_option("nocompress",0,0)!=0; |
| 151 | int noOpen = find_option("no-open",0,0)!=0; |
| 152 | int allowNested = find_option("nested",0,0)!=0; /* Used by open */ |
| 153 | const char *zRepo = 0; /* Name of the new local repository file */ |
| 154 | const char *zWorkDir = 0; /* Open in this directory, if not zero */ |
| 155 | |
| 156 | |
| 157 | /* Also clone private branches */ |
| 158 | if( find_option("private",0,0)!=0 ) syncFlags |= SYNC_PRIVATE; |
| 159 | if( find_option("once",0,0)!=0) urlFlags &= ~URL_REMEMBER; |
| @@ -189,10 +192,16 @@ | |
| 192 | } |
| 193 | fossil_free(zBase); |
| 194 | } |
| 195 | if( -1 != file_size(zRepo, ExtFILE) ){ |
| 196 | fossil_fatal("file already exists: %s", zRepo); |
| 197 | } |
| 198 | /* Fail before clone if open will fail because inside an open checkout */ |
| 199 | if( zWorkDir!=0 && zWorkDir[0]!=0 && !noOpen ){ |
| 200 | if( db_open_local_v2(0, allowNested) ){ |
| 201 | fossil_fatal("there is already an open tree at %s", g.zLocalRoot); |
| 202 | } |
| 203 | } |
| 204 | url_parse(g.argv[2], urlFlags); |
| 205 | if( zDefaultUser==0 && g.url.user!=0 ) zDefaultUser = g.url.user; |
| 206 | if( g.url.isFile ){ |
| 207 | file_copy(g.url.name, zRepo); |
| @@ -277,22 +286,29 @@ | |
| 286 | fossil_print("\nproject-id: %s\n", db_get("project-code", 0)); |
| 287 | fossil_print("server-id: %s\n", db_get("server-code", 0)); |
| 288 | zPassword = db_text(0, "SELECT pw FROM user WHERE login=%Q", g.zLogin); |
| 289 | fossil_print("admin-user: %s (password is \"%s\")\n", g.zLogin, zPassword); |
| 290 | if( zWorkDir!=0 && zWorkDir[0]!=0 && !noOpen ){ |
| 291 | char *azNew[7]; |
| 292 | int nargs = 5; |
| 293 | fossil_print("opening the new %s repository in directory %s...\n", |
| 294 | zRepo, zWorkDir); |
| 295 | azNew[0] = g.argv[0]; |
| 296 | azNew[1] = "open"; |
| 297 | azNew[2] = (char*)zRepo; |
| 298 | azNew[3] = "--workdir"; |
| 299 | azNew[4] = (char*)zWorkDir; |
| 300 | if( allowNested ){ |
| 301 | azNew[5] = "--nested"; |
| 302 | nargs++; |
| 303 | }else{ |
| 304 | azNew[5] = 0; |
| 305 | } |
| 306 | azNew[6] = 0; |
| 307 | g.argv = azNew; |
| 308 | g.argc = nargs; |
| 309 | cmd_open(); |
| 310 | } |
| 311 | } |
| 312 | |
| 313 | /* |
| 314 | ** If user chooses to use HTTP Authentication over unencrypted HTTP, |
| 315 |
+3
| --- src/config.h | ||
| +++ src/config.h | ||
| @@ -245,14 +245,17 @@ | ||
| 245 | 245 | /* |
| 246 | 246 | ** A marker for functions that never return. |
| 247 | 247 | */ |
| 248 | 248 | #if defined(__GNUC__) || defined(__clang__) |
| 249 | 249 | # define NORETURN __attribute__((__noreturn__)) |
| 250 | +# define NULL_SENTINEL __attribute__((sentinel)) | |
| 250 | 251 | #elif defined(_MSC_VER) && (_MSC_VER >= 1310) |
| 251 | 252 | # define NORETURN __declspec(noreturn) |
| 253 | +# define NULL_SENTINEL | |
| 252 | 254 | #else |
| 253 | 255 | # define NORETURN |
| 256 | +# define NULL_SENTINEL | |
| 254 | 257 | #endif |
| 255 | 258 | |
| 256 | 259 | /* |
| 257 | 260 | ** Number of elements in an array |
| 258 | 261 | */ |
| 259 | 262 |
| --- src/config.h | |
| +++ src/config.h | |
| @@ -245,14 +245,17 @@ | |
| 245 | /* |
| 246 | ** A marker for functions that never return. |
| 247 | */ |
| 248 | #if defined(__GNUC__) || defined(__clang__) |
| 249 | # define NORETURN __attribute__((__noreturn__)) |
| 250 | #elif defined(_MSC_VER) && (_MSC_VER >= 1310) |
| 251 | # define NORETURN __declspec(noreturn) |
| 252 | #else |
| 253 | # define NORETURN |
| 254 | #endif |
| 255 | |
| 256 | /* |
| 257 | ** Number of elements in an array |
| 258 | */ |
| 259 |
| --- src/config.h | |
| +++ src/config.h | |
| @@ -245,14 +245,17 @@ | |
| 245 | /* |
| 246 | ** A marker for functions that never return. |
| 247 | */ |
| 248 | #if defined(__GNUC__) || defined(__clang__) |
| 249 | # define NORETURN __attribute__((__noreturn__)) |
| 250 | # define NULL_SENTINEL __attribute__((sentinel)) |
| 251 | #elif defined(_MSC_VER) && (_MSC_VER >= 1310) |
| 252 | # define NORETURN __declspec(noreturn) |
| 253 | # define NULL_SENTINEL |
| 254 | #else |
| 255 | # define NORETURN |
| 256 | # define NULL_SENTINEL |
| 257 | #endif |
| 258 | |
| 259 | /* |
| 260 | ** Number of elements in an array |
| 261 | */ |
| 262 |
M
src/db.c
+5
-1
| --- src/db.c | ||
| +++ src/db.c | ||
| @@ -1617,10 +1617,15 @@ | ||
| 1617 | 1617 | ); |
| 1618 | 1618 | if( rc!=SQLITE_OK ){ |
| 1619 | 1619 | db_err("[%s]: %s", zDbName, sqlite3_errmsg(db)); |
| 1620 | 1620 | } |
| 1621 | 1621 | db_maybe_set_encryption_key(db, zDbName); |
| 1622 | + sqlite3_db_config(db, SQLITE_DBCONFIG_ENABLE_FKEY, 0, &rc); | |
| 1623 | + sqlite3_db_config(db, SQLITE_DBCONFIG_TRUSTED_SCHEMA, 0, &rc); | |
| 1624 | + sqlite3_db_config(db, SQLITE_DBCONFIG_DQS_DDL, 0, &rc); | |
| 1625 | + sqlite3_db_config(db, SQLITE_DBCONFIG_DQS_DML, 0, &rc); | |
| 1626 | + sqlite3_db_config(db, SQLITE_DBCONFIG_DEFENSIVE, 1, &rc); | |
| 1622 | 1627 | sqlite3_busy_timeout(db, 15000); |
| 1623 | 1628 | sqlite3_wal_autocheckpoint(db, 1); /* Set to checkpoint frequently */ |
| 1624 | 1629 | sqlite3_create_function(db, "user", 0, SQLITE_UTF8, 0, db_sql_user, 0, 0); |
| 1625 | 1630 | sqlite3_create_function(db, "cgi", 1, SQLITE_UTF8, 0, db_sql_cgi, 0, 0); |
| 1626 | 1631 | sqlite3_create_function(db, "cgi", 2, SQLITE_UTF8, 0, db_sql_cgi, 0, 0); |
| @@ -1633,11 +1638,10 @@ | ||
| 1633 | 1638 | ); |
| 1634 | 1639 | if( g.fSqlTrace ) sqlite3_trace_v2(db, SQLITE_TRACE_PROFILE, db_sql_trace, 0); |
| 1635 | 1640 | db_add_aux_functions(db); |
| 1636 | 1641 | re_add_sql_func(db); /* The REGEXP operator */ |
| 1637 | 1642 | foci_register(db); /* The "files_of_checkin" virtual table */ |
| 1638 | - sqlite3_db_config(db, SQLITE_DBCONFIG_ENABLE_FKEY, 0, &rc); | |
| 1639 | 1643 | sqlite3_set_authorizer(db, db_top_authorizer, db); |
| 1640 | 1644 | return db; |
| 1641 | 1645 | } |
| 1642 | 1646 | |
| 1643 | 1647 | |
| 1644 | 1648 |
| --- src/db.c | |
| +++ src/db.c | |
| @@ -1617,10 +1617,15 @@ | |
| 1617 | ); |
| 1618 | if( rc!=SQLITE_OK ){ |
| 1619 | db_err("[%s]: %s", zDbName, sqlite3_errmsg(db)); |
| 1620 | } |
| 1621 | db_maybe_set_encryption_key(db, zDbName); |
| 1622 | sqlite3_busy_timeout(db, 15000); |
| 1623 | sqlite3_wal_autocheckpoint(db, 1); /* Set to checkpoint frequently */ |
| 1624 | sqlite3_create_function(db, "user", 0, SQLITE_UTF8, 0, db_sql_user, 0, 0); |
| 1625 | sqlite3_create_function(db, "cgi", 1, SQLITE_UTF8, 0, db_sql_cgi, 0, 0); |
| 1626 | sqlite3_create_function(db, "cgi", 2, SQLITE_UTF8, 0, db_sql_cgi, 0, 0); |
| @@ -1633,11 +1638,10 @@ | |
| 1633 | ); |
| 1634 | if( g.fSqlTrace ) sqlite3_trace_v2(db, SQLITE_TRACE_PROFILE, db_sql_trace, 0); |
| 1635 | db_add_aux_functions(db); |
| 1636 | re_add_sql_func(db); /* The REGEXP operator */ |
| 1637 | foci_register(db); /* The "files_of_checkin" virtual table */ |
| 1638 | sqlite3_db_config(db, SQLITE_DBCONFIG_ENABLE_FKEY, 0, &rc); |
| 1639 | sqlite3_set_authorizer(db, db_top_authorizer, db); |
| 1640 | return db; |
| 1641 | } |
| 1642 | |
| 1643 | |
| 1644 |
| --- src/db.c | |
| +++ src/db.c | |
| @@ -1617,10 +1617,15 @@ | |
| 1617 | ); |
| 1618 | if( rc!=SQLITE_OK ){ |
| 1619 | db_err("[%s]: %s", zDbName, sqlite3_errmsg(db)); |
| 1620 | } |
| 1621 | db_maybe_set_encryption_key(db, zDbName); |
| 1622 | sqlite3_db_config(db, SQLITE_DBCONFIG_ENABLE_FKEY, 0, &rc); |
| 1623 | sqlite3_db_config(db, SQLITE_DBCONFIG_TRUSTED_SCHEMA, 0, &rc); |
| 1624 | sqlite3_db_config(db, SQLITE_DBCONFIG_DQS_DDL, 0, &rc); |
| 1625 | sqlite3_db_config(db, SQLITE_DBCONFIG_DQS_DML, 0, &rc); |
| 1626 | sqlite3_db_config(db, SQLITE_DBCONFIG_DEFENSIVE, 1, &rc); |
| 1627 | sqlite3_busy_timeout(db, 15000); |
| 1628 | sqlite3_wal_autocheckpoint(db, 1); /* Set to checkpoint frequently */ |
| 1629 | sqlite3_create_function(db, "user", 0, SQLITE_UTF8, 0, db_sql_user, 0, 0); |
| 1630 | sqlite3_create_function(db, "cgi", 1, SQLITE_UTF8, 0, db_sql_cgi, 0, 0); |
| 1631 | sqlite3_create_function(db, "cgi", 2, SQLITE_UTF8, 0, db_sql_cgi, 0, 0); |
| @@ -1633,11 +1638,10 @@ | |
| 1638 | ); |
| 1639 | if( g.fSqlTrace ) sqlite3_trace_v2(db, SQLITE_TRACE_PROFILE, db_sql_trace, 0); |
| 1640 | db_add_aux_functions(db); |
| 1641 | re_add_sql_func(db); /* The REGEXP operator */ |
| 1642 | foci_register(db); /* The "files_of_checkin" virtual table */ |
| 1643 | sqlite3_set_authorizer(db, db_top_authorizer, db); |
| 1644 | return db; |
| 1645 | } |
| 1646 | |
| 1647 | |
| 1648 |
+11
-5
| --- src/dispatch.c | ||
| +++ src/dispatch.c | ||
| @@ -1008,13 +1008,15 @@ | ||
| 1008 | 1008 | ** -w|--www List all web pages |
| 1009 | 1009 | ** |
| 1010 | 1010 | ** These options can be used when TOPIC is present: |
| 1011 | 1011 | ** |
| 1012 | 1012 | ** -h|--html Format output as HTML rather than plain text |
| 1013 | +** -c|--commands Restrict TOPIC search to commands | |
| 1013 | 1014 | */ |
| 1014 | 1015 | void help_cmd(void){ |
| 1015 | 1016 | int rc; |
| 1017 | + int mask = CMDFLAG_ANY; | |
| 1016 | 1018 | int isPage = 0; |
| 1017 | 1019 | const char *z; |
| 1018 | 1020 | const char *zCmdOrPage; |
| 1019 | 1021 | const CmdOrPage *pCmd = 0; |
| 1020 | 1022 | int useHtml = 0; |
| @@ -1056,32 +1058,36 @@ | ||
| 1056 | 1058 | } |
| 1057 | 1059 | useHtml = find_option("html","h",0)!=0; |
| 1058 | 1060 | isPage = ('/' == *g.argv[2]) ? 1 : 0; |
| 1059 | 1061 | if(isPage){ |
| 1060 | 1062 | zCmdOrPage = "page"; |
| 1063 | + }else if( find_option("commands","c",0)!=0 ){ | |
| 1064 | + mask = CMDFLAG_COMMAND; | |
| 1065 | + zCmdOrPage = "command"; | |
| 1061 | 1066 | }else{ |
| 1062 | 1067 | zCmdOrPage = "command or setting"; |
| 1063 | 1068 | } |
| 1064 | - rc = dispatch_name_search(g.argv[2], CMDFLAG_ANY|CMDFLAG_PREFIX, &pCmd); | |
| 1069 | + rc = dispatch_name_search(g.argv[2], mask|CMDFLAG_PREFIX, &pCmd); | |
| 1065 | 1070 | if( rc ){ |
| 1066 | 1071 | int i, n; |
| 1067 | 1072 | const char *az[5]; |
| 1068 | 1073 | if( rc==1 ){ |
| 1069 | 1074 | fossil_print("unknown %s: %s\n", zCmdOrPage, g.argv[2]); |
| 1070 | 1075 | }else{ |
| 1071 | 1076 | fossil_print("ambiguous %s prefix: %s\n", |
| 1072 | 1077 | zCmdOrPage, g.argv[2]); |
| 1073 | 1078 | } |
| 1074 | - fossil_print("Did you mean one of:\n"); | |
| 1079 | + fossil_print("Did you mean one of these TOPICs:\n"); | |
| 1075 | 1080 | n = dispatch_approx_match(g.argv[2], 5, az); |
| 1076 | 1081 | for(i=0; i<n; i++){ |
| 1077 | 1082 | fossil_print(" * %s\n", az[i]); |
| 1078 | 1083 | } |
| 1079 | 1084 | fossil_print("Also consider using:\n"); |
| 1080 | - fossil_print(" fossil help -a ;# show all commands\n"); | |
| 1081 | - fossil_print(" fossil help -w ;# show all web-pages\n"); | |
| 1082 | - fossil_print(" fossil help -s ;# show all settings\n"); | |
| 1085 | + fossil_print(" fossil help TOPIC ;# show help on TOPIC\n"); | |
| 1086 | + fossil_print(" fossil help -a ;# show all commands\n"); | |
| 1087 | + fossil_print(" fossil help -w ;# show all web-pages\n"); | |
| 1088 | + fossil_print(" fossil help -s ;# show all settings\n"); | |
| 1083 | 1089 | fossil_exit(1); |
| 1084 | 1090 | } |
| 1085 | 1091 | z = pCmd->zHelp; |
| 1086 | 1092 | if( z==0 ){ |
| 1087 | 1093 | fossil_fatal("no help available for the %s %s", |
| 1088 | 1094 |
| --- src/dispatch.c | |
| +++ src/dispatch.c | |
| @@ -1008,13 +1008,15 @@ | |
| 1008 | ** -w|--www List all web pages |
| 1009 | ** |
| 1010 | ** These options can be used when TOPIC is present: |
| 1011 | ** |
| 1012 | ** -h|--html Format output as HTML rather than plain text |
| 1013 | */ |
| 1014 | void help_cmd(void){ |
| 1015 | int rc; |
| 1016 | int isPage = 0; |
| 1017 | const char *z; |
| 1018 | const char *zCmdOrPage; |
| 1019 | const CmdOrPage *pCmd = 0; |
| 1020 | int useHtml = 0; |
| @@ -1056,32 +1058,36 @@ | |
| 1056 | } |
| 1057 | useHtml = find_option("html","h",0)!=0; |
| 1058 | isPage = ('/' == *g.argv[2]) ? 1 : 0; |
| 1059 | if(isPage){ |
| 1060 | zCmdOrPage = "page"; |
| 1061 | }else{ |
| 1062 | zCmdOrPage = "command or setting"; |
| 1063 | } |
| 1064 | rc = dispatch_name_search(g.argv[2], CMDFLAG_ANY|CMDFLAG_PREFIX, &pCmd); |
| 1065 | if( rc ){ |
| 1066 | int i, n; |
| 1067 | const char *az[5]; |
| 1068 | if( rc==1 ){ |
| 1069 | fossil_print("unknown %s: %s\n", zCmdOrPage, g.argv[2]); |
| 1070 | }else{ |
| 1071 | fossil_print("ambiguous %s prefix: %s\n", |
| 1072 | zCmdOrPage, g.argv[2]); |
| 1073 | } |
| 1074 | fossil_print("Did you mean one of:\n"); |
| 1075 | n = dispatch_approx_match(g.argv[2], 5, az); |
| 1076 | for(i=0; i<n; i++){ |
| 1077 | fossil_print(" * %s\n", az[i]); |
| 1078 | } |
| 1079 | fossil_print("Also consider using:\n"); |
| 1080 | fossil_print(" fossil help -a ;# show all commands\n"); |
| 1081 | fossil_print(" fossil help -w ;# show all web-pages\n"); |
| 1082 | fossil_print(" fossil help -s ;# show all settings\n"); |
| 1083 | fossil_exit(1); |
| 1084 | } |
| 1085 | z = pCmd->zHelp; |
| 1086 | if( z==0 ){ |
| 1087 | fossil_fatal("no help available for the %s %s", |
| 1088 |
| --- src/dispatch.c | |
| +++ src/dispatch.c | |
| @@ -1008,13 +1008,15 @@ | |
| 1008 | ** -w|--www List all web pages |
| 1009 | ** |
| 1010 | ** These options can be used when TOPIC is present: |
| 1011 | ** |
| 1012 | ** -h|--html Format output as HTML rather than plain text |
| 1013 | ** -c|--commands Restrict TOPIC search to commands |
| 1014 | */ |
| 1015 | void help_cmd(void){ |
| 1016 | int rc; |
| 1017 | int mask = CMDFLAG_ANY; |
| 1018 | int isPage = 0; |
| 1019 | const char *z; |
| 1020 | const char *zCmdOrPage; |
| 1021 | const CmdOrPage *pCmd = 0; |
| 1022 | int useHtml = 0; |
| @@ -1056,32 +1058,36 @@ | |
| 1058 | } |
| 1059 | useHtml = find_option("html","h",0)!=0; |
| 1060 | isPage = ('/' == *g.argv[2]) ? 1 : 0; |
| 1061 | if(isPage){ |
| 1062 | zCmdOrPage = "page"; |
| 1063 | }else if( find_option("commands","c",0)!=0 ){ |
| 1064 | mask = CMDFLAG_COMMAND; |
| 1065 | zCmdOrPage = "command"; |
| 1066 | }else{ |
| 1067 | zCmdOrPage = "command or setting"; |
| 1068 | } |
| 1069 | rc = dispatch_name_search(g.argv[2], mask|CMDFLAG_PREFIX, &pCmd); |
| 1070 | if( rc ){ |
| 1071 | int i, n; |
| 1072 | const char *az[5]; |
| 1073 | if( rc==1 ){ |
| 1074 | fossil_print("unknown %s: %s\n", zCmdOrPage, g.argv[2]); |
| 1075 | }else{ |
| 1076 | fossil_print("ambiguous %s prefix: %s\n", |
| 1077 | zCmdOrPage, g.argv[2]); |
| 1078 | } |
| 1079 | fossil_print("Did you mean one of these TOPICs:\n"); |
| 1080 | n = dispatch_approx_match(g.argv[2], 5, az); |
| 1081 | for(i=0; i<n; i++){ |
| 1082 | fossil_print(" * %s\n", az[i]); |
| 1083 | } |
| 1084 | fossil_print("Also consider using:\n"); |
| 1085 | fossil_print(" fossil help TOPIC ;# show help on TOPIC\n"); |
| 1086 | fossil_print(" fossil help -a ;# show all commands\n"); |
| 1087 | fossil_print(" fossil help -w ;# show all web-pages\n"); |
| 1088 | fossil_print(" fossil help -s ;# show all settings\n"); |
| 1089 | fossil_exit(1); |
| 1090 | } |
| 1091 | z = pCmd->zHelp; |
| 1092 | if( z==0 ){ |
| 1093 | fossil_fatal("no help available for the %s %s", |
| 1094 |
+1
-1
| --- src/doc.c | ||
| +++ src/doc.c | ||
| @@ -408,11 +408,11 @@ | ||
| 408 | 408 | ** no-ops. |
| 409 | 409 | */ |
| 410 | 410 | void document_emit_js(void){ |
| 411 | 411 | static int once = 0; |
| 412 | 412 | if(0==once++){ |
| 413 | - builtin_fossil_js_bundle_or("pikchr", 0); | |
| 413 | + builtin_fossil_js_bundle_or("pikchr", NULL); | |
| 414 | 414 | style_script_begin(__FILE__,__LINE__); |
| 415 | 415 | CX("window.addEventListener('load', " |
| 416 | 416 | "()=>window.fossil.pikchr.addSrcView(), " |
| 417 | 417 | "false);\n"); |
| 418 | 418 | style_script_end(); |
| 419 | 419 |
| --- src/doc.c | |
| +++ src/doc.c | |
| @@ -408,11 +408,11 @@ | |
| 408 | ** no-ops. |
| 409 | */ |
| 410 | void document_emit_js(void){ |
| 411 | static int once = 0; |
| 412 | if(0==once++){ |
| 413 | builtin_fossil_js_bundle_or("pikchr", 0); |
| 414 | style_script_begin(__FILE__,__LINE__); |
| 415 | CX("window.addEventListener('load', " |
| 416 | "()=>window.fossil.pikchr.addSrcView(), " |
| 417 | "false);\n"); |
| 418 | style_script_end(); |
| 419 |
| --- src/doc.c | |
| +++ src/doc.c | |
| @@ -408,11 +408,11 @@ | |
| 408 | ** no-ops. |
| 409 | */ |
| 410 | void document_emit_js(void){ |
| 411 | static int once = 0; |
| 412 | if(0==once++){ |
| 413 | builtin_fossil_js_bundle_or("pikchr", NULL); |
| 414 | style_script_begin(__FILE__,__LINE__); |
| 415 | CX("window.addEventListener('load', " |
| 416 | "()=>window.fossil.pikchr.addSrcView(), " |
| 417 | "false);\n"); |
| 418 | style_script_end(); |
| 419 |
+4
| --- src/export.c | ||
| +++ src/export.c | ||
| @@ -1300,10 +1300,11 @@ | ||
| 1300 | 1300 | double rEnd; /* time of most recent export */ |
| 1301 | 1301 | int rc; /* Result code */ |
| 1302 | 1302 | int bForce; /* Do the export and sync even if no changes*/ |
| 1303 | 1303 | int bNeedRepack = 0; /* True if we should run repack at the end */ |
| 1304 | 1304 | int fManifest; /* Current "manifest" setting */ |
| 1305 | + int bIfExists; /* The --if-mirrored flag */ | |
| 1305 | 1306 | FILE *xCmd; /* Pipe to the "git fast-import" command */ |
| 1306 | 1307 | FILE *pMarks; /* Git mark files */ |
| 1307 | 1308 | Stmt q; /* Queries */ |
| 1308 | 1309 | char zLine[200]; /* One line of a mark file */ |
| 1309 | 1310 | |
| @@ -1314,10 +1315,11 @@ | ||
| 1314 | 1315 | nLimit = (unsigned int)atoi(zLimit); |
| 1315 | 1316 | if( nLimit<=0 ) fossil_fatal("--limit must be positive"); |
| 1316 | 1317 | } |
| 1317 | 1318 | zAutoPush = find_option("autopush",0,1); |
| 1318 | 1319 | bForce = find_option("force","f",0)!=0; |
| 1320 | + bIfExists = find_option("if-mirrored",0,0)!=0; | |
| 1319 | 1321 | gitmirror_verbosity = VERB_NORMAL; |
| 1320 | 1322 | while( find_option("quiet","q",0)!=0 ){ gitmirror_verbosity--; } |
| 1321 | 1323 | while( find_option("verbose","v",0)!=0 ){ gitmirror_verbosity++; } |
| 1322 | 1324 | verify_all_options(); |
| 1323 | 1325 | if( g.argc!=4 && g.argc!=3 ){ usage("export ?MIRROR?"); } |
| @@ -1327,10 +1329,11 @@ | ||
| 1327 | 1329 | db_set("last-git-export-repo", blob_str(&mirror), 0); |
| 1328 | 1330 | blob_reset(&mirror); |
| 1329 | 1331 | } |
| 1330 | 1332 | zMirror = db_get("last-git-export-repo", 0); |
| 1331 | 1333 | if( zMirror==0 ){ |
| 1334 | + if( bIfExists ) return; | |
| 1332 | 1335 | fossil_fatal("no Git repository specified"); |
| 1333 | 1336 | } |
| 1334 | 1337 | |
| 1335 | 1338 | /* Make sure the GIT repository directory exists */ |
| 1336 | 1339 | rc = file_mkdir(zMirror, ExtFILE, 0); |
| @@ -1700,10 +1703,11 @@ | ||
| 1700 | 1703 | ** to the same repository. Or if URL is "off" the |
| 1701 | 1704 | ** auto-push mechanism is disabled |
| 1702 | 1705 | ** --debug FILE Write fast-export text to FILE rather than |
| 1703 | 1706 | ** piping it into "git fast-import". |
| 1704 | 1707 | ** --force|-f Do the export even if nothing has changed |
| 1708 | +** --if-mirrored No-op if the mirror does not already exist. | |
| 1705 | 1709 | ** --limit N Add no more than N new check-ins to MIRROR. |
| 1706 | 1710 | ** Useful for debugging |
| 1707 | 1711 | ** --quiet|-q Reduce output. Repeat for even less output. |
| 1708 | 1712 | ** --verbose|-v More output. |
| 1709 | 1713 | ** |
| 1710 | 1714 |
| --- src/export.c | |
| +++ src/export.c | |
| @@ -1300,10 +1300,11 @@ | |
| 1300 | double rEnd; /* time of most recent export */ |
| 1301 | int rc; /* Result code */ |
| 1302 | int bForce; /* Do the export and sync even if no changes*/ |
| 1303 | int bNeedRepack = 0; /* True if we should run repack at the end */ |
| 1304 | int fManifest; /* Current "manifest" setting */ |
| 1305 | FILE *xCmd; /* Pipe to the "git fast-import" command */ |
| 1306 | FILE *pMarks; /* Git mark files */ |
| 1307 | Stmt q; /* Queries */ |
| 1308 | char zLine[200]; /* One line of a mark file */ |
| 1309 | |
| @@ -1314,10 +1315,11 @@ | |
| 1314 | nLimit = (unsigned int)atoi(zLimit); |
| 1315 | if( nLimit<=0 ) fossil_fatal("--limit must be positive"); |
| 1316 | } |
| 1317 | zAutoPush = find_option("autopush",0,1); |
| 1318 | bForce = find_option("force","f",0)!=0; |
| 1319 | gitmirror_verbosity = VERB_NORMAL; |
| 1320 | while( find_option("quiet","q",0)!=0 ){ gitmirror_verbosity--; } |
| 1321 | while( find_option("verbose","v",0)!=0 ){ gitmirror_verbosity++; } |
| 1322 | verify_all_options(); |
| 1323 | if( g.argc!=4 && g.argc!=3 ){ usage("export ?MIRROR?"); } |
| @@ -1327,10 +1329,11 @@ | |
| 1327 | db_set("last-git-export-repo", blob_str(&mirror), 0); |
| 1328 | blob_reset(&mirror); |
| 1329 | } |
| 1330 | zMirror = db_get("last-git-export-repo", 0); |
| 1331 | if( zMirror==0 ){ |
| 1332 | fossil_fatal("no Git repository specified"); |
| 1333 | } |
| 1334 | |
| 1335 | /* Make sure the GIT repository directory exists */ |
| 1336 | rc = file_mkdir(zMirror, ExtFILE, 0); |
| @@ -1700,10 +1703,11 @@ | |
| 1700 | ** to the same repository. Or if URL is "off" the |
| 1701 | ** auto-push mechanism is disabled |
| 1702 | ** --debug FILE Write fast-export text to FILE rather than |
| 1703 | ** piping it into "git fast-import". |
| 1704 | ** --force|-f Do the export even if nothing has changed |
| 1705 | ** --limit N Add no more than N new check-ins to MIRROR. |
| 1706 | ** Useful for debugging |
| 1707 | ** --quiet|-q Reduce output. Repeat for even less output. |
| 1708 | ** --verbose|-v More output. |
| 1709 | ** |
| 1710 |
| --- src/export.c | |
| +++ src/export.c | |
| @@ -1300,10 +1300,11 @@ | |
| 1300 | double rEnd; /* time of most recent export */ |
| 1301 | int rc; /* Result code */ |
| 1302 | int bForce; /* Do the export and sync even if no changes*/ |
| 1303 | int bNeedRepack = 0; /* True if we should run repack at the end */ |
| 1304 | int fManifest; /* Current "manifest" setting */ |
| 1305 | int bIfExists; /* The --if-mirrored flag */ |
| 1306 | FILE *xCmd; /* Pipe to the "git fast-import" command */ |
| 1307 | FILE *pMarks; /* Git mark files */ |
| 1308 | Stmt q; /* Queries */ |
| 1309 | char zLine[200]; /* One line of a mark file */ |
| 1310 | |
| @@ -1314,10 +1315,11 @@ | |
| 1315 | nLimit = (unsigned int)atoi(zLimit); |
| 1316 | if( nLimit<=0 ) fossil_fatal("--limit must be positive"); |
| 1317 | } |
| 1318 | zAutoPush = find_option("autopush",0,1); |
| 1319 | bForce = find_option("force","f",0)!=0; |
| 1320 | bIfExists = find_option("if-mirrored",0,0)!=0; |
| 1321 | gitmirror_verbosity = VERB_NORMAL; |
| 1322 | while( find_option("quiet","q",0)!=0 ){ gitmirror_verbosity--; } |
| 1323 | while( find_option("verbose","v",0)!=0 ){ gitmirror_verbosity++; } |
| 1324 | verify_all_options(); |
| 1325 | if( g.argc!=4 && g.argc!=3 ){ usage("export ?MIRROR?"); } |
| @@ -1327,10 +1329,11 @@ | |
| 1329 | db_set("last-git-export-repo", blob_str(&mirror), 0); |
| 1330 | blob_reset(&mirror); |
| 1331 | } |
| 1332 | zMirror = db_get("last-git-export-repo", 0); |
| 1333 | if( zMirror==0 ){ |
| 1334 | if( bIfExists ) return; |
| 1335 | fossil_fatal("no Git repository specified"); |
| 1336 | } |
| 1337 | |
| 1338 | /* Make sure the GIT repository directory exists */ |
| 1339 | rc = file_mkdir(zMirror, ExtFILE, 0); |
| @@ -1700,10 +1703,11 @@ | |
| 1703 | ** to the same repository. Or if URL is "off" the |
| 1704 | ** auto-push mechanism is disabled |
| 1705 | ** --debug FILE Write fast-export text to FILE rather than |
| 1706 | ** piping it into "git fast-import". |
| 1707 | ** --force|-f Do the export even if nothing has changed |
| 1708 | ** --if-mirrored No-op if the mirror does not already exist. |
| 1709 | ** --limit N Add no more than N new check-ins to MIRROR. |
| 1710 | ** Useful for debugging |
| 1711 | ** --quiet|-q Reduce output. Repeat for even less output. |
| 1712 | ** --verbose|-v More output. |
| 1713 | ** |
| 1714 |
+1
-1
| --- src/fileedit.c | ||
| +++ src/fileedit.c | ||
| @@ -1989,11 +1989,11 @@ | ||
| 1989 | 1989 | } |
| 1990 | 1990 | CX("</div>"/*#fileedit-tab-help*/); |
| 1991 | 1991 | |
| 1992 | 1992 | builtin_fossil_js_bundle_or("fetch", "dom", "tabs", "confirmer", |
| 1993 | 1993 | "storage", "popupwidget", "copybutton", |
| 1994 | - "pikchr", 0); | |
| 1994 | + "pikchr", NULL); | |
| 1995 | 1995 | /* |
| 1996 | 1996 | ** Set up a JS-side mapping of the AJAX_RENDER_xyz values. This is |
| 1997 | 1997 | ** used for dynamically toggling certain UI components on and off. |
| 1998 | 1998 | ** Must come after window.fossil has been intialized and before |
| 1999 | 1999 | ** fossil.page.fileedit.js. Potential TODO: move this into the |
| 2000 | 2000 |
| --- src/fileedit.c | |
| +++ src/fileedit.c | |
| @@ -1989,11 +1989,11 @@ | |
| 1989 | } |
| 1990 | CX("</div>"/*#fileedit-tab-help*/); |
| 1991 | |
| 1992 | builtin_fossil_js_bundle_or("fetch", "dom", "tabs", "confirmer", |
| 1993 | "storage", "popupwidget", "copybutton", |
| 1994 | "pikchr", 0); |
| 1995 | /* |
| 1996 | ** Set up a JS-side mapping of the AJAX_RENDER_xyz values. This is |
| 1997 | ** used for dynamically toggling certain UI components on and off. |
| 1998 | ** Must come after window.fossil has been intialized and before |
| 1999 | ** fossil.page.fileedit.js. Potential TODO: move this into the |
| 2000 |
| --- src/fileedit.c | |
| +++ src/fileedit.c | |
| @@ -1989,11 +1989,11 @@ | |
| 1989 | } |
| 1990 | CX("</div>"/*#fileedit-tab-help*/); |
| 1991 | |
| 1992 | builtin_fossil_js_bundle_or("fetch", "dom", "tabs", "confirmer", |
| 1993 | "storage", "popupwidget", "copybutton", |
| 1994 | "pikchr", NULL); |
| 1995 | /* |
| 1996 | ** Set up a JS-side mapping of the AJAX_RENDER_xyz values. This is |
| 1997 | ** used for dynamically toggling certain UI components on and off. |
| 1998 | ** Must come after window.fossil has been intialized and before |
| 1999 | ** fossil.page.fileedit.js. Potential TODO: move this into the |
| 2000 |
+1
-1
| --- src/forum.c | ||
| +++ src/forum.c | ||
| @@ -743,11 +743,11 @@ | ||
| 743 | 743 | ** Emit Forum Javascript which applies (or optionally can apply) |
| 744 | 744 | ** to all forum-related pages. It does not include page-specific |
| 745 | 745 | ** code (e.g. "forum.js"). |
| 746 | 746 | */ |
| 747 | 747 | static void forum_emit_js(void){ |
| 748 | - builtin_fossil_js_bundle_or("copybutton", "pikchr", 0); | |
| 748 | + builtin_fossil_js_bundle_or("copybutton", "pikchr", NULL); | |
| 749 | 749 | builtin_request_js("fossil.page.forumpost.js"); |
| 750 | 750 | } |
| 751 | 751 | |
| 752 | 752 | /* |
| 753 | 753 | ** WEBPAGE: forumpost |
| 754 | 754 |
| --- src/forum.c | |
| +++ src/forum.c | |
| @@ -743,11 +743,11 @@ | |
| 743 | ** Emit Forum Javascript which applies (or optionally can apply) |
| 744 | ** to all forum-related pages. It does not include page-specific |
| 745 | ** code (e.g. "forum.js"). |
| 746 | */ |
| 747 | static void forum_emit_js(void){ |
| 748 | builtin_fossil_js_bundle_or("copybutton", "pikchr", 0); |
| 749 | builtin_request_js("fossil.page.forumpost.js"); |
| 750 | } |
| 751 | |
| 752 | /* |
| 753 | ** WEBPAGE: forumpost |
| 754 |
| --- src/forum.c | |
| +++ src/forum.c | |
| @@ -743,11 +743,11 @@ | |
| 743 | ** Emit Forum Javascript which applies (or optionally can apply) |
| 744 | ** to all forum-related pages. It does not include page-specific |
| 745 | ** code (e.g. "forum.js"). |
| 746 | */ |
| 747 | static void forum_emit_js(void){ |
| 748 | builtin_fossil_js_bundle_or("copybutton", "pikchr", NULL); |
| 749 | builtin_request_js("fossil.page.forumpost.js"); |
| 750 | } |
| 751 | |
| 752 | /* |
| 753 | ** WEBPAGE: forumpost |
| 754 |
+4
-1
| --- src/info.c | ||
| +++ src/info.c | ||
| @@ -2136,11 +2136,11 @@ | ||
| 2136 | 2136 | if(includeJS && !emittedJS){ |
| 2137 | 2137 | emittedJS = 1; |
| 2138 | 2138 | if( db_int(0, "SELECT EXISTS(SELECT 1 FROM lnos)") ){ |
| 2139 | 2139 | builtin_request_js("scroll.js"); |
| 2140 | 2140 | } |
| 2141 | - builtin_fossil_js_bundle_or("numbered-lines", 0); | |
| 2141 | + builtin_fossil_js_bundle_or("numbered-lines", NULL); | |
| 2142 | 2142 | } |
| 2143 | 2143 | } |
| 2144 | 2144 | |
| 2145 | 2145 | /* |
| 2146 | 2146 | ** COMMAND: test-line-numbers |
| @@ -2398,14 +2398,17 @@ | ||
| 2398 | 2398 | } |
| 2399 | 2399 | |
| 2400 | 2400 | if( isFile ){ |
| 2401 | 2401 | if( isSymbolicCI ){ |
| 2402 | 2402 | zHeader = mprintf("%s at %s", file_tail(zName), zCI); |
| 2403 | + style_set_current_page("doc/%t/%T", zCI, zName); | |
| 2403 | 2404 | }else if( zCIUuid && zCIUuid[0] ){ |
| 2404 | 2405 | zHeader = mprintf("%s at [%S]", file_tail(zName), zCIUuid); |
| 2406 | + style_set_current_page("doc/%S/%T", zCIUuid, zName); | |
| 2405 | 2407 | }else{ |
| 2406 | 2408 | zHeader = mprintf("%s", file_tail(zName)); |
| 2409 | + style_set_current_page("doc/tip/%T", zName); | |
| 2407 | 2410 | } |
| 2408 | 2411 | }else if( descOnly ){ |
| 2409 | 2412 | zHeader = mprintf("Artifact Description [%S]", zUuid); |
| 2410 | 2413 | }else{ |
| 2411 | 2414 | zHeader = mprintf("Artifact [%S]", zUuid); |
| 2412 | 2415 |
| --- src/info.c | |
| +++ src/info.c | |
| @@ -2136,11 +2136,11 @@ | |
| 2136 | if(includeJS && !emittedJS){ |
| 2137 | emittedJS = 1; |
| 2138 | if( db_int(0, "SELECT EXISTS(SELECT 1 FROM lnos)") ){ |
| 2139 | builtin_request_js("scroll.js"); |
| 2140 | } |
| 2141 | builtin_fossil_js_bundle_or("numbered-lines", 0); |
| 2142 | } |
| 2143 | } |
| 2144 | |
| 2145 | /* |
| 2146 | ** COMMAND: test-line-numbers |
| @@ -2398,14 +2398,17 @@ | |
| 2398 | } |
| 2399 | |
| 2400 | if( isFile ){ |
| 2401 | if( isSymbolicCI ){ |
| 2402 | zHeader = mprintf("%s at %s", file_tail(zName), zCI); |
| 2403 | }else if( zCIUuid && zCIUuid[0] ){ |
| 2404 | zHeader = mprintf("%s at [%S]", file_tail(zName), zCIUuid); |
| 2405 | }else{ |
| 2406 | zHeader = mprintf("%s", file_tail(zName)); |
| 2407 | } |
| 2408 | }else if( descOnly ){ |
| 2409 | zHeader = mprintf("Artifact Description [%S]", zUuid); |
| 2410 | }else{ |
| 2411 | zHeader = mprintf("Artifact [%S]", zUuid); |
| 2412 |
| --- src/info.c | |
| +++ src/info.c | |
| @@ -2136,11 +2136,11 @@ | |
| 2136 | if(includeJS && !emittedJS){ |
| 2137 | emittedJS = 1; |
| 2138 | if( db_int(0, "SELECT EXISTS(SELECT 1 FROM lnos)") ){ |
| 2139 | builtin_request_js("scroll.js"); |
| 2140 | } |
| 2141 | builtin_fossil_js_bundle_or("numbered-lines", NULL); |
| 2142 | } |
| 2143 | } |
| 2144 | |
| 2145 | /* |
| 2146 | ** COMMAND: test-line-numbers |
| @@ -2398,14 +2398,17 @@ | |
| 2398 | } |
| 2399 | |
| 2400 | if( isFile ){ |
| 2401 | if( isSymbolicCI ){ |
| 2402 | zHeader = mprintf("%s at %s", file_tail(zName), zCI); |
| 2403 | style_set_current_page("doc/%t/%T", zCI, zName); |
| 2404 | }else if( zCIUuid && zCIUuid[0] ){ |
| 2405 | zHeader = mprintf("%s at [%S]", file_tail(zName), zCIUuid); |
| 2406 | style_set_current_page("doc/%S/%T", zCIUuid, zName); |
| 2407 | }else{ |
| 2408 | zHeader = mprintf("%s", file_tail(zName)); |
| 2409 | style_set_current_page("doc/tip/%T", zName); |
| 2410 | } |
| 2411 | }else if( descOnly ){ |
| 2412 | zHeader = mprintf("Artifact Description [%S]", zUuid); |
| 2413 | }else{ |
| 2414 | zHeader = mprintf("Artifact [%S]", zUuid); |
| 2415 |
+5
-4
| --- src/main.c | ||
| +++ src/main.c | ||
| @@ -796,18 +796,19 @@ | ||
| 796 | 796 | /* If --help is found anywhere on the command line, translate the command |
| 797 | 797 | * to "fossil help cmdname" where "cmdname" is the first argument that |
| 798 | 798 | * does not begin with a "-" character. If all arguments start with "-", |
| 799 | 799 | * translate to "fossil help argv[1] argv[2]...". */ |
| 800 | 800 | int i, nNewArgc; |
| 801 | - char **zNewArgv = fossil_malloc( sizeof(char*)*(g.argc+2) ); | |
| 801 | + char **zNewArgv = fossil_malloc( sizeof(char*)*(g.argc+3) ); | |
| 802 | 802 | zNewArgv[0] = g.argv[0]; |
| 803 | 803 | zNewArgv[1] = "help"; |
| 804 | + zNewArgv[2] = "-c"; | |
| 804 | 805 | for(i=1; i<g.argc; i++){ |
| 805 | 806 | if( g.argv[i][0]!='-' ){ |
| 806 | - nNewArgc = 3; | |
| 807 | - zNewArgv[2] = g.argv[i]; | |
| 808 | - zNewArgv[3] = 0; | |
| 807 | + nNewArgc = 4; | |
| 808 | + zNewArgv[3] = g.argv[i]; | |
| 809 | + zNewArgv[4] = 0; | |
| 809 | 810 | break; |
| 810 | 811 | } |
| 811 | 812 | } |
| 812 | 813 | if( i==g.argc ){ |
| 813 | 814 | for(i=1; i<g.argc; i++) zNewArgv[i+1] = g.argv[i]; |
| 814 | 815 |
| --- src/main.c | |
| +++ src/main.c | |
| @@ -796,18 +796,19 @@ | |
| 796 | /* If --help is found anywhere on the command line, translate the command |
| 797 | * to "fossil help cmdname" where "cmdname" is the first argument that |
| 798 | * does not begin with a "-" character. If all arguments start with "-", |
| 799 | * translate to "fossil help argv[1] argv[2]...". */ |
| 800 | int i, nNewArgc; |
| 801 | char **zNewArgv = fossil_malloc( sizeof(char*)*(g.argc+2) ); |
| 802 | zNewArgv[0] = g.argv[0]; |
| 803 | zNewArgv[1] = "help"; |
| 804 | for(i=1; i<g.argc; i++){ |
| 805 | if( g.argv[i][0]!='-' ){ |
| 806 | nNewArgc = 3; |
| 807 | zNewArgv[2] = g.argv[i]; |
| 808 | zNewArgv[3] = 0; |
| 809 | break; |
| 810 | } |
| 811 | } |
| 812 | if( i==g.argc ){ |
| 813 | for(i=1; i<g.argc; i++) zNewArgv[i+1] = g.argv[i]; |
| 814 |
| --- src/main.c | |
| +++ src/main.c | |
| @@ -796,18 +796,19 @@ | |
| 796 | /* If --help is found anywhere on the command line, translate the command |
| 797 | * to "fossil help cmdname" where "cmdname" is the first argument that |
| 798 | * does not begin with a "-" character. If all arguments start with "-", |
| 799 | * translate to "fossil help argv[1] argv[2]...". */ |
| 800 | int i, nNewArgc; |
| 801 | char **zNewArgv = fossil_malloc( sizeof(char*)*(g.argc+3) ); |
| 802 | zNewArgv[0] = g.argv[0]; |
| 803 | zNewArgv[1] = "help"; |
| 804 | zNewArgv[2] = "-c"; |
| 805 | for(i=1; i<g.argc; i++){ |
| 806 | if( g.argv[i][0]!='-' ){ |
| 807 | nNewArgc = 4; |
| 808 | zNewArgv[3] = g.argv[i]; |
| 809 | zNewArgv[4] = 0; |
| 810 | break; |
| 811 | } |
| 812 | } |
| 813 | if( i==g.argc ){ |
| 814 | for(i=1; i<g.argc; i++) zNewArgv[i+1] = g.argv[i]; |
| 815 |
+8
-37
| --- src/manifest.c | ||
| +++ src/manifest.c | ||
| @@ -2358,14 +2358,14 @@ | ||
| 2358 | 2358 | } |
| 2359 | 2359 | if( p->type==CFTYPE_WIKI ){ |
| 2360 | 2360 | char *zTag = mprintf("wiki-%s", p->zWikiTitle); |
| 2361 | 2361 | int tagid = tag_findid(zTag, 1); |
| 2362 | 2362 | int prior; |
| 2363 | - char *zComment; | |
| 2364 | - const char *zPrefix; | |
| 2363 | + char cPrefix; | |
| 2365 | 2364 | int nWiki; |
| 2366 | 2365 | char zLength[40]; |
| 2366 | + | |
| 2367 | 2367 | while( fossil_isspace(p->zWiki[0]) ) p->zWiki++; |
| 2368 | 2368 | nWiki = strlen(p->zWiki); |
| 2369 | 2369 | sqlite3_snprintf(sizeof(zLength), zLength, "%d", nWiki); |
| 2370 | 2370 | tag_insert(zTag, 1, zLength, rid, p->rDate, rid); |
| 2371 | 2371 | fossil_free(zTag); |
| @@ -2377,56 +2377,27 @@ | ||
| 2377 | 2377 | ); |
| 2378 | 2378 | if( prior ){ |
| 2379 | 2379 | content_deltify(prior, &rid, 1, 0); |
| 2380 | 2380 | } |
| 2381 | 2381 | if( nWiki<=0 ){ |
| 2382 | - zPrefix = "Deleted"; | |
| 2382 | + cPrefix = '-'; | |
| 2383 | 2383 | }else if( !prior ){ |
| 2384 | - zPrefix = "Added"; | |
| 2384 | + cPrefix = '+'; | |
| 2385 | 2385 | }else{ |
| 2386 | - zPrefix = "Changes to"; | |
| 2387 | - } | |
| 2388 | - switch( wiki_page_type(p->zWikiTitle) ){ | |
| 2389 | - case WIKITYPE_CHECKIN: { | |
| 2390 | - zComment = mprintf("%s wiki for check-in [%S]", zPrefix, | |
| 2391 | - p->zWikiTitle+8); | |
| 2392 | - break; | |
| 2393 | - } | |
| 2394 | - case WIKITYPE_BRANCH: { | |
| 2395 | - zComment = mprintf("%s wiki for branch [/timeline?r=%t|%h]", | |
| 2396 | - zPrefix, p->zWikiTitle+7, p->zWikiTitle+7); | |
| 2397 | - break; | |
| 2398 | - } | |
| 2399 | - case WIKITYPE_TAG: { | |
| 2400 | - zComment = mprintf("%s wiki for tag [/timeline?t=%t|%h]", | |
| 2401 | - zPrefix, p->zWikiTitle+4, p->zWikiTitle+4); | |
| 2402 | - break; | |
| 2403 | - } | |
| 2404 | - default: { | |
| 2405 | - zComment = mprintf("%s wiki page [%h]", zPrefix, p->zWikiTitle); | |
| 2406 | - break; | |
| 2407 | - } | |
| 2386 | + cPrefix = ':'; | |
| 2408 | 2387 | } |
| 2409 | 2388 | search_doc_touch('w',rid,p->zWikiTitle); |
| 2410 | 2389 | if( manifest_crosslink_busy ){ |
| 2411 | 2390 | add_pending_crosslink('w',p->zWikiTitle); |
| 2412 | 2391 | }else{ |
| 2413 | 2392 | backlink_wiki_refresh(p->zWikiTitle); |
| 2414 | 2393 | } |
| 2415 | 2394 | db_multi_exec( |
| 2416 | - "REPLACE INTO event(type,mtime,objid,user,comment," | |
| 2417 | - " bgcolor,euser,ecomment)" | |
| 2418 | - "VALUES('w',%.17g,%d,%Q,%Q," | |
| 2419 | - " (SELECT value FROM tagxref WHERE tagid=%d AND rid=%d AND tagtype>1)," | |
| 2420 | - " (SELECT value FROM tagxref WHERE tagid=%d AND rid=%d)," | |
| 2421 | - " (SELECT value FROM tagxref WHERE tagid=%d AND rid=%d));", | |
| 2422 | - p->rDate, rid, p->zUser, zComment, | |
| 2423 | - TAG_BGCOLOR, rid, | |
| 2424 | - TAG_USER, rid, | |
| 2425 | - TAG_COMMENT, rid | |
| 2395 | + "REPLACE INTO event(type,mtime,objid,user,comment)" | |
| 2396 | + "VALUES('w',%.17g,%d,%Q,'%c%q');", | |
| 2397 | + p->rDate, rid, p->zUser, cPrefix, p->zWikiTitle | |
| 2426 | 2398 | ); |
| 2427 | - fossil_free(zComment); | |
| 2428 | 2399 | } |
| 2429 | 2400 | if( p->type==CFTYPE_EVENT ){ |
| 2430 | 2401 | char *zTag = mprintf("event-%s", p->zEventId); |
| 2431 | 2402 | int tagid = tag_findid(zTag, 1); |
| 2432 | 2403 | int prior, subsequent; |
| 2433 | 2404 |
| --- src/manifest.c | |
| +++ src/manifest.c | |
| @@ -2358,14 +2358,14 @@ | |
| 2358 | } |
| 2359 | if( p->type==CFTYPE_WIKI ){ |
| 2360 | char *zTag = mprintf("wiki-%s", p->zWikiTitle); |
| 2361 | int tagid = tag_findid(zTag, 1); |
| 2362 | int prior; |
| 2363 | char *zComment; |
| 2364 | const char *zPrefix; |
| 2365 | int nWiki; |
| 2366 | char zLength[40]; |
| 2367 | while( fossil_isspace(p->zWiki[0]) ) p->zWiki++; |
| 2368 | nWiki = strlen(p->zWiki); |
| 2369 | sqlite3_snprintf(sizeof(zLength), zLength, "%d", nWiki); |
| 2370 | tag_insert(zTag, 1, zLength, rid, p->rDate, rid); |
| 2371 | fossil_free(zTag); |
| @@ -2377,56 +2377,27 @@ | |
| 2377 | ); |
| 2378 | if( prior ){ |
| 2379 | content_deltify(prior, &rid, 1, 0); |
| 2380 | } |
| 2381 | if( nWiki<=0 ){ |
| 2382 | zPrefix = "Deleted"; |
| 2383 | }else if( !prior ){ |
| 2384 | zPrefix = "Added"; |
| 2385 | }else{ |
| 2386 | zPrefix = "Changes to"; |
| 2387 | } |
| 2388 | switch( wiki_page_type(p->zWikiTitle) ){ |
| 2389 | case WIKITYPE_CHECKIN: { |
| 2390 | zComment = mprintf("%s wiki for check-in [%S]", zPrefix, |
| 2391 | p->zWikiTitle+8); |
| 2392 | break; |
| 2393 | } |
| 2394 | case WIKITYPE_BRANCH: { |
| 2395 | zComment = mprintf("%s wiki for branch [/timeline?r=%t|%h]", |
| 2396 | zPrefix, p->zWikiTitle+7, p->zWikiTitle+7); |
| 2397 | break; |
| 2398 | } |
| 2399 | case WIKITYPE_TAG: { |
| 2400 | zComment = mprintf("%s wiki for tag [/timeline?t=%t|%h]", |
| 2401 | zPrefix, p->zWikiTitle+4, p->zWikiTitle+4); |
| 2402 | break; |
| 2403 | } |
| 2404 | default: { |
| 2405 | zComment = mprintf("%s wiki page [%h]", zPrefix, p->zWikiTitle); |
| 2406 | break; |
| 2407 | } |
| 2408 | } |
| 2409 | search_doc_touch('w',rid,p->zWikiTitle); |
| 2410 | if( manifest_crosslink_busy ){ |
| 2411 | add_pending_crosslink('w',p->zWikiTitle); |
| 2412 | }else{ |
| 2413 | backlink_wiki_refresh(p->zWikiTitle); |
| 2414 | } |
| 2415 | db_multi_exec( |
| 2416 | "REPLACE INTO event(type,mtime,objid,user,comment," |
| 2417 | " bgcolor,euser,ecomment)" |
| 2418 | "VALUES('w',%.17g,%d,%Q,%Q," |
| 2419 | " (SELECT value FROM tagxref WHERE tagid=%d AND rid=%d AND tagtype>1)," |
| 2420 | " (SELECT value FROM tagxref WHERE tagid=%d AND rid=%d)," |
| 2421 | " (SELECT value FROM tagxref WHERE tagid=%d AND rid=%d));", |
| 2422 | p->rDate, rid, p->zUser, zComment, |
| 2423 | TAG_BGCOLOR, rid, |
| 2424 | TAG_USER, rid, |
| 2425 | TAG_COMMENT, rid |
| 2426 | ); |
| 2427 | fossil_free(zComment); |
| 2428 | } |
| 2429 | if( p->type==CFTYPE_EVENT ){ |
| 2430 | char *zTag = mprintf("event-%s", p->zEventId); |
| 2431 | int tagid = tag_findid(zTag, 1); |
| 2432 | int prior, subsequent; |
| 2433 |
| --- src/manifest.c | |
| +++ src/manifest.c | |
| @@ -2358,14 +2358,14 @@ | |
| 2358 | } |
| 2359 | if( p->type==CFTYPE_WIKI ){ |
| 2360 | char *zTag = mprintf("wiki-%s", p->zWikiTitle); |
| 2361 | int tagid = tag_findid(zTag, 1); |
| 2362 | int prior; |
| 2363 | char cPrefix; |
| 2364 | int nWiki; |
| 2365 | char zLength[40]; |
| 2366 | |
| 2367 | while( fossil_isspace(p->zWiki[0]) ) p->zWiki++; |
| 2368 | nWiki = strlen(p->zWiki); |
| 2369 | sqlite3_snprintf(sizeof(zLength), zLength, "%d", nWiki); |
| 2370 | tag_insert(zTag, 1, zLength, rid, p->rDate, rid); |
| 2371 | fossil_free(zTag); |
| @@ -2377,56 +2377,27 @@ | |
| 2377 | ); |
| 2378 | if( prior ){ |
| 2379 | content_deltify(prior, &rid, 1, 0); |
| 2380 | } |
| 2381 | if( nWiki<=0 ){ |
| 2382 | cPrefix = '-'; |
| 2383 | }else if( !prior ){ |
| 2384 | cPrefix = '+'; |
| 2385 | }else{ |
| 2386 | cPrefix = ':'; |
| 2387 | } |
| 2388 | search_doc_touch('w',rid,p->zWikiTitle); |
| 2389 | if( manifest_crosslink_busy ){ |
| 2390 | add_pending_crosslink('w',p->zWikiTitle); |
| 2391 | }else{ |
| 2392 | backlink_wiki_refresh(p->zWikiTitle); |
| 2393 | } |
| 2394 | db_multi_exec( |
| 2395 | "REPLACE INTO event(type,mtime,objid,user,comment)" |
| 2396 | "VALUES('w',%.17g,%d,%Q,'%c%q');", |
| 2397 | p->rDate, rid, p->zUser, cPrefix, p->zWikiTitle |
| 2398 | ); |
| 2399 | } |
| 2400 | if( p->type==CFTYPE_EVENT ){ |
| 2401 | char *zTag = mprintf("event-%s", p->zEventId); |
| 2402 | int tagid = tag_findid(zTag, 1); |
| 2403 | int prior, subsequent; |
| 2404 |
+1
-1
| --- src/pikchrshow.c | ||
| +++ src/pikchrshow.c | ||
| @@ -371,11 +371,11 @@ | ||
| 371 | 371 | blob_reset(&out); |
| 372 | 372 | } CX("</div>"/*#pikchrshow-output*/); |
| 373 | 373 | } CX("</fieldset>"/*#pikchrshow-output-wrapper*/); |
| 374 | 374 | } CX("</div>"/*sbs-wrapper*/); |
| 375 | 375 | builtin_fossil_js_bundle_or("fetch", "copybutton", "popupwidget", |
| 376 | - "storage", "pikchr", 0); | |
| 376 | + "storage", "pikchr", NULL); | |
| 377 | 377 | builtin_request_js("fossil.page.pikchrshow.js"); |
| 378 | 378 | builtin_fulfill_js_requests(); |
| 379 | 379 | style_finish_page("pikchrshow"); |
| 380 | 380 | } |
| 381 | 381 | |
| 382 | 382 |
| --- src/pikchrshow.c | |
| +++ src/pikchrshow.c | |
| @@ -371,11 +371,11 @@ | |
| 371 | blob_reset(&out); |
| 372 | } CX("</div>"/*#pikchrshow-output*/); |
| 373 | } CX("</fieldset>"/*#pikchrshow-output-wrapper*/); |
| 374 | } CX("</div>"/*sbs-wrapper*/); |
| 375 | builtin_fossil_js_bundle_or("fetch", "copybutton", "popupwidget", |
| 376 | "storage", "pikchr", 0); |
| 377 | builtin_request_js("fossil.page.pikchrshow.js"); |
| 378 | builtin_fulfill_js_requests(); |
| 379 | style_finish_page("pikchrshow"); |
| 380 | } |
| 381 | |
| 382 |
| --- src/pikchrshow.c | |
| +++ src/pikchrshow.c | |
| @@ -371,11 +371,11 @@ | |
| 371 | blob_reset(&out); |
| 372 | } CX("</div>"/*#pikchrshow-output*/); |
| 373 | } CX("</fieldset>"/*#pikchrshow-output-wrapper*/); |
| 374 | } CX("</div>"/*sbs-wrapper*/); |
| 375 | builtin_fossil_js_bundle_or("fetch", "copybutton", "popupwidget", |
| 376 | "storage", "pikchr", NULL); |
| 377 | builtin_request_js("fossil.page.pikchrshow.js"); |
| 378 | builtin_fulfill_js_requests(); |
| 379 | style_finish_page("pikchrshow"); |
| 380 | } |
| 381 | |
| 382 |
+39
-16
| --- src/shell.c | ||
| +++ src/shell.c | ||
| @@ -5420,14 +5420,14 @@ | ||
| 5420 | 5420 | ** (2) Tell SQLite (via the sqlite3_declare_vtab() interface) what the |
| 5421 | 5421 | ** result set of queries against generate_series will look like. |
| 5422 | 5422 | */ |
| 5423 | 5423 | static int seriesConnect( |
| 5424 | 5424 | sqlite3 *db, |
| 5425 | - void *pAux, | |
| 5426 | - int argc, const char *const*argv, | |
| 5425 | + void *pUnused, | |
| 5426 | + int argcUnused, const char *const*argvUnused, | |
| 5427 | 5427 | sqlite3_vtab **ppVtab, |
| 5428 | - char **pzErr | |
| 5428 | + char **pzErrUnused | |
| 5429 | 5429 | ){ |
| 5430 | 5430 | sqlite3_vtab *pNew; |
| 5431 | 5431 | int rc; |
| 5432 | 5432 | |
| 5433 | 5433 | /* Column numbers */ |
| @@ -5434,10 +5434,14 @@ | ||
| 5434 | 5434 | #define SERIES_COLUMN_VALUE 0 |
| 5435 | 5435 | #define SERIES_COLUMN_START 1 |
| 5436 | 5436 | #define SERIES_COLUMN_STOP 2 |
| 5437 | 5437 | #define SERIES_COLUMN_STEP 3 |
| 5438 | 5438 | |
| 5439 | + (void)pUnused; | |
| 5440 | + (void)argcUnused; | |
| 5441 | + (void)argvUnused; | |
| 5442 | + (void)pzErrUnused; | |
| 5439 | 5443 | rc = sqlite3_declare_vtab(db, |
| 5440 | 5444 | "CREATE TABLE x(value,start hidden,stop hidden,step hidden)"); |
| 5441 | 5445 | if( rc==SQLITE_OK ){ |
| 5442 | 5446 | pNew = *ppVtab = sqlite3_malloc( sizeof(*pNew) ); |
| 5443 | 5447 | if( pNew==0 ) return SQLITE_NOMEM; |
| @@ -5456,12 +5460,13 @@ | ||
| 5456 | 5460 | } |
| 5457 | 5461 | |
| 5458 | 5462 | /* |
| 5459 | 5463 | ** Constructor for a new series_cursor object. |
| 5460 | 5464 | */ |
| 5461 | -static int seriesOpen(sqlite3_vtab *p, sqlite3_vtab_cursor **ppCursor){ | |
| 5465 | +static int seriesOpen(sqlite3_vtab *pUnused, sqlite3_vtab_cursor **ppCursor){ | |
| 5462 | 5466 | series_cursor *pCur; |
| 5467 | + (void)pUnused; | |
| 5463 | 5468 | pCur = sqlite3_malloc( sizeof(*pCur) ); |
| 5464 | 5469 | if( pCur==0 ) return SQLITE_NOMEM; |
| 5465 | 5470 | memset(pCur, 0, sizeof(*pCur)); |
| 5466 | 5471 | *ppCursor = &pCur->base; |
| 5467 | 5472 | return SQLITE_OK; |
| @@ -5564,15 +5569,16 @@ | ||
| 5564 | 5569 | ** is pointing at the first row, or pointing off the end of the table |
| 5565 | 5570 | ** (so that seriesEof() will return true) if the table is empty. |
| 5566 | 5571 | */ |
| 5567 | 5572 | static int seriesFilter( |
| 5568 | 5573 | sqlite3_vtab_cursor *pVtabCursor, |
| 5569 | - int idxNum, const char *idxStr, | |
| 5574 | + int idxNum, const char *idxStrUnused, | |
| 5570 | 5575 | int argc, sqlite3_value **argv |
| 5571 | 5576 | ){ |
| 5572 | 5577 | series_cursor *pCur = (series_cursor *)pVtabCursor; |
| 5573 | 5578 | int i = 0; |
| 5579 | + (void)idxStrUnused; | |
| 5574 | 5580 | if( idxNum & 1 ){ |
| 5575 | 5581 | pCur->mnValue = sqlite3_value_int64(argv[i++]); |
| 5576 | 5582 | }else{ |
| 5577 | 5583 | pCur->mnValue = 0; |
| 5578 | 5584 | } |
| @@ -5625,11 +5631,11 @@ | ||
| 5625 | 5631 | ** (2) stop = $value -- constraint exists |
| 5626 | 5632 | ** (4) step = $value -- constraint exists |
| 5627 | 5633 | ** (8) output in descending order |
| 5628 | 5634 | */ |
| 5629 | 5635 | static int seriesBestIndex( |
| 5630 | - sqlite3_vtab *tab, | |
| 5636 | + sqlite3_vtab *tabUnused, | |
| 5631 | 5637 | sqlite3_index_info *pIdxInfo |
| 5632 | 5638 | ){ |
| 5633 | 5639 | int i, j; /* Loop over constraints */ |
| 5634 | 5640 | int idxNum = 0; /* The query plan bitmask */ |
| 5635 | 5641 | int unusableMask = 0; /* Mask of unusable constraints */ |
| @@ -5639,10 +5645,11 @@ | ||
| 5639 | 5645 | |
| 5640 | 5646 | /* This implementation assumes that the start, stop, and step columns |
| 5641 | 5647 | ** are the last three columns in the virtual table. */ |
| 5642 | 5648 | assert( SERIES_COLUMN_STOP == SERIES_COLUMN_START+1 ); |
| 5643 | 5649 | assert( SERIES_COLUMN_STEP == SERIES_COLUMN_START+2 ); |
| 5650 | + (void)tabUnused; | |
| 5644 | 5651 | aIdx[0] = aIdx[1] = aIdx[2] = -1; |
| 5645 | 5652 | pConstraint = pIdxInfo->aConstraint; |
| 5646 | 5653 | for(i=0; i<pIdxInfo->nConstraint; i++, pConstraint++){ |
| 5647 | 5654 | int iCol; /* 0 for start, 1 for stop, 2 for step */ |
| 5648 | 5655 | int iMask; /* bitmask for those column */ |
| @@ -5712,10 +5719,14 @@ | ||
| 5712 | 5719 | 0, /* xSync */ |
| 5713 | 5720 | 0, /* xCommit */ |
| 5714 | 5721 | 0, /* xRollback */ |
| 5715 | 5722 | 0, /* xFindMethod */ |
| 5716 | 5723 | 0, /* xRename */ |
| 5724 | + 0, /* xSavepoint */ | |
| 5725 | + 0, /* xRelease */ | |
| 5726 | + 0, /* xRollbackTo */ | |
| 5727 | + 0 /* xShadowName */ | |
| 5717 | 5728 | }; |
| 5718 | 5729 | |
| 5719 | 5730 | #endif /* SQLITE_OMIT_VIRTUALTABLE */ |
| 5720 | 5731 | |
| 5721 | 5732 | #ifdef _WIN32 |
| @@ -14455,14 +14466,15 @@ | ||
| 14455 | 14466 | /* |
| 14456 | 14467 | ** Scalar function "usleep(X)" invokes sqlite3_sleep(X) and returns X. |
| 14457 | 14468 | */ |
| 14458 | 14469 | static void shellUSleepFunc( |
| 14459 | 14470 | sqlite3_context *context, |
| 14460 | - int argc, | |
| 14471 | + int argcUnused, | |
| 14461 | 14472 | sqlite3_value **argv |
| 14462 | 14473 | ){ |
| 14463 | 14474 | int sleep = sqlite3_value_int(argv[0]); |
| 14475 | + (void)argcUnused; | |
| 14464 | 14476 | sqlite3_sleep(sleep/1000); |
| 14465 | 14477 | sqlite3_result_int(context, sleep); |
| 14466 | 14478 | } |
| 14467 | 14479 | |
| 14468 | 14480 | /* |
| @@ -20657,12 +20669,15 @@ | ||
| 20657 | 20669 | p->in = fopen(sqliterc,"rb"); |
| 20658 | 20670 | if( p->in ){ |
| 20659 | 20671 | if( stdin_is_interactive ){ |
| 20660 | 20672 | utf8_printf(stderr,"-- Loading resources from %s\n",sqliterc); |
| 20661 | 20673 | } |
| 20662 | - process_input(p); | |
| 20674 | + if( process_input(p) && bail_on_error ) exit(1); | |
| 20663 | 20675 | fclose(p->in); |
| 20676 | + }else if( sqliterc_override!=0 ){ | |
| 20677 | + utf8_printf(stderr,"cannot open: \"%s\"\n", sqliterc); | |
| 20678 | + if( bail_on_error ) exit(1); | |
| 20664 | 20679 | } |
| 20665 | 20680 | p->in = inSaved; |
| 20666 | 20681 | p->lineno = savedLineno; |
| 20667 | 20682 | sqlite3_free(zBuf); |
| 20668 | 20683 | } |
| @@ -21044,10 +21059,12 @@ | ||
| 21044 | 21059 | ** command, so ignore them */ |
| 21045 | 21060 | break; |
| 21046 | 21061 | #endif |
| 21047 | 21062 | }else if( strcmp(z, "-memtrace")==0 ){ |
| 21048 | 21063 | sqlite3MemTraceActivate(stderr); |
| 21064 | + }else if( strcmp(z,"-bail")==0 ){ | |
| 21065 | + bail_on_error = 1; | |
| 21049 | 21066 | } |
| 21050 | 21067 | } |
| 21051 | 21068 | verify_uninitialized(); |
| 21052 | 21069 | |
| 21053 | 21070 | |
| @@ -21190,11 +21207,11 @@ | ||
| 21190 | 21207 | ** prior to sending the SQL into SQLite. Useful for injecting |
| 21191 | 21208 | ** crazy bytes in the middle of SQL statements for testing and debugging. |
| 21192 | 21209 | */ |
| 21193 | 21210 | ShellSetFlag(&data, SHFLG_Backslash); |
| 21194 | 21211 | }else if( strcmp(z,"-bail")==0 ){ |
| 21195 | - bail_on_error = 1; | |
| 21212 | + /* No-op. The bail_on_error flag should already be set. */ | |
| 21196 | 21213 | }else if( strcmp(z,"-version")==0 ){ |
| 21197 | 21214 | printf("%s %s\n", sqlite3_libversion(), sqlite3_sourceid()); |
| 21198 | 21215 | return 0; |
| 21199 | 21216 | }else if( strcmp(z,"-interactive")==0 ){ |
| 21200 | 21217 | stdin_is_interactive = 1; |
| @@ -21278,24 +21295,29 @@ | ||
| 21278 | 21295 | ** the database filename. |
| 21279 | 21296 | */ |
| 21280 | 21297 | for(i=0; i<nCmd; i++){ |
| 21281 | 21298 | if( azCmd[i][0]=='.' ){ |
| 21282 | 21299 | rc = do_meta_command(azCmd[i], &data); |
| 21283 | - if( rc ) return rc==2 ? 0 : rc; | |
| 21300 | + if( rc ){ | |
| 21301 | + free(azCmd); | |
| 21302 | + return rc==2 ? 0 : rc; | |
| 21303 | + } | |
| 21284 | 21304 | }else{ |
| 21285 | 21305 | open_db(&data, 0); |
| 21286 | 21306 | rc = shell_exec(&data, azCmd[i], &zErrMsg); |
| 21287 | - if( zErrMsg!=0 ){ | |
| 21288 | - utf8_printf(stderr,"Error: %s\n", zErrMsg); | |
| 21307 | + if( zErrMsg || rc ){ | |
| 21308 | + if( zErrMsg!=0 ){ | |
| 21309 | + utf8_printf(stderr,"Error: %s\n", zErrMsg); | |
| 21310 | + }else{ | |
| 21311 | + utf8_printf(stderr,"Error: unable to process SQL: %s\n", azCmd[i]); | |
| 21312 | + } | |
| 21313 | + sqlite3_free(zErrMsg); | |
| 21314 | + free(azCmd); | |
| 21289 | 21315 | return rc!=0 ? rc : 1; |
| 21290 | - }else if( rc!=0 ){ | |
| 21291 | - utf8_printf(stderr,"Error: unable to process SQL: %s\n", azCmd[i]); | |
| 21292 | - return rc; | |
| 21293 | 21316 | } |
| 21294 | 21317 | } |
| 21295 | 21318 | } |
| 21296 | - free(azCmd); | |
| 21297 | 21319 | }else{ |
| 21298 | 21320 | /* Run commands received from standard input |
| 21299 | 21321 | */ |
| 21300 | 21322 | if( stdin_is_interactive ){ |
| 21301 | 21323 | char *zHome; |
| @@ -21337,10 +21359,11 @@ | ||
| 21337 | 21359 | }else{ |
| 21338 | 21360 | data.in = stdin; |
| 21339 | 21361 | rc = process_input(&data); |
| 21340 | 21362 | } |
| 21341 | 21363 | } |
| 21364 | + free(azCmd); | |
| 21342 | 21365 | set_table_name(&data, 0); |
| 21343 | 21366 | if( data.db ){ |
| 21344 | 21367 | session_close_all(&data); |
| 21345 | 21368 | close_db(data.db); |
| 21346 | 21369 | } |
| 21347 | 21370 |
| --- src/shell.c | |
| +++ src/shell.c | |
| @@ -5420,14 +5420,14 @@ | |
| 5420 | ** (2) Tell SQLite (via the sqlite3_declare_vtab() interface) what the |
| 5421 | ** result set of queries against generate_series will look like. |
| 5422 | */ |
| 5423 | static int seriesConnect( |
| 5424 | sqlite3 *db, |
| 5425 | void *pAux, |
| 5426 | int argc, const char *const*argv, |
| 5427 | sqlite3_vtab **ppVtab, |
| 5428 | char **pzErr |
| 5429 | ){ |
| 5430 | sqlite3_vtab *pNew; |
| 5431 | int rc; |
| 5432 | |
| 5433 | /* Column numbers */ |
| @@ -5434,10 +5434,14 @@ | |
| 5434 | #define SERIES_COLUMN_VALUE 0 |
| 5435 | #define SERIES_COLUMN_START 1 |
| 5436 | #define SERIES_COLUMN_STOP 2 |
| 5437 | #define SERIES_COLUMN_STEP 3 |
| 5438 | |
| 5439 | rc = sqlite3_declare_vtab(db, |
| 5440 | "CREATE TABLE x(value,start hidden,stop hidden,step hidden)"); |
| 5441 | if( rc==SQLITE_OK ){ |
| 5442 | pNew = *ppVtab = sqlite3_malloc( sizeof(*pNew) ); |
| 5443 | if( pNew==0 ) return SQLITE_NOMEM; |
| @@ -5456,12 +5460,13 @@ | |
| 5456 | } |
| 5457 | |
| 5458 | /* |
| 5459 | ** Constructor for a new series_cursor object. |
| 5460 | */ |
| 5461 | static int seriesOpen(sqlite3_vtab *p, sqlite3_vtab_cursor **ppCursor){ |
| 5462 | series_cursor *pCur; |
| 5463 | pCur = sqlite3_malloc( sizeof(*pCur) ); |
| 5464 | if( pCur==0 ) return SQLITE_NOMEM; |
| 5465 | memset(pCur, 0, sizeof(*pCur)); |
| 5466 | *ppCursor = &pCur->base; |
| 5467 | return SQLITE_OK; |
| @@ -5564,15 +5569,16 @@ | |
| 5564 | ** is pointing at the first row, or pointing off the end of the table |
| 5565 | ** (so that seriesEof() will return true) if the table is empty. |
| 5566 | */ |
| 5567 | static int seriesFilter( |
| 5568 | sqlite3_vtab_cursor *pVtabCursor, |
| 5569 | int idxNum, const char *idxStr, |
| 5570 | int argc, sqlite3_value **argv |
| 5571 | ){ |
| 5572 | series_cursor *pCur = (series_cursor *)pVtabCursor; |
| 5573 | int i = 0; |
| 5574 | if( idxNum & 1 ){ |
| 5575 | pCur->mnValue = sqlite3_value_int64(argv[i++]); |
| 5576 | }else{ |
| 5577 | pCur->mnValue = 0; |
| 5578 | } |
| @@ -5625,11 +5631,11 @@ | |
| 5625 | ** (2) stop = $value -- constraint exists |
| 5626 | ** (4) step = $value -- constraint exists |
| 5627 | ** (8) output in descending order |
| 5628 | */ |
| 5629 | static int seriesBestIndex( |
| 5630 | sqlite3_vtab *tab, |
| 5631 | sqlite3_index_info *pIdxInfo |
| 5632 | ){ |
| 5633 | int i, j; /* Loop over constraints */ |
| 5634 | int idxNum = 0; /* The query plan bitmask */ |
| 5635 | int unusableMask = 0; /* Mask of unusable constraints */ |
| @@ -5639,10 +5645,11 @@ | |
| 5639 | |
| 5640 | /* This implementation assumes that the start, stop, and step columns |
| 5641 | ** are the last three columns in the virtual table. */ |
| 5642 | assert( SERIES_COLUMN_STOP == SERIES_COLUMN_START+1 ); |
| 5643 | assert( SERIES_COLUMN_STEP == SERIES_COLUMN_START+2 ); |
| 5644 | aIdx[0] = aIdx[1] = aIdx[2] = -1; |
| 5645 | pConstraint = pIdxInfo->aConstraint; |
| 5646 | for(i=0; i<pIdxInfo->nConstraint; i++, pConstraint++){ |
| 5647 | int iCol; /* 0 for start, 1 for stop, 2 for step */ |
| 5648 | int iMask; /* bitmask for those column */ |
| @@ -5712,10 +5719,14 @@ | |
| 5712 | 0, /* xSync */ |
| 5713 | 0, /* xCommit */ |
| 5714 | 0, /* xRollback */ |
| 5715 | 0, /* xFindMethod */ |
| 5716 | 0, /* xRename */ |
| 5717 | }; |
| 5718 | |
| 5719 | #endif /* SQLITE_OMIT_VIRTUALTABLE */ |
| 5720 | |
| 5721 | #ifdef _WIN32 |
| @@ -14455,14 +14466,15 @@ | |
| 14455 | /* |
| 14456 | ** Scalar function "usleep(X)" invokes sqlite3_sleep(X) and returns X. |
| 14457 | */ |
| 14458 | static void shellUSleepFunc( |
| 14459 | sqlite3_context *context, |
| 14460 | int argc, |
| 14461 | sqlite3_value **argv |
| 14462 | ){ |
| 14463 | int sleep = sqlite3_value_int(argv[0]); |
| 14464 | sqlite3_sleep(sleep/1000); |
| 14465 | sqlite3_result_int(context, sleep); |
| 14466 | } |
| 14467 | |
| 14468 | /* |
| @@ -20657,12 +20669,15 @@ | |
| 20657 | p->in = fopen(sqliterc,"rb"); |
| 20658 | if( p->in ){ |
| 20659 | if( stdin_is_interactive ){ |
| 20660 | utf8_printf(stderr,"-- Loading resources from %s\n",sqliterc); |
| 20661 | } |
| 20662 | process_input(p); |
| 20663 | fclose(p->in); |
| 20664 | } |
| 20665 | p->in = inSaved; |
| 20666 | p->lineno = savedLineno; |
| 20667 | sqlite3_free(zBuf); |
| 20668 | } |
| @@ -21044,10 +21059,12 @@ | |
| 21044 | ** command, so ignore them */ |
| 21045 | break; |
| 21046 | #endif |
| 21047 | }else if( strcmp(z, "-memtrace")==0 ){ |
| 21048 | sqlite3MemTraceActivate(stderr); |
| 21049 | } |
| 21050 | } |
| 21051 | verify_uninitialized(); |
| 21052 | |
| 21053 | |
| @@ -21190,11 +21207,11 @@ | |
| 21190 | ** prior to sending the SQL into SQLite. Useful for injecting |
| 21191 | ** crazy bytes in the middle of SQL statements for testing and debugging. |
| 21192 | */ |
| 21193 | ShellSetFlag(&data, SHFLG_Backslash); |
| 21194 | }else if( strcmp(z,"-bail")==0 ){ |
| 21195 | bail_on_error = 1; |
| 21196 | }else if( strcmp(z,"-version")==0 ){ |
| 21197 | printf("%s %s\n", sqlite3_libversion(), sqlite3_sourceid()); |
| 21198 | return 0; |
| 21199 | }else if( strcmp(z,"-interactive")==0 ){ |
| 21200 | stdin_is_interactive = 1; |
| @@ -21278,24 +21295,29 @@ | |
| 21278 | ** the database filename. |
| 21279 | */ |
| 21280 | for(i=0; i<nCmd; i++){ |
| 21281 | if( azCmd[i][0]=='.' ){ |
| 21282 | rc = do_meta_command(azCmd[i], &data); |
| 21283 | if( rc ) return rc==2 ? 0 : rc; |
| 21284 | }else{ |
| 21285 | open_db(&data, 0); |
| 21286 | rc = shell_exec(&data, azCmd[i], &zErrMsg); |
| 21287 | if( zErrMsg!=0 ){ |
| 21288 | utf8_printf(stderr,"Error: %s\n", zErrMsg); |
| 21289 | return rc!=0 ? rc : 1; |
| 21290 | }else if( rc!=0 ){ |
| 21291 | utf8_printf(stderr,"Error: unable to process SQL: %s\n", azCmd[i]); |
| 21292 | return rc; |
| 21293 | } |
| 21294 | } |
| 21295 | } |
| 21296 | free(azCmd); |
| 21297 | }else{ |
| 21298 | /* Run commands received from standard input |
| 21299 | */ |
| 21300 | if( stdin_is_interactive ){ |
| 21301 | char *zHome; |
| @@ -21337,10 +21359,11 @@ | |
| 21337 | }else{ |
| 21338 | data.in = stdin; |
| 21339 | rc = process_input(&data); |
| 21340 | } |
| 21341 | } |
| 21342 | set_table_name(&data, 0); |
| 21343 | if( data.db ){ |
| 21344 | session_close_all(&data); |
| 21345 | close_db(data.db); |
| 21346 | } |
| 21347 |
| --- src/shell.c | |
| +++ src/shell.c | |
| @@ -5420,14 +5420,14 @@ | |
| 5420 | ** (2) Tell SQLite (via the sqlite3_declare_vtab() interface) what the |
| 5421 | ** result set of queries against generate_series will look like. |
| 5422 | */ |
| 5423 | static int seriesConnect( |
| 5424 | sqlite3 *db, |
| 5425 | void *pUnused, |
| 5426 | int argcUnused, const char *const*argvUnused, |
| 5427 | sqlite3_vtab **ppVtab, |
| 5428 | char **pzErrUnused |
| 5429 | ){ |
| 5430 | sqlite3_vtab *pNew; |
| 5431 | int rc; |
| 5432 | |
| 5433 | /* Column numbers */ |
| @@ -5434,10 +5434,14 @@ | |
| 5434 | #define SERIES_COLUMN_VALUE 0 |
| 5435 | #define SERIES_COLUMN_START 1 |
| 5436 | #define SERIES_COLUMN_STOP 2 |
| 5437 | #define SERIES_COLUMN_STEP 3 |
| 5438 | |
| 5439 | (void)pUnused; |
| 5440 | (void)argcUnused; |
| 5441 | (void)argvUnused; |
| 5442 | (void)pzErrUnused; |
| 5443 | rc = sqlite3_declare_vtab(db, |
| 5444 | "CREATE TABLE x(value,start hidden,stop hidden,step hidden)"); |
| 5445 | if( rc==SQLITE_OK ){ |
| 5446 | pNew = *ppVtab = sqlite3_malloc( sizeof(*pNew) ); |
| 5447 | if( pNew==0 ) return SQLITE_NOMEM; |
| @@ -5456,12 +5460,13 @@ | |
| 5460 | } |
| 5461 | |
| 5462 | /* |
| 5463 | ** Constructor for a new series_cursor object. |
| 5464 | */ |
| 5465 | static int seriesOpen(sqlite3_vtab *pUnused, sqlite3_vtab_cursor **ppCursor){ |
| 5466 | series_cursor *pCur; |
| 5467 | (void)pUnused; |
| 5468 | pCur = sqlite3_malloc( sizeof(*pCur) ); |
| 5469 | if( pCur==0 ) return SQLITE_NOMEM; |
| 5470 | memset(pCur, 0, sizeof(*pCur)); |
| 5471 | *ppCursor = &pCur->base; |
| 5472 | return SQLITE_OK; |
| @@ -5564,15 +5569,16 @@ | |
| 5569 | ** is pointing at the first row, or pointing off the end of the table |
| 5570 | ** (so that seriesEof() will return true) if the table is empty. |
| 5571 | */ |
| 5572 | static int seriesFilter( |
| 5573 | sqlite3_vtab_cursor *pVtabCursor, |
| 5574 | int idxNum, const char *idxStrUnused, |
| 5575 | int argc, sqlite3_value **argv |
| 5576 | ){ |
| 5577 | series_cursor *pCur = (series_cursor *)pVtabCursor; |
| 5578 | int i = 0; |
| 5579 | (void)idxStrUnused; |
| 5580 | if( idxNum & 1 ){ |
| 5581 | pCur->mnValue = sqlite3_value_int64(argv[i++]); |
| 5582 | }else{ |
| 5583 | pCur->mnValue = 0; |
| 5584 | } |
| @@ -5625,11 +5631,11 @@ | |
| 5631 | ** (2) stop = $value -- constraint exists |
| 5632 | ** (4) step = $value -- constraint exists |
| 5633 | ** (8) output in descending order |
| 5634 | */ |
| 5635 | static int seriesBestIndex( |
| 5636 | sqlite3_vtab *tabUnused, |
| 5637 | sqlite3_index_info *pIdxInfo |
| 5638 | ){ |
| 5639 | int i, j; /* Loop over constraints */ |
| 5640 | int idxNum = 0; /* The query plan bitmask */ |
| 5641 | int unusableMask = 0; /* Mask of unusable constraints */ |
| @@ -5639,10 +5645,11 @@ | |
| 5645 | |
| 5646 | /* This implementation assumes that the start, stop, and step columns |
| 5647 | ** are the last three columns in the virtual table. */ |
| 5648 | assert( SERIES_COLUMN_STOP == SERIES_COLUMN_START+1 ); |
| 5649 | assert( SERIES_COLUMN_STEP == SERIES_COLUMN_START+2 ); |
| 5650 | (void)tabUnused; |
| 5651 | aIdx[0] = aIdx[1] = aIdx[2] = -1; |
| 5652 | pConstraint = pIdxInfo->aConstraint; |
| 5653 | for(i=0; i<pIdxInfo->nConstraint; i++, pConstraint++){ |
| 5654 | int iCol; /* 0 for start, 1 for stop, 2 for step */ |
| 5655 | int iMask; /* bitmask for those column */ |
| @@ -5712,10 +5719,14 @@ | |
| 5719 | 0, /* xSync */ |
| 5720 | 0, /* xCommit */ |
| 5721 | 0, /* xRollback */ |
| 5722 | 0, /* xFindMethod */ |
| 5723 | 0, /* xRename */ |
| 5724 | 0, /* xSavepoint */ |
| 5725 | 0, /* xRelease */ |
| 5726 | 0, /* xRollbackTo */ |
| 5727 | 0 /* xShadowName */ |
| 5728 | }; |
| 5729 | |
| 5730 | #endif /* SQLITE_OMIT_VIRTUALTABLE */ |
| 5731 | |
| 5732 | #ifdef _WIN32 |
| @@ -14455,14 +14466,15 @@ | |
| 14466 | /* |
| 14467 | ** Scalar function "usleep(X)" invokes sqlite3_sleep(X) and returns X. |
| 14468 | */ |
| 14469 | static void shellUSleepFunc( |
| 14470 | sqlite3_context *context, |
| 14471 | int argcUnused, |
| 14472 | sqlite3_value **argv |
| 14473 | ){ |
| 14474 | int sleep = sqlite3_value_int(argv[0]); |
| 14475 | (void)argcUnused; |
| 14476 | sqlite3_sleep(sleep/1000); |
| 14477 | sqlite3_result_int(context, sleep); |
| 14478 | } |
| 14479 | |
| 14480 | /* |
| @@ -20657,12 +20669,15 @@ | |
| 20669 | p->in = fopen(sqliterc,"rb"); |
| 20670 | if( p->in ){ |
| 20671 | if( stdin_is_interactive ){ |
| 20672 | utf8_printf(stderr,"-- Loading resources from %s\n",sqliterc); |
| 20673 | } |
| 20674 | if( process_input(p) && bail_on_error ) exit(1); |
| 20675 | fclose(p->in); |
| 20676 | }else if( sqliterc_override!=0 ){ |
| 20677 | utf8_printf(stderr,"cannot open: \"%s\"\n", sqliterc); |
| 20678 | if( bail_on_error ) exit(1); |
| 20679 | } |
| 20680 | p->in = inSaved; |
| 20681 | p->lineno = savedLineno; |
| 20682 | sqlite3_free(zBuf); |
| 20683 | } |
| @@ -21044,10 +21059,12 @@ | |
| 21059 | ** command, so ignore them */ |
| 21060 | break; |
| 21061 | #endif |
| 21062 | }else if( strcmp(z, "-memtrace")==0 ){ |
| 21063 | sqlite3MemTraceActivate(stderr); |
| 21064 | }else if( strcmp(z,"-bail")==0 ){ |
| 21065 | bail_on_error = 1; |
| 21066 | } |
| 21067 | } |
| 21068 | verify_uninitialized(); |
| 21069 | |
| 21070 | |
| @@ -21190,11 +21207,11 @@ | |
| 21207 | ** prior to sending the SQL into SQLite. Useful for injecting |
| 21208 | ** crazy bytes in the middle of SQL statements for testing and debugging. |
| 21209 | */ |
| 21210 | ShellSetFlag(&data, SHFLG_Backslash); |
| 21211 | }else if( strcmp(z,"-bail")==0 ){ |
| 21212 | /* No-op. The bail_on_error flag should already be set. */ |
| 21213 | }else if( strcmp(z,"-version")==0 ){ |
| 21214 | printf("%s %s\n", sqlite3_libversion(), sqlite3_sourceid()); |
| 21215 | return 0; |
| 21216 | }else if( strcmp(z,"-interactive")==0 ){ |
| 21217 | stdin_is_interactive = 1; |
| @@ -21278,24 +21295,29 @@ | |
| 21295 | ** the database filename. |
| 21296 | */ |
| 21297 | for(i=0; i<nCmd; i++){ |
| 21298 | if( azCmd[i][0]=='.' ){ |
| 21299 | rc = do_meta_command(azCmd[i], &data); |
| 21300 | if( rc ){ |
| 21301 | free(azCmd); |
| 21302 | return rc==2 ? 0 : rc; |
| 21303 | } |
| 21304 | }else{ |
| 21305 | open_db(&data, 0); |
| 21306 | rc = shell_exec(&data, azCmd[i], &zErrMsg); |
| 21307 | if( zErrMsg || rc ){ |
| 21308 | if( zErrMsg!=0 ){ |
| 21309 | utf8_printf(stderr,"Error: %s\n", zErrMsg); |
| 21310 | }else{ |
| 21311 | utf8_printf(stderr,"Error: unable to process SQL: %s\n", azCmd[i]); |
| 21312 | } |
| 21313 | sqlite3_free(zErrMsg); |
| 21314 | free(azCmd); |
| 21315 | return rc!=0 ? rc : 1; |
| 21316 | } |
| 21317 | } |
| 21318 | } |
| 21319 | }else{ |
| 21320 | /* Run commands received from standard input |
| 21321 | */ |
| 21322 | if( stdin_is_interactive ){ |
| 21323 | char *zHome; |
| @@ -21337,10 +21359,11 @@ | |
| 21359 | }else{ |
| 21360 | data.in = stdin; |
| 21361 | rc = process_input(&data); |
| 21362 | } |
| 21363 | } |
| 21364 | free(azCmd); |
| 21365 | set_table_name(&data, 0); |
| 21366 | if( data.db ){ |
| 21367 | session_close_all(&data); |
| 21368 | close_db(data.db); |
| 21369 | } |
| 21370 |
+93
-13
| --- src/sitemap.c | ||
| +++ src/sitemap.c | ||
| @@ -100,14 +100,11 @@ | ||
| 100 | 100 | } |
| 101 | 101 | if( g.perm.Read ){ |
| 102 | 102 | @ <li>%z(href("%R/timeline"))Project Timeline</a> |
| 103 | 103 | @ <ul> |
| 104 | 104 | @ <li>%z(href("%R/reports"))Activity Reports</a></li> |
| 105 | - @ <li>%z(href("%R/timeline?n=all&namechng"))File name changes</a></li> | |
| 106 | - @ <li>%z(href("%R/timeline?n=all&forks"))Forks</a></li> | |
| 107 | - @ <li>%z(href("%R/timeline?a=1970-01-01&y=ci&n=10"))First 10 | |
| 108 | - @ check-ins</a></li> | |
| 105 | + @ <li>%z(href("%R/sitemap-timeline"))Other timelines</a></li> | |
| 109 | 106 | @ </ul> |
| 110 | 107 | @ </li> |
| 111 | 108 | } |
| 112 | 109 | if( g.perm.Read ){ |
| 113 | 110 | @ <li>%z(href("%R/brlist"))Branches</a> |
| @@ -192,11 +189,10 @@ | ||
| 192 | 189 | if( g.perm.Admin ){ |
| 193 | 190 | @ <li>%z(href("%R/urllist"))List of URLs used to access |
| 194 | 191 | @ this repository</a></li> |
| 195 | 192 | } |
| 196 | 193 | @ <li>%z(href("%R/bloblist"))List of Artifacts</a></li> |
| 197 | - @ <li>%z(href("%R/timewarps"))List of "Timewarp" Check-ins</a></li> | |
| 198 | 194 | @ </ul> |
| 199 | 195 | @ </li> |
| 200 | 196 | } |
| 201 | 197 | @ <li>%z(href("%R/help"))Help</a> |
| 202 | 198 | @ <ul> |
| @@ -219,22 +215,106 @@ | ||
| 219 | 215 | @ <li>%z(href("%R/modreq"))Pending Moderation Requests</a></li> |
| 220 | 216 | @ <li>%z(href("%R/admin_log"))Admin log</a></li> |
| 221 | 217 | @ <li>%z(href("%R/cachestat"))Status of the web-page cache</a></li> |
| 222 | 218 | @ </ul></li> |
| 223 | 219 | } |
| 224 | - @ <li>Test Pages | |
| 225 | - @ <ul> | |
| 220 | + @ <li>%z(href("%R/sitemap-test"))Test Pages</a></li> | |
| 221 | + if( isPopup ){ | |
| 222 | + @ <li>%z(href("%R/sitemap"))Site Map</a></li> | |
| 223 | + } | |
| 224 | + @ </ul> | |
| 225 | + if( !isPopup ){ | |
| 226 | + style_finish_page("sitemap"); | |
| 227 | + } | |
| 228 | +} | |
| 229 | + | |
| 230 | +/* | |
| 231 | +** WEBPAGE: sitemap-test | |
| 232 | +** | |
| 233 | +** List some of the web pages offered by the Fossil web engine for testing | |
| 234 | +** purposes. This is similar to /sitemap, but is focused only on showing | |
| 235 | +** pages associated with testing. | |
| 236 | +*/ | |
| 237 | +void sitemap_test_page(void){ | |
| 238 | + int isPopup = 0; /* This is an XMLHttpRequest() for /sitemap */ | |
| 239 | + | |
| 240 | + login_check_credentials(); | |
| 241 | + if( P("popup")!=0 && cgi_csrf_safe(0) ){ | |
| 242 | + /* If this is a POST from the same origin with the popup=1 parameter, | |
| 243 | + ** then disable anti-robot defenses */ | |
| 244 | + isPopup = 1; | |
| 245 | + g.perm.Hyperlink = 1; | |
| 246 | + g.javascriptHyperlink = 0; | |
| 247 | + } | |
| 248 | + if( !isPopup ){ | |
| 249 | + style_header("Test Page Map"); | |
| 250 | + style_adunit_config(ADUNIT_RIGHT_OK); | |
| 251 | + } | |
| 252 | + @ <ul id="sitemap" class="columns" style="column-width:20em"> | |
| 226 | 253 | if( g.perm.Admin || db_get_boolean("test_env_enable",0) ){ |
| 227 | - @ <li>%z(href("%R/test_env"))CGI Environment Test</a></li> | |
| 254 | + @ <li>%z(href("%R/test_env"))CGI Environment Test</a></li> | |
| 228 | 255 | } |
| 229 | 256 | if( g.perm.Read ){ |
| 230 | - @ <li>%z(href("%R/test-rename-list"))List of file renames</a></li> | |
| 257 | + @ <li>%z(href("%R/test-rename-list"))List of file renames</a></li> | |
| 258 | + } | |
| 259 | + @ <li>%z(href("%R/test-builtin-files"))List of built-in files</a></li> | |
| 260 | + @ <li>%z(href("%R/mimetype_list"))List of MIME types</a></li> | |
| 261 | + @ <li>%z(href("%R/hash-color-test"))Page to experiment with the automatic | |
| 262 | + @ colors assigned to branch names</a> | |
| 263 | + if( g.perm.Admin ){ | |
| 264 | + @ <li>%z(href("%R/test-backlinks"))List of backlinks</a></li> | |
| 265 | + @ <li>%z(href("%R/test-backlink-timeline"))Backlink timeline</a></li> | |
| 266 | + @ <li>%z(href("%R/phantoms"))List of phantom artifacts</a></li> | |
| 267 | + @ <li>%z(href("%R/test-warning"))Error Log test page</a></li> | |
| 268 | + @ <li>%z(href("%R/repo_stat1"))Repository <tt>sqlite_stat1</tt> table</a> | |
| 269 | + @ <li>%z(href("%R/repo_schema"))Repository schema</a></li> | |
| 270 | + } | |
| 271 | + if( g.perm.Read && g.perm.Hyperlink ){ | |
| 272 | + @ <li>%z(href("%R/timewarps"))Timeline of timewarps</a></li> | |
| 273 | + } | |
| 274 | + @ <li>%z(href("%R/cookies"))Content of display preference cookie</a></li> | |
| 275 | + @ <li>%z(href("%R/test-captcha"))Random ASCII-art Captcha image</a></li> | |
| 276 | + @ <li>%z(href("%R/test-piechart"))Pie-Chart generator test</a></li> | |
| 277 | + if( !isPopup ){ | |
| 278 | + style_finish_page("sitemap"); | |
| 279 | + } | |
| 280 | +} | |
| 281 | + | |
| 282 | +/* | |
| 283 | +** WEBPAGE: sitemap-timeline | |
| 284 | +** | |
| 285 | +** Generate a list of hyperlinks to various (obscure) variations on | |
| 286 | +** the /timeline page. | |
| 287 | +*/ | |
| 288 | +void sitemap_timeline_page(void){ | |
| 289 | + int isPopup = 0; /* This is an XMLHttpRequest() for /sitemap */ | |
| 290 | + | |
| 291 | + login_check_credentials(); | |
| 292 | + if( P("popup")!=0 && cgi_csrf_safe(0) ){ | |
| 293 | + /* If this is a POST from the same origin with the popup=1 parameter, | |
| 294 | + ** then disable anti-robot defenses */ | |
| 295 | + isPopup = 1; | |
| 296 | + g.perm.Hyperlink = 1; | |
| 297 | + g.javascriptHyperlink = 0; | |
| 298 | + } | |
| 299 | + if( !isPopup ){ | |
| 300 | + style_header("Timeline Examples"); | |
| 301 | + style_adunit_config(ADUNIT_RIGHT_OK); | |
| 231 | 302 | } |
| 232 | - @ <li>%z(href("%R/hash-color-test"))Page to experiment with the automatic | |
| 233 | - @ colors assigned to branch names</a> | |
| 234 | - @ <li>%z(href("%R/test-captcha"))Random ASCII-art Captcha image</a></li> | |
| 235 | - @ </ul></li> | |
| 303 | + @ <ul id="sitemap" class="columns" style="column-width:20em"> | |
| 304 | + @ <li>%z(href("%R/timeline?ymd"))Current day</a></li> | |
| 305 | + @ <li>%z(href("%R/timeline?yw"))Current week</a></li> | |
| 306 | + @ <li>%z(href("%R/timeline?ym"))Current month</a></li> | |
| 307 | + @ <li>%z(href("%R/thisdayinhistory"))Today in history</a></li> | |
| 308 | + @ <li>%z(href("%R/timeline?a=1970-01-01&y=ci&n=10"))First 10 | |
| 309 | + @ check-ins</a></li> | |
| 310 | + @ <li>%z(href("%R/timeline?namechng"))File name changes</a></li> | |
| 311 | + @ <li>%z(href("%R/timeline?forks"))Forks</a></li> | |
| 312 | + @ <li>%z(href("%R/timeline?cherrypicks"))Cherrypick merges</a></li> | |
| 313 | + @ <li>%z(href("%R/timewarps"))Timewarps</a></li> | |
| 314 | + @ <li>%z(href("%R/timeline?ubg"))Color-coded by user</a></li> | |
| 315 | + @ <li>%z(href("%R/timeline?deltabg"))Delta vs. baseline manifests</a></li> | |
| 236 | 316 | @ </ul> |
| 237 | 317 | if( !isPopup ){ |
| 238 | 318 | style_finish_page("sitemap"); |
| 239 | 319 | } |
| 240 | 320 | } |
| 241 | 321 |
| --- src/sitemap.c | |
| +++ src/sitemap.c | |
| @@ -100,14 +100,11 @@ | |
| 100 | } |
| 101 | if( g.perm.Read ){ |
| 102 | @ <li>%z(href("%R/timeline"))Project Timeline</a> |
| 103 | @ <ul> |
| 104 | @ <li>%z(href("%R/reports"))Activity Reports</a></li> |
| 105 | @ <li>%z(href("%R/timeline?n=all&namechng"))File name changes</a></li> |
| 106 | @ <li>%z(href("%R/timeline?n=all&forks"))Forks</a></li> |
| 107 | @ <li>%z(href("%R/timeline?a=1970-01-01&y=ci&n=10"))First 10 |
| 108 | @ check-ins</a></li> |
| 109 | @ </ul> |
| 110 | @ </li> |
| 111 | } |
| 112 | if( g.perm.Read ){ |
| 113 | @ <li>%z(href("%R/brlist"))Branches</a> |
| @@ -192,11 +189,10 @@ | |
| 192 | if( g.perm.Admin ){ |
| 193 | @ <li>%z(href("%R/urllist"))List of URLs used to access |
| 194 | @ this repository</a></li> |
| 195 | } |
| 196 | @ <li>%z(href("%R/bloblist"))List of Artifacts</a></li> |
| 197 | @ <li>%z(href("%R/timewarps"))List of "Timewarp" Check-ins</a></li> |
| 198 | @ </ul> |
| 199 | @ </li> |
| 200 | } |
| 201 | @ <li>%z(href("%R/help"))Help</a> |
| 202 | @ <ul> |
| @@ -219,22 +215,106 @@ | |
| 219 | @ <li>%z(href("%R/modreq"))Pending Moderation Requests</a></li> |
| 220 | @ <li>%z(href("%R/admin_log"))Admin log</a></li> |
| 221 | @ <li>%z(href("%R/cachestat"))Status of the web-page cache</a></li> |
| 222 | @ </ul></li> |
| 223 | } |
| 224 | @ <li>Test Pages |
| 225 | @ <ul> |
| 226 | if( g.perm.Admin || db_get_boolean("test_env_enable",0) ){ |
| 227 | @ <li>%z(href("%R/test_env"))CGI Environment Test</a></li> |
| 228 | } |
| 229 | if( g.perm.Read ){ |
| 230 | @ <li>%z(href("%R/test-rename-list"))List of file renames</a></li> |
| 231 | } |
| 232 | @ <li>%z(href("%R/hash-color-test"))Page to experiment with the automatic |
| 233 | @ colors assigned to branch names</a> |
| 234 | @ <li>%z(href("%R/test-captcha"))Random ASCII-art Captcha image</a></li> |
| 235 | @ </ul></li> |
| 236 | @ </ul> |
| 237 | if( !isPopup ){ |
| 238 | style_finish_page("sitemap"); |
| 239 | } |
| 240 | } |
| 241 |
| --- src/sitemap.c | |
| +++ src/sitemap.c | |
| @@ -100,14 +100,11 @@ | |
| 100 | } |
| 101 | if( g.perm.Read ){ |
| 102 | @ <li>%z(href("%R/timeline"))Project Timeline</a> |
| 103 | @ <ul> |
| 104 | @ <li>%z(href("%R/reports"))Activity Reports</a></li> |
| 105 | @ <li>%z(href("%R/sitemap-timeline"))Other timelines</a></li> |
| 106 | @ </ul> |
| 107 | @ </li> |
| 108 | } |
| 109 | if( g.perm.Read ){ |
| 110 | @ <li>%z(href("%R/brlist"))Branches</a> |
| @@ -192,11 +189,10 @@ | |
| 189 | if( g.perm.Admin ){ |
| 190 | @ <li>%z(href("%R/urllist"))List of URLs used to access |
| 191 | @ this repository</a></li> |
| 192 | } |
| 193 | @ <li>%z(href("%R/bloblist"))List of Artifacts</a></li> |
| 194 | @ </ul> |
| 195 | @ </li> |
| 196 | } |
| 197 | @ <li>%z(href("%R/help"))Help</a> |
| 198 | @ <ul> |
| @@ -219,22 +215,106 @@ | |
| 215 | @ <li>%z(href("%R/modreq"))Pending Moderation Requests</a></li> |
| 216 | @ <li>%z(href("%R/admin_log"))Admin log</a></li> |
| 217 | @ <li>%z(href("%R/cachestat"))Status of the web-page cache</a></li> |
| 218 | @ </ul></li> |
| 219 | } |
| 220 | @ <li>%z(href("%R/sitemap-test"))Test Pages</a></li> |
| 221 | if( isPopup ){ |
| 222 | @ <li>%z(href("%R/sitemap"))Site Map</a></li> |
| 223 | } |
| 224 | @ </ul> |
| 225 | if( !isPopup ){ |
| 226 | style_finish_page("sitemap"); |
| 227 | } |
| 228 | } |
| 229 | |
| 230 | /* |
| 231 | ** WEBPAGE: sitemap-test |
| 232 | ** |
| 233 | ** List some of the web pages offered by the Fossil web engine for testing |
| 234 | ** purposes. This is similar to /sitemap, but is focused only on showing |
| 235 | ** pages associated with testing. |
| 236 | */ |
| 237 | void sitemap_test_page(void){ |
| 238 | int isPopup = 0; /* This is an XMLHttpRequest() for /sitemap */ |
| 239 | |
| 240 | login_check_credentials(); |
| 241 | if( P("popup")!=0 && cgi_csrf_safe(0) ){ |
| 242 | /* If this is a POST from the same origin with the popup=1 parameter, |
| 243 | ** then disable anti-robot defenses */ |
| 244 | isPopup = 1; |
| 245 | g.perm.Hyperlink = 1; |
| 246 | g.javascriptHyperlink = 0; |
| 247 | } |
| 248 | if( !isPopup ){ |
| 249 | style_header("Test Page Map"); |
| 250 | style_adunit_config(ADUNIT_RIGHT_OK); |
| 251 | } |
| 252 | @ <ul id="sitemap" class="columns" style="column-width:20em"> |
| 253 | if( g.perm.Admin || db_get_boolean("test_env_enable",0) ){ |
| 254 | @ <li>%z(href("%R/test_env"))CGI Environment Test</a></li> |
| 255 | } |
| 256 | if( g.perm.Read ){ |
| 257 | @ <li>%z(href("%R/test-rename-list"))List of file renames</a></li> |
| 258 | } |
| 259 | @ <li>%z(href("%R/test-builtin-files"))List of built-in files</a></li> |
| 260 | @ <li>%z(href("%R/mimetype_list"))List of MIME types</a></li> |
| 261 | @ <li>%z(href("%R/hash-color-test"))Page to experiment with the automatic |
| 262 | @ colors assigned to branch names</a> |
| 263 | if( g.perm.Admin ){ |
| 264 | @ <li>%z(href("%R/test-backlinks"))List of backlinks</a></li> |
| 265 | @ <li>%z(href("%R/test-backlink-timeline"))Backlink timeline</a></li> |
| 266 | @ <li>%z(href("%R/phantoms"))List of phantom artifacts</a></li> |
| 267 | @ <li>%z(href("%R/test-warning"))Error Log test page</a></li> |
| 268 | @ <li>%z(href("%R/repo_stat1"))Repository <tt>sqlite_stat1</tt> table</a> |
| 269 | @ <li>%z(href("%R/repo_schema"))Repository schema</a></li> |
| 270 | } |
| 271 | if( g.perm.Read && g.perm.Hyperlink ){ |
| 272 | @ <li>%z(href("%R/timewarps"))Timeline of timewarps</a></li> |
| 273 | } |
| 274 | @ <li>%z(href("%R/cookies"))Content of display preference cookie</a></li> |
| 275 | @ <li>%z(href("%R/test-captcha"))Random ASCII-art Captcha image</a></li> |
| 276 | @ <li>%z(href("%R/test-piechart"))Pie-Chart generator test</a></li> |
| 277 | if( !isPopup ){ |
| 278 | style_finish_page("sitemap"); |
| 279 | } |
| 280 | } |
| 281 | |
| 282 | /* |
| 283 | ** WEBPAGE: sitemap-timeline |
| 284 | ** |
| 285 | ** Generate a list of hyperlinks to various (obscure) variations on |
| 286 | ** the /timeline page. |
| 287 | */ |
| 288 | void sitemap_timeline_page(void){ |
| 289 | int isPopup = 0; /* This is an XMLHttpRequest() for /sitemap */ |
| 290 | |
| 291 | login_check_credentials(); |
| 292 | if( P("popup")!=0 && cgi_csrf_safe(0) ){ |
| 293 | /* If this is a POST from the same origin with the popup=1 parameter, |
| 294 | ** then disable anti-robot defenses */ |
| 295 | isPopup = 1; |
| 296 | g.perm.Hyperlink = 1; |
| 297 | g.javascriptHyperlink = 0; |
| 298 | } |
| 299 | if( !isPopup ){ |
| 300 | style_header("Timeline Examples"); |
| 301 | style_adunit_config(ADUNIT_RIGHT_OK); |
| 302 | } |
| 303 | @ <ul id="sitemap" class="columns" style="column-width:20em"> |
| 304 | @ <li>%z(href("%R/timeline?ymd"))Current day</a></li> |
| 305 | @ <li>%z(href("%R/timeline?yw"))Current week</a></li> |
| 306 | @ <li>%z(href("%R/timeline?ym"))Current month</a></li> |
| 307 | @ <li>%z(href("%R/thisdayinhistory"))Today in history</a></li> |
| 308 | @ <li>%z(href("%R/timeline?a=1970-01-01&y=ci&n=10"))First 10 |
| 309 | @ check-ins</a></li> |
| 310 | @ <li>%z(href("%R/timeline?namechng"))File name changes</a></li> |
| 311 | @ <li>%z(href("%R/timeline?forks"))Forks</a></li> |
| 312 | @ <li>%z(href("%R/timeline?cherrypicks"))Cherrypick merges</a></li> |
| 313 | @ <li>%z(href("%R/timewarps"))Timewarps</a></li> |
| 314 | @ <li>%z(href("%R/timeline?ubg"))Color-coded by user</a></li> |
| 315 | @ <li>%z(href("%R/timeline?deltabg"))Delta vs. baseline manifests</a></li> |
| 316 | @ </ul> |
| 317 | if( !isPopup ){ |
| 318 | style_finish_page("sitemap"); |
| 319 | } |
| 320 | } |
| 321 |
+37
-14
| --- src/sqlcmd.c | ||
| +++ src/sqlcmd.c | ||
| @@ -32,10 +32,15 @@ | ||
| 32 | 32 | |
| 33 | 33 | #ifndef _WIN32 |
| 34 | 34 | # include "linenoise.h" |
| 35 | 35 | #endif |
| 36 | 36 | |
| 37 | +/* | |
| 38 | +** True if the "fossil sql" command has the --test flag. False otherwise. | |
| 39 | +*/ | |
| 40 | +static int local_bSqlCmdTest = 0; | |
| 41 | + | |
| 37 | 42 | /* |
| 38 | 43 | ** Implementation of the "content(X)" SQL function. Return the complete |
| 39 | 44 | ** content of artifact identified by X as a blob. |
| 40 | 45 | */ |
| 41 | 46 | static void sqlcmd_content( |
| @@ -114,11 +119,10 @@ | ||
| 114 | 119 | pIn = sqlite3_value_blob(argv[0]); |
| 115 | 120 | if( pIn==0 ) return; |
| 116 | 121 | nIn = sqlite3_value_bytes(argv[0]); |
| 117 | 122 | if( nIn<4 ) return; |
| 118 | 123 | nOut = (pIn[0]<<24) + (pIn[1]<<16) + (pIn[2]<<8) + pIn[3]; |
| 119 | - if( nOut<0 ) return; | |
| 120 | 124 | pOut = sqlite3_malloc( nOut+1 ); |
| 121 | 125 | rc = uncompress(pOut, &nOut, &pIn[4], nIn-4); |
| 122 | 126 | if( rc==Z_OK ){ |
| 123 | 127 | sqlite3_result_blob(context, pOut, nOut, sqlite3_free); |
| 124 | 128 | }else if( rc==Z_MEM_ERROR ){ |
| @@ -163,33 +167,46 @@ | ||
| 163 | 167 | ** Undocumented test SQL functions: |
| 164 | 168 | ** |
| 165 | 169 | ** db_protect(X) |
| 166 | 170 | ** db_protect_pop(X) |
| 167 | 171 | ** |
| 168 | -** These invoke the corresponding C routines. Misuse may result in | |
| 169 | -** an assertion fault. | |
| 172 | +** These invoke the corresponding C routines. | |
| 173 | +** | |
| 174 | +** WARNING: | |
| 175 | +** Do not instantiate these functions for any Fossil webpage or command | |
| 176 | +** method of than the "fossil sql" command. If an attacker gains access | |
| 177 | +** to these functions, he will be able to disable other defense mechanisms. | |
| 178 | +** | |
| 179 | +** This routines are for interactiving testing only. They are experimental | |
| 180 | +** and undocumented (apart from this comments) and might go away or change | |
| 181 | +** in future releases. | |
| 182 | +** | |
| 183 | +** 2020-11-29: This functions are now only available if the "fossil sql" | |
| 184 | +** command is started with the --test option. | |
| 170 | 185 | */ |
| 171 | 186 | static void sqlcmd_db_protect( |
| 172 | 187 | sqlite3_context *context, |
| 173 | 188 | int argc, |
| 174 | 189 | sqlite3_value **argv |
| 175 | 190 | ){ |
| 176 | 191 | unsigned mask = 0; |
| 177 | 192 | const char *z = (const char*)sqlite3_value_text(argv[0]); |
| 178 | - if( sqlite3_stricmp(z,"user")==0 ) mask |= PROTECT_USER; | |
| 179 | - if( sqlite3_stricmp(z,"config")==0 ) mask |= PROTECT_CONFIG; | |
| 180 | - if( sqlite3_stricmp(z,"sensitive")==0 ) mask |= PROTECT_SENSITIVE; | |
| 181 | - if( sqlite3_stricmp(z,"readonly")==0 ) mask |= PROTECT_READONLY; | |
| 182 | - if( sqlite3_stricmp(z,"all")==0 ) mask |= PROTECT_ALL; | |
| 183 | - db_protect(mask); | |
| 193 | + if( z!=0 && local_bSqlCmdTest ){ | |
| 194 | + if( sqlite3_stricmp(z,"user")==0 ) mask |= PROTECT_USER; | |
| 195 | + if( sqlite3_stricmp(z,"config")==0 ) mask |= PROTECT_CONFIG; | |
| 196 | + if( sqlite3_stricmp(z,"sensitive")==0 ) mask |= PROTECT_SENSITIVE; | |
| 197 | + if( sqlite3_stricmp(z,"readonly")==0 ) mask |= PROTECT_READONLY; | |
| 198 | + if( sqlite3_stricmp(z,"all")==0 ) mask |= PROTECT_ALL; | |
| 199 | + db_protect(mask); | |
| 200 | + } | |
| 184 | 201 | } |
| 185 | 202 | static void sqlcmd_db_protect_pop( |
| 186 | 203 | sqlite3_context *context, |
| 187 | 204 | int argc, |
| 188 | 205 | sqlite3_value **argv |
| 189 | 206 | ){ |
| 190 | - db_protect_pop(); | |
| 207 | + if( !local_bSqlCmdTest ) db_protect_pop(); | |
| 191 | 208 | } |
| 192 | 209 | |
| 193 | 210 | |
| 194 | 211 | |
| 195 | 212 | |
| @@ -232,14 +249,16 @@ | ||
| 232 | 249 | ** will get cleaned up when the shell closes the database connection */ |
| 233 | 250 | if( g.fSqlTrace ) mTrace |= SQLITE_TRACE_PROFILE; |
| 234 | 251 | sqlite3_trace_v2(db, mTrace, db_sql_trace, 0); |
| 235 | 252 | db_protect_only(PROTECT_NONE); |
| 236 | 253 | sqlite3_set_authorizer(db, db_top_authorizer, db); |
| 237 | - sqlite3_create_function(db, "db_protect", 1, SQLITE_UTF8, 0, | |
| 238 | - sqlcmd_db_protect, 0, 0); | |
| 239 | - sqlite3_create_function(db, "db_protect_pop", 0, SQLITE_UTF8, 0, | |
| 240 | - sqlcmd_db_protect_pop, 0, 0); | |
| 254 | + if( local_bSqlCmdTest ){ | |
| 255 | + sqlite3_create_function(db, "db_protect", 1, SQLITE_UTF8, 0, | |
| 256 | + sqlcmd_db_protect, 0, 0); | |
| 257 | + sqlite3_create_function(db, "db_protect_pop", 0, SQLITE_UTF8, 0, | |
| 258 | + sqlcmd_db_protect_pop, 0, 0); | |
| 259 | + } | |
| 241 | 260 | return SQLITE_OK; |
| 242 | 261 | } |
| 243 | 262 | |
| 244 | 263 | /* |
| 245 | 264 | ** atexit() handler that cleans up global state modified by this module. |
| @@ -329,10 +348,13 @@ | ||
| 329 | 348 | ** --readonly Open the repository read-only. No changes |
| 330 | 349 | ** are allowed. This is a recommended safety |
| 331 | 350 | ** precaution to prevent repository damage. |
| 332 | 351 | ** |
| 333 | 352 | ** -R REPOSITORY Use REPOSITORY as the repository database |
| 353 | +** | |
| 354 | +** --test Enable some testing and analysis features | |
| 355 | +** that are normally disabled. | |
| 334 | 356 | ** |
| 335 | 357 | ** All of the standard sqlite3 command-line shell options should also |
| 336 | 358 | ** work. |
| 337 | 359 | ** |
| 338 | 360 | ** The following SQL extensions are provided with this Fossil-enhanced |
| @@ -397,10 +419,11 @@ | ||
| 397 | 419 | extern int sqlite3_shell(int, char**); |
| 398 | 420 | #ifdef FOSSIL_ENABLE_TH1_HOOKS |
| 399 | 421 | g.fNoThHook = 1; |
| 400 | 422 | #endif |
| 401 | 423 | noRepository = find_option("no-repository", 0, 0)!=0; |
| 424 | + local_bSqlCmdTest = find_option("test",0,0)!=0; | |
| 402 | 425 | if( !noRepository ){ |
| 403 | 426 | db_find_and_open_repository(OPEN_ANY_SCHEMA, 0); |
| 404 | 427 | } |
| 405 | 428 | db_open_config(1,0); |
| 406 | 429 | zConfigDb = fossil_strdup(g.zConfigDbName); |
| 407 | 430 |
| --- src/sqlcmd.c | |
| +++ src/sqlcmd.c | |
| @@ -32,10 +32,15 @@ | |
| 32 | |
| 33 | #ifndef _WIN32 |
| 34 | # include "linenoise.h" |
| 35 | #endif |
| 36 | |
| 37 | /* |
| 38 | ** Implementation of the "content(X)" SQL function. Return the complete |
| 39 | ** content of artifact identified by X as a blob. |
| 40 | */ |
| 41 | static void sqlcmd_content( |
| @@ -114,11 +119,10 @@ | |
| 114 | pIn = sqlite3_value_blob(argv[0]); |
| 115 | if( pIn==0 ) return; |
| 116 | nIn = sqlite3_value_bytes(argv[0]); |
| 117 | if( nIn<4 ) return; |
| 118 | nOut = (pIn[0]<<24) + (pIn[1]<<16) + (pIn[2]<<8) + pIn[3]; |
| 119 | if( nOut<0 ) return; |
| 120 | pOut = sqlite3_malloc( nOut+1 ); |
| 121 | rc = uncompress(pOut, &nOut, &pIn[4], nIn-4); |
| 122 | if( rc==Z_OK ){ |
| 123 | sqlite3_result_blob(context, pOut, nOut, sqlite3_free); |
| 124 | }else if( rc==Z_MEM_ERROR ){ |
| @@ -163,33 +167,46 @@ | |
| 163 | ** Undocumented test SQL functions: |
| 164 | ** |
| 165 | ** db_protect(X) |
| 166 | ** db_protect_pop(X) |
| 167 | ** |
| 168 | ** These invoke the corresponding C routines. Misuse may result in |
| 169 | ** an assertion fault. |
| 170 | */ |
| 171 | static void sqlcmd_db_protect( |
| 172 | sqlite3_context *context, |
| 173 | int argc, |
| 174 | sqlite3_value **argv |
| 175 | ){ |
| 176 | unsigned mask = 0; |
| 177 | const char *z = (const char*)sqlite3_value_text(argv[0]); |
| 178 | if( sqlite3_stricmp(z,"user")==0 ) mask |= PROTECT_USER; |
| 179 | if( sqlite3_stricmp(z,"config")==0 ) mask |= PROTECT_CONFIG; |
| 180 | if( sqlite3_stricmp(z,"sensitive")==0 ) mask |= PROTECT_SENSITIVE; |
| 181 | if( sqlite3_stricmp(z,"readonly")==0 ) mask |= PROTECT_READONLY; |
| 182 | if( sqlite3_stricmp(z,"all")==0 ) mask |= PROTECT_ALL; |
| 183 | db_protect(mask); |
| 184 | } |
| 185 | static void sqlcmd_db_protect_pop( |
| 186 | sqlite3_context *context, |
| 187 | int argc, |
| 188 | sqlite3_value **argv |
| 189 | ){ |
| 190 | db_protect_pop(); |
| 191 | } |
| 192 | |
| 193 | |
| 194 | |
| 195 | |
| @@ -232,14 +249,16 @@ | |
| 232 | ** will get cleaned up when the shell closes the database connection */ |
| 233 | if( g.fSqlTrace ) mTrace |= SQLITE_TRACE_PROFILE; |
| 234 | sqlite3_trace_v2(db, mTrace, db_sql_trace, 0); |
| 235 | db_protect_only(PROTECT_NONE); |
| 236 | sqlite3_set_authorizer(db, db_top_authorizer, db); |
| 237 | sqlite3_create_function(db, "db_protect", 1, SQLITE_UTF8, 0, |
| 238 | sqlcmd_db_protect, 0, 0); |
| 239 | sqlite3_create_function(db, "db_protect_pop", 0, SQLITE_UTF8, 0, |
| 240 | sqlcmd_db_protect_pop, 0, 0); |
| 241 | return SQLITE_OK; |
| 242 | } |
| 243 | |
| 244 | /* |
| 245 | ** atexit() handler that cleans up global state modified by this module. |
| @@ -329,10 +348,13 @@ | |
| 329 | ** --readonly Open the repository read-only. No changes |
| 330 | ** are allowed. This is a recommended safety |
| 331 | ** precaution to prevent repository damage. |
| 332 | ** |
| 333 | ** -R REPOSITORY Use REPOSITORY as the repository database |
| 334 | ** |
| 335 | ** All of the standard sqlite3 command-line shell options should also |
| 336 | ** work. |
| 337 | ** |
| 338 | ** The following SQL extensions are provided with this Fossil-enhanced |
| @@ -397,10 +419,11 @@ | |
| 397 | extern int sqlite3_shell(int, char**); |
| 398 | #ifdef FOSSIL_ENABLE_TH1_HOOKS |
| 399 | g.fNoThHook = 1; |
| 400 | #endif |
| 401 | noRepository = find_option("no-repository", 0, 0)!=0; |
| 402 | if( !noRepository ){ |
| 403 | db_find_and_open_repository(OPEN_ANY_SCHEMA, 0); |
| 404 | } |
| 405 | db_open_config(1,0); |
| 406 | zConfigDb = fossil_strdup(g.zConfigDbName); |
| 407 |
| --- src/sqlcmd.c | |
| +++ src/sqlcmd.c | |
| @@ -32,10 +32,15 @@ | |
| 32 | |
| 33 | #ifndef _WIN32 |
| 34 | # include "linenoise.h" |
| 35 | #endif |
| 36 | |
| 37 | /* |
| 38 | ** True if the "fossil sql" command has the --test flag. False otherwise. |
| 39 | */ |
| 40 | static int local_bSqlCmdTest = 0; |
| 41 | |
| 42 | /* |
| 43 | ** Implementation of the "content(X)" SQL function. Return the complete |
| 44 | ** content of artifact identified by X as a blob. |
| 45 | */ |
| 46 | static void sqlcmd_content( |
| @@ -114,11 +119,10 @@ | |
| 119 | pIn = sqlite3_value_blob(argv[0]); |
| 120 | if( pIn==0 ) return; |
| 121 | nIn = sqlite3_value_bytes(argv[0]); |
| 122 | if( nIn<4 ) return; |
| 123 | nOut = (pIn[0]<<24) + (pIn[1]<<16) + (pIn[2]<<8) + pIn[3]; |
| 124 | pOut = sqlite3_malloc( nOut+1 ); |
| 125 | rc = uncompress(pOut, &nOut, &pIn[4], nIn-4); |
| 126 | if( rc==Z_OK ){ |
| 127 | sqlite3_result_blob(context, pOut, nOut, sqlite3_free); |
| 128 | }else if( rc==Z_MEM_ERROR ){ |
| @@ -163,33 +167,46 @@ | |
| 167 | ** Undocumented test SQL functions: |
| 168 | ** |
| 169 | ** db_protect(X) |
| 170 | ** db_protect_pop(X) |
| 171 | ** |
| 172 | ** These invoke the corresponding C routines. |
| 173 | ** |
| 174 | ** WARNING: |
| 175 | ** Do not instantiate these functions for any Fossil webpage or command |
| 176 | ** method of than the "fossil sql" command. If an attacker gains access |
| 177 | ** to these functions, he will be able to disable other defense mechanisms. |
| 178 | ** |
| 179 | ** This routines are for interactiving testing only. They are experimental |
| 180 | ** and undocumented (apart from this comments) and might go away or change |
| 181 | ** in future releases. |
| 182 | ** |
| 183 | ** 2020-11-29: This functions are now only available if the "fossil sql" |
| 184 | ** command is started with the --test option. |
| 185 | */ |
| 186 | static void sqlcmd_db_protect( |
| 187 | sqlite3_context *context, |
| 188 | int argc, |
| 189 | sqlite3_value **argv |
| 190 | ){ |
| 191 | unsigned mask = 0; |
| 192 | const char *z = (const char*)sqlite3_value_text(argv[0]); |
| 193 | if( z!=0 && local_bSqlCmdTest ){ |
| 194 | if( sqlite3_stricmp(z,"user")==0 ) mask |= PROTECT_USER; |
| 195 | if( sqlite3_stricmp(z,"config")==0 ) mask |= PROTECT_CONFIG; |
| 196 | if( sqlite3_stricmp(z,"sensitive")==0 ) mask |= PROTECT_SENSITIVE; |
| 197 | if( sqlite3_stricmp(z,"readonly")==0 ) mask |= PROTECT_READONLY; |
| 198 | if( sqlite3_stricmp(z,"all")==0 ) mask |= PROTECT_ALL; |
| 199 | db_protect(mask); |
| 200 | } |
| 201 | } |
| 202 | static void sqlcmd_db_protect_pop( |
| 203 | sqlite3_context *context, |
| 204 | int argc, |
| 205 | sqlite3_value **argv |
| 206 | ){ |
| 207 | if( !local_bSqlCmdTest ) db_protect_pop(); |
| 208 | } |
| 209 | |
| 210 | |
| 211 | |
| 212 | |
| @@ -232,14 +249,16 @@ | |
| 249 | ** will get cleaned up when the shell closes the database connection */ |
| 250 | if( g.fSqlTrace ) mTrace |= SQLITE_TRACE_PROFILE; |
| 251 | sqlite3_trace_v2(db, mTrace, db_sql_trace, 0); |
| 252 | db_protect_only(PROTECT_NONE); |
| 253 | sqlite3_set_authorizer(db, db_top_authorizer, db); |
| 254 | if( local_bSqlCmdTest ){ |
| 255 | sqlite3_create_function(db, "db_protect", 1, SQLITE_UTF8, 0, |
| 256 | sqlcmd_db_protect, 0, 0); |
| 257 | sqlite3_create_function(db, "db_protect_pop", 0, SQLITE_UTF8, 0, |
| 258 | sqlcmd_db_protect_pop, 0, 0); |
| 259 | } |
| 260 | return SQLITE_OK; |
| 261 | } |
| 262 | |
| 263 | /* |
| 264 | ** atexit() handler that cleans up global state modified by this module. |
| @@ -329,10 +348,13 @@ | |
| 348 | ** --readonly Open the repository read-only. No changes |
| 349 | ** are allowed. This is a recommended safety |
| 350 | ** precaution to prevent repository damage. |
| 351 | ** |
| 352 | ** -R REPOSITORY Use REPOSITORY as the repository database |
| 353 | ** |
| 354 | ** --test Enable some testing and analysis features |
| 355 | ** that are normally disabled. |
| 356 | ** |
| 357 | ** All of the standard sqlite3 command-line shell options should also |
| 358 | ** work. |
| 359 | ** |
| 360 | ** The following SQL extensions are provided with this Fossil-enhanced |
| @@ -397,10 +419,11 @@ | |
| 419 | extern int sqlite3_shell(int, char**); |
| 420 | #ifdef FOSSIL_ENABLE_TH1_HOOKS |
| 421 | g.fNoThHook = 1; |
| 422 | #endif |
| 423 | noRepository = find_option("no-repository", 0, 0)!=0; |
| 424 | local_bSqlCmdTest = find_option("test",0,0)!=0; |
| 425 | if( !noRepository ){ |
| 426 | db_find_and_open_repository(OPEN_ANY_SCHEMA, 0); |
| 427 | } |
| 428 | db_open_config(1,0); |
| 429 | zConfigDb = fossil_strdup(g.zConfigDbName); |
| 430 |
+215
-74
| --- src/sqlite3.c | ||
| +++ src/sqlite3.c | ||
| @@ -1171,11 +1171,11 @@ | ||
| 1171 | 1171 | ** [sqlite3_libversion_number()], [sqlite3_sourceid()], |
| 1172 | 1172 | ** [sqlite_version()] and [sqlite_source_id()]. |
| 1173 | 1173 | */ |
| 1174 | 1174 | #define SQLITE_VERSION "3.34.0" |
| 1175 | 1175 | #define SQLITE_VERSION_NUMBER 3034000 |
| 1176 | -#define SQLITE_SOURCE_ID "2020-10-31 18:58:37 7d01e84dc49074e6364267eea9fd20d46a457d2498121a0f218fbf482692392d" | |
| 1176 | +#define SQLITE_SOURCE_ID "2020-12-01 16:14:00 a26b6597e3ae272231b96f9982c3bcc17ddec2f2b6eb4df06a224b91089fed5b" | |
| 1177 | 1177 | |
| 1178 | 1178 | /* |
| 1179 | 1179 | ** CAPI3REF: Run-Time Library Version Numbers |
| 1180 | 1180 | ** KEYWORDS: sqlite3_version sqlite3_sourceid |
| 1181 | 1181 | ** |
| @@ -1550,10 +1550,11 @@ | ||
| 1550 | 1550 | #define SQLITE_IOERR_AUTH (SQLITE_IOERR | (28<<8)) |
| 1551 | 1551 | #define SQLITE_IOERR_BEGIN_ATOMIC (SQLITE_IOERR | (29<<8)) |
| 1552 | 1552 | #define SQLITE_IOERR_COMMIT_ATOMIC (SQLITE_IOERR | (30<<8)) |
| 1553 | 1553 | #define SQLITE_IOERR_ROLLBACK_ATOMIC (SQLITE_IOERR | (31<<8)) |
| 1554 | 1554 | #define SQLITE_IOERR_DATA (SQLITE_IOERR | (32<<8)) |
| 1555 | +#define SQLITE_IOERR_CORRUPTFS (SQLITE_IOERR | (33<<8)) | |
| 1555 | 1556 | #define SQLITE_LOCKED_SHAREDCACHE (SQLITE_LOCKED | (1<<8)) |
| 1556 | 1557 | #define SQLITE_LOCKED_VTAB (SQLITE_LOCKED | (2<<8)) |
| 1557 | 1558 | #define SQLITE_BUSY_RECOVERY (SQLITE_BUSY | (1<<8)) |
| 1558 | 1559 | #define SQLITE_BUSY_SNAPSHOT (SQLITE_BUSY | (2<<8)) |
| 1559 | 1560 | #define SQLITE_BUSY_TIMEOUT (SQLITE_BUSY | (3<<8)) |
| @@ -7238,11 +7239,11 @@ | ||
| 7238 | 7239 | ** CAPI3REF: Determine the transaction state of a database |
| 7239 | 7240 | ** METHOD: sqlite3 |
| 7240 | 7241 | ** |
| 7241 | 7242 | ** ^The sqlite3_txn_state(D,S) interface returns the current |
| 7242 | 7243 | ** [transaction state] of schema S in database connection D. ^If S is NULL, |
| 7243 | -** then the highest transaction state of any schema on databse connection D | |
| 7244 | +** then the highest transaction state of any schema on database connection D | |
| 7244 | 7245 | ** is returned. Transaction states are (in order of lowest to highest): |
| 7245 | 7246 | ** <ol> |
| 7246 | 7247 | ** <li value="0"> SQLITE_TXN_NONE |
| 7247 | 7248 | ** <li value="1"> SQLITE_TXN_READ |
| 7248 | 7249 | ** <li value="2"> SQLITE_TXN_WRITE |
| @@ -8785,11 +8786,10 @@ | ||
| 8785 | 8786 | */ |
| 8786 | 8787 | #define SQLITE_TESTCTRL_FIRST 5 |
| 8787 | 8788 | #define SQLITE_TESTCTRL_PRNG_SAVE 5 |
| 8788 | 8789 | #define SQLITE_TESTCTRL_PRNG_RESTORE 6 |
| 8789 | 8790 | #define SQLITE_TESTCTRL_PRNG_RESET 7 /* NOT USED */ |
| 8790 | -#define SQLITE_TESTCTRL_SEEK_COUNT 7 | |
| 8791 | 8791 | #define SQLITE_TESTCTRL_BITVEC_TEST 8 |
| 8792 | 8792 | #define SQLITE_TESTCTRL_FAULT_INSTALL 9 |
| 8793 | 8793 | #define SQLITE_TESTCTRL_BENIGN_MALLOC_HOOKS 10 |
| 8794 | 8794 | #define SQLITE_TESTCTRL_PENDING_BYTE 11 |
| 8795 | 8795 | #define SQLITE_TESTCTRL_ASSERT 12 |
| @@ -8810,11 +8810,12 @@ | ||
| 8810 | 8810 | #define SQLITE_TESTCTRL_IMPOSTER 25 |
| 8811 | 8811 | #define SQLITE_TESTCTRL_PARSER_COVERAGE 26 |
| 8812 | 8812 | #define SQLITE_TESTCTRL_RESULT_INTREAL 27 |
| 8813 | 8813 | #define SQLITE_TESTCTRL_PRNG_SEED 28 |
| 8814 | 8814 | #define SQLITE_TESTCTRL_EXTRA_SCHEMA_CHECKS 29 |
| 8815 | -#define SQLITE_TESTCTRL_LAST 29 /* Largest TESTCTRL */ | |
| 8815 | +#define SQLITE_TESTCTRL_SEEK_COUNT 30 | |
| 8816 | +#define SQLITE_TESTCTRL_LAST 30 /* Largest TESTCTRL */ | |
| 8816 | 8817 | |
| 8817 | 8818 | /* |
| 8818 | 8819 | ** CAPI3REF: SQL Keyword Checking |
| 8819 | 8820 | ** |
| 8820 | 8821 | ** These routines provide access to the set of SQL language keywords |
| @@ -28182,16 +28183,19 @@ | ||
| 28182 | 28183 | EnableLookaside; |
| 28183 | 28184 | } |
| 28184 | 28185 | } |
| 28185 | 28186 | |
| 28186 | 28187 | /* |
| 28187 | -** Take actions at the end of an API call to indicate an OOM error | |
| 28188 | +** Take actions at the end of an API call to deal with error codes. | |
| 28188 | 28189 | */ |
| 28189 | -static SQLITE_NOINLINE int apiOomError(sqlite3 *db){ | |
| 28190 | - sqlite3OomClear(db); | |
| 28191 | - sqlite3Error(db, SQLITE_NOMEM); | |
| 28192 | - return SQLITE_NOMEM_BKPT; | |
| 28190 | +static SQLITE_NOINLINE int apiHandleError(sqlite3 *db, int rc){ | |
| 28191 | + if( db->mallocFailed || rc==SQLITE_IOERR_NOMEM ){ | |
| 28192 | + sqlite3OomClear(db); | |
| 28193 | + sqlite3Error(db, SQLITE_NOMEM); | |
| 28194 | + return SQLITE_NOMEM_BKPT; | |
| 28195 | + } | |
| 28196 | + return rc & db->errMask; | |
| 28193 | 28197 | } |
| 28194 | 28198 | |
| 28195 | 28199 | /* |
| 28196 | 28200 | ** This function must be called before exiting any API function (i.e. |
| 28197 | 28201 | ** returning control to the user) that has called sqlite3_malloc or |
| @@ -28209,12 +28213,12 @@ | ||
| 28209 | 28213 | ** Otherwise the read (and possible write) of db->mallocFailed |
| 28210 | 28214 | ** is unsafe, as is the call to sqlite3Error(). |
| 28211 | 28215 | */ |
| 28212 | 28216 | assert( db!=0 ); |
| 28213 | 28217 | assert( sqlite3_mutex_held(db->mutex) ); |
| 28214 | - if( db->mallocFailed || rc==SQLITE_IOERR_NOMEM ){ | |
| 28215 | - return apiOomError(db); | |
| 28218 | + if( db->mallocFailed || rc ){ | |
| 28219 | + return apiHandleError(db, rc); | |
| 28216 | 28220 | } |
| 28217 | 28221 | return rc & db->errMask; |
| 28218 | 28222 | } |
| 28219 | 28223 | |
| 28220 | 28224 | /************** End of malloc.c **********************************************/ |
| @@ -37003,11 +37007,28 @@ | ||
| 37003 | 37007 | |
| 37004 | 37008 | got = seekAndRead(pFile, offset, pBuf, amt); |
| 37005 | 37009 | if( got==amt ){ |
| 37006 | 37010 | return SQLITE_OK; |
| 37007 | 37011 | }else if( got<0 ){ |
| 37008 | - /* lastErrno set by seekAndRead */ | |
| 37012 | + /* pFile->lastErrno has been set by seekAndRead(). | |
| 37013 | + ** Usually we return SQLITE_IOERR_READ here, though for some | |
| 37014 | + ** kinds of errors we return SQLITE_IOERR_CORRUPTFS. The | |
| 37015 | + ** SQLITE_IOERR_CORRUPTFS will be converted into SQLITE_CORRUPT | |
| 37016 | + ** prior to returning to the application by the sqlite3ApiExit() | |
| 37017 | + ** routine. | |
| 37018 | + */ | |
| 37019 | + switch( pFile->lastErrno ){ | |
| 37020 | + case ERANGE: | |
| 37021 | + case EIO: | |
| 37022 | +#ifdef ENXIO | |
| 37023 | + case ENXIO: | |
| 37024 | +#endif | |
| 37025 | +#ifdef EDEVERR | |
| 37026 | + case EDEVERR: | |
| 37027 | +#endif | |
| 37028 | + return SQLITE_IOERR_CORRUPTFS; | |
| 37029 | + } | |
| 37009 | 37030 | return SQLITE_IOERR_READ; |
| 37010 | 37031 | }else{ |
| 37011 | 37032 | storeLastErrno(pFile, 0); /* not a system error */ |
| 37012 | 37033 | /* Unread parts of the buffer must be zero-filled */ |
| 37013 | 37034 | memset(&((char*)pBuf)[got], 0, amt-got); |
| @@ -38535,11 +38556,11 @@ | ||
| 38535 | 38556 | if( bUnlock ){ |
| 38536 | 38557 | rc = unixShmSystemLock(pDbFd, F_UNLCK, ofst+UNIX_SHM_BASE, n); |
| 38537 | 38558 | if( rc==SQLITE_OK ){ |
| 38538 | 38559 | memset(&aLock[ofst], 0, sizeof(int)*n); |
| 38539 | 38560 | } |
| 38540 | - }else if( p->sharedMask & (1<<ofst) ){ | |
| 38561 | + }else if( ALWAYS(p->sharedMask & (1<<ofst)) ){ | |
| 38541 | 38562 | assert( n==1 && aLock[ofst]>1 ); |
| 38542 | 38563 | aLock[ofst]--; |
| 38543 | 38564 | } |
| 38544 | 38565 | |
| 38545 | 38566 | /* Undo the local locks */ |
| @@ -38568,11 +38589,11 @@ | ||
| 38568 | 38589 | /* Make sure no sibling connections hold locks that will block this |
| 38569 | 38590 | ** lock. If any do, return SQLITE_BUSY right away. */ |
| 38570 | 38591 | int ii; |
| 38571 | 38592 | for(ii=ofst; ii<ofst+n; ii++){ |
| 38572 | 38593 | assert( (p->sharedMask & mask)==0 ); |
| 38573 | - if( (p->exclMask & (1<<ii))==0 && aLock[ii] ){ | |
| 38594 | + if( ALWAYS((p->exclMask & (1<<ii))==0) && aLock[ii] ){ | |
| 38574 | 38595 | rc = SQLITE_BUSY; |
| 38575 | 38596 | break; |
| 38576 | 38597 | } |
| 38577 | 38598 | } |
| 38578 | 38599 | |
| @@ -39964,19 +39985,39 @@ | ||
| 39964 | 39985 | } |
| 39965 | 39986 | return SQLITE_OK; |
| 39966 | 39987 | } |
| 39967 | 39988 | |
| 39968 | 39989 | /* |
| 39990 | +** If the last component of the pathname in z[0]..z[j-1] is something | |
| 39991 | +** other than ".." then back it out and return true. If the last | |
| 39992 | +** component is empty or if it is ".." then return false. | |
| 39993 | +*/ | |
| 39994 | +static int unixBackupDir(const char *z, int *pJ){ | |
| 39995 | + int j = *pJ; | |
| 39996 | + int i; | |
| 39997 | + if( j<=0 ) return 0; | |
| 39998 | + for(i=j-1; ALWAYS(i>0) && z[i-1]!='/'; i--){} | |
| 39999 | + if( z[i]=='.' && i==j-2 && z[i+1]=='.' ) return 0; | |
| 40000 | + *pJ = i-1; | |
| 40001 | + return 1; | |
| 40002 | +} | |
| 40003 | + | |
| 40004 | +/* | |
| 40005 | +** Convert a relative pathname into a full pathname. Also | |
| 40006 | +** simplify the pathname as follows: | |
| 39969 | 40007 | ** |
| 40008 | +** Remove all instances of /./ | |
| 40009 | +** Remove all isntances of /X/../ for any X | |
| 39970 | 40010 | */ |
| 39971 | 40011 | static int mkFullPathname( |
| 39972 | 40012 | const char *zPath, /* Input path */ |
| 39973 | 40013 | char *zOut, /* Output buffer */ |
| 39974 | 40014 | int nOut /* Allocated size of buffer zOut */ |
| 39975 | 40015 | ){ |
| 39976 | 40016 | int nPath = sqlite3Strlen30(zPath); |
| 39977 | 40017 | int iOff = 0; |
| 40018 | + int i, j; | |
| 39978 | 40019 | if( zPath[0]!='/' ){ |
| 39979 | 40020 | if( osGetcwd(zOut, nOut-2)==0 ){ |
| 39980 | 40021 | return unixLogError(SQLITE_CANTOPEN_BKPT, "getcwd", zPath); |
| 39981 | 40022 | } |
| 39982 | 40023 | iOff = sqlite3Strlen30(zOut); |
| @@ -39987,10 +40028,45 @@ | ||
| 39987 | 40028 | ** even if it returns an error. */ |
| 39988 | 40029 | zOut[iOff] = '\0'; |
| 39989 | 40030 | return SQLITE_CANTOPEN_BKPT; |
| 39990 | 40031 | } |
| 39991 | 40032 | sqlite3_snprintf(nOut-iOff, &zOut[iOff], "%s", zPath); |
| 40033 | + | |
| 40034 | + /* Remove duplicate '/' characters. Except, two // at the beginning | |
| 40035 | + ** of a pathname is allowed since this is important on windows. */ | |
| 40036 | + for(i=j=1; zOut[i]; i++){ | |
| 40037 | + zOut[j++] = zOut[i]; | |
| 40038 | + while( zOut[i]=='/' && zOut[i+1]=='/' ) i++; | |
| 40039 | + } | |
| 40040 | + zOut[j] = 0; | |
| 40041 | + | |
| 40042 | + assert( zOut[0]=='/' ); | |
| 40043 | + for(i=j=0; zOut[i]; i++){ | |
| 40044 | + if( zOut[i]=='/' ){ | |
| 40045 | + /* Skip over internal "/." directory components */ | |
| 40046 | + if( zOut[i+1]=='.' && zOut[i+2]=='/' ){ | |
| 40047 | + i += 1; | |
| 40048 | + continue; | |
| 40049 | + } | |
| 40050 | + | |
| 40051 | + /* If this is a "/.." directory component then back out the | |
| 40052 | + ** previous term of the directory if it is something other than "..". | |
| 40053 | + */ | |
| 40054 | + if( zOut[i+1]=='.' | |
| 40055 | + && zOut[i+2]=='.' | |
| 40056 | + && zOut[i+3]=='/' | |
| 40057 | + && unixBackupDir(zOut, &j) | |
| 40058 | + ){ | |
| 40059 | + i += 2; | |
| 40060 | + continue; | |
| 40061 | + } | |
| 40062 | + } | |
| 40063 | + if( ALWAYS(j>=0) ) zOut[j] = zOut[i]; | |
| 40064 | + j++; | |
| 40065 | + } | |
| 40066 | + if( NEVER(j==0) ) zOut[j++] = '/'; | |
| 40067 | + zOut[j] = 0; | |
| 39992 | 40068 | return SQLITE_OK; |
| 39993 | 40069 | } |
| 39994 | 40070 | |
| 39995 | 40071 | /* |
| 39996 | 40072 | ** Turn a relative pathname into a full pathname. The relative path |
| @@ -54326,10 +54402,11 @@ | ||
| 54326 | 54402 | sqlite3_file *pJournal; /* Malloc'd child-journal file descriptor */ |
| 54327 | 54403 | char *zSuperJournal = 0; /* Contents of super-journal file */ |
| 54328 | 54404 | i64 nSuperJournal; /* Size of super-journal file */ |
| 54329 | 54405 | char *zJournal; /* Pointer to one journal within MJ file */ |
| 54330 | 54406 | char *zSuperPtr; /* Space to hold super-journal filename */ |
| 54407 | + char *zFree = 0; /* Free this buffer */ | |
| 54331 | 54408 | int nSuperPtr; /* Amount of space allocated to zSuperPtr[] */ |
| 54332 | 54409 | |
| 54333 | 54410 | /* Allocate space for both the pJournal and pSuper file descriptors. |
| 54334 | 54411 | ** If successful, open the super-journal file for reading. |
| 54335 | 54412 | */ |
| @@ -54350,15 +54427,17 @@ | ||
| 54350 | 54427 | ** files extracted from regular rollback-journals. |
| 54351 | 54428 | */ |
| 54352 | 54429 | rc = sqlite3OsFileSize(pSuper, &nSuperJournal); |
| 54353 | 54430 | if( rc!=SQLITE_OK ) goto delsuper_out; |
| 54354 | 54431 | nSuperPtr = pVfs->mxPathname+1; |
| 54355 | - zSuperJournal = sqlite3Malloc(nSuperJournal + nSuperPtr + 2); | |
| 54356 | - if( !zSuperJournal ){ | |
| 54432 | + zFree = sqlite3Malloc(4 + nSuperJournal + nSuperPtr + 2); | |
| 54433 | + if( !zFree ){ | |
| 54357 | 54434 | rc = SQLITE_NOMEM_BKPT; |
| 54358 | 54435 | goto delsuper_out; |
| 54359 | 54436 | } |
| 54437 | + zFree[0] = zFree[1] = zFree[2] = zFree[3] = 0; | |
| 54438 | + zSuperJournal = &zFree[4]; | |
| 54360 | 54439 | zSuperPtr = &zSuperJournal[nSuperJournal+2]; |
| 54361 | 54440 | rc = sqlite3OsRead(pSuper, zSuperJournal, (int)nSuperJournal, 0); |
| 54362 | 54441 | if( rc!=SQLITE_OK ) goto delsuper_out; |
| 54363 | 54442 | zSuperJournal[nSuperJournal] = 0; |
| 54364 | 54443 | zSuperJournal[nSuperJournal+1] = 0; |
| @@ -54402,11 +54481,11 @@ | ||
| 54402 | 54481 | |
| 54403 | 54482 | sqlite3OsClose(pSuper); |
| 54404 | 54483 | rc = sqlite3OsDelete(pVfs, zSuper, 0); |
| 54405 | 54484 | |
| 54406 | 54485 | delsuper_out: |
| 54407 | - sqlite3_free(zSuperJournal); | |
| 54486 | + sqlite3_free(zFree); | |
| 54408 | 54487 | if( pSuper ){ |
| 54409 | 54488 | sqlite3OsClose(pSuper); |
| 54410 | 54489 | assert( !isOpen(pJournal) ); |
| 54411 | 54490 | sqlite3_free(pSuper); |
| 54412 | 54491 | } |
| @@ -54740,11 +54819,15 @@ | ||
| 54740 | 54819 | ** in case this has happened, clear the changeCountDone flag now. |
| 54741 | 54820 | */ |
| 54742 | 54821 | pPager->changeCountDone = pPager->tempFile; |
| 54743 | 54822 | |
| 54744 | 54823 | if( rc==SQLITE_OK ){ |
| 54745 | - zSuper = pPager->pTmpSpace; | |
| 54824 | + /* Leave 4 bytes of space before the super-journal filename in memory. | |
| 54825 | + ** This is because it may end up being passed to sqlite3OsOpen(), in | |
| 54826 | + ** which case it requires 4 0x00 bytes in memory immediately before | |
| 54827 | + ** the filename. */ | |
| 54828 | + zSuper = &pPager->pTmpSpace[4]; | |
| 54746 | 54829 | rc = readSuperJournal(pPager->jfd, zSuper, pPager->pVfs->mxPathname+1); |
| 54747 | 54830 | testcase( rc!=SQLITE_OK ); |
| 54748 | 54831 | } |
| 54749 | 54832 | if( rc==SQLITE_OK |
| 54750 | 54833 | && (pPager->eState>=PAGER_WRITER_DBMOD || pPager->eState==PAGER_OPEN) |
| @@ -54757,10 +54840,12 @@ | ||
| 54757 | 54840 | } |
| 54758 | 54841 | if( rc==SQLITE_OK && zSuper[0] && res ){ |
| 54759 | 54842 | /* If there was a super-journal and this routine will return success, |
| 54760 | 54843 | ** see if it is possible to delete the super-journal. |
| 54761 | 54844 | */ |
| 54845 | + assert( zSuper==&pPager->pTmpSpace[4] ); | |
| 54846 | + memset(&zSuper[-4], 0, 4); | |
| 54762 | 54847 | rc = pager_delsuper(pPager, zSuper); |
| 54763 | 54848 | testcase( rc!=SQLITE_OK ); |
| 54764 | 54849 | } |
| 54765 | 54850 | if( isHot && nPlayback ){ |
| 54766 | 54851 | sqlite3_log(SQLITE_NOTICE_RECOVER_ROLLBACK, "recovered %d pages from %s", |
| @@ -64746,11 +64831,11 @@ | ||
| 64746 | 64831 | #define hasReadConflicts(a, b) 0 |
| 64747 | 64832 | #endif |
| 64748 | 64833 | |
| 64749 | 64834 | #ifdef SQLITE_DEBUG |
| 64750 | 64835 | /* |
| 64751 | -** Return an reset the seek counter for a Btree object. | |
| 64836 | +** Return and reset the seek counter for a Btree object. | |
| 64752 | 64837 | */ |
| 64753 | 64838 | SQLITE_PRIVATE sqlite3_uint64 sqlite3BtreeSeekCount(Btree *pBt){ |
| 64754 | 64839 | u64 n = pBt->nSeek; |
| 64755 | 64840 | pBt->nSeek = 0; |
| 64756 | 64841 | return n; |
| @@ -82192,13 +82277,16 @@ | ||
| 82192 | 82277 | ** equal to, or greater than the second (double). |
| 82193 | 82278 | */ |
| 82194 | 82279 | static int sqlite3IntFloatCompare(i64 i, double r){ |
| 82195 | 82280 | if( sizeof(LONGDOUBLE_TYPE)>8 ){ |
| 82196 | 82281 | LONGDOUBLE_TYPE x = (LONGDOUBLE_TYPE)i; |
| 82282 | + testcase( x<r ); | |
| 82283 | + testcase( x>r ); | |
| 82284 | + testcase( x==r ); | |
| 82197 | 82285 | if( x<r ) return -1; |
| 82198 | - if( x>r ) return +1; | |
| 82199 | - return 0; | |
| 82286 | + if( x>r ) return +1; /*NO_TEST*/ /* work around bugs in gcov */ | |
| 82287 | + return 0; /*NO_TEST*/ /* work around bugs in gcov */ | |
| 82200 | 82288 | }else{ |
| 82201 | 82289 | i64 y; |
| 82202 | 82290 | double s; |
| 82203 | 82291 | if( r<-9223372036854775808.0 ) return +1; |
| 82204 | 82292 | if( r>=9223372036854775808.0 ) return -1; |
| @@ -89388,11 +89476,11 @@ | ||
| 89388 | 89476 | assert( rc==SQLITE_OK ); |
| 89389 | 89477 | break; |
| 89390 | 89478 | } |
| 89391 | 89479 | |
| 89392 | 89480 | |
| 89393 | -/* Opcode: OpenEphemeral P1 P2 * P4 P5 | |
| 89481 | +/* Opcode: OpenEphemeral P1 P2 P3 P4 P5 | |
| 89394 | 89482 | ** Synopsis: nColumn=P2 |
| 89395 | 89483 | ** |
| 89396 | 89484 | ** Open a new cursor P1 to a transient table. |
| 89397 | 89485 | ** The cursor is always opened read/write even if |
| 89398 | 89486 | ** the main database is read-only. The ephemeral |
| @@ -89408,10 +89496,14 @@ | ||
| 89408 | 89496 | ** |
| 89409 | 89497 | ** The P5 parameter can be a mask of the BTREE_* flags defined |
| 89410 | 89498 | ** in btree.h. These flags control aspects of the operation of |
| 89411 | 89499 | ** the btree. The BTREE_OMIT_JOURNAL and BTREE_SINGLE flags are |
| 89412 | 89500 | ** added automatically. |
| 89501 | +** | |
| 89502 | +** If P3 is positive, then reg[P3] is modified slightly so that it | |
| 89503 | +** can be used as zero-length data for OP_Insert. This is an optimization | |
| 89504 | +** that avoids an extra OP_Blob opcode to initialize that register. | |
| 89413 | 89505 | */ |
| 89414 | 89506 | /* Opcode: OpenAutoindex P1 P2 * P4 * |
| 89415 | 89507 | ** Synopsis: nColumn=P2 |
| 89416 | 89508 | ** |
| 89417 | 89509 | ** This opcode works the same as OP_OpenEphemeral. It has a |
| @@ -89430,10 +89522,19 @@ | ||
| 89430 | 89522 | SQLITE_OPEN_EXCLUSIVE | |
| 89431 | 89523 | SQLITE_OPEN_DELETEONCLOSE | |
| 89432 | 89524 | SQLITE_OPEN_TRANSIENT_DB; |
| 89433 | 89525 | assert( pOp->p1>=0 ); |
| 89434 | 89526 | assert( pOp->p2>=0 ); |
| 89527 | + if( pOp->p3>0 ){ | |
| 89528 | + /* Make register reg[P3] into a value that can be used as the data | |
| 89529 | + ** form sqlite3BtreeInsert() where the length of the data is zero. */ | |
| 89530 | + assert( pOp->p2==0 ); /* Only used when number of columns is zero */ | |
| 89531 | + assert( pOp->opcode==OP_OpenEphemeral ); | |
| 89532 | + assert( aMem[pOp->p3].flags & MEM_Null ); | |
| 89533 | + aMem[pOp->p3].n = 0; | |
| 89534 | + aMem[pOp->p3].z = ""; | |
| 89535 | + } | |
| 89435 | 89536 | pCx = p->apCsr[pOp->p1]; |
| 89436 | 89537 | if( pCx && pCx->pBtx ){ |
| 89437 | 89538 | /* If the ephermeral table is already open, erase all existing content |
| 89438 | 89539 | ** so that the table is empty again, rather than creating a new table. */ |
| 89439 | 89540 | assert( pCx->isEphemeral ); |
| @@ -90589,11 +90690,11 @@ | ||
| 90589 | 90690 | if( pOp->p5 & OPFLAG_ISNOOP ) break; |
| 90590 | 90691 | #endif |
| 90591 | 90692 | |
| 90592 | 90693 | if( pOp->p5 & OPFLAG_NCHANGE ) p->nChange++; |
| 90593 | 90694 | if( pOp->p5 & OPFLAG_LASTROWID ) db->lastRowid = x.nKey; |
| 90594 | - assert( pData->flags & (MEM_Blob|MEM_Str) ); | |
| 90695 | + assert( (pData->flags & (MEM_Blob|MEM_Str))!=0 || pData->n==0 ); | |
| 90595 | 90696 | x.pData = pData->z; |
| 90596 | 90697 | x.nData = pData->n; |
| 90597 | 90698 | seekResult = ((pOp->p5 & OPFLAG_USESEEKRESULT) ? pC->seekResult : 0); |
| 90598 | 90699 | if( pData->flags & MEM_Zero ){ |
| 90599 | 90700 | x.nZero = pData->u.nZero; |
| @@ -93644,11 +93745,15 @@ | ||
| 93644 | 93745 | |
| 93645 | 93746 | /* If we reach this point, it means that execution is finished with |
| 93646 | 93747 | ** an error of some kind. |
| 93647 | 93748 | */ |
| 93648 | 93749 | abort_due_to_error: |
| 93649 | - if( db->mallocFailed ) rc = SQLITE_NOMEM_BKPT; | |
| 93750 | + if( db->mallocFailed ){ | |
| 93751 | + rc = SQLITE_NOMEM_BKPT; | |
| 93752 | + }else if( rc==SQLITE_IOERR_CORRUPTFS ){ | |
| 93753 | + rc = SQLITE_CORRUPT_BKPT; | |
| 93754 | + } | |
| 93650 | 93755 | assert( rc ); |
| 93651 | 93756 | if( p->zErrMsg==0 && rc!=SQLITE_IOERR_NOMEM ){ |
| 93652 | 93757 | sqlite3VdbeError(p, "%s", sqlite3ErrStr(rc)); |
| 93653 | 93758 | } |
| 93654 | 93759 | p->rc = rc; |
| @@ -99381,10 +99486,11 @@ | ||
| 99381 | 99486 | for(i=0, pItem=pOrderBy->a; i<pOrderBy->nExpr; i++, pItem++){ |
| 99382 | 99487 | int iCol = -1; |
| 99383 | 99488 | Expr *pE, *pDup; |
| 99384 | 99489 | if( pItem->done ) continue; |
| 99385 | 99490 | pE = sqlite3ExprSkipCollateAndLikely(pItem->pExpr); |
| 99491 | + if( NEVER(pE==0) ) continue; | |
| 99386 | 99492 | if( sqlite3ExprIsInteger(pE, &iCol) ){ |
| 99387 | 99493 | if( iCol<=0 || iCol>pEList->nExpr ){ |
| 99388 | 99494 | resolveOutOfRangeError(pParse, "ORDER", i+1, pEList->nExpr); |
| 99389 | 99495 | return 1; |
| 99390 | 99496 | } |
| @@ -99560,10 +99666,11 @@ | ||
| 99560 | 99666 | nResult = pSelect->pEList->nExpr; |
| 99561 | 99667 | pParse = pNC->pParse; |
| 99562 | 99668 | for(i=0, pItem=pOrderBy->a; i<pOrderBy->nExpr; i++, pItem++){ |
| 99563 | 99669 | Expr *pE = pItem->pExpr; |
| 99564 | 99670 | Expr *pE2 = sqlite3ExprSkipCollateAndLikely(pE); |
| 99671 | + if( NEVER(pE2==0) ) continue; | |
| 99565 | 99672 | if( zType[0]!='G' ){ |
| 99566 | 99673 | iCol = resolveAsName(pParse, pSelect->pEList, pE2); |
| 99567 | 99674 | if( iCol>0 ){ |
| 99568 | 99675 | /* If an AS-name match is found, mark this ORDER BY column as being |
| 99569 | 99676 | ** a copy of the iCol-th result-set column. The subsequent call to |
| @@ -103679,10 +103786,11 @@ | ||
| 103679 | 103786 | ** register iReg. The caller must ensure that iReg already contains |
| 103680 | 103787 | ** the correct value for the expression. |
| 103681 | 103788 | */ |
| 103682 | 103789 | static void exprToRegister(Expr *pExpr, int iReg){ |
| 103683 | 103790 | Expr *p = sqlite3ExprSkipCollateAndLikely(pExpr); |
| 103791 | + if( NEVER(p==0) ) return; | |
| 103684 | 103792 | p->op2 = p->op; |
| 103685 | 103793 | p->op = TK_REGISTER; |
| 103686 | 103794 | p->iTable = iReg; |
| 103687 | 103795 | ExprClearProperty(p, EP_Skip); |
| 103688 | 103796 | } |
| @@ -104666,10 +104774,11 @@ | ||
| 104666 | 104774 | */ |
| 104667 | 104775 | SQLITE_PRIVATE int sqlite3ExprCodeTemp(Parse *pParse, Expr *pExpr, int *pReg){ |
| 104668 | 104776 | int r2; |
| 104669 | 104777 | pExpr = sqlite3ExprSkipCollateAndLikely(pExpr); |
| 104670 | 104778 | if( ConstFactorOk(pParse) |
| 104779 | + && ALWAYS(pExpr!=0) | |
| 104671 | 104780 | && pExpr->op!=TK_REGISTER |
| 104672 | 104781 | && sqlite3ExprIsConstantNotJoin(pExpr) |
| 104673 | 104782 | ){ |
| 104674 | 104783 | *pReg = 0; |
| 104675 | 104784 | r2 = sqlite3ExprCodeRunJustOnce(pParse, pExpr, -1); |
| @@ -119424,10 +119533,12 @@ | ||
| 119424 | 119533 | VFUNCTION(total_changes, 0, 0, 0, total_changes ), |
| 119425 | 119534 | FUNCTION(replace, 3, 0, 0, replaceFunc ), |
| 119426 | 119535 | FUNCTION(zeroblob, 1, 0, 0, zeroblobFunc ), |
| 119427 | 119536 | FUNCTION(substr, 2, 0, 0, substrFunc ), |
| 119428 | 119537 | FUNCTION(substr, 3, 0, 0, substrFunc ), |
| 119538 | + FUNCTION(substring, 2, 0, 0, substrFunc ), | |
| 119539 | + FUNCTION(substring, 3, 0, 0, substrFunc ), | |
| 119429 | 119540 | WAGGREGATE(sum, 1,0,0, sumStep, sumFinalize, sumFinalize, sumInverse, 0), |
| 119430 | 119541 | WAGGREGATE(total, 1,0,0, sumStep,totalFinalize,totalFinalize,sumInverse, 0), |
| 119431 | 119542 | WAGGREGATE(avg, 1,0,0, sumStep, avgFinalize, avgFinalize, sumInverse, 0), |
| 119432 | 119543 | WAGGREGATE(count, 0,0,0, countStep, |
| 119433 | 119544 | countFinalize, countFinalize, countInverse, SQLITE_FUNC_COUNT ), |
| @@ -124310,10 +124421,12 @@ | ||
| 124310 | 124421 | /* Version 3.32.0 and later */ |
| 124311 | 124422 | char *(*create_filename)(const char*,const char*,const char*, |
| 124312 | 124423 | int,const char**); |
| 124313 | 124424 | void (*free_filename)(char*); |
| 124314 | 124425 | sqlite3_file *(*database_file_object)(const char*); |
| 124426 | + /* Version 3.34.0 and later */ | |
| 124427 | + int (*txn_state)(sqlite3*,const char*); | |
| 124315 | 124428 | }; |
| 124316 | 124429 | |
| 124317 | 124430 | /* |
| 124318 | 124431 | ** This is the function signature used for all extension entry points. It |
| 124319 | 124432 | ** is also defined in the file "loadext.c". |
| @@ -124614,10 +124727,12 @@ | ||
| 124614 | 124727 | #define sqlite3_filename_wal sqlite3_api->filename_wal |
| 124615 | 124728 | /* Version 3.32.0 and later */ |
| 124616 | 124729 | #define sqlite3_create_filename sqlite3_api->create_filename |
| 124617 | 124730 | #define sqlite3_free_filename sqlite3_api->free_filename |
| 124618 | 124731 | #define sqlite3_database_file_object sqlite3_api->database_file_object |
| 124732 | +/* Version 3.34.0 and later */ | |
| 124733 | +#define sqlite3_txn_state sqlite3_api->txn_state | |
| 124619 | 124734 | #endif /* !defined(SQLITE_CORE) && !defined(SQLITE_OMIT_LOAD_EXTENSION) */ |
| 124620 | 124735 | |
| 124621 | 124736 | #if !defined(SQLITE_CORE) && !defined(SQLITE_OMIT_LOAD_EXTENSION) |
| 124622 | 124737 | /* This case when the file really is being compiled as a loadable |
| 124623 | 124738 | ** extension */ |
| @@ -125096,10 +125211,12 @@ | ||
| 125096 | 125211 | sqlite3_filename_wal, |
| 125097 | 125212 | /* Version 3.32.0 and later */ |
| 125098 | 125213 | sqlite3_create_filename, |
| 125099 | 125214 | sqlite3_free_filename, |
| 125100 | 125215 | sqlite3_database_file_object, |
| 125216 | + /* Version 3.34.0 and later */ | |
| 125217 | + sqlite3_txn_state, | |
| 125101 | 125218 | }; |
| 125102 | 125219 | |
| 125103 | 125220 | /* True if x is the directory separator character |
| 125104 | 125221 | */ |
| 125105 | 125222 | #if SQLITE_OS_WIN |
| @@ -131654,10 +131771,11 @@ | ||
| 131654 | 131771 | Column *aCol, *pCol; /* For looping over result columns */ |
| 131655 | 131772 | int nCol; /* Number of columns in the result set */ |
| 131656 | 131773 | char *zName; /* Column name */ |
| 131657 | 131774 | int nName; /* Size of name in zName[] */ |
| 131658 | 131775 | Hash ht; /* Hash table of column names */ |
| 131776 | + Table *pTab; | |
| 131659 | 131777 | |
| 131660 | 131778 | sqlite3HashInit(&ht); |
| 131661 | 131779 | if( pEList ){ |
| 131662 | 131780 | nCol = pEList->nExpr; |
| 131663 | 131781 | aCol = sqlite3DbMallocZero(db, sizeof(aCol[0])*nCol); |
| @@ -131676,19 +131794,17 @@ | ||
| 131676 | 131794 | */ |
| 131677 | 131795 | if( (zName = pEList->a[i].zEName)!=0 && pEList->a[i].eEName==ENAME_NAME ){ |
| 131678 | 131796 | /* If the column contains an "AS <name>" phrase, use <name> as the name */ |
| 131679 | 131797 | }else{ |
| 131680 | 131798 | Expr *pColExpr = sqlite3ExprSkipCollateAndLikely(pEList->a[i].pExpr); |
| 131681 | - while( pColExpr->op==TK_DOT ){ | |
| 131799 | + while( ALWAYS(pColExpr!=0) && pColExpr->op==TK_DOT ){ | |
| 131682 | 131800 | pColExpr = pColExpr->pRight; |
| 131683 | 131801 | assert( pColExpr!=0 ); |
| 131684 | 131802 | } |
| 131685 | - if( pColExpr->op==TK_COLUMN ){ | |
| 131803 | + if( pColExpr->op==TK_COLUMN && (pTab = pColExpr->y.pTab)!=0 ){ | |
| 131686 | 131804 | /* For columns use the column name name */ |
| 131687 | 131805 | int iCol = pColExpr->iColumn; |
| 131688 | - Table *pTab = pColExpr->y.pTab; | |
| 131689 | - assert( pTab!=0 ); | |
| 131690 | 131806 | if( iCol<0 ) iCol = pTab->iPKey; |
| 131691 | 131807 | zName = iCol>=0 ? pTab->aCol[iCol].zName : "rowid"; |
| 131692 | 131808 | }else if( pColExpr->op==TK_ID ){ |
| 131693 | 131809 | assert( !ExprHasProperty(pColExpr, EP_IntValue) ); |
| 131694 | 131810 | zName = pColExpr->u.zToken; |
| @@ -136959,26 +137075,15 @@ | ||
| 136959 | 137075 | goto trigger_cleanup; |
| 136960 | 137076 | } |
| 136961 | 137077 | pTab = sqlite3SrcListLookup(pParse, pTableName); |
| 136962 | 137078 | if( !pTab ){ |
| 136963 | 137079 | /* The table does not exist. */ |
| 136964 | - if( db->init.iDb==1 ){ | |
| 136965 | - /* Ticket #3810. | |
| 136966 | - ** Normally, whenever a table is dropped, all associated triggers are | |
| 136967 | - ** dropped too. But if a TEMP trigger is created on a non-TEMP table | |
| 136968 | - ** and the table is dropped by a different database connection, the | |
| 136969 | - ** trigger is not visible to the database connection that does the | |
| 136970 | - ** drop so the trigger cannot be dropped. This results in an | |
| 136971 | - ** "orphaned trigger" - a trigger whose associated table is missing. | |
| 136972 | - */ | |
| 136973 | - db->init.orphanTrigger = 1; | |
| 136974 | - } | |
| 136975 | - goto trigger_cleanup; | |
| 137080 | + goto trigger_orphan_error; | |
| 136976 | 137081 | } |
| 136977 | 137082 | if( IsVirtual(pTab) ){ |
| 136978 | 137083 | sqlite3ErrorMsg(pParse, "cannot create triggers on virtual tables"); |
| 136979 | - goto trigger_cleanup; | |
| 137084 | + goto trigger_orphan_error; | |
| 136980 | 137085 | } |
| 136981 | 137086 | |
| 136982 | 137087 | /* Check that the trigger name is not reserved and that no trigger of the |
| 136983 | 137088 | ** specified name exists */ |
| 136984 | 137089 | zName = sqlite3NameFromToken(db, pName); |
| @@ -137012,16 +137117,16 @@ | ||
| 137012 | 137117 | ** of triggers. |
| 137013 | 137118 | */ |
| 137014 | 137119 | if( pTab->pSelect && tr_tm!=TK_INSTEAD ){ |
| 137015 | 137120 | sqlite3ErrorMsg(pParse, "cannot create %s trigger on view: %S", |
| 137016 | 137121 | (tr_tm == TK_BEFORE)?"BEFORE":"AFTER", pTableName, 0); |
| 137017 | - goto trigger_cleanup; | |
| 137122 | + goto trigger_orphan_error; | |
| 137018 | 137123 | } |
| 137019 | 137124 | if( !pTab->pSelect && tr_tm==TK_INSTEAD ){ |
| 137020 | 137125 | sqlite3ErrorMsg(pParse, "cannot create INSTEAD OF" |
| 137021 | 137126 | " trigger on table: %S", pTableName, 0); |
| 137022 | - goto trigger_cleanup; | |
| 137127 | + goto trigger_orphan_error; | |
| 137023 | 137128 | } |
| 137024 | 137129 | |
| 137025 | 137130 | #ifndef SQLITE_OMIT_AUTHORIZATION |
| 137026 | 137131 | if( !IN_RENAME_OBJECT ){ |
| 137027 | 137132 | int iTabDb = sqlite3SchemaToIndex(db, pTab->pSchema); |
| @@ -137077,10 +137182,27 @@ | ||
| 137077 | 137182 | if( !pParse->pNewTrigger ){ |
| 137078 | 137183 | sqlite3DeleteTrigger(db, pTrigger); |
| 137079 | 137184 | }else{ |
| 137080 | 137185 | assert( pParse->pNewTrigger==pTrigger ); |
| 137081 | 137186 | } |
| 137187 | + return; | |
| 137188 | + | |
| 137189 | +trigger_orphan_error: | |
| 137190 | + if( db->init.iDb==1 ){ | |
| 137191 | + /* Ticket #3810. | |
| 137192 | + ** Normally, whenever a table is dropped, all associated triggers are | |
| 137193 | + ** dropped too. But if a TEMP trigger is created on a non-TEMP table | |
| 137194 | + ** and the table is dropped by a different database connection, the | |
| 137195 | + ** trigger is not visible to the database connection that does the | |
| 137196 | + ** drop so the trigger cannot be dropped. This results in an | |
| 137197 | + ** "orphaned trigger" - a trigger whose associated table is missing. | |
| 137198 | + ** | |
| 137199 | + ** 2020-11-05 see also https://sqlite.org/forum/forumpost/157dc791df | |
| 137200 | + */ | |
| 137201 | + db->init.orphanTrigger = 1; | |
| 137202 | + } | |
| 137203 | + goto trigger_cleanup; | |
| 137082 | 137204 | } |
| 137083 | 137205 | |
| 137084 | 137206 | /* |
| 137085 | 137207 | ** This routine is called after all of the trigger actions have been parsed |
| 137086 | 137208 | ** in order to complete the process of building the trigger. |
| @@ -138664,10 +138786,12 @@ | ||
| 138664 | 138786 | sqlite3VdbeAddOp2(v, OP_Integer, 0, regRowCount); |
| 138665 | 138787 | } |
| 138666 | 138788 | |
| 138667 | 138789 | if( nChangeFrom==0 && HasRowid(pTab) ){ |
| 138668 | 138790 | sqlite3VdbeAddOp3(v, OP_Null, 0, regRowSet, regOldRowid); |
| 138791 | + iEph = pParse->nTab++; | |
| 138792 | + addrOpen = sqlite3VdbeAddOp3(v, OP_OpenEphemeral, iEph, 0, regRowSet); | |
| 138669 | 138793 | }else{ |
| 138670 | 138794 | assert( pPk!=0 || HasRowid(pTab) ); |
| 138671 | 138795 | nPk = pPk ? pPk->nKeyCol : 0; |
| 138672 | 138796 | iPk = pParse->nMem+1; |
| 138673 | 138797 | pParse->nMem += nPk; |
| @@ -138755,13 +138879,14 @@ | ||
| 138755 | 138879 | /* Read the rowid of the current row of the WHERE scan. In ONEPASS_OFF |
| 138756 | 138880 | ** mode, write the rowid into the FIFO. In either of the one-pass modes, |
| 138757 | 138881 | ** leave it in register regOldRowid. */ |
| 138758 | 138882 | sqlite3VdbeAddOp2(v, OP_Rowid, iDataCur, regOldRowid); |
| 138759 | 138883 | if( eOnePass==ONEPASS_OFF ){ |
| 138760 | - /* We need to use regRowSet, so reallocate aRegIdx[nAllIdx] */ | |
| 138761 | 138884 | aRegIdx[nAllIdx] = ++pParse->nMem; |
| 138762 | - sqlite3VdbeAddOp2(v, OP_RowSetAdd, regRowSet, regOldRowid); | |
| 138885 | + sqlite3VdbeAddOp3(v, OP_Insert, iEph, regRowSet, regOldRowid); | |
| 138886 | + }else{ | |
| 138887 | + if( ALWAYS(addrOpen) ) sqlite3VdbeChangeToNoop(v, addrOpen); | |
| 138763 | 138888 | } |
| 138764 | 138889 | }else{ |
| 138765 | 138890 | /* Read the PK of the current row into an array of registers. In |
| 138766 | 138891 | ** ONEPASS_OFF mode, serialize the array into a record and store it in |
| 138767 | 138892 | ** the ephemeral table. Or, in ONEPASS_SINGLE or MULTI mode, change |
| @@ -138845,12 +138970,13 @@ | ||
| 138845 | 138970 | sqlite3VdbeAddOp2(v, OP_RowData, iEph, regKey); |
| 138846 | 138971 | sqlite3VdbeAddOp4Int(v, OP_NotFound, iDataCur, labelContinue, regKey,0); |
| 138847 | 138972 | VdbeCoverage(v); |
| 138848 | 138973 | } |
| 138849 | 138974 | }else{ |
| 138850 | - labelContinue = sqlite3VdbeAddOp3(v, OP_RowSetRead, regRowSet,labelBreak, | |
| 138851 | - regOldRowid); | |
| 138975 | + sqlite3VdbeAddOp2(v, OP_Rewind, iEph, labelBreak); VdbeCoverage(v); | |
| 138976 | + labelContinue = sqlite3VdbeMakeLabel(pParse); | |
| 138977 | + addrTop = sqlite3VdbeAddOp2(v, OP_Rowid, iEph, regOldRowid); | |
| 138852 | 138978 | VdbeCoverage(v); |
| 138853 | 138979 | sqlite3VdbeAddOp3(v, OP_NotExists, iDataCur, labelContinue, regOldRowid); |
| 138854 | 138980 | VdbeCoverage(v); |
| 138855 | 138981 | } |
| 138856 | 138982 | } |
| @@ -139096,15 +139222,13 @@ | ||
| 139096 | 139222 | if( eOnePass==ONEPASS_SINGLE ){ |
| 139097 | 139223 | /* Nothing to do at end-of-loop for a single-pass */ |
| 139098 | 139224 | }else if( eOnePass==ONEPASS_MULTI ){ |
| 139099 | 139225 | sqlite3VdbeResolveLabel(v, labelContinue); |
| 139100 | 139226 | sqlite3WhereEnd(pWInfo); |
| 139101 | - }else if( pPk || nChangeFrom ){ | |
| 139227 | + }else{ | |
| 139102 | 139228 | sqlite3VdbeResolveLabel(v, labelContinue); |
| 139103 | 139229 | sqlite3VdbeAddOp2(v, OP_Next, iEph, addrTop); VdbeCoverage(v); |
| 139104 | - }else{ | |
| 139105 | - sqlite3VdbeGoto(v, labelContinue); | |
| 139106 | 139230 | } |
| 139107 | 139231 | sqlite3VdbeResolveLabel(v, labelBreak); |
| 139108 | 139232 | |
| 139109 | 139233 | /* Update the sqlite_sequence table by storing the content of the |
| 139110 | 139234 | ** maximum rowid counter values recorded while inserting into |
| @@ -145894,10 +146018,11 @@ | ||
| 145894 | 146018 | ** all terms of the WHERE clause. |
| 145895 | 146019 | */ |
| 145896 | 146020 | SQLITE_PRIVATE void sqlite3WhereSplit(WhereClause *pWC, Expr *pExpr, u8 op){ |
| 145897 | 146021 | Expr *pE2 = sqlite3ExprSkipCollateAndLikely(pExpr); |
| 145898 | 146022 | pWC->op = op; |
| 146023 | + assert( pE2!=0 || pExpr==0 ); | |
| 145899 | 146024 | if( pE2==0 ) return; |
| 145900 | 146025 | if( pE2->op!=op ){ |
| 145901 | 146026 | whereClauseInsert(pWC, pExpr, 0); |
| 145902 | 146027 | }else{ |
| 145903 | 146028 | sqlite3WhereSplit(pWC, pE2->pLeft, op); |
| @@ -146292,10 +146417,20 @@ | ||
| 146292 | 146417 | */ |
| 146293 | 146418 | static void createMask(WhereMaskSet *pMaskSet, int iCursor){ |
| 146294 | 146419 | assert( pMaskSet->n < ArraySize(pMaskSet->ix) ); |
| 146295 | 146420 | pMaskSet->ix[pMaskSet->n++] = iCursor; |
| 146296 | 146421 | } |
| 146422 | + | |
| 146423 | +/* | |
| 146424 | +** If the right-hand branch of the expression is a TK_COLUMN, then return | |
| 146425 | +** a pointer to the right-hand branch. Otherwise, return NULL. | |
| 146426 | +*/ | |
| 146427 | +static Expr *whereRightSubexprIsColumn(Expr *p){ | |
| 146428 | + p = sqlite3ExprSkipCollateAndLikely(p->pRight); | |
| 146429 | + if( ALWAYS(p!=0) && p->op==TK_COLUMN ) return p; | |
| 146430 | + return 0; | |
| 146431 | +} | |
| 146297 | 146432 | |
| 146298 | 146433 | /* |
| 146299 | 146434 | ** Advance to the next WhereTerm that matches according to the criteria |
| 146300 | 146435 | ** established when the pScan object was initialized by whereScanInit(). |
| 146301 | 146436 | ** Return NULL if there are no more matching WhereTerms. |
| @@ -146323,12 +146458,11 @@ | ||
| 146323 | 146458 | pScan->pIdxExpr,iCur)==0) |
| 146324 | 146459 | && (pScan->iEquiv<=1 || !ExprHasProperty(pTerm->pExpr, EP_FromJoin)) |
| 146325 | 146460 | ){ |
| 146326 | 146461 | if( (pTerm->eOperator & WO_EQUIV)!=0 |
| 146327 | 146462 | && pScan->nEquiv<ArraySize(pScan->aiCur) |
| 146328 | - && (pX = sqlite3ExprSkipCollateAndLikely(pTerm->pExpr->pRight))->op | |
| 146329 | - ==TK_COLUMN | |
| 146463 | + && (pX = whereRightSubexprIsColumn(pTerm->pExpr))!=0 | |
| 146330 | 146464 | ){ |
| 146331 | 146465 | int j; |
| 146332 | 146466 | for(j=0; j<pScan->nEquiv; j++){ |
| 146333 | 146467 | if( pScan->aiCur[j]==pX->iTable |
| 146334 | 146468 | && pScan->aiColumn[j]==pX->iColumn ){ |
| @@ -146520,11 +146654,12 @@ | ||
| 146520 | 146654 | int i; |
| 146521 | 146655 | const char *zColl = pIdx->azColl[iCol]; |
| 146522 | 146656 | |
| 146523 | 146657 | for(i=0; i<pList->nExpr; i++){ |
| 146524 | 146658 | Expr *p = sqlite3ExprSkipCollateAndLikely(pList->a[i].pExpr); |
| 146525 | - if( p->op==TK_COLUMN | |
| 146659 | + if( ALWAYS(p!=0) | |
| 146660 | + && p->op==TK_COLUMN | |
| 146526 | 146661 | && p->iColumn==pIdx->aiColumn[iCol] |
| 146527 | 146662 | && p->iTable==iBase |
| 146528 | 146663 | ){ |
| 146529 | 146664 | CollSeq *pColl = sqlite3ExprNNCollSeq(pParse, pList->a[i].pExpr); |
| 146530 | 146665 | if( 0==sqlite3StrICmp(pColl->zName, zColl) ){ |
| @@ -146584,10 +146719,11 @@ | ||
| 146584 | 146719 | ** true. Note: The (p->iTable==iBase) part of this test may be false if the |
| 146585 | 146720 | ** current SELECT is a correlated sub-query. |
| 146586 | 146721 | */ |
| 146587 | 146722 | for(i=0; i<pDistinct->nExpr; i++){ |
| 146588 | 146723 | Expr *p = sqlite3ExprSkipCollateAndLikely(pDistinct->a[i].pExpr); |
| 146724 | + if( NEVER(p==0) ) continue; | |
| 146589 | 146725 | if( p->op==TK_COLUMN && p->iTable==iBase && p->iColumn<0 ) return 1; |
| 146590 | 146726 | } |
| 146591 | 146727 | |
| 146592 | 146728 | /* Loop through all indices on the table, checking each to see if it makes |
| 146593 | 146729 | ** the DISTINCT qualifier redundant. It does so if: |
| @@ -148498,13 +148634,13 @@ | ||
| 148498 | 148634 | LogEst rLogSize; /* Logarithm of table size */ |
| 148499 | 148635 | WhereTerm *pTop = 0, *pBtm = 0; /* Top and bottom range constraints */ |
| 148500 | 148636 | |
| 148501 | 148637 | pNew = pBuilder->pNew; |
| 148502 | 148638 | if( db->mallocFailed ) return SQLITE_NOMEM_BKPT; |
| 148503 | - WHERETRACE(0x800, ("BEGIN %s.addBtreeIdx(%s), nEq=%d, nSkip=%d\n", | |
| 148639 | + WHERETRACE(0x800, ("BEGIN %s.addBtreeIdx(%s), nEq=%d, nSkip=%d, rRun=%d\n", | |
| 148504 | 148640 | pProbe->pTable->zName,pProbe->zName, |
| 148505 | - pNew->u.btree.nEq, pNew->nSkip)); | |
| 148641 | + pNew->u.btree.nEq, pNew->nSkip, pNew->rRun)); | |
| 148506 | 148642 | |
| 148507 | 148643 | assert( (pNew->wsFlags & WHERE_VIRTUALTABLE)==0 ); |
| 148508 | 148644 | assert( (pNew->wsFlags & WHERE_TOP_LIMIT)==0 ); |
| 148509 | 148645 | if( pNew->wsFlags & WHERE_BTM_LIMIT ){ |
| 148510 | 148646 | opMask = WO_LT|WO_LE; |
| @@ -148869,10 +149005,11 @@ | ||
| 148869 | 149005 | |
| 148870 | 149006 | if( pIndex->bUnordered ) return 0; |
| 148871 | 149007 | if( (pOB = pBuilder->pWInfo->pOrderBy)==0 ) return 0; |
| 148872 | 149008 | for(ii=0; ii<pOB->nExpr; ii++){ |
| 148873 | 149009 | Expr *pExpr = sqlite3ExprSkipCollateAndLikely(pOB->a[ii].pExpr); |
| 149010 | + if( NEVER(pExpr==0) ) continue; | |
| 148874 | 149011 | if( pExpr->op==TK_COLUMN && pExpr->iTable==iCursor ){ |
| 148875 | 149012 | if( pExpr->iColumn<0 ) return 1; |
| 148876 | 149013 | for(jj=0; jj<pIndex->nKeyCol; jj++){ |
| 148877 | 149014 | if( pExpr->iColumn==pIndex->aiColumn[jj] ) return 1; |
| 148878 | 149015 | } |
| @@ -149847,10 +149984,11 @@ | ||
| 149847 | 149984 | ** loops. |
| 149848 | 149985 | */ |
| 149849 | 149986 | for(i=0; i<nOrderBy; i++){ |
| 149850 | 149987 | if( MASKBIT(i) & obSat ) continue; |
| 149851 | 149988 | pOBExpr = sqlite3ExprSkipCollateAndLikely(pOrderBy->a[i].pExpr); |
| 149989 | + if( NEVER(pOBExpr==0) ) continue; | |
| 149852 | 149990 | if( pOBExpr->op!=TK_COLUMN ) continue; |
| 149853 | 149991 | if( pOBExpr->iTable!=iCur ) continue; |
| 149854 | 149992 | pTerm = sqlite3WhereFindTerm(&pWInfo->sWC, iCur, pOBExpr->iColumn, |
| 149855 | 149993 | ~ready, eqOpMask, 0); |
| 149856 | 149994 | if( pTerm==0 ) continue; |
| @@ -149973,10 +150111,11 @@ | ||
| 149973 | 150111 | for(i=0; bOnce && i<nOrderBy; i++){ |
| 149974 | 150112 | if( MASKBIT(i) & obSat ) continue; |
| 149975 | 150113 | pOBExpr = sqlite3ExprSkipCollateAndLikely(pOrderBy->a[i].pExpr); |
| 149976 | 150114 | testcase( wctrlFlags & WHERE_GROUPBY ); |
| 149977 | 150115 | testcase( wctrlFlags & WHERE_DISTINCTBY ); |
| 150116 | + if( NEVER(pOBExpr==0) ) continue; | |
| 149978 | 150117 | if( (wctrlFlags & (WHERE_GROUPBY|WHERE_DISTINCTBY))==0 ) bOnce = 0; |
| 149979 | 150118 | if( iColumn>=XN_ROWID ){ |
| 149980 | 150119 | if( pOBExpr->op!=TK_COLUMN ) continue; |
| 149981 | 150120 | if( pOBExpr->iTable!=iCur ) continue; |
| 149982 | 150121 | if( pOBExpr->iColumn!=iColumn ) continue; |
| @@ -150136,11 +150275,11 @@ | ||
| 150136 | 150275 | rScale = sqlite3LogEst((nOrderBy-nSorted)*100/nOrderBy) - 66; |
| 150137 | 150276 | rSortCost = nRow + rScale + 16; |
| 150138 | 150277 | |
| 150139 | 150278 | /* Multiple by log(M) where M is the number of output rows. |
| 150140 | 150279 | ** Use the LIMIT for M if it is smaller. Or if this sort is for |
| 150141 | - ** a DISTINT operator, M will be the number of distinct output | |
| 150280 | + ** a DISTINCT operator, M will be the number of distinct output | |
| 150142 | 150281 | ** rows, so fudge it downwards a bit. |
| 150143 | 150282 | */ |
| 150144 | 150283 | if( (pWInfo->wctrlFlags & WHERE_USE_LIMIT)!=0 && pWInfo->iLimit<nRow ){ |
| 150145 | 150284 | nRow = pWInfo->iLimit; |
| 150146 | 150285 | }else if( (pWInfo->wctrlFlags & WHERE_WANT_DISTINCT) ){ |
| @@ -194220,11 +194359,11 @@ | ||
| 194220 | 194359 | p->aSegment = (GeoSegment*)&p->aEvent[nVertex*2]; |
| 194221 | 194360 | p->nEvent = p->nSegment = 0; |
| 194222 | 194361 | geopolyAddSegments(p, p1, 1); |
| 194223 | 194362 | geopolyAddSegments(p, p2, 2); |
| 194224 | 194363 | pThisEvent = geopolySortEventsByX(p->aEvent, p->nEvent); |
| 194225 | - rX = pThisEvent->x==0.0 ? -1.0 : 0.0; | |
| 194364 | + rX = pThisEvent && pThisEvent->x==0.0 ? -1.0 : 0.0; | |
| 194226 | 194365 | memset(aOverlap, 0, sizeof(aOverlap)); |
| 194227 | 194366 | while( pThisEvent ){ |
| 194228 | 194367 | if( pThisEvent->x!=rX ){ |
| 194229 | 194368 | GeoSegment *pPrev = 0; |
| 194230 | 194369 | int iMask = 0; |
| @@ -212129,11 +212268,11 @@ | ||
| 212129 | 212268 | Fts5Bm25Data **ppData /* OUT: bm25-data object for this query */ |
| 212130 | 212269 | ){ |
| 212131 | 212270 | int rc = SQLITE_OK; /* Return code */ |
| 212132 | 212271 | Fts5Bm25Data *p; /* Object to return */ |
| 212133 | 212272 | |
| 212134 | - p = pApi->xGetAuxdata(pFts, 0); | |
| 212273 | + p = (Fts5Bm25Data*)pApi->xGetAuxdata(pFts, 0); | |
| 212135 | 212274 | if( p==0 ){ |
| 212136 | 212275 | int nPhrase; /* Number of phrases in query */ |
| 212137 | 212276 | sqlite3_int64 nRow = 0; /* Number of rows in table */ |
| 212138 | 212277 | sqlite3_int64 nToken = 0; /* Number of tokens in table */ |
| 212139 | 212278 | sqlite3_int64 nByte; /* Bytes of space to allocate */ |
| @@ -212203,11 +212342,11 @@ | ||
| 212203 | 212342 | int nVal, /* Number of values in apVal[] array */ |
| 212204 | 212343 | sqlite3_value **apVal /* Array of trailing arguments */ |
| 212205 | 212344 | ){ |
| 212206 | 212345 | const double k1 = 1.2; /* Constant "k1" from BM25 formula */ |
| 212207 | 212346 | const double b = 0.75; /* Constant "b" from BM25 formula */ |
| 212208 | - int rc = SQLITE_OK; /* Error code */ | |
| 212347 | + int rc; /* Error code */ | |
| 212209 | 212348 | double score = 0.0; /* SQL function return value */ |
| 212210 | 212349 | Fts5Bm25Data *pData; /* Values allocated/calculated once only */ |
| 212211 | 212350 | int i; /* Iterator variable */ |
| 212212 | 212351 | int nInst = 0; /* Value returned by xInstCount() */ |
| 212213 | 212352 | double D = 0.0; /* Total number of tokens in row */ |
| @@ -212235,21 +212374,19 @@ | ||
| 212235 | 212374 | int nTok; |
| 212236 | 212375 | rc = pApi->xColumnSize(pFts, -1, &nTok); |
| 212237 | 212376 | D = (double)nTok; |
| 212238 | 212377 | } |
| 212239 | 212378 | |
| 212240 | - /* Determine the BM25 score for the current row. */ | |
| 212241 | - for(i=0; rc==SQLITE_OK && i<pData->nPhrase; i++){ | |
| 212242 | - score += pData->aIDF[i] * ( | |
| 212243 | - ( aFreq[i] * (k1 + 1.0) ) / | |
| 212244 | - ( aFreq[i] + k1 * (1 - b + b * D / pData->avgdl) ) | |
| 212245 | - ); | |
| 212246 | - } | |
| 212247 | - | |
| 212248 | - /* If no error has occurred, return the calculated score. Otherwise, | |
| 212249 | - ** throw an SQL exception. */ | |
| 212379 | + /* Determine and return the BM25 score for the current row. Or, if an | |
| 212380 | + ** error has occurred, throw an exception. */ | |
| 212250 | 212381 | if( rc==SQLITE_OK ){ |
| 212382 | + for(i=0; i<pData->nPhrase; i++){ | |
| 212383 | + score += pData->aIDF[i] * ( | |
| 212384 | + ( aFreq[i] * (k1 + 1.0) ) / | |
| 212385 | + ( aFreq[i] + k1 * (1 - b + b * D / pData->avgdl) ) | |
| 212386 | + ); | |
| 212387 | + } | |
| 212251 | 212388 | sqlite3_result_double(pCtx, -1.0 * score); |
| 212252 | 212389 | }else{ |
| 212253 | 212390 | sqlite3_result_error_code(pCtx, rc); |
| 212254 | 212391 | } |
| 212255 | 212392 | } |
| @@ -223384,10 +223521,11 @@ | ||
| 223384 | 223521 | cksum2 ^= sqlite3Fts5IndexEntryCksum(iRowid, 0, 0, -1, z, n); |
| 223385 | 223522 | } |
| 223386 | 223523 | }else{ |
| 223387 | 223524 | poslist.n = 0; |
| 223388 | 223525 | fts5SegiterPoslist(p, &pIter->aSeg[pIter->aFirst[1].iFirst], 0, &poslist); |
| 223526 | + fts5BufferAppendBlob(&p->rc, &poslist, 4, (const u8*)"\0\0\0\0"); | |
| 223389 | 223527 | while( 0==sqlite3Fts5PoslistNext64(poslist.p, poslist.n, &iOff, &iPos) ){ |
| 223390 | 223528 | int iCol = FTS5_POS2COLUMN(iPos); |
| 223391 | 223529 | int iTokOff = FTS5_POS2OFFSET(iPos); |
| 223392 | 223530 | cksum2 ^= sqlite3Fts5IndexEntryCksum(iRowid, iCol, iTokOff, -1, z, n); |
| 223393 | 223531 | } |
| @@ -226679,11 +226817,11 @@ | ||
| 226679 | 226817 | int nArg, /* Number of args */ |
| 226680 | 226818 | sqlite3_value **apUnused /* Function arguments */ |
| 226681 | 226819 | ){ |
| 226682 | 226820 | assert( nArg==0 ); |
| 226683 | 226821 | UNUSED_PARAM2(nArg, apUnused); |
| 226684 | - sqlite3_result_text(pCtx, "fts5: 2020-10-26 16:22:31 80eba105d6d1b49ba8ca2ad4e14ddec2de0bdc2f6686c2f8a1c1d24fc1fe846f", -1, SQLITE_TRANSIENT); | |
| 226822 | + sqlite3_result_text(pCtx, "fts5: 2020-12-01 16:14:00 a26b6597e3ae272231b96f9982c3bcc17ddec2f2b6eb4df06a224b91089fed5b", -1, SQLITE_TRANSIENT); | |
| 226685 | 226823 | } |
| 226686 | 226824 | |
| 226687 | 226825 | /* |
| 226688 | 226826 | ** Return true if zName is the extension on one of the shadow tables used |
| 226689 | 226827 | ** by this module. |
| @@ -229252,17 +229390,18 @@ | ||
| 229252 | 229390 | |
| 229253 | 229391 | /* |
| 229254 | 229392 | ** Allocate a trigram tokenizer. |
| 229255 | 229393 | */ |
| 229256 | 229394 | static int fts5TriCreate( |
| 229257 | - void *pCtx, | |
| 229395 | + void *pUnused, | |
| 229258 | 229396 | const char **azArg, |
| 229259 | 229397 | int nArg, |
| 229260 | 229398 | Fts5Tokenizer **ppOut |
| 229261 | 229399 | ){ |
| 229262 | 229400 | int rc = SQLITE_OK; |
| 229263 | 229401 | TrigramTokenizer *pNew = (TrigramTokenizer*)sqlite3_malloc(sizeof(*pNew)); |
| 229402 | + UNUSED_PARAM(pUnused); | |
| 229264 | 229403 | if( pNew==0 ){ |
| 229265 | 229404 | rc = SQLITE_NOMEM; |
| 229266 | 229405 | }else{ |
| 229267 | 229406 | int i; |
| 229268 | 229407 | pNew->bFold = 1; |
| @@ -229291,11 +229430,11 @@ | ||
| 229291 | 229430 | ** Trigram tokenizer tokenize routine. |
| 229292 | 229431 | */ |
| 229293 | 229432 | static int fts5TriTokenize( |
| 229294 | 229433 | Fts5Tokenizer *pTok, |
| 229295 | 229434 | void *pCtx, |
| 229296 | - int flags, | |
| 229435 | + int unusedFlags, | |
| 229297 | 229436 | const char *pText, int nText, |
| 229298 | 229437 | int (*xToken)(void*, int, const char*, int, int, int) |
| 229299 | 229438 | ){ |
| 229300 | 229439 | TrigramTokenizer *p = (TrigramTokenizer*)pTok; |
| 229301 | 229440 | int rc = SQLITE_OK; |
| @@ -229302,10 +229441,11 @@ | ||
| 229302 | 229441 | char aBuf[32]; |
| 229303 | 229442 | const unsigned char *zIn = (const unsigned char*)pText; |
| 229304 | 229443 | const unsigned char *zEof = &zIn[nText]; |
| 229305 | 229444 | u32 iCode; |
| 229306 | 229445 | |
| 229446 | + UNUSED_PARAM(unusedFlags); | |
| 229307 | 229447 | while( 1 ){ |
| 229308 | 229448 | char *zOut = aBuf; |
| 229309 | 229449 | int iStart = zIn - (const unsigned char*)pText; |
| 229310 | 229450 | const unsigned char *zNext; |
| 229311 | 229451 | |
| @@ -230164,10 +230304,11 @@ | ||
| 230164 | 230304 | } |
| 230165 | 230305 | iTbl++; |
| 230166 | 230306 | } |
| 230167 | 230307 | aAscii[0] = 0; /* 0x00 is never a token character */ |
| 230168 | 230308 | } |
| 230309 | + | |
| 230169 | 230310 | |
| 230170 | 230311 | /* |
| 230171 | 230312 | ** 2015 May 30 |
| 230172 | 230313 | ** |
| 230173 | 230314 | ** The author disclaims copyright to this source code. In place of |
| @@ -231602,12 +231743,12 @@ | ||
| 231602 | 231743 | } |
| 231603 | 231744 | #endif /* SQLITE_CORE */ |
| 231604 | 231745 | #endif /* !defined(SQLITE_CORE) || defined(SQLITE_ENABLE_STMTVTAB) */ |
| 231605 | 231746 | |
| 231606 | 231747 | /************** End of stmt.c ************************************************/ |
| 231607 | -#if __LINE__!=231607 | |
| 231748 | +#if __LINE__!=231748 | |
| 231608 | 231749 | #undef SQLITE_SOURCE_ID |
| 231609 | -#define SQLITE_SOURCE_ID "2020-10-31 18:58:37 7d01e84dc49074e6364267eea9fd20d46a457d2498121a0f218fbf482692alt2" | |
| 231750 | +#define SQLITE_SOURCE_ID "2020-12-01 16:14:00 a26b6597e3ae272231b96f9982c3bcc17ddec2f2b6eb4df06a224b91089falt2" | |
| 231610 | 231751 | #endif |
| 231611 | 231752 | /* Return the source-id for this library */ |
| 231612 | 231753 | SQLITE_API const char *sqlite3_sourceid(void){ return SQLITE_SOURCE_ID; } |
| 231613 | 231754 | /************************** End of sqlite3.c ******************************/ |
| 231614 | 231755 |
| --- src/sqlite3.c | |
| +++ src/sqlite3.c | |
| @@ -1171,11 +1171,11 @@ | |
| 1171 | ** [sqlite3_libversion_number()], [sqlite3_sourceid()], |
| 1172 | ** [sqlite_version()] and [sqlite_source_id()]. |
| 1173 | */ |
| 1174 | #define SQLITE_VERSION "3.34.0" |
| 1175 | #define SQLITE_VERSION_NUMBER 3034000 |
| 1176 | #define SQLITE_SOURCE_ID "2020-10-31 18:58:37 7d01e84dc49074e6364267eea9fd20d46a457d2498121a0f218fbf482692392d" |
| 1177 | |
| 1178 | /* |
| 1179 | ** CAPI3REF: Run-Time Library Version Numbers |
| 1180 | ** KEYWORDS: sqlite3_version sqlite3_sourceid |
| 1181 | ** |
| @@ -1550,10 +1550,11 @@ | |
| 1550 | #define SQLITE_IOERR_AUTH (SQLITE_IOERR | (28<<8)) |
| 1551 | #define SQLITE_IOERR_BEGIN_ATOMIC (SQLITE_IOERR | (29<<8)) |
| 1552 | #define SQLITE_IOERR_COMMIT_ATOMIC (SQLITE_IOERR | (30<<8)) |
| 1553 | #define SQLITE_IOERR_ROLLBACK_ATOMIC (SQLITE_IOERR | (31<<8)) |
| 1554 | #define SQLITE_IOERR_DATA (SQLITE_IOERR | (32<<8)) |
| 1555 | #define SQLITE_LOCKED_SHAREDCACHE (SQLITE_LOCKED | (1<<8)) |
| 1556 | #define SQLITE_LOCKED_VTAB (SQLITE_LOCKED | (2<<8)) |
| 1557 | #define SQLITE_BUSY_RECOVERY (SQLITE_BUSY | (1<<8)) |
| 1558 | #define SQLITE_BUSY_SNAPSHOT (SQLITE_BUSY | (2<<8)) |
| 1559 | #define SQLITE_BUSY_TIMEOUT (SQLITE_BUSY | (3<<8)) |
| @@ -7238,11 +7239,11 @@ | |
| 7238 | ** CAPI3REF: Determine the transaction state of a database |
| 7239 | ** METHOD: sqlite3 |
| 7240 | ** |
| 7241 | ** ^The sqlite3_txn_state(D,S) interface returns the current |
| 7242 | ** [transaction state] of schema S in database connection D. ^If S is NULL, |
| 7243 | ** then the highest transaction state of any schema on databse connection D |
| 7244 | ** is returned. Transaction states are (in order of lowest to highest): |
| 7245 | ** <ol> |
| 7246 | ** <li value="0"> SQLITE_TXN_NONE |
| 7247 | ** <li value="1"> SQLITE_TXN_READ |
| 7248 | ** <li value="2"> SQLITE_TXN_WRITE |
| @@ -8785,11 +8786,10 @@ | |
| 8785 | */ |
| 8786 | #define SQLITE_TESTCTRL_FIRST 5 |
| 8787 | #define SQLITE_TESTCTRL_PRNG_SAVE 5 |
| 8788 | #define SQLITE_TESTCTRL_PRNG_RESTORE 6 |
| 8789 | #define SQLITE_TESTCTRL_PRNG_RESET 7 /* NOT USED */ |
| 8790 | #define SQLITE_TESTCTRL_SEEK_COUNT 7 |
| 8791 | #define SQLITE_TESTCTRL_BITVEC_TEST 8 |
| 8792 | #define SQLITE_TESTCTRL_FAULT_INSTALL 9 |
| 8793 | #define SQLITE_TESTCTRL_BENIGN_MALLOC_HOOKS 10 |
| 8794 | #define SQLITE_TESTCTRL_PENDING_BYTE 11 |
| 8795 | #define SQLITE_TESTCTRL_ASSERT 12 |
| @@ -8810,11 +8810,12 @@ | |
| 8810 | #define SQLITE_TESTCTRL_IMPOSTER 25 |
| 8811 | #define SQLITE_TESTCTRL_PARSER_COVERAGE 26 |
| 8812 | #define SQLITE_TESTCTRL_RESULT_INTREAL 27 |
| 8813 | #define SQLITE_TESTCTRL_PRNG_SEED 28 |
| 8814 | #define SQLITE_TESTCTRL_EXTRA_SCHEMA_CHECKS 29 |
| 8815 | #define SQLITE_TESTCTRL_LAST 29 /* Largest TESTCTRL */ |
| 8816 | |
| 8817 | /* |
| 8818 | ** CAPI3REF: SQL Keyword Checking |
| 8819 | ** |
| 8820 | ** These routines provide access to the set of SQL language keywords |
| @@ -28182,16 +28183,19 @@ | |
| 28182 | EnableLookaside; |
| 28183 | } |
| 28184 | } |
| 28185 | |
| 28186 | /* |
| 28187 | ** Take actions at the end of an API call to indicate an OOM error |
| 28188 | */ |
| 28189 | static SQLITE_NOINLINE int apiOomError(sqlite3 *db){ |
| 28190 | sqlite3OomClear(db); |
| 28191 | sqlite3Error(db, SQLITE_NOMEM); |
| 28192 | return SQLITE_NOMEM_BKPT; |
| 28193 | } |
| 28194 | |
| 28195 | /* |
| 28196 | ** This function must be called before exiting any API function (i.e. |
| 28197 | ** returning control to the user) that has called sqlite3_malloc or |
| @@ -28209,12 +28213,12 @@ | |
| 28209 | ** Otherwise the read (and possible write) of db->mallocFailed |
| 28210 | ** is unsafe, as is the call to sqlite3Error(). |
| 28211 | */ |
| 28212 | assert( db!=0 ); |
| 28213 | assert( sqlite3_mutex_held(db->mutex) ); |
| 28214 | if( db->mallocFailed || rc==SQLITE_IOERR_NOMEM ){ |
| 28215 | return apiOomError(db); |
| 28216 | } |
| 28217 | return rc & db->errMask; |
| 28218 | } |
| 28219 | |
| 28220 | /************** End of malloc.c **********************************************/ |
| @@ -37003,11 +37007,28 @@ | |
| 37003 | |
| 37004 | got = seekAndRead(pFile, offset, pBuf, amt); |
| 37005 | if( got==amt ){ |
| 37006 | return SQLITE_OK; |
| 37007 | }else if( got<0 ){ |
| 37008 | /* lastErrno set by seekAndRead */ |
| 37009 | return SQLITE_IOERR_READ; |
| 37010 | }else{ |
| 37011 | storeLastErrno(pFile, 0); /* not a system error */ |
| 37012 | /* Unread parts of the buffer must be zero-filled */ |
| 37013 | memset(&((char*)pBuf)[got], 0, amt-got); |
| @@ -38535,11 +38556,11 @@ | |
| 38535 | if( bUnlock ){ |
| 38536 | rc = unixShmSystemLock(pDbFd, F_UNLCK, ofst+UNIX_SHM_BASE, n); |
| 38537 | if( rc==SQLITE_OK ){ |
| 38538 | memset(&aLock[ofst], 0, sizeof(int)*n); |
| 38539 | } |
| 38540 | }else if( p->sharedMask & (1<<ofst) ){ |
| 38541 | assert( n==1 && aLock[ofst]>1 ); |
| 38542 | aLock[ofst]--; |
| 38543 | } |
| 38544 | |
| 38545 | /* Undo the local locks */ |
| @@ -38568,11 +38589,11 @@ | |
| 38568 | /* Make sure no sibling connections hold locks that will block this |
| 38569 | ** lock. If any do, return SQLITE_BUSY right away. */ |
| 38570 | int ii; |
| 38571 | for(ii=ofst; ii<ofst+n; ii++){ |
| 38572 | assert( (p->sharedMask & mask)==0 ); |
| 38573 | if( (p->exclMask & (1<<ii))==0 && aLock[ii] ){ |
| 38574 | rc = SQLITE_BUSY; |
| 38575 | break; |
| 38576 | } |
| 38577 | } |
| 38578 | |
| @@ -39964,19 +39985,39 @@ | |
| 39964 | } |
| 39965 | return SQLITE_OK; |
| 39966 | } |
| 39967 | |
| 39968 | /* |
| 39969 | ** |
| 39970 | */ |
| 39971 | static int mkFullPathname( |
| 39972 | const char *zPath, /* Input path */ |
| 39973 | char *zOut, /* Output buffer */ |
| 39974 | int nOut /* Allocated size of buffer zOut */ |
| 39975 | ){ |
| 39976 | int nPath = sqlite3Strlen30(zPath); |
| 39977 | int iOff = 0; |
| 39978 | if( zPath[0]!='/' ){ |
| 39979 | if( osGetcwd(zOut, nOut-2)==0 ){ |
| 39980 | return unixLogError(SQLITE_CANTOPEN_BKPT, "getcwd", zPath); |
| 39981 | } |
| 39982 | iOff = sqlite3Strlen30(zOut); |
| @@ -39987,10 +40028,45 @@ | |
| 39987 | ** even if it returns an error. */ |
| 39988 | zOut[iOff] = '\0'; |
| 39989 | return SQLITE_CANTOPEN_BKPT; |
| 39990 | } |
| 39991 | sqlite3_snprintf(nOut-iOff, &zOut[iOff], "%s", zPath); |
| 39992 | return SQLITE_OK; |
| 39993 | } |
| 39994 | |
| 39995 | /* |
| 39996 | ** Turn a relative pathname into a full pathname. The relative path |
| @@ -54326,10 +54402,11 @@ | |
| 54326 | sqlite3_file *pJournal; /* Malloc'd child-journal file descriptor */ |
| 54327 | char *zSuperJournal = 0; /* Contents of super-journal file */ |
| 54328 | i64 nSuperJournal; /* Size of super-journal file */ |
| 54329 | char *zJournal; /* Pointer to one journal within MJ file */ |
| 54330 | char *zSuperPtr; /* Space to hold super-journal filename */ |
| 54331 | int nSuperPtr; /* Amount of space allocated to zSuperPtr[] */ |
| 54332 | |
| 54333 | /* Allocate space for both the pJournal and pSuper file descriptors. |
| 54334 | ** If successful, open the super-journal file for reading. |
| 54335 | */ |
| @@ -54350,15 +54427,17 @@ | |
| 54350 | ** files extracted from regular rollback-journals. |
| 54351 | */ |
| 54352 | rc = sqlite3OsFileSize(pSuper, &nSuperJournal); |
| 54353 | if( rc!=SQLITE_OK ) goto delsuper_out; |
| 54354 | nSuperPtr = pVfs->mxPathname+1; |
| 54355 | zSuperJournal = sqlite3Malloc(nSuperJournal + nSuperPtr + 2); |
| 54356 | if( !zSuperJournal ){ |
| 54357 | rc = SQLITE_NOMEM_BKPT; |
| 54358 | goto delsuper_out; |
| 54359 | } |
| 54360 | zSuperPtr = &zSuperJournal[nSuperJournal+2]; |
| 54361 | rc = sqlite3OsRead(pSuper, zSuperJournal, (int)nSuperJournal, 0); |
| 54362 | if( rc!=SQLITE_OK ) goto delsuper_out; |
| 54363 | zSuperJournal[nSuperJournal] = 0; |
| 54364 | zSuperJournal[nSuperJournal+1] = 0; |
| @@ -54402,11 +54481,11 @@ | |
| 54402 | |
| 54403 | sqlite3OsClose(pSuper); |
| 54404 | rc = sqlite3OsDelete(pVfs, zSuper, 0); |
| 54405 | |
| 54406 | delsuper_out: |
| 54407 | sqlite3_free(zSuperJournal); |
| 54408 | if( pSuper ){ |
| 54409 | sqlite3OsClose(pSuper); |
| 54410 | assert( !isOpen(pJournal) ); |
| 54411 | sqlite3_free(pSuper); |
| 54412 | } |
| @@ -54740,11 +54819,15 @@ | |
| 54740 | ** in case this has happened, clear the changeCountDone flag now. |
| 54741 | */ |
| 54742 | pPager->changeCountDone = pPager->tempFile; |
| 54743 | |
| 54744 | if( rc==SQLITE_OK ){ |
| 54745 | zSuper = pPager->pTmpSpace; |
| 54746 | rc = readSuperJournal(pPager->jfd, zSuper, pPager->pVfs->mxPathname+1); |
| 54747 | testcase( rc!=SQLITE_OK ); |
| 54748 | } |
| 54749 | if( rc==SQLITE_OK |
| 54750 | && (pPager->eState>=PAGER_WRITER_DBMOD || pPager->eState==PAGER_OPEN) |
| @@ -54757,10 +54840,12 @@ | |
| 54757 | } |
| 54758 | if( rc==SQLITE_OK && zSuper[0] && res ){ |
| 54759 | /* If there was a super-journal and this routine will return success, |
| 54760 | ** see if it is possible to delete the super-journal. |
| 54761 | */ |
| 54762 | rc = pager_delsuper(pPager, zSuper); |
| 54763 | testcase( rc!=SQLITE_OK ); |
| 54764 | } |
| 54765 | if( isHot && nPlayback ){ |
| 54766 | sqlite3_log(SQLITE_NOTICE_RECOVER_ROLLBACK, "recovered %d pages from %s", |
| @@ -64746,11 +64831,11 @@ | |
| 64746 | #define hasReadConflicts(a, b) 0 |
| 64747 | #endif |
| 64748 | |
| 64749 | #ifdef SQLITE_DEBUG |
| 64750 | /* |
| 64751 | ** Return an reset the seek counter for a Btree object. |
| 64752 | */ |
| 64753 | SQLITE_PRIVATE sqlite3_uint64 sqlite3BtreeSeekCount(Btree *pBt){ |
| 64754 | u64 n = pBt->nSeek; |
| 64755 | pBt->nSeek = 0; |
| 64756 | return n; |
| @@ -82192,13 +82277,16 @@ | |
| 82192 | ** equal to, or greater than the second (double). |
| 82193 | */ |
| 82194 | static int sqlite3IntFloatCompare(i64 i, double r){ |
| 82195 | if( sizeof(LONGDOUBLE_TYPE)>8 ){ |
| 82196 | LONGDOUBLE_TYPE x = (LONGDOUBLE_TYPE)i; |
| 82197 | if( x<r ) return -1; |
| 82198 | if( x>r ) return +1; |
| 82199 | return 0; |
| 82200 | }else{ |
| 82201 | i64 y; |
| 82202 | double s; |
| 82203 | if( r<-9223372036854775808.0 ) return +1; |
| 82204 | if( r>=9223372036854775808.0 ) return -1; |
| @@ -89388,11 +89476,11 @@ | |
| 89388 | assert( rc==SQLITE_OK ); |
| 89389 | break; |
| 89390 | } |
| 89391 | |
| 89392 | |
| 89393 | /* Opcode: OpenEphemeral P1 P2 * P4 P5 |
| 89394 | ** Synopsis: nColumn=P2 |
| 89395 | ** |
| 89396 | ** Open a new cursor P1 to a transient table. |
| 89397 | ** The cursor is always opened read/write even if |
| 89398 | ** the main database is read-only. The ephemeral |
| @@ -89408,10 +89496,14 @@ | |
| 89408 | ** |
| 89409 | ** The P5 parameter can be a mask of the BTREE_* flags defined |
| 89410 | ** in btree.h. These flags control aspects of the operation of |
| 89411 | ** the btree. The BTREE_OMIT_JOURNAL and BTREE_SINGLE flags are |
| 89412 | ** added automatically. |
| 89413 | */ |
| 89414 | /* Opcode: OpenAutoindex P1 P2 * P4 * |
| 89415 | ** Synopsis: nColumn=P2 |
| 89416 | ** |
| 89417 | ** This opcode works the same as OP_OpenEphemeral. It has a |
| @@ -89430,10 +89522,19 @@ | |
| 89430 | SQLITE_OPEN_EXCLUSIVE | |
| 89431 | SQLITE_OPEN_DELETEONCLOSE | |
| 89432 | SQLITE_OPEN_TRANSIENT_DB; |
| 89433 | assert( pOp->p1>=0 ); |
| 89434 | assert( pOp->p2>=0 ); |
| 89435 | pCx = p->apCsr[pOp->p1]; |
| 89436 | if( pCx && pCx->pBtx ){ |
| 89437 | /* If the ephermeral table is already open, erase all existing content |
| 89438 | ** so that the table is empty again, rather than creating a new table. */ |
| 89439 | assert( pCx->isEphemeral ); |
| @@ -90589,11 +90690,11 @@ | |
| 90589 | if( pOp->p5 & OPFLAG_ISNOOP ) break; |
| 90590 | #endif |
| 90591 | |
| 90592 | if( pOp->p5 & OPFLAG_NCHANGE ) p->nChange++; |
| 90593 | if( pOp->p5 & OPFLAG_LASTROWID ) db->lastRowid = x.nKey; |
| 90594 | assert( pData->flags & (MEM_Blob|MEM_Str) ); |
| 90595 | x.pData = pData->z; |
| 90596 | x.nData = pData->n; |
| 90597 | seekResult = ((pOp->p5 & OPFLAG_USESEEKRESULT) ? pC->seekResult : 0); |
| 90598 | if( pData->flags & MEM_Zero ){ |
| 90599 | x.nZero = pData->u.nZero; |
| @@ -93644,11 +93745,15 @@ | |
| 93644 | |
| 93645 | /* If we reach this point, it means that execution is finished with |
| 93646 | ** an error of some kind. |
| 93647 | */ |
| 93648 | abort_due_to_error: |
| 93649 | if( db->mallocFailed ) rc = SQLITE_NOMEM_BKPT; |
| 93650 | assert( rc ); |
| 93651 | if( p->zErrMsg==0 && rc!=SQLITE_IOERR_NOMEM ){ |
| 93652 | sqlite3VdbeError(p, "%s", sqlite3ErrStr(rc)); |
| 93653 | } |
| 93654 | p->rc = rc; |
| @@ -99381,10 +99486,11 @@ | |
| 99381 | for(i=0, pItem=pOrderBy->a; i<pOrderBy->nExpr; i++, pItem++){ |
| 99382 | int iCol = -1; |
| 99383 | Expr *pE, *pDup; |
| 99384 | if( pItem->done ) continue; |
| 99385 | pE = sqlite3ExprSkipCollateAndLikely(pItem->pExpr); |
| 99386 | if( sqlite3ExprIsInteger(pE, &iCol) ){ |
| 99387 | if( iCol<=0 || iCol>pEList->nExpr ){ |
| 99388 | resolveOutOfRangeError(pParse, "ORDER", i+1, pEList->nExpr); |
| 99389 | return 1; |
| 99390 | } |
| @@ -99560,10 +99666,11 @@ | |
| 99560 | nResult = pSelect->pEList->nExpr; |
| 99561 | pParse = pNC->pParse; |
| 99562 | for(i=0, pItem=pOrderBy->a; i<pOrderBy->nExpr; i++, pItem++){ |
| 99563 | Expr *pE = pItem->pExpr; |
| 99564 | Expr *pE2 = sqlite3ExprSkipCollateAndLikely(pE); |
| 99565 | if( zType[0]!='G' ){ |
| 99566 | iCol = resolveAsName(pParse, pSelect->pEList, pE2); |
| 99567 | if( iCol>0 ){ |
| 99568 | /* If an AS-name match is found, mark this ORDER BY column as being |
| 99569 | ** a copy of the iCol-th result-set column. The subsequent call to |
| @@ -103679,10 +103786,11 @@ | |
| 103679 | ** register iReg. The caller must ensure that iReg already contains |
| 103680 | ** the correct value for the expression. |
| 103681 | */ |
| 103682 | static void exprToRegister(Expr *pExpr, int iReg){ |
| 103683 | Expr *p = sqlite3ExprSkipCollateAndLikely(pExpr); |
| 103684 | p->op2 = p->op; |
| 103685 | p->op = TK_REGISTER; |
| 103686 | p->iTable = iReg; |
| 103687 | ExprClearProperty(p, EP_Skip); |
| 103688 | } |
| @@ -104666,10 +104774,11 @@ | |
| 104666 | */ |
| 104667 | SQLITE_PRIVATE int sqlite3ExprCodeTemp(Parse *pParse, Expr *pExpr, int *pReg){ |
| 104668 | int r2; |
| 104669 | pExpr = sqlite3ExprSkipCollateAndLikely(pExpr); |
| 104670 | if( ConstFactorOk(pParse) |
| 104671 | && pExpr->op!=TK_REGISTER |
| 104672 | && sqlite3ExprIsConstantNotJoin(pExpr) |
| 104673 | ){ |
| 104674 | *pReg = 0; |
| 104675 | r2 = sqlite3ExprCodeRunJustOnce(pParse, pExpr, -1); |
| @@ -119424,10 +119533,12 @@ | |
| 119424 | VFUNCTION(total_changes, 0, 0, 0, total_changes ), |
| 119425 | FUNCTION(replace, 3, 0, 0, replaceFunc ), |
| 119426 | FUNCTION(zeroblob, 1, 0, 0, zeroblobFunc ), |
| 119427 | FUNCTION(substr, 2, 0, 0, substrFunc ), |
| 119428 | FUNCTION(substr, 3, 0, 0, substrFunc ), |
| 119429 | WAGGREGATE(sum, 1,0,0, sumStep, sumFinalize, sumFinalize, sumInverse, 0), |
| 119430 | WAGGREGATE(total, 1,0,0, sumStep,totalFinalize,totalFinalize,sumInverse, 0), |
| 119431 | WAGGREGATE(avg, 1,0,0, sumStep, avgFinalize, avgFinalize, sumInverse, 0), |
| 119432 | WAGGREGATE(count, 0,0,0, countStep, |
| 119433 | countFinalize, countFinalize, countInverse, SQLITE_FUNC_COUNT ), |
| @@ -124310,10 +124421,12 @@ | |
| 124310 | /* Version 3.32.0 and later */ |
| 124311 | char *(*create_filename)(const char*,const char*,const char*, |
| 124312 | int,const char**); |
| 124313 | void (*free_filename)(char*); |
| 124314 | sqlite3_file *(*database_file_object)(const char*); |
| 124315 | }; |
| 124316 | |
| 124317 | /* |
| 124318 | ** This is the function signature used for all extension entry points. It |
| 124319 | ** is also defined in the file "loadext.c". |
| @@ -124614,10 +124727,12 @@ | |
| 124614 | #define sqlite3_filename_wal sqlite3_api->filename_wal |
| 124615 | /* Version 3.32.0 and later */ |
| 124616 | #define sqlite3_create_filename sqlite3_api->create_filename |
| 124617 | #define sqlite3_free_filename sqlite3_api->free_filename |
| 124618 | #define sqlite3_database_file_object sqlite3_api->database_file_object |
| 124619 | #endif /* !defined(SQLITE_CORE) && !defined(SQLITE_OMIT_LOAD_EXTENSION) */ |
| 124620 | |
| 124621 | #if !defined(SQLITE_CORE) && !defined(SQLITE_OMIT_LOAD_EXTENSION) |
| 124622 | /* This case when the file really is being compiled as a loadable |
| 124623 | ** extension */ |
| @@ -125096,10 +125211,12 @@ | |
| 125096 | sqlite3_filename_wal, |
| 125097 | /* Version 3.32.0 and later */ |
| 125098 | sqlite3_create_filename, |
| 125099 | sqlite3_free_filename, |
| 125100 | sqlite3_database_file_object, |
| 125101 | }; |
| 125102 | |
| 125103 | /* True if x is the directory separator character |
| 125104 | */ |
| 125105 | #if SQLITE_OS_WIN |
| @@ -131654,10 +131771,11 @@ | |
| 131654 | Column *aCol, *pCol; /* For looping over result columns */ |
| 131655 | int nCol; /* Number of columns in the result set */ |
| 131656 | char *zName; /* Column name */ |
| 131657 | int nName; /* Size of name in zName[] */ |
| 131658 | Hash ht; /* Hash table of column names */ |
| 131659 | |
| 131660 | sqlite3HashInit(&ht); |
| 131661 | if( pEList ){ |
| 131662 | nCol = pEList->nExpr; |
| 131663 | aCol = sqlite3DbMallocZero(db, sizeof(aCol[0])*nCol); |
| @@ -131676,19 +131794,17 @@ | |
| 131676 | */ |
| 131677 | if( (zName = pEList->a[i].zEName)!=0 && pEList->a[i].eEName==ENAME_NAME ){ |
| 131678 | /* If the column contains an "AS <name>" phrase, use <name> as the name */ |
| 131679 | }else{ |
| 131680 | Expr *pColExpr = sqlite3ExprSkipCollateAndLikely(pEList->a[i].pExpr); |
| 131681 | while( pColExpr->op==TK_DOT ){ |
| 131682 | pColExpr = pColExpr->pRight; |
| 131683 | assert( pColExpr!=0 ); |
| 131684 | } |
| 131685 | if( pColExpr->op==TK_COLUMN ){ |
| 131686 | /* For columns use the column name name */ |
| 131687 | int iCol = pColExpr->iColumn; |
| 131688 | Table *pTab = pColExpr->y.pTab; |
| 131689 | assert( pTab!=0 ); |
| 131690 | if( iCol<0 ) iCol = pTab->iPKey; |
| 131691 | zName = iCol>=0 ? pTab->aCol[iCol].zName : "rowid"; |
| 131692 | }else if( pColExpr->op==TK_ID ){ |
| 131693 | assert( !ExprHasProperty(pColExpr, EP_IntValue) ); |
| 131694 | zName = pColExpr->u.zToken; |
| @@ -136959,26 +137075,15 @@ | |
| 136959 | goto trigger_cleanup; |
| 136960 | } |
| 136961 | pTab = sqlite3SrcListLookup(pParse, pTableName); |
| 136962 | if( !pTab ){ |
| 136963 | /* The table does not exist. */ |
| 136964 | if( db->init.iDb==1 ){ |
| 136965 | /* Ticket #3810. |
| 136966 | ** Normally, whenever a table is dropped, all associated triggers are |
| 136967 | ** dropped too. But if a TEMP trigger is created on a non-TEMP table |
| 136968 | ** and the table is dropped by a different database connection, the |
| 136969 | ** trigger is not visible to the database connection that does the |
| 136970 | ** drop so the trigger cannot be dropped. This results in an |
| 136971 | ** "orphaned trigger" - a trigger whose associated table is missing. |
| 136972 | */ |
| 136973 | db->init.orphanTrigger = 1; |
| 136974 | } |
| 136975 | goto trigger_cleanup; |
| 136976 | } |
| 136977 | if( IsVirtual(pTab) ){ |
| 136978 | sqlite3ErrorMsg(pParse, "cannot create triggers on virtual tables"); |
| 136979 | goto trigger_cleanup; |
| 136980 | } |
| 136981 | |
| 136982 | /* Check that the trigger name is not reserved and that no trigger of the |
| 136983 | ** specified name exists */ |
| 136984 | zName = sqlite3NameFromToken(db, pName); |
| @@ -137012,16 +137117,16 @@ | |
| 137012 | ** of triggers. |
| 137013 | */ |
| 137014 | if( pTab->pSelect && tr_tm!=TK_INSTEAD ){ |
| 137015 | sqlite3ErrorMsg(pParse, "cannot create %s trigger on view: %S", |
| 137016 | (tr_tm == TK_BEFORE)?"BEFORE":"AFTER", pTableName, 0); |
| 137017 | goto trigger_cleanup; |
| 137018 | } |
| 137019 | if( !pTab->pSelect && tr_tm==TK_INSTEAD ){ |
| 137020 | sqlite3ErrorMsg(pParse, "cannot create INSTEAD OF" |
| 137021 | " trigger on table: %S", pTableName, 0); |
| 137022 | goto trigger_cleanup; |
| 137023 | } |
| 137024 | |
| 137025 | #ifndef SQLITE_OMIT_AUTHORIZATION |
| 137026 | if( !IN_RENAME_OBJECT ){ |
| 137027 | int iTabDb = sqlite3SchemaToIndex(db, pTab->pSchema); |
| @@ -137077,10 +137182,27 @@ | |
| 137077 | if( !pParse->pNewTrigger ){ |
| 137078 | sqlite3DeleteTrigger(db, pTrigger); |
| 137079 | }else{ |
| 137080 | assert( pParse->pNewTrigger==pTrigger ); |
| 137081 | } |
| 137082 | } |
| 137083 | |
| 137084 | /* |
| 137085 | ** This routine is called after all of the trigger actions have been parsed |
| 137086 | ** in order to complete the process of building the trigger. |
| @@ -138664,10 +138786,12 @@ | |
| 138664 | sqlite3VdbeAddOp2(v, OP_Integer, 0, regRowCount); |
| 138665 | } |
| 138666 | |
| 138667 | if( nChangeFrom==0 && HasRowid(pTab) ){ |
| 138668 | sqlite3VdbeAddOp3(v, OP_Null, 0, regRowSet, regOldRowid); |
| 138669 | }else{ |
| 138670 | assert( pPk!=0 || HasRowid(pTab) ); |
| 138671 | nPk = pPk ? pPk->nKeyCol : 0; |
| 138672 | iPk = pParse->nMem+1; |
| 138673 | pParse->nMem += nPk; |
| @@ -138755,13 +138879,14 @@ | |
| 138755 | /* Read the rowid of the current row of the WHERE scan. In ONEPASS_OFF |
| 138756 | ** mode, write the rowid into the FIFO. In either of the one-pass modes, |
| 138757 | ** leave it in register regOldRowid. */ |
| 138758 | sqlite3VdbeAddOp2(v, OP_Rowid, iDataCur, regOldRowid); |
| 138759 | if( eOnePass==ONEPASS_OFF ){ |
| 138760 | /* We need to use regRowSet, so reallocate aRegIdx[nAllIdx] */ |
| 138761 | aRegIdx[nAllIdx] = ++pParse->nMem; |
| 138762 | sqlite3VdbeAddOp2(v, OP_RowSetAdd, regRowSet, regOldRowid); |
| 138763 | } |
| 138764 | }else{ |
| 138765 | /* Read the PK of the current row into an array of registers. In |
| 138766 | ** ONEPASS_OFF mode, serialize the array into a record and store it in |
| 138767 | ** the ephemeral table. Or, in ONEPASS_SINGLE or MULTI mode, change |
| @@ -138845,12 +138970,13 @@ | |
| 138845 | sqlite3VdbeAddOp2(v, OP_RowData, iEph, regKey); |
| 138846 | sqlite3VdbeAddOp4Int(v, OP_NotFound, iDataCur, labelContinue, regKey,0); |
| 138847 | VdbeCoverage(v); |
| 138848 | } |
| 138849 | }else{ |
| 138850 | labelContinue = sqlite3VdbeAddOp3(v, OP_RowSetRead, regRowSet,labelBreak, |
| 138851 | regOldRowid); |
| 138852 | VdbeCoverage(v); |
| 138853 | sqlite3VdbeAddOp3(v, OP_NotExists, iDataCur, labelContinue, regOldRowid); |
| 138854 | VdbeCoverage(v); |
| 138855 | } |
| 138856 | } |
| @@ -139096,15 +139222,13 @@ | |
| 139096 | if( eOnePass==ONEPASS_SINGLE ){ |
| 139097 | /* Nothing to do at end-of-loop for a single-pass */ |
| 139098 | }else if( eOnePass==ONEPASS_MULTI ){ |
| 139099 | sqlite3VdbeResolveLabel(v, labelContinue); |
| 139100 | sqlite3WhereEnd(pWInfo); |
| 139101 | }else if( pPk || nChangeFrom ){ |
| 139102 | sqlite3VdbeResolveLabel(v, labelContinue); |
| 139103 | sqlite3VdbeAddOp2(v, OP_Next, iEph, addrTop); VdbeCoverage(v); |
| 139104 | }else{ |
| 139105 | sqlite3VdbeGoto(v, labelContinue); |
| 139106 | } |
| 139107 | sqlite3VdbeResolveLabel(v, labelBreak); |
| 139108 | |
| 139109 | /* Update the sqlite_sequence table by storing the content of the |
| 139110 | ** maximum rowid counter values recorded while inserting into |
| @@ -145894,10 +146018,11 @@ | |
| 145894 | ** all terms of the WHERE clause. |
| 145895 | */ |
| 145896 | SQLITE_PRIVATE void sqlite3WhereSplit(WhereClause *pWC, Expr *pExpr, u8 op){ |
| 145897 | Expr *pE2 = sqlite3ExprSkipCollateAndLikely(pExpr); |
| 145898 | pWC->op = op; |
| 145899 | if( pE2==0 ) return; |
| 145900 | if( pE2->op!=op ){ |
| 145901 | whereClauseInsert(pWC, pExpr, 0); |
| 145902 | }else{ |
| 145903 | sqlite3WhereSplit(pWC, pE2->pLeft, op); |
| @@ -146292,10 +146417,20 @@ | |
| 146292 | */ |
| 146293 | static void createMask(WhereMaskSet *pMaskSet, int iCursor){ |
| 146294 | assert( pMaskSet->n < ArraySize(pMaskSet->ix) ); |
| 146295 | pMaskSet->ix[pMaskSet->n++] = iCursor; |
| 146296 | } |
| 146297 | |
| 146298 | /* |
| 146299 | ** Advance to the next WhereTerm that matches according to the criteria |
| 146300 | ** established when the pScan object was initialized by whereScanInit(). |
| 146301 | ** Return NULL if there are no more matching WhereTerms. |
| @@ -146323,12 +146458,11 @@ | |
| 146323 | pScan->pIdxExpr,iCur)==0) |
| 146324 | && (pScan->iEquiv<=1 || !ExprHasProperty(pTerm->pExpr, EP_FromJoin)) |
| 146325 | ){ |
| 146326 | if( (pTerm->eOperator & WO_EQUIV)!=0 |
| 146327 | && pScan->nEquiv<ArraySize(pScan->aiCur) |
| 146328 | && (pX = sqlite3ExprSkipCollateAndLikely(pTerm->pExpr->pRight))->op |
| 146329 | ==TK_COLUMN |
| 146330 | ){ |
| 146331 | int j; |
| 146332 | for(j=0; j<pScan->nEquiv; j++){ |
| 146333 | if( pScan->aiCur[j]==pX->iTable |
| 146334 | && pScan->aiColumn[j]==pX->iColumn ){ |
| @@ -146520,11 +146654,12 @@ | |
| 146520 | int i; |
| 146521 | const char *zColl = pIdx->azColl[iCol]; |
| 146522 | |
| 146523 | for(i=0; i<pList->nExpr; i++){ |
| 146524 | Expr *p = sqlite3ExprSkipCollateAndLikely(pList->a[i].pExpr); |
| 146525 | if( p->op==TK_COLUMN |
| 146526 | && p->iColumn==pIdx->aiColumn[iCol] |
| 146527 | && p->iTable==iBase |
| 146528 | ){ |
| 146529 | CollSeq *pColl = sqlite3ExprNNCollSeq(pParse, pList->a[i].pExpr); |
| 146530 | if( 0==sqlite3StrICmp(pColl->zName, zColl) ){ |
| @@ -146584,10 +146719,11 @@ | |
| 146584 | ** true. Note: The (p->iTable==iBase) part of this test may be false if the |
| 146585 | ** current SELECT is a correlated sub-query. |
| 146586 | */ |
| 146587 | for(i=0; i<pDistinct->nExpr; i++){ |
| 146588 | Expr *p = sqlite3ExprSkipCollateAndLikely(pDistinct->a[i].pExpr); |
| 146589 | if( p->op==TK_COLUMN && p->iTable==iBase && p->iColumn<0 ) return 1; |
| 146590 | } |
| 146591 | |
| 146592 | /* Loop through all indices on the table, checking each to see if it makes |
| 146593 | ** the DISTINCT qualifier redundant. It does so if: |
| @@ -148498,13 +148634,13 @@ | |
| 148498 | LogEst rLogSize; /* Logarithm of table size */ |
| 148499 | WhereTerm *pTop = 0, *pBtm = 0; /* Top and bottom range constraints */ |
| 148500 | |
| 148501 | pNew = pBuilder->pNew; |
| 148502 | if( db->mallocFailed ) return SQLITE_NOMEM_BKPT; |
| 148503 | WHERETRACE(0x800, ("BEGIN %s.addBtreeIdx(%s), nEq=%d, nSkip=%d\n", |
| 148504 | pProbe->pTable->zName,pProbe->zName, |
| 148505 | pNew->u.btree.nEq, pNew->nSkip)); |
| 148506 | |
| 148507 | assert( (pNew->wsFlags & WHERE_VIRTUALTABLE)==0 ); |
| 148508 | assert( (pNew->wsFlags & WHERE_TOP_LIMIT)==0 ); |
| 148509 | if( pNew->wsFlags & WHERE_BTM_LIMIT ){ |
| 148510 | opMask = WO_LT|WO_LE; |
| @@ -148869,10 +149005,11 @@ | |
| 148869 | |
| 148870 | if( pIndex->bUnordered ) return 0; |
| 148871 | if( (pOB = pBuilder->pWInfo->pOrderBy)==0 ) return 0; |
| 148872 | for(ii=0; ii<pOB->nExpr; ii++){ |
| 148873 | Expr *pExpr = sqlite3ExprSkipCollateAndLikely(pOB->a[ii].pExpr); |
| 148874 | if( pExpr->op==TK_COLUMN && pExpr->iTable==iCursor ){ |
| 148875 | if( pExpr->iColumn<0 ) return 1; |
| 148876 | for(jj=0; jj<pIndex->nKeyCol; jj++){ |
| 148877 | if( pExpr->iColumn==pIndex->aiColumn[jj] ) return 1; |
| 148878 | } |
| @@ -149847,10 +149984,11 @@ | |
| 149847 | ** loops. |
| 149848 | */ |
| 149849 | for(i=0; i<nOrderBy; i++){ |
| 149850 | if( MASKBIT(i) & obSat ) continue; |
| 149851 | pOBExpr = sqlite3ExprSkipCollateAndLikely(pOrderBy->a[i].pExpr); |
| 149852 | if( pOBExpr->op!=TK_COLUMN ) continue; |
| 149853 | if( pOBExpr->iTable!=iCur ) continue; |
| 149854 | pTerm = sqlite3WhereFindTerm(&pWInfo->sWC, iCur, pOBExpr->iColumn, |
| 149855 | ~ready, eqOpMask, 0); |
| 149856 | if( pTerm==0 ) continue; |
| @@ -149973,10 +150111,11 @@ | |
| 149973 | for(i=0; bOnce && i<nOrderBy; i++){ |
| 149974 | if( MASKBIT(i) & obSat ) continue; |
| 149975 | pOBExpr = sqlite3ExprSkipCollateAndLikely(pOrderBy->a[i].pExpr); |
| 149976 | testcase( wctrlFlags & WHERE_GROUPBY ); |
| 149977 | testcase( wctrlFlags & WHERE_DISTINCTBY ); |
| 149978 | if( (wctrlFlags & (WHERE_GROUPBY|WHERE_DISTINCTBY))==0 ) bOnce = 0; |
| 149979 | if( iColumn>=XN_ROWID ){ |
| 149980 | if( pOBExpr->op!=TK_COLUMN ) continue; |
| 149981 | if( pOBExpr->iTable!=iCur ) continue; |
| 149982 | if( pOBExpr->iColumn!=iColumn ) continue; |
| @@ -150136,11 +150275,11 @@ | |
| 150136 | rScale = sqlite3LogEst((nOrderBy-nSorted)*100/nOrderBy) - 66; |
| 150137 | rSortCost = nRow + rScale + 16; |
| 150138 | |
| 150139 | /* Multiple by log(M) where M is the number of output rows. |
| 150140 | ** Use the LIMIT for M if it is smaller. Or if this sort is for |
| 150141 | ** a DISTINT operator, M will be the number of distinct output |
| 150142 | ** rows, so fudge it downwards a bit. |
| 150143 | */ |
| 150144 | if( (pWInfo->wctrlFlags & WHERE_USE_LIMIT)!=0 && pWInfo->iLimit<nRow ){ |
| 150145 | nRow = pWInfo->iLimit; |
| 150146 | }else if( (pWInfo->wctrlFlags & WHERE_WANT_DISTINCT) ){ |
| @@ -194220,11 +194359,11 @@ | |
| 194220 | p->aSegment = (GeoSegment*)&p->aEvent[nVertex*2]; |
| 194221 | p->nEvent = p->nSegment = 0; |
| 194222 | geopolyAddSegments(p, p1, 1); |
| 194223 | geopolyAddSegments(p, p2, 2); |
| 194224 | pThisEvent = geopolySortEventsByX(p->aEvent, p->nEvent); |
| 194225 | rX = pThisEvent->x==0.0 ? -1.0 : 0.0; |
| 194226 | memset(aOverlap, 0, sizeof(aOverlap)); |
| 194227 | while( pThisEvent ){ |
| 194228 | if( pThisEvent->x!=rX ){ |
| 194229 | GeoSegment *pPrev = 0; |
| 194230 | int iMask = 0; |
| @@ -212129,11 +212268,11 @@ | |
| 212129 | Fts5Bm25Data **ppData /* OUT: bm25-data object for this query */ |
| 212130 | ){ |
| 212131 | int rc = SQLITE_OK; /* Return code */ |
| 212132 | Fts5Bm25Data *p; /* Object to return */ |
| 212133 | |
| 212134 | p = pApi->xGetAuxdata(pFts, 0); |
| 212135 | if( p==0 ){ |
| 212136 | int nPhrase; /* Number of phrases in query */ |
| 212137 | sqlite3_int64 nRow = 0; /* Number of rows in table */ |
| 212138 | sqlite3_int64 nToken = 0; /* Number of tokens in table */ |
| 212139 | sqlite3_int64 nByte; /* Bytes of space to allocate */ |
| @@ -212203,11 +212342,11 @@ | |
| 212203 | int nVal, /* Number of values in apVal[] array */ |
| 212204 | sqlite3_value **apVal /* Array of trailing arguments */ |
| 212205 | ){ |
| 212206 | const double k1 = 1.2; /* Constant "k1" from BM25 formula */ |
| 212207 | const double b = 0.75; /* Constant "b" from BM25 formula */ |
| 212208 | int rc = SQLITE_OK; /* Error code */ |
| 212209 | double score = 0.0; /* SQL function return value */ |
| 212210 | Fts5Bm25Data *pData; /* Values allocated/calculated once only */ |
| 212211 | int i; /* Iterator variable */ |
| 212212 | int nInst = 0; /* Value returned by xInstCount() */ |
| 212213 | double D = 0.0; /* Total number of tokens in row */ |
| @@ -212235,21 +212374,19 @@ | |
| 212235 | int nTok; |
| 212236 | rc = pApi->xColumnSize(pFts, -1, &nTok); |
| 212237 | D = (double)nTok; |
| 212238 | } |
| 212239 | |
| 212240 | /* Determine the BM25 score for the current row. */ |
| 212241 | for(i=0; rc==SQLITE_OK && i<pData->nPhrase; i++){ |
| 212242 | score += pData->aIDF[i] * ( |
| 212243 | ( aFreq[i] * (k1 + 1.0) ) / |
| 212244 | ( aFreq[i] + k1 * (1 - b + b * D / pData->avgdl) ) |
| 212245 | ); |
| 212246 | } |
| 212247 | |
| 212248 | /* If no error has occurred, return the calculated score. Otherwise, |
| 212249 | ** throw an SQL exception. */ |
| 212250 | if( rc==SQLITE_OK ){ |
| 212251 | sqlite3_result_double(pCtx, -1.0 * score); |
| 212252 | }else{ |
| 212253 | sqlite3_result_error_code(pCtx, rc); |
| 212254 | } |
| 212255 | } |
| @@ -223384,10 +223521,11 @@ | |
| 223384 | cksum2 ^= sqlite3Fts5IndexEntryCksum(iRowid, 0, 0, -1, z, n); |
| 223385 | } |
| 223386 | }else{ |
| 223387 | poslist.n = 0; |
| 223388 | fts5SegiterPoslist(p, &pIter->aSeg[pIter->aFirst[1].iFirst], 0, &poslist); |
| 223389 | while( 0==sqlite3Fts5PoslistNext64(poslist.p, poslist.n, &iOff, &iPos) ){ |
| 223390 | int iCol = FTS5_POS2COLUMN(iPos); |
| 223391 | int iTokOff = FTS5_POS2OFFSET(iPos); |
| 223392 | cksum2 ^= sqlite3Fts5IndexEntryCksum(iRowid, iCol, iTokOff, -1, z, n); |
| 223393 | } |
| @@ -226679,11 +226817,11 @@ | |
| 226679 | int nArg, /* Number of args */ |
| 226680 | sqlite3_value **apUnused /* Function arguments */ |
| 226681 | ){ |
| 226682 | assert( nArg==0 ); |
| 226683 | UNUSED_PARAM2(nArg, apUnused); |
| 226684 | sqlite3_result_text(pCtx, "fts5: 2020-10-26 16:22:31 80eba105d6d1b49ba8ca2ad4e14ddec2de0bdc2f6686c2f8a1c1d24fc1fe846f", -1, SQLITE_TRANSIENT); |
| 226685 | } |
| 226686 | |
| 226687 | /* |
| 226688 | ** Return true if zName is the extension on one of the shadow tables used |
| 226689 | ** by this module. |
| @@ -229252,17 +229390,18 @@ | |
| 229252 | |
| 229253 | /* |
| 229254 | ** Allocate a trigram tokenizer. |
| 229255 | */ |
| 229256 | static int fts5TriCreate( |
| 229257 | void *pCtx, |
| 229258 | const char **azArg, |
| 229259 | int nArg, |
| 229260 | Fts5Tokenizer **ppOut |
| 229261 | ){ |
| 229262 | int rc = SQLITE_OK; |
| 229263 | TrigramTokenizer *pNew = (TrigramTokenizer*)sqlite3_malloc(sizeof(*pNew)); |
| 229264 | if( pNew==0 ){ |
| 229265 | rc = SQLITE_NOMEM; |
| 229266 | }else{ |
| 229267 | int i; |
| 229268 | pNew->bFold = 1; |
| @@ -229291,11 +229430,11 @@ | |
| 229291 | ** Trigram tokenizer tokenize routine. |
| 229292 | */ |
| 229293 | static int fts5TriTokenize( |
| 229294 | Fts5Tokenizer *pTok, |
| 229295 | void *pCtx, |
| 229296 | int flags, |
| 229297 | const char *pText, int nText, |
| 229298 | int (*xToken)(void*, int, const char*, int, int, int) |
| 229299 | ){ |
| 229300 | TrigramTokenizer *p = (TrigramTokenizer*)pTok; |
| 229301 | int rc = SQLITE_OK; |
| @@ -229302,10 +229441,11 @@ | |
| 229302 | char aBuf[32]; |
| 229303 | const unsigned char *zIn = (const unsigned char*)pText; |
| 229304 | const unsigned char *zEof = &zIn[nText]; |
| 229305 | u32 iCode; |
| 229306 | |
| 229307 | while( 1 ){ |
| 229308 | char *zOut = aBuf; |
| 229309 | int iStart = zIn - (const unsigned char*)pText; |
| 229310 | const unsigned char *zNext; |
| 229311 | |
| @@ -230164,10 +230304,11 @@ | |
| 230164 | } |
| 230165 | iTbl++; |
| 230166 | } |
| 230167 | aAscii[0] = 0; /* 0x00 is never a token character */ |
| 230168 | } |
| 230169 | |
| 230170 | /* |
| 230171 | ** 2015 May 30 |
| 230172 | ** |
| 230173 | ** The author disclaims copyright to this source code. In place of |
| @@ -231602,12 +231743,12 @@ | |
| 231602 | } |
| 231603 | #endif /* SQLITE_CORE */ |
| 231604 | #endif /* !defined(SQLITE_CORE) || defined(SQLITE_ENABLE_STMTVTAB) */ |
| 231605 | |
| 231606 | /************** End of stmt.c ************************************************/ |
| 231607 | #if __LINE__!=231607 |
| 231608 | #undef SQLITE_SOURCE_ID |
| 231609 | #define SQLITE_SOURCE_ID "2020-10-31 18:58:37 7d01e84dc49074e6364267eea9fd20d46a457d2498121a0f218fbf482692alt2" |
| 231610 | #endif |
| 231611 | /* Return the source-id for this library */ |
| 231612 | SQLITE_API const char *sqlite3_sourceid(void){ return SQLITE_SOURCE_ID; } |
| 231613 | /************************** End of sqlite3.c ******************************/ |
| 231614 |
| --- src/sqlite3.c | |
| +++ src/sqlite3.c | |
| @@ -1171,11 +1171,11 @@ | |
| 1171 | ** [sqlite3_libversion_number()], [sqlite3_sourceid()], |
| 1172 | ** [sqlite_version()] and [sqlite_source_id()]. |
| 1173 | */ |
| 1174 | #define SQLITE_VERSION "3.34.0" |
| 1175 | #define SQLITE_VERSION_NUMBER 3034000 |
| 1176 | #define SQLITE_SOURCE_ID "2020-12-01 16:14:00 a26b6597e3ae272231b96f9982c3bcc17ddec2f2b6eb4df06a224b91089fed5b" |
| 1177 | |
| 1178 | /* |
| 1179 | ** CAPI3REF: Run-Time Library Version Numbers |
| 1180 | ** KEYWORDS: sqlite3_version sqlite3_sourceid |
| 1181 | ** |
| @@ -1550,10 +1550,11 @@ | |
| 1550 | #define SQLITE_IOERR_AUTH (SQLITE_IOERR | (28<<8)) |
| 1551 | #define SQLITE_IOERR_BEGIN_ATOMIC (SQLITE_IOERR | (29<<8)) |
| 1552 | #define SQLITE_IOERR_COMMIT_ATOMIC (SQLITE_IOERR | (30<<8)) |
| 1553 | #define SQLITE_IOERR_ROLLBACK_ATOMIC (SQLITE_IOERR | (31<<8)) |
| 1554 | #define SQLITE_IOERR_DATA (SQLITE_IOERR | (32<<8)) |
| 1555 | #define SQLITE_IOERR_CORRUPTFS (SQLITE_IOERR | (33<<8)) |
| 1556 | #define SQLITE_LOCKED_SHAREDCACHE (SQLITE_LOCKED | (1<<8)) |
| 1557 | #define SQLITE_LOCKED_VTAB (SQLITE_LOCKED | (2<<8)) |
| 1558 | #define SQLITE_BUSY_RECOVERY (SQLITE_BUSY | (1<<8)) |
| 1559 | #define SQLITE_BUSY_SNAPSHOT (SQLITE_BUSY | (2<<8)) |
| 1560 | #define SQLITE_BUSY_TIMEOUT (SQLITE_BUSY | (3<<8)) |
| @@ -7238,11 +7239,11 @@ | |
| 7239 | ** CAPI3REF: Determine the transaction state of a database |
| 7240 | ** METHOD: sqlite3 |
| 7241 | ** |
| 7242 | ** ^The sqlite3_txn_state(D,S) interface returns the current |
| 7243 | ** [transaction state] of schema S in database connection D. ^If S is NULL, |
| 7244 | ** then the highest transaction state of any schema on database connection D |
| 7245 | ** is returned. Transaction states are (in order of lowest to highest): |
| 7246 | ** <ol> |
| 7247 | ** <li value="0"> SQLITE_TXN_NONE |
| 7248 | ** <li value="1"> SQLITE_TXN_READ |
| 7249 | ** <li value="2"> SQLITE_TXN_WRITE |
| @@ -8785,11 +8786,10 @@ | |
| 8786 | */ |
| 8787 | #define SQLITE_TESTCTRL_FIRST 5 |
| 8788 | #define SQLITE_TESTCTRL_PRNG_SAVE 5 |
| 8789 | #define SQLITE_TESTCTRL_PRNG_RESTORE 6 |
| 8790 | #define SQLITE_TESTCTRL_PRNG_RESET 7 /* NOT USED */ |
| 8791 | #define SQLITE_TESTCTRL_BITVEC_TEST 8 |
| 8792 | #define SQLITE_TESTCTRL_FAULT_INSTALL 9 |
| 8793 | #define SQLITE_TESTCTRL_BENIGN_MALLOC_HOOKS 10 |
| 8794 | #define SQLITE_TESTCTRL_PENDING_BYTE 11 |
| 8795 | #define SQLITE_TESTCTRL_ASSERT 12 |
| @@ -8810,11 +8810,12 @@ | |
| 8810 | #define SQLITE_TESTCTRL_IMPOSTER 25 |
| 8811 | #define SQLITE_TESTCTRL_PARSER_COVERAGE 26 |
| 8812 | #define SQLITE_TESTCTRL_RESULT_INTREAL 27 |
| 8813 | #define SQLITE_TESTCTRL_PRNG_SEED 28 |
| 8814 | #define SQLITE_TESTCTRL_EXTRA_SCHEMA_CHECKS 29 |
| 8815 | #define SQLITE_TESTCTRL_SEEK_COUNT 30 |
| 8816 | #define SQLITE_TESTCTRL_LAST 30 /* Largest TESTCTRL */ |
| 8817 | |
| 8818 | /* |
| 8819 | ** CAPI3REF: SQL Keyword Checking |
| 8820 | ** |
| 8821 | ** These routines provide access to the set of SQL language keywords |
| @@ -28182,16 +28183,19 @@ | |
| 28183 | EnableLookaside; |
| 28184 | } |
| 28185 | } |
| 28186 | |
| 28187 | /* |
| 28188 | ** Take actions at the end of an API call to deal with error codes. |
| 28189 | */ |
| 28190 | static SQLITE_NOINLINE int apiHandleError(sqlite3 *db, int rc){ |
| 28191 | if( db->mallocFailed || rc==SQLITE_IOERR_NOMEM ){ |
| 28192 | sqlite3OomClear(db); |
| 28193 | sqlite3Error(db, SQLITE_NOMEM); |
| 28194 | return SQLITE_NOMEM_BKPT; |
| 28195 | } |
| 28196 | return rc & db->errMask; |
| 28197 | } |
| 28198 | |
| 28199 | /* |
| 28200 | ** This function must be called before exiting any API function (i.e. |
| 28201 | ** returning control to the user) that has called sqlite3_malloc or |
| @@ -28209,12 +28213,12 @@ | |
| 28213 | ** Otherwise the read (and possible write) of db->mallocFailed |
| 28214 | ** is unsafe, as is the call to sqlite3Error(). |
| 28215 | */ |
| 28216 | assert( db!=0 ); |
| 28217 | assert( sqlite3_mutex_held(db->mutex) ); |
| 28218 | if( db->mallocFailed || rc ){ |
| 28219 | return apiHandleError(db, rc); |
| 28220 | } |
| 28221 | return rc & db->errMask; |
| 28222 | } |
| 28223 | |
| 28224 | /************** End of malloc.c **********************************************/ |
| @@ -37003,11 +37007,28 @@ | |
| 37007 | |
| 37008 | got = seekAndRead(pFile, offset, pBuf, amt); |
| 37009 | if( got==amt ){ |
| 37010 | return SQLITE_OK; |
| 37011 | }else if( got<0 ){ |
| 37012 | /* pFile->lastErrno has been set by seekAndRead(). |
| 37013 | ** Usually we return SQLITE_IOERR_READ here, though for some |
| 37014 | ** kinds of errors we return SQLITE_IOERR_CORRUPTFS. The |
| 37015 | ** SQLITE_IOERR_CORRUPTFS will be converted into SQLITE_CORRUPT |
| 37016 | ** prior to returning to the application by the sqlite3ApiExit() |
| 37017 | ** routine. |
| 37018 | */ |
| 37019 | switch( pFile->lastErrno ){ |
| 37020 | case ERANGE: |
| 37021 | case EIO: |
| 37022 | #ifdef ENXIO |
| 37023 | case ENXIO: |
| 37024 | #endif |
| 37025 | #ifdef EDEVERR |
| 37026 | case EDEVERR: |
| 37027 | #endif |
| 37028 | return SQLITE_IOERR_CORRUPTFS; |
| 37029 | } |
| 37030 | return SQLITE_IOERR_READ; |
| 37031 | }else{ |
| 37032 | storeLastErrno(pFile, 0); /* not a system error */ |
| 37033 | /* Unread parts of the buffer must be zero-filled */ |
| 37034 | memset(&((char*)pBuf)[got], 0, amt-got); |
| @@ -38535,11 +38556,11 @@ | |
| 38556 | if( bUnlock ){ |
| 38557 | rc = unixShmSystemLock(pDbFd, F_UNLCK, ofst+UNIX_SHM_BASE, n); |
| 38558 | if( rc==SQLITE_OK ){ |
| 38559 | memset(&aLock[ofst], 0, sizeof(int)*n); |
| 38560 | } |
| 38561 | }else if( ALWAYS(p->sharedMask & (1<<ofst)) ){ |
| 38562 | assert( n==1 && aLock[ofst]>1 ); |
| 38563 | aLock[ofst]--; |
| 38564 | } |
| 38565 | |
| 38566 | /* Undo the local locks */ |
| @@ -38568,11 +38589,11 @@ | |
| 38589 | /* Make sure no sibling connections hold locks that will block this |
| 38590 | ** lock. If any do, return SQLITE_BUSY right away. */ |
| 38591 | int ii; |
| 38592 | for(ii=ofst; ii<ofst+n; ii++){ |
| 38593 | assert( (p->sharedMask & mask)==0 ); |
| 38594 | if( ALWAYS((p->exclMask & (1<<ii))==0) && aLock[ii] ){ |
| 38595 | rc = SQLITE_BUSY; |
| 38596 | break; |
| 38597 | } |
| 38598 | } |
| 38599 | |
| @@ -39964,19 +39985,39 @@ | |
| 39985 | } |
| 39986 | return SQLITE_OK; |
| 39987 | } |
| 39988 | |
| 39989 | /* |
| 39990 | ** If the last component of the pathname in z[0]..z[j-1] is something |
| 39991 | ** other than ".." then back it out and return true. If the last |
| 39992 | ** component is empty or if it is ".." then return false. |
| 39993 | */ |
| 39994 | static int unixBackupDir(const char *z, int *pJ){ |
| 39995 | int j = *pJ; |
| 39996 | int i; |
| 39997 | if( j<=0 ) return 0; |
| 39998 | for(i=j-1; ALWAYS(i>0) && z[i-1]!='/'; i--){} |
| 39999 | if( z[i]=='.' && i==j-2 && z[i+1]=='.' ) return 0; |
| 40000 | *pJ = i-1; |
| 40001 | return 1; |
| 40002 | } |
| 40003 | |
| 40004 | /* |
| 40005 | ** Convert a relative pathname into a full pathname. Also |
| 40006 | ** simplify the pathname as follows: |
| 40007 | ** |
| 40008 | ** Remove all instances of /./ |
| 40009 | ** Remove all isntances of /X/../ for any X |
| 40010 | */ |
| 40011 | static int mkFullPathname( |
| 40012 | const char *zPath, /* Input path */ |
| 40013 | char *zOut, /* Output buffer */ |
| 40014 | int nOut /* Allocated size of buffer zOut */ |
| 40015 | ){ |
| 40016 | int nPath = sqlite3Strlen30(zPath); |
| 40017 | int iOff = 0; |
| 40018 | int i, j; |
| 40019 | if( zPath[0]!='/' ){ |
| 40020 | if( osGetcwd(zOut, nOut-2)==0 ){ |
| 40021 | return unixLogError(SQLITE_CANTOPEN_BKPT, "getcwd", zPath); |
| 40022 | } |
| 40023 | iOff = sqlite3Strlen30(zOut); |
| @@ -39987,10 +40028,45 @@ | |
| 40028 | ** even if it returns an error. */ |
| 40029 | zOut[iOff] = '\0'; |
| 40030 | return SQLITE_CANTOPEN_BKPT; |
| 40031 | } |
| 40032 | sqlite3_snprintf(nOut-iOff, &zOut[iOff], "%s", zPath); |
| 40033 | |
| 40034 | /* Remove duplicate '/' characters. Except, two // at the beginning |
| 40035 | ** of a pathname is allowed since this is important on windows. */ |
| 40036 | for(i=j=1; zOut[i]; i++){ |
| 40037 | zOut[j++] = zOut[i]; |
| 40038 | while( zOut[i]=='/' && zOut[i+1]=='/' ) i++; |
| 40039 | } |
| 40040 | zOut[j] = 0; |
| 40041 | |
| 40042 | assert( zOut[0]=='/' ); |
| 40043 | for(i=j=0; zOut[i]; i++){ |
| 40044 | if( zOut[i]=='/' ){ |
| 40045 | /* Skip over internal "/." directory components */ |
| 40046 | if( zOut[i+1]=='.' && zOut[i+2]=='/' ){ |
| 40047 | i += 1; |
| 40048 | continue; |
| 40049 | } |
| 40050 | |
| 40051 | /* If this is a "/.." directory component then back out the |
| 40052 | ** previous term of the directory if it is something other than "..". |
| 40053 | */ |
| 40054 | if( zOut[i+1]=='.' |
| 40055 | && zOut[i+2]=='.' |
| 40056 | && zOut[i+3]=='/' |
| 40057 | && unixBackupDir(zOut, &j) |
| 40058 | ){ |
| 40059 | i += 2; |
| 40060 | continue; |
| 40061 | } |
| 40062 | } |
| 40063 | if( ALWAYS(j>=0) ) zOut[j] = zOut[i]; |
| 40064 | j++; |
| 40065 | } |
| 40066 | if( NEVER(j==0) ) zOut[j++] = '/'; |
| 40067 | zOut[j] = 0; |
| 40068 | return SQLITE_OK; |
| 40069 | } |
| 40070 | |
| 40071 | /* |
| 40072 | ** Turn a relative pathname into a full pathname. The relative path |
| @@ -54326,10 +54402,11 @@ | |
| 54402 | sqlite3_file *pJournal; /* Malloc'd child-journal file descriptor */ |
| 54403 | char *zSuperJournal = 0; /* Contents of super-journal file */ |
| 54404 | i64 nSuperJournal; /* Size of super-journal file */ |
| 54405 | char *zJournal; /* Pointer to one journal within MJ file */ |
| 54406 | char *zSuperPtr; /* Space to hold super-journal filename */ |
| 54407 | char *zFree = 0; /* Free this buffer */ |
| 54408 | int nSuperPtr; /* Amount of space allocated to zSuperPtr[] */ |
| 54409 | |
| 54410 | /* Allocate space for both the pJournal and pSuper file descriptors. |
| 54411 | ** If successful, open the super-journal file for reading. |
| 54412 | */ |
| @@ -54350,15 +54427,17 @@ | |
| 54427 | ** files extracted from regular rollback-journals. |
| 54428 | */ |
| 54429 | rc = sqlite3OsFileSize(pSuper, &nSuperJournal); |
| 54430 | if( rc!=SQLITE_OK ) goto delsuper_out; |
| 54431 | nSuperPtr = pVfs->mxPathname+1; |
| 54432 | zFree = sqlite3Malloc(4 + nSuperJournal + nSuperPtr + 2); |
| 54433 | if( !zFree ){ |
| 54434 | rc = SQLITE_NOMEM_BKPT; |
| 54435 | goto delsuper_out; |
| 54436 | } |
| 54437 | zFree[0] = zFree[1] = zFree[2] = zFree[3] = 0; |
| 54438 | zSuperJournal = &zFree[4]; |
| 54439 | zSuperPtr = &zSuperJournal[nSuperJournal+2]; |
| 54440 | rc = sqlite3OsRead(pSuper, zSuperJournal, (int)nSuperJournal, 0); |
| 54441 | if( rc!=SQLITE_OK ) goto delsuper_out; |
| 54442 | zSuperJournal[nSuperJournal] = 0; |
| 54443 | zSuperJournal[nSuperJournal+1] = 0; |
| @@ -54402,11 +54481,11 @@ | |
| 54481 | |
| 54482 | sqlite3OsClose(pSuper); |
| 54483 | rc = sqlite3OsDelete(pVfs, zSuper, 0); |
| 54484 | |
| 54485 | delsuper_out: |
| 54486 | sqlite3_free(zFree); |
| 54487 | if( pSuper ){ |
| 54488 | sqlite3OsClose(pSuper); |
| 54489 | assert( !isOpen(pJournal) ); |
| 54490 | sqlite3_free(pSuper); |
| 54491 | } |
| @@ -54740,11 +54819,15 @@ | |
| 54819 | ** in case this has happened, clear the changeCountDone flag now. |
| 54820 | */ |
| 54821 | pPager->changeCountDone = pPager->tempFile; |
| 54822 | |
| 54823 | if( rc==SQLITE_OK ){ |
| 54824 | /* Leave 4 bytes of space before the super-journal filename in memory. |
| 54825 | ** This is because it may end up being passed to sqlite3OsOpen(), in |
| 54826 | ** which case it requires 4 0x00 bytes in memory immediately before |
| 54827 | ** the filename. */ |
| 54828 | zSuper = &pPager->pTmpSpace[4]; |
| 54829 | rc = readSuperJournal(pPager->jfd, zSuper, pPager->pVfs->mxPathname+1); |
| 54830 | testcase( rc!=SQLITE_OK ); |
| 54831 | } |
| 54832 | if( rc==SQLITE_OK |
| 54833 | && (pPager->eState>=PAGER_WRITER_DBMOD || pPager->eState==PAGER_OPEN) |
| @@ -54757,10 +54840,12 @@ | |
| 54840 | } |
| 54841 | if( rc==SQLITE_OK && zSuper[0] && res ){ |
| 54842 | /* If there was a super-journal and this routine will return success, |
| 54843 | ** see if it is possible to delete the super-journal. |
| 54844 | */ |
| 54845 | assert( zSuper==&pPager->pTmpSpace[4] ); |
| 54846 | memset(&zSuper[-4], 0, 4); |
| 54847 | rc = pager_delsuper(pPager, zSuper); |
| 54848 | testcase( rc!=SQLITE_OK ); |
| 54849 | } |
| 54850 | if( isHot && nPlayback ){ |
| 54851 | sqlite3_log(SQLITE_NOTICE_RECOVER_ROLLBACK, "recovered %d pages from %s", |
| @@ -64746,11 +64831,11 @@ | |
| 64831 | #define hasReadConflicts(a, b) 0 |
| 64832 | #endif |
| 64833 | |
| 64834 | #ifdef SQLITE_DEBUG |
| 64835 | /* |
| 64836 | ** Return and reset the seek counter for a Btree object. |
| 64837 | */ |
| 64838 | SQLITE_PRIVATE sqlite3_uint64 sqlite3BtreeSeekCount(Btree *pBt){ |
| 64839 | u64 n = pBt->nSeek; |
| 64840 | pBt->nSeek = 0; |
| 64841 | return n; |
| @@ -82192,13 +82277,16 @@ | |
| 82277 | ** equal to, or greater than the second (double). |
| 82278 | */ |
| 82279 | static int sqlite3IntFloatCompare(i64 i, double r){ |
| 82280 | if( sizeof(LONGDOUBLE_TYPE)>8 ){ |
| 82281 | LONGDOUBLE_TYPE x = (LONGDOUBLE_TYPE)i; |
| 82282 | testcase( x<r ); |
| 82283 | testcase( x>r ); |
| 82284 | testcase( x==r ); |
| 82285 | if( x<r ) return -1; |
| 82286 | if( x>r ) return +1; /*NO_TEST*/ /* work around bugs in gcov */ |
| 82287 | return 0; /*NO_TEST*/ /* work around bugs in gcov */ |
| 82288 | }else{ |
| 82289 | i64 y; |
| 82290 | double s; |
| 82291 | if( r<-9223372036854775808.0 ) return +1; |
| 82292 | if( r>=9223372036854775808.0 ) return -1; |
| @@ -89388,11 +89476,11 @@ | |
| 89476 | assert( rc==SQLITE_OK ); |
| 89477 | break; |
| 89478 | } |
| 89479 | |
| 89480 | |
| 89481 | /* Opcode: OpenEphemeral P1 P2 P3 P4 P5 |
| 89482 | ** Synopsis: nColumn=P2 |
| 89483 | ** |
| 89484 | ** Open a new cursor P1 to a transient table. |
| 89485 | ** The cursor is always opened read/write even if |
| 89486 | ** the main database is read-only. The ephemeral |
| @@ -89408,10 +89496,14 @@ | |
| 89496 | ** |
| 89497 | ** The P5 parameter can be a mask of the BTREE_* flags defined |
| 89498 | ** in btree.h. These flags control aspects of the operation of |
| 89499 | ** the btree. The BTREE_OMIT_JOURNAL and BTREE_SINGLE flags are |
| 89500 | ** added automatically. |
| 89501 | ** |
| 89502 | ** If P3 is positive, then reg[P3] is modified slightly so that it |
| 89503 | ** can be used as zero-length data for OP_Insert. This is an optimization |
| 89504 | ** that avoids an extra OP_Blob opcode to initialize that register. |
| 89505 | */ |
| 89506 | /* Opcode: OpenAutoindex P1 P2 * P4 * |
| 89507 | ** Synopsis: nColumn=P2 |
| 89508 | ** |
| 89509 | ** This opcode works the same as OP_OpenEphemeral. It has a |
| @@ -89430,10 +89522,19 @@ | |
| 89522 | SQLITE_OPEN_EXCLUSIVE | |
| 89523 | SQLITE_OPEN_DELETEONCLOSE | |
| 89524 | SQLITE_OPEN_TRANSIENT_DB; |
| 89525 | assert( pOp->p1>=0 ); |
| 89526 | assert( pOp->p2>=0 ); |
| 89527 | if( pOp->p3>0 ){ |
| 89528 | /* Make register reg[P3] into a value that can be used as the data |
| 89529 | ** form sqlite3BtreeInsert() where the length of the data is zero. */ |
| 89530 | assert( pOp->p2==0 ); /* Only used when number of columns is zero */ |
| 89531 | assert( pOp->opcode==OP_OpenEphemeral ); |
| 89532 | assert( aMem[pOp->p3].flags & MEM_Null ); |
| 89533 | aMem[pOp->p3].n = 0; |
| 89534 | aMem[pOp->p3].z = ""; |
| 89535 | } |
| 89536 | pCx = p->apCsr[pOp->p1]; |
| 89537 | if( pCx && pCx->pBtx ){ |
| 89538 | /* If the ephermeral table is already open, erase all existing content |
| 89539 | ** so that the table is empty again, rather than creating a new table. */ |
| 89540 | assert( pCx->isEphemeral ); |
| @@ -90589,11 +90690,11 @@ | |
| 90690 | if( pOp->p5 & OPFLAG_ISNOOP ) break; |
| 90691 | #endif |
| 90692 | |
| 90693 | if( pOp->p5 & OPFLAG_NCHANGE ) p->nChange++; |
| 90694 | if( pOp->p5 & OPFLAG_LASTROWID ) db->lastRowid = x.nKey; |
| 90695 | assert( (pData->flags & (MEM_Blob|MEM_Str))!=0 || pData->n==0 ); |
| 90696 | x.pData = pData->z; |
| 90697 | x.nData = pData->n; |
| 90698 | seekResult = ((pOp->p5 & OPFLAG_USESEEKRESULT) ? pC->seekResult : 0); |
| 90699 | if( pData->flags & MEM_Zero ){ |
| 90700 | x.nZero = pData->u.nZero; |
| @@ -93644,11 +93745,15 @@ | |
| 93745 | |
| 93746 | /* If we reach this point, it means that execution is finished with |
| 93747 | ** an error of some kind. |
| 93748 | */ |
| 93749 | abort_due_to_error: |
| 93750 | if( db->mallocFailed ){ |
| 93751 | rc = SQLITE_NOMEM_BKPT; |
| 93752 | }else if( rc==SQLITE_IOERR_CORRUPTFS ){ |
| 93753 | rc = SQLITE_CORRUPT_BKPT; |
| 93754 | } |
| 93755 | assert( rc ); |
| 93756 | if( p->zErrMsg==0 && rc!=SQLITE_IOERR_NOMEM ){ |
| 93757 | sqlite3VdbeError(p, "%s", sqlite3ErrStr(rc)); |
| 93758 | } |
| 93759 | p->rc = rc; |
| @@ -99381,10 +99486,11 @@ | |
| 99486 | for(i=0, pItem=pOrderBy->a; i<pOrderBy->nExpr; i++, pItem++){ |
| 99487 | int iCol = -1; |
| 99488 | Expr *pE, *pDup; |
| 99489 | if( pItem->done ) continue; |
| 99490 | pE = sqlite3ExprSkipCollateAndLikely(pItem->pExpr); |
| 99491 | if( NEVER(pE==0) ) continue; |
| 99492 | if( sqlite3ExprIsInteger(pE, &iCol) ){ |
| 99493 | if( iCol<=0 || iCol>pEList->nExpr ){ |
| 99494 | resolveOutOfRangeError(pParse, "ORDER", i+1, pEList->nExpr); |
| 99495 | return 1; |
| 99496 | } |
| @@ -99560,10 +99666,11 @@ | |
| 99666 | nResult = pSelect->pEList->nExpr; |
| 99667 | pParse = pNC->pParse; |
| 99668 | for(i=0, pItem=pOrderBy->a; i<pOrderBy->nExpr; i++, pItem++){ |
| 99669 | Expr *pE = pItem->pExpr; |
| 99670 | Expr *pE2 = sqlite3ExprSkipCollateAndLikely(pE); |
| 99671 | if( NEVER(pE2==0) ) continue; |
| 99672 | if( zType[0]!='G' ){ |
| 99673 | iCol = resolveAsName(pParse, pSelect->pEList, pE2); |
| 99674 | if( iCol>0 ){ |
| 99675 | /* If an AS-name match is found, mark this ORDER BY column as being |
| 99676 | ** a copy of the iCol-th result-set column. The subsequent call to |
| @@ -103679,10 +103786,11 @@ | |
| 103786 | ** register iReg. The caller must ensure that iReg already contains |
| 103787 | ** the correct value for the expression. |
| 103788 | */ |
| 103789 | static void exprToRegister(Expr *pExpr, int iReg){ |
| 103790 | Expr *p = sqlite3ExprSkipCollateAndLikely(pExpr); |
| 103791 | if( NEVER(p==0) ) return; |
| 103792 | p->op2 = p->op; |
| 103793 | p->op = TK_REGISTER; |
| 103794 | p->iTable = iReg; |
| 103795 | ExprClearProperty(p, EP_Skip); |
| 103796 | } |
| @@ -104666,10 +104774,11 @@ | |
| 104774 | */ |
| 104775 | SQLITE_PRIVATE int sqlite3ExprCodeTemp(Parse *pParse, Expr *pExpr, int *pReg){ |
| 104776 | int r2; |
| 104777 | pExpr = sqlite3ExprSkipCollateAndLikely(pExpr); |
| 104778 | if( ConstFactorOk(pParse) |
| 104779 | && ALWAYS(pExpr!=0) |
| 104780 | && pExpr->op!=TK_REGISTER |
| 104781 | && sqlite3ExprIsConstantNotJoin(pExpr) |
| 104782 | ){ |
| 104783 | *pReg = 0; |
| 104784 | r2 = sqlite3ExprCodeRunJustOnce(pParse, pExpr, -1); |
| @@ -119424,10 +119533,12 @@ | |
| 119533 | VFUNCTION(total_changes, 0, 0, 0, total_changes ), |
| 119534 | FUNCTION(replace, 3, 0, 0, replaceFunc ), |
| 119535 | FUNCTION(zeroblob, 1, 0, 0, zeroblobFunc ), |
| 119536 | FUNCTION(substr, 2, 0, 0, substrFunc ), |
| 119537 | FUNCTION(substr, 3, 0, 0, substrFunc ), |
| 119538 | FUNCTION(substring, 2, 0, 0, substrFunc ), |
| 119539 | FUNCTION(substring, 3, 0, 0, substrFunc ), |
| 119540 | WAGGREGATE(sum, 1,0,0, sumStep, sumFinalize, sumFinalize, sumInverse, 0), |
| 119541 | WAGGREGATE(total, 1,0,0, sumStep,totalFinalize,totalFinalize,sumInverse, 0), |
| 119542 | WAGGREGATE(avg, 1,0,0, sumStep, avgFinalize, avgFinalize, sumInverse, 0), |
| 119543 | WAGGREGATE(count, 0,0,0, countStep, |
| 119544 | countFinalize, countFinalize, countInverse, SQLITE_FUNC_COUNT ), |
| @@ -124310,10 +124421,12 @@ | |
| 124421 | /* Version 3.32.0 and later */ |
| 124422 | char *(*create_filename)(const char*,const char*,const char*, |
| 124423 | int,const char**); |
| 124424 | void (*free_filename)(char*); |
| 124425 | sqlite3_file *(*database_file_object)(const char*); |
| 124426 | /* Version 3.34.0 and later */ |
| 124427 | int (*txn_state)(sqlite3*,const char*); |
| 124428 | }; |
| 124429 | |
| 124430 | /* |
| 124431 | ** This is the function signature used for all extension entry points. It |
| 124432 | ** is also defined in the file "loadext.c". |
| @@ -124614,10 +124727,12 @@ | |
| 124727 | #define sqlite3_filename_wal sqlite3_api->filename_wal |
| 124728 | /* Version 3.32.0 and later */ |
| 124729 | #define sqlite3_create_filename sqlite3_api->create_filename |
| 124730 | #define sqlite3_free_filename sqlite3_api->free_filename |
| 124731 | #define sqlite3_database_file_object sqlite3_api->database_file_object |
| 124732 | /* Version 3.34.0 and later */ |
| 124733 | #define sqlite3_txn_state sqlite3_api->txn_state |
| 124734 | #endif /* !defined(SQLITE_CORE) && !defined(SQLITE_OMIT_LOAD_EXTENSION) */ |
| 124735 | |
| 124736 | #if !defined(SQLITE_CORE) && !defined(SQLITE_OMIT_LOAD_EXTENSION) |
| 124737 | /* This case when the file really is being compiled as a loadable |
| 124738 | ** extension */ |
| @@ -125096,10 +125211,12 @@ | |
| 125211 | sqlite3_filename_wal, |
| 125212 | /* Version 3.32.0 and later */ |
| 125213 | sqlite3_create_filename, |
| 125214 | sqlite3_free_filename, |
| 125215 | sqlite3_database_file_object, |
| 125216 | /* Version 3.34.0 and later */ |
| 125217 | sqlite3_txn_state, |
| 125218 | }; |
| 125219 | |
| 125220 | /* True if x is the directory separator character |
| 125221 | */ |
| 125222 | #if SQLITE_OS_WIN |
| @@ -131654,10 +131771,11 @@ | |
| 131771 | Column *aCol, *pCol; /* For looping over result columns */ |
| 131772 | int nCol; /* Number of columns in the result set */ |
| 131773 | char *zName; /* Column name */ |
| 131774 | int nName; /* Size of name in zName[] */ |
| 131775 | Hash ht; /* Hash table of column names */ |
| 131776 | Table *pTab; |
| 131777 | |
| 131778 | sqlite3HashInit(&ht); |
| 131779 | if( pEList ){ |
| 131780 | nCol = pEList->nExpr; |
| 131781 | aCol = sqlite3DbMallocZero(db, sizeof(aCol[0])*nCol); |
| @@ -131676,19 +131794,17 @@ | |
| 131794 | */ |
| 131795 | if( (zName = pEList->a[i].zEName)!=0 && pEList->a[i].eEName==ENAME_NAME ){ |
| 131796 | /* If the column contains an "AS <name>" phrase, use <name> as the name */ |
| 131797 | }else{ |
| 131798 | Expr *pColExpr = sqlite3ExprSkipCollateAndLikely(pEList->a[i].pExpr); |
| 131799 | while( ALWAYS(pColExpr!=0) && pColExpr->op==TK_DOT ){ |
| 131800 | pColExpr = pColExpr->pRight; |
| 131801 | assert( pColExpr!=0 ); |
| 131802 | } |
| 131803 | if( pColExpr->op==TK_COLUMN && (pTab = pColExpr->y.pTab)!=0 ){ |
| 131804 | /* For columns use the column name name */ |
| 131805 | int iCol = pColExpr->iColumn; |
| 131806 | if( iCol<0 ) iCol = pTab->iPKey; |
| 131807 | zName = iCol>=0 ? pTab->aCol[iCol].zName : "rowid"; |
| 131808 | }else if( pColExpr->op==TK_ID ){ |
| 131809 | assert( !ExprHasProperty(pColExpr, EP_IntValue) ); |
| 131810 | zName = pColExpr->u.zToken; |
| @@ -136959,26 +137075,15 @@ | |
| 137075 | goto trigger_cleanup; |
| 137076 | } |
| 137077 | pTab = sqlite3SrcListLookup(pParse, pTableName); |
| 137078 | if( !pTab ){ |
| 137079 | /* The table does not exist. */ |
| 137080 | goto trigger_orphan_error; |
| 137081 | } |
| 137082 | if( IsVirtual(pTab) ){ |
| 137083 | sqlite3ErrorMsg(pParse, "cannot create triggers on virtual tables"); |
| 137084 | goto trigger_orphan_error; |
| 137085 | } |
| 137086 | |
| 137087 | /* Check that the trigger name is not reserved and that no trigger of the |
| 137088 | ** specified name exists */ |
| 137089 | zName = sqlite3NameFromToken(db, pName); |
| @@ -137012,16 +137117,16 @@ | |
| 137117 | ** of triggers. |
| 137118 | */ |
| 137119 | if( pTab->pSelect && tr_tm!=TK_INSTEAD ){ |
| 137120 | sqlite3ErrorMsg(pParse, "cannot create %s trigger on view: %S", |
| 137121 | (tr_tm == TK_BEFORE)?"BEFORE":"AFTER", pTableName, 0); |
| 137122 | goto trigger_orphan_error; |
| 137123 | } |
| 137124 | if( !pTab->pSelect && tr_tm==TK_INSTEAD ){ |
| 137125 | sqlite3ErrorMsg(pParse, "cannot create INSTEAD OF" |
| 137126 | " trigger on table: %S", pTableName, 0); |
| 137127 | goto trigger_orphan_error; |
| 137128 | } |
| 137129 | |
| 137130 | #ifndef SQLITE_OMIT_AUTHORIZATION |
| 137131 | if( !IN_RENAME_OBJECT ){ |
| 137132 | int iTabDb = sqlite3SchemaToIndex(db, pTab->pSchema); |
| @@ -137077,10 +137182,27 @@ | |
| 137182 | if( !pParse->pNewTrigger ){ |
| 137183 | sqlite3DeleteTrigger(db, pTrigger); |
| 137184 | }else{ |
| 137185 | assert( pParse->pNewTrigger==pTrigger ); |
| 137186 | } |
| 137187 | return; |
| 137188 | |
| 137189 | trigger_orphan_error: |
| 137190 | if( db->init.iDb==1 ){ |
| 137191 | /* Ticket #3810. |
| 137192 | ** Normally, whenever a table is dropped, all associated triggers are |
| 137193 | ** dropped too. But if a TEMP trigger is created on a non-TEMP table |
| 137194 | ** and the table is dropped by a different database connection, the |
| 137195 | ** trigger is not visible to the database connection that does the |
| 137196 | ** drop so the trigger cannot be dropped. This results in an |
| 137197 | ** "orphaned trigger" - a trigger whose associated table is missing. |
| 137198 | ** |
| 137199 | ** 2020-11-05 see also https://sqlite.org/forum/forumpost/157dc791df |
| 137200 | */ |
| 137201 | db->init.orphanTrigger = 1; |
| 137202 | } |
| 137203 | goto trigger_cleanup; |
| 137204 | } |
| 137205 | |
| 137206 | /* |
| 137207 | ** This routine is called after all of the trigger actions have been parsed |
| 137208 | ** in order to complete the process of building the trigger. |
| @@ -138664,10 +138786,12 @@ | |
| 138786 | sqlite3VdbeAddOp2(v, OP_Integer, 0, regRowCount); |
| 138787 | } |
| 138788 | |
| 138789 | if( nChangeFrom==0 && HasRowid(pTab) ){ |
| 138790 | sqlite3VdbeAddOp3(v, OP_Null, 0, regRowSet, regOldRowid); |
| 138791 | iEph = pParse->nTab++; |
| 138792 | addrOpen = sqlite3VdbeAddOp3(v, OP_OpenEphemeral, iEph, 0, regRowSet); |
| 138793 | }else{ |
| 138794 | assert( pPk!=0 || HasRowid(pTab) ); |
| 138795 | nPk = pPk ? pPk->nKeyCol : 0; |
| 138796 | iPk = pParse->nMem+1; |
| 138797 | pParse->nMem += nPk; |
| @@ -138755,13 +138879,14 @@ | |
| 138879 | /* Read the rowid of the current row of the WHERE scan. In ONEPASS_OFF |
| 138880 | ** mode, write the rowid into the FIFO. In either of the one-pass modes, |
| 138881 | ** leave it in register regOldRowid. */ |
| 138882 | sqlite3VdbeAddOp2(v, OP_Rowid, iDataCur, regOldRowid); |
| 138883 | if( eOnePass==ONEPASS_OFF ){ |
| 138884 | aRegIdx[nAllIdx] = ++pParse->nMem; |
| 138885 | sqlite3VdbeAddOp3(v, OP_Insert, iEph, regRowSet, regOldRowid); |
| 138886 | }else{ |
| 138887 | if( ALWAYS(addrOpen) ) sqlite3VdbeChangeToNoop(v, addrOpen); |
| 138888 | } |
| 138889 | }else{ |
| 138890 | /* Read the PK of the current row into an array of registers. In |
| 138891 | ** ONEPASS_OFF mode, serialize the array into a record and store it in |
| 138892 | ** the ephemeral table. Or, in ONEPASS_SINGLE or MULTI mode, change |
| @@ -138845,12 +138970,13 @@ | |
| 138970 | sqlite3VdbeAddOp2(v, OP_RowData, iEph, regKey); |
| 138971 | sqlite3VdbeAddOp4Int(v, OP_NotFound, iDataCur, labelContinue, regKey,0); |
| 138972 | VdbeCoverage(v); |
| 138973 | } |
| 138974 | }else{ |
| 138975 | sqlite3VdbeAddOp2(v, OP_Rewind, iEph, labelBreak); VdbeCoverage(v); |
| 138976 | labelContinue = sqlite3VdbeMakeLabel(pParse); |
| 138977 | addrTop = sqlite3VdbeAddOp2(v, OP_Rowid, iEph, regOldRowid); |
| 138978 | VdbeCoverage(v); |
| 138979 | sqlite3VdbeAddOp3(v, OP_NotExists, iDataCur, labelContinue, regOldRowid); |
| 138980 | VdbeCoverage(v); |
| 138981 | } |
| 138982 | } |
| @@ -139096,15 +139222,13 @@ | |
| 139222 | if( eOnePass==ONEPASS_SINGLE ){ |
| 139223 | /* Nothing to do at end-of-loop for a single-pass */ |
| 139224 | }else if( eOnePass==ONEPASS_MULTI ){ |
| 139225 | sqlite3VdbeResolveLabel(v, labelContinue); |
| 139226 | sqlite3WhereEnd(pWInfo); |
| 139227 | }else{ |
| 139228 | sqlite3VdbeResolveLabel(v, labelContinue); |
| 139229 | sqlite3VdbeAddOp2(v, OP_Next, iEph, addrTop); VdbeCoverage(v); |
| 139230 | } |
| 139231 | sqlite3VdbeResolveLabel(v, labelBreak); |
| 139232 | |
| 139233 | /* Update the sqlite_sequence table by storing the content of the |
| 139234 | ** maximum rowid counter values recorded while inserting into |
| @@ -145894,10 +146018,11 @@ | |
| 146018 | ** all terms of the WHERE clause. |
| 146019 | */ |
| 146020 | SQLITE_PRIVATE void sqlite3WhereSplit(WhereClause *pWC, Expr *pExpr, u8 op){ |
| 146021 | Expr *pE2 = sqlite3ExprSkipCollateAndLikely(pExpr); |
| 146022 | pWC->op = op; |
| 146023 | assert( pE2!=0 || pExpr==0 ); |
| 146024 | if( pE2==0 ) return; |
| 146025 | if( pE2->op!=op ){ |
| 146026 | whereClauseInsert(pWC, pExpr, 0); |
| 146027 | }else{ |
| 146028 | sqlite3WhereSplit(pWC, pE2->pLeft, op); |
| @@ -146292,10 +146417,20 @@ | |
| 146417 | */ |
| 146418 | static void createMask(WhereMaskSet *pMaskSet, int iCursor){ |
| 146419 | assert( pMaskSet->n < ArraySize(pMaskSet->ix) ); |
| 146420 | pMaskSet->ix[pMaskSet->n++] = iCursor; |
| 146421 | } |
| 146422 | |
| 146423 | /* |
| 146424 | ** If the right-hand branch of the expression is a TK_COLUMN, then return |
| 146425 | ** a pointer to the right-hand branch. Otherwise, return NULL. |
| 146426 | */ |
| 146427 | static Expr *whereRightSubexprIsColumn(Expr *p){ |
| 146428 | p = sqlite3ExprSkipCollateAndLikely(p->pRight); |
| 146429 | if( ALWAYS(p!=0) && p->op==TK_COLUMN ) return p; |
| 146430 | return 0; |
| 146431 | } |
| 146432 | |
| 146433 | /* |
| 146434 | ** Advance to the next WhereTerm that matches according to the criteria |
| 146435 | ** established when the pScan object was initialized by whereScanInit(). |
| 146436 | ** Return NULL if there are no more matching WhereTerms. |
| @@ -146323,12 +146458,11 @@ | |
| 146458 | pScan->pIdxExpr,iCur)==0) |
| 146459 | && (pScan->iEquiv<=1 || !ExprHasProperty(pTerm->pExpr, EP_FromJoin)) |
| 146460 | ){ |
| 146461 | if( (pTerm->eOperator & WO_EQUIV)!=0 |
| 146462 | && pScan->nEquiv<ArraySize(pScan->aiCur) |
| 146463 | && (pX = whereRightSubexprIsColumn(pTerm->pExpr))!=0 |
| 146464 | ){ |
| 146465 | int j; |
| 146466 | for(j=0; j<pScan->nEquiv; j++){ |
| 146467 | if( pScan->aiCur[j]==pX->iTable |
| 146468 | && pScan->aiColumn[j]==pX->iColumn ){ |
| @@ -146520,11 +146654,12 @@ | |
| 146654 | int i; |
| 146655 | const char *zColl = pIdx->azColl[iCol]; |
| 146656 | |
| 146657 | for(i=0; i<pList->nExpr; i++){ |
| 146658 | Expr *p = sqlite3ExprSkipCollateAndLikely(pList->a[i].pExpr); |
| 146659 | if( ALWAYS(p!=0) |
| 146660 | && p->op==TK_COLUMN |
| 146661 | && p->iColumn==pIdx->aiColumn[iCol] |
| 146662 | && p->iTable==iBase |
| 146663 | ){ |
| 146664 | CollSeq *pColl = sqlite3ExprNNCollSeq(pParse, pList->a[i].pExpr); |
| 146665 | if( 0==sqlite3StrICmp(pColl->zName, zColl) ){ |
| @@ -146584,10 +146719,11 @@ | |
| 146719 | ** true. Note: The (p->iTable==iBase) part of this test may be false if the |
| 146720 | ** current SELECT is a correlated sub-query. |
| 146721 | */ |
| 146722 | for(i=0; i<pDistinct->nExpr; i++){ |
| 146723 | Expr *p = sqlite3ExprSkipCollateAndLikely(pDistinct->a[i].pExpr); |
| 146724 | if( NEVER(p==0) ) continue; |
| 146725 | if( p->op==TK_COLUMN && p->iTable==iBase && p->iColumn<0 ) return 1; |
| 146726 | } |
| 146727 | |
| 146728 | /* Loop through all indices on the table, checking each to see if it makes |
| 146729 | ** the DISTINCT qualifier redundant. It does so if: |
| @@ -148498,13 +148634,13 @@ | |
| 148634 | LogEst rLogSize; /* Logarithm of table size */ |
| 148635 | WhereTerm *pTop = 0, *pBtm = 0; /* Top and bottom range constraints */ |
| 148636 | |
| 148637 | pNew = pBuilder->pNew; |
| 148638 | if( db->mallocFailed ) return SQLITE_NOMEM_BKPT; |
| 148639 | WHERETRACE(0x800, ("BEGIN %s.addBtreeIdx(%s), nEq=%d, nSkip=%d, rRun=%d\n", |
| 148640 | pProbe->pTable->zName,pProbe->zName, |
| 148641 | pNew->u.btree.nEq, pNew->nSkip, pNew->rRun)); |
| 148642 | |
| 148643 | assert( (pNew->wsFlags & WHERE_VIRTUALTABLE)==0 ); |
| 148644 | assert( (pNew->wsFlags & WHERE_TOP_LIMIT)==0 ); |
| 148645 | if( pNew->wsFlags & WHERE_BTM_LIMIT ){ |
| 148646 | opMask = WO_LT|WO_LE; |
| @@ -148869,10 +149005,11 @@ | |
| 149005 | |
| 149006 | if( pIndex->bUnordered ) return 0; |
| 149007 | if( (pOB = pBuilder->pWInfo->pOrderBy)==0 ) return 0; |
| 149008 | for(ii=0; ii<pOB->nExpr; ii++){ |
| 149009 | Expr *pExpr = sqlite3ExprSkipCollateAndLikely(pOB->a[ii].pExpr); |
| 149010 | if( NEVER(pExpr==0) ) continue; |
| 149011 | if( pExpr->op==TK_COLUMN && pExpr->iTable==iCursor ){ |
| 149012 | if( pExpr->iColumn<0 ) return 1; |
| 149013 | for(jj=0; jj<pIndex->nKeyCol; jj++){ |
| 149014 | if( pExpr->iColumn==pIndex->aiColumn[jj] ) return 1; |
| 149015 | } |
| @@ -149847,10 +149984,11 @@ | |
| 149984 | ** loops. |
| 149985 | */ |
| 149986 | for(i=0; i<nOrderBy; i++){ |
| 149987 | if( MASKBIT(i) & obSat ) continue; |
| 149988 | pOBExpr = sqlite3ExprSkipCollateAndLikely(pOrderBy->a[i].pExpr); |
| 149989 | if( NEVER(pOBExpr==0) ) continue; |
| 149990 | if( pOBExpr->op!=TK_COLUMN ) continue; |
| 149991 | if( pOBExpr->iTable!=iCur ) continue; |
| 149992 | pTerm = sqlite3WhereFindTerm(&pWInfo->sWC, iCur, pOBExpr->iColumn, |
| 149993 | ~ready, eqOpMask, 0); |
| 149994 | if( pTerm==0 ) continue; |
| @@ -149973,10 +150111,11 @@ | |
| 150111 | for(i=0; bOnce && i<nOrderBy; i++){ |
| 150112 | if( MASKBIT(i) & obSat ) continue; |
| 150113 | pOBExpr = sqlite3ExprSkipCollateAndLikely(pOrderBy->a[i].pExpr); |
| 150114 | testcase( wctrlFlags & WHERE_GROUPBY ); |
| 150115 | testcase( wctrlFlags & WHERE_DISTINCTBY ); |
| 150116 | if( NEVER(pOBExpr==0) ) continue; |
| 150117 | if( (wctrlFlags & (WHERE_GROUPBY|WHERE_DISTINCTBY))==0 ) bOnce = 0; |
| 150118 | if( iColumn>=XN_ROWID ){ |
| 150119 | if( pOBExpr->op!=TK_COLUMN ) continue; |
| 150120 | if( pOBExpr->iTable!=iCur ) continue; |
| 150121 | if( pOBExpr->iColumn!=iColumn ) continue; |
| @@ -150136,11 +150275,11 @@ | |
| 150275 | rScale = sqlite3LogEst((nOrderBy-nSorted)*100/nOrderBy) - 66; |
| 150276 | rSortCost = nRow + rScale + 16; |
| 150277 | |
| 150278 | /* Multiple by log(M) where M is the number of output rows. |
| 150279 | ** Use the LIMIT for M if it is smaller. Or if this sort is for |
| 150280 | ** a DISTINCT operator, M will be the number of distinct output |
| 150281 | ** rows, so fudge it downwards a bit. |
| 150282 | */ |
| 150283 | if( (pWInfo->wctrlFlags & WHERE_USE_LIMIT)!=0 && pWInfo->iLimit<nRow ){ |
| 150284 | nRow = pWInfo->iLimit; |
| 150285 | }else if( (pWInfo->wctrlFlags & WHERE_WANT_DISTINCT) ){ |
| @@ -194220,11 +194359,11 @@ | |
| 194359 | p->aSegment = (GeoSegment*)&p->aEvent[nVertex*2]; |
| 194360 | p->nEvent = p->nSegment = 0; |
| 194361 | geopolyAddSegments(p, p1, 1); |
| 194362 | geopolyAddSegments(p, p2, 2); |
| 194363 | pThisEvent = geopolySortEventsByX(p->aEvent, p->nEvent); |
| 194364 | rX = pThisEvent && pThisEvent->x==0.0 ? -1.0 : 0.0; |
| 194365 | memset(aOverlap, 0, sizeof(aOverlap)); |
| 194366 | while( pThisEvent ){ |
| 194367 | if( pThisEvent->x!=rX ){ |
| 194368 | GeoSegment *pPrev = 0; |
| 194369 | int iMask = 0; |
| @@ -212129,11 +212268,11 @@ | |
| 212268 | Fts5Bm25Data **ppData /* OUT: bm25-data object for this query */ |
| 212269 | ){ |
| 212270 | int rc = SQLITE_OK; /* Return code */ |
| 212271 | Fts5Bm25Data *p; /* Object to return */ |
| 212272 | |
| 212273 | p = (Fts5Bm25Data*)pApi->xGetAuxdata(pFts, 0); |
| 212274 | if( p==0 ){ |
| 212275 | int nPhrase; /* Number of phrases in query */ |
| 212276 | sqlite3_int64 nRow = 0; /* Number of rows in table */ |
| 212277 | sqlite3_int64 nToken = 0; /* Number of tokens in table */ |
| 212278 | sqlite3_int64 nByte; /* Bytes of space to allocate */ |
| @@ -212203,11 +212342,11 @@ | |
| 212342 | int nVal, /* Number of values in apVal[] array */ |
| 212343 | sqlite3_value **apVal /* Array of trailing arguments */ |
| 212344 | ){ |
| 212345 | const double k1 = 1.2; /* Constant "k1" from BM25 formula */ |
| 212346 | const double b = 0.75; /* Constant "b" from BM25 formula */ |
| 212347 | int rc; /* Error code */ |
| 212348 | double score = 0.0; /* SQL function return value */ |
| 212349 | Fts5Bm25Data *pData; /* Values allocated/calculated once only */ |
| 212350 | int i; /* Iterator variable */ |
| 212351 | int nInst = 0; /* Value returned by xInstCount() */ |
| 212352 | double D = 0.0; /* Total number of tokens in row */ |
| @@ -212235,21 +212374,19 @@ | |
| 212374 | int nTok; |
| 212375 | rc = pApi->xColumnSize(pFts, -1, &nTok); |
| 212376 | D = (double)nTok; |
| 212377 | } |
| 212378 | |
| 212379 | /* Determine and return the BM25 score for the current row. Or, if an |
| 212380 | ** error has occurred, throw an exception. */ |
| 212381 | if( rc==SQLITE_OK ){ |
| 212382 | for(i=0; i<pData->nPhrase; i++){ |
| 212383 | score += pData->aIDF[i] * ( |
| 212384 | ( aFreq[i] * (k1 + 1.0) ) / |
| 212385 | ( aFreq[i] + k1 * (1 - b + b * D / pData->avgdl) ) |
| 212386 | ); |
| 212387 | } |
| 212388 | sqlite3_result_double(pCtx, -1.0 * score); |
| 212389 | }else{ |
| 212390 | sqlite3_result_error_code(pCtx, rc); |
| 212391 | } |
| 212392 | } |
| @@ -223384,10 +223521,11 @@ | |
| 223521 | cksum2 ^= sqlite3Fts5IndexEntryCksum(iRowid, 0, 0, -1, z, n); |
| 223522 | } |
| 223523 | }else{ |
| 223524 | poslist.n = 0; |
| 223525 | fts5SegiterPoslist(p, &pIter->aSeg[pIter->aFirst[1].iFirst], 0, &poslist); |
| 223526 | fts5BufferAppendBlob(&p->rc, &poslist, 4, (const u8*)"\0\0\0\0"); |
| 223527 | while( 0==sqlite3Fts5PoslistNext64(poslist.p, poslist.n, &iOff, &iPos) ){ |
| 223528 | int iCol = FTS5_POS2COLUMN(iPos); |
| 223529 | int iTokOff = FTS5_POS2OFFSET(iPos); |
| 223530 | cksum2 ^= sqlite3Fts5IndexEntryCksum(iRowid, iCol, iTokOff, -1, z, n); |
| 223531 | } |
| @@ -226679,11 +226817,11 @@ | |
| 226817 | int nArg, /* Number of args */ |
| 226818 | sqlite3_value **apUnused /* Function arguments */ |
| 226819 | ){ |
| 226820 | assert( nArg==0 ); |
| 226821 | UNUSED_PARAM2(nArg, apUnused); |
| 226822 | sqlite3_result_text(pCtx, "fts5: 2020-12-01 16:14:00 a26b6597e3ae272231b96f9982c3bcc17ddec2f2b6eb4df06a224b91089fed5b", -1, SQLITE_TRANSIENT); |
| 226823 | } |
| 226824 | |
| 226825 | /* |
| 226826 | ** Return true if zName is the extension on one of the shadow tables used |
| 226827 | ** by this module. |
| @@ -229252,17 +229390,18 @@ | |
| 229390 | |
| 229391 | /* |
| 229392 | ** Allocate a trigram tokenizer. |
| 229393 | */ |
| 229394 | static int fts5TriCreate( |
| 229395 | void *pUnused, |
| 229396 | const char **azArg, |
| 229397 | int nArg, |
| 229398 | Fts5Tokenizer **ppOut |
| 229399 | ){ |
| 229400 | int rc = SQLITE_OK; |
| 229401 | TrigramTokenizer *pNew = (TrigramTokenizer*)sqlite3_malloc(sizeof(*pNew)); |
| 229402 | UNUSED_PARAM(pUnused); |
| 229403 | if( pNew==0 ){ |
| 229404 | rc = SQLITE_NOMEM; |
| 229405 | }else{ |
| 229406 | int i; |
| 229407 | pNew->bFold = 1; |
| @@ -229291,11 +229430,11 @@ | |
| 229430 | ** Trigram tokenizer tokenize routine. |
| 229431 | */ |
| 229432 | static int fts5TriTokenize( |
| 229433 | Fts5Tokenizer *pTok, |
| 229434 | void *pCtx, |
| 229435 | int unusedFlags, |
| 229436 | const char *pText, int nText, |
| 229437 | int (*xToken)(void*, int, const char*, int, int, int) |
| 229438 | ){ |
| 229439 | TrigramTokenizer *p = (TrigramTokenizer*)pTok; |
| 229440 | int rc = SQLITE_OK; |
| @@ -229302,10 +229441,11 @@ | |
| 229441 | char aBuf[32]; |
| 229442 | const unsigned char *zIn = (const unsigned char*)pText; |
| 229443 | const unsigned char *zEof = &zIn[nText]; |
| 229444 | u32 iCode; |
| 229445 | |
| 229446 | UNUSED_PARAM(unusedFlags); |
| 229447 | while( 1 ){ |
| 229448 | char *zOut = aBuf; |
| 229449 | int iStart = zIn - (const unsigned char*)pText; |
| 229450 | const unsigned char *zNext; |
| 229451 | |
| @@ -230164,10 +230304,11 @@ | |
| 230304 | } |
| 230305 | iTbl++; |
| 230306 | } |
| 230307 | aAscii[0] = 0; /* 0x00 is never a token character */ |
| 230308 | } |
| 230309 | |
| 230310 | |
| 230311 | /* |
| 230312 | ** 2015 May 30 |
| 230313 | ** |
| 230314 | ** The author disclaims copyright to this source code. In place of |
| @@ -231602,12 +231743,12 @@ | |
| 231743 | } |
| 231744 | #endif /* SQLITE_CORE */ |
| 231745 | #endif /* !defined(SQLITE_CORE) || defined(SQLITE_ENABLE_STMTVTAB) */ |
| 231746 | |
| 231747 | /************** End of stmt.c ************************************************/ |
| 231748 | #if __LINE__!=231748 |
| 231749 | #undef SQLITE_SOURCE_ID |
| 231750 | #define SQLITE_SOURCE_ID "2020-12-01 16:14:00 a26b6597e3ae272231b96f9982c3bcc17ddec2f2b6eb4df06a224b91089falt2" |
| 231751 | #endif |
| 231752 | /* Return the source-id for this library */ |
| 231753 | SQLITE_API const char *sqlite3_sourceid(void){ return SQLITE_SOURCE_ID; } |
| 231754 | /************************** End of sqlite3.c ******************************/ |
| 231755 |
+5
-4
| --- src/sqlite3.h | ||
| +++ src/sqlite3.h | ||
| @@ -123,11 +123,11 @@ | ||
| 123 | 123 | ** [sqlite3_libversion_number()], [sqlite3_sourceid()], |
| 124 | 124 | ** [sqlite_version()] and [sqlite_source_id()]. |
| 125 | 125 | */ |
| 126 | 126 | #define SQLITE_VERSION "3.34.0" |
| 127 | 127 | #define SQLITE_VERSION_NUMBER 3034000 |
| 128 | -#define SQLITE_SOURCE_ID "2020-10-31 18:58:37 7d01e84dc49074e6364267eea9fd20d46a457d2498121a0f218fbf482692392d" | |
| 128 | +#define SQLITE_SOURCE_ID "2020-12-01 16:14:00 a26b6597e3ae272231b96f9982c3bcc17ddec2f2b6eb4df06a224b91089fed5b" | |
| 129 | 129 | |
| 130 | 130 | /* |
| 131 | 131 | ** CAPI3REF: Run-Time Library Version Numbers |
| 132 | 132 | ** KEYWORDS: sqlite3_version sqlite3_sourceid |
| 133 | 133 | ** |
| @@ -502,10 +502,11 @@ | ||
| 502 | 502 | #define SQLITE_IOERR_AUTH (SQLITE_IOERR | (28<<8)) |
| 503 | 503 | #define SQLITE_IOERR_BEGIN_ATOMIC (SQLITE_IOERR | (29<<8)) |
| 504 | 504 | #define SQLITE_IOERR_COMMIT_ATOMIC (SQLITE_IOERR | (30<<8)) |
| 505 | 505 | #define SQLITE_IOERR_ROLLBACK_ATOMIC (SQLITE_IOERR | (31<<8)) |
| 506 | 506 | #define SQLITE_IOERR_DATA (SQLITE_IOERR | (32<<8)) |
| 507 | +#define SQLITE_IOERR_CORRUPTFS (SQLITE_IOERR | (33<<8)) | |
| 507 | 508 | #define SQLITE_LOCKED_SHAREDCACHE (SQLITE_LOCKED | (1<<8)) |
| 508 | 509 | #define SQLITE_LOCKED_VTAB (SQLITE_LOCKED | (2<<8)) |
| 509 | 510 | #define SQLITE_BUSY_RECOVERY (SQLITE_BUSY | (1<<8)) |
| 510 | 511 | #define SQLITE_BUSY_SNAPSHOT (SQLITE_BUSY | (2<<8)) |
| 511 | 512 | #define SQLITE_BUSY_TIMEOUT (SQLITE_BUSY | (3<<8)) |
| @@ -6190,11 +6191,11 @@ | ||
| 6190 | 6191 | ** CAPI3REF: Determine the transaction state of a database |
| 6191 | 6192 | ** METHOD: sqlite3 |
| 6192 | 6193 | ** |
| 6193 | 6194 | ** ^The sqlite3_txn_state(D,S) interface returns the current |
| 6194 | 6195 | ** [transaction state] of schema S in database connection D. ^If S is NULL, |
| 6195 | -** then the highest transaction state of any schema on databse connection D | |
| 6196 | +** then the highest transaction state of any schema on database connection D | |
| 6196 | 6197 | ** is returned. Transaction states are (in order of lowest to highest): |
| 6197 | 6198 | ** <ol> |
| 6198 | 6199 | ** <li value="0"> SQLITE_TXN_NONE |
| 6199 | 6200 | ** <li value="1"> SQLITE_TXN_READ |
| 6200 | 6201 | ** <li value="2"> SQLITE_TXN_WRITE |
| @@ -7737,11 +7738,10 @@ | ||
| 7737 | 7738 | */ |
| 7738 | 7739 | #define SQLITE_TESTCTRL_FIRST 5 |
| 7739 | 7740 | #define SQLITE_TESTCTRL_PRNG_SAVE 5 |
| 7740 | 7741 | #define SQLITE_TESTCTRL_PRNG_RESTORE 6 |
| 7741 | 7742 | #define SQLITE_TESTCTRL_PRNG_RESET 7 /* NOT USED */ |
| 7742 | -#define SQLITE_TESTCTRL_SEEK_COUNT 7 | |
| 7743 | 7743 | #define SQLITE_TESTCTRL_BITVEC_TEST 8 |
| 7744 | 7744 | #define SQLITE_TESTCTRL_FAULT_INSTALL 9 |
| 7745 | 7745 | #define SQLITE_TESTCTRL_BENIGN_MALLOC_HOOKS 10 |
| 7746 | 7746 | #define SQLITE_TESTCTRL_PENDING_BYTE 11 |
| 7747 | 7747 | #define SQLITE_TESTCTRL_ASSERT 12 |
| @@ -7762,11 +7762,12 @@ | ||
| 7762 | 7762 | #define SQLITE_TESTCTRL_IMPOSTER 25 |
| 7763 | 7763 | #define SQLITE_TESTCTRL_PARSER_COVERAGE 26 |
| 7764 | 7764 | #define SQLITE_TESTCTRL_RESULT_INTREAL 27 |
| 7765 | 7765 | #define SQLITE_TESTCTRL_PRNG_SEED 28 |
| 7766 | 7766 | #define SQLITE_TESTCTRL_EXTRA_SCHEMA_CHECKS 29 |
| 7767 | -#define SQLITE_TESTCTRL_LAST 29 /* Largest TESTCTRL */ | |
| 7767 | +#define SQLITE_TESTCTRL_SEEK_COUNT 30 | |
| 7768 | +#define SQLITE_TESTCTRL_LAST 30 /* Largest TESTCTRL */ | |
| 7768 | 7769 | |
| 7769 | 7770 | /* |
| 7770 | 7771 | ** CAPI3REF: SQL Keyword Checking |
| 7771 | 7772 | ** |
| 7772 | 7773 | ** These routines provide access to the set of SQL language keywords |
| 7773 | 7774 |
| --- src/sqlite3.h | |
| +++ src/sqlite3.h | |
| @@ -123,11 +123,11 @@ | |
| 123 | ** [sqlite3_libversion_number()], [sqlite3_sourceid()], |
| 124 | ** [sqlite_version()] and [sqlite_source_id()]. |
| 125 | */ |
| 126 | #define SQLITE_VERSION "3.34.0" |
| 127 | #define SQLITE_VERSION_NUMBER 3034000 |
| 128 | #define SQLITE_SOURCE_ID "2020-10-31 18:58:37 7d01e84dc49074e6364267eea9fd20d46a457d2498121a0f218fbf482692392d" |
| 129 | |
| 130 | /* |
| 131 | ** CAPI3REF: Run-Time Library Version Numbers |
| 132 | ** KEYWORDS: sqlite3_version sqlite3_sourceid |
| 133 | ** |
| @@ -502,10 +502,11 @@ | |
| 502 | #define SQLITE_IOERR_AUTH (SQLITE_IOERR | (28<<8)) |
| 503 | #define SQLITE_IOERR_BEGIN_ATOMIC (SQLITE_IOERR | (29<<8)) |
| 504 | #define SQLITE_IOERR_COMMIT_ATOMIC (SQLITE_IOERR | (30<<8)) |
| 505 | #define SQLITE_IOERR_ROLLBACK_ATOMIC (SQLITE_IOERR | (31<<8)) |
| 506 | #define SQLITE_IOERR_DATA (SQLITE_IOERR | (32<<8)) |
| 507 | #define SQLITE_LOCKED_SHAREDCACHE (SQLITE_LOCKED | (1<<8)) |
| 508 | #define SQLITE_LOCKED_VTAB (SQLITE_LOCKED | (2<<8)) |
| 509 | #define SQLITE_BUSY_RECOVERY (SQLITE_BUSY | (1<<8)) |
| 510 | #define SQLITE_BUSY_SNAPSHOT (SQLITE_BUSY | (2<<8)) |
| 511 | #define SQLITE_BUSY_TIMEOUT (SQLITE_BUSY | (3<<8)) |
| @@ -6190,11 +6191,11 @@ | |
| 6190 | ** CAPI3REF: Determine the transaction state of a database |
| 6191 | ** METHOD: sqlite3 |
| 6192 | ** |
| 6193 | ** ^The sqlite3_txn_state(D,S) interface returns the current |
| 6194 | ** [transaction state] of schema S in database connection D. ^If S is NULL, |
| 6195 | ** then the highest transaction state of any schema on databse connection D |
| 6196 | ** is returned. Transaction states are (in order of lowest to highest): |
| 6197 | ** <ol> |
| 6198 | ** <li value="0"> SQLITE_TXN_NONE |
| 6199 | ** <li value="1"> SQLITE_TXN_READ |
| 6200 | ** <li value="2"> SQLITE_TXN_WRITE |
| @@ -7737,11 +7738,10 @@ | |
| 7737 | */ |
| 7738 | #define SQLITE_TESTCTRL_FIRST 5 |
| 7739 | #define SQLITE_TESTCTRL_PRNG_SAVE 5 |
| 7740 | #define SQLITE_TESTCTRL_PRNG_RESTORE 6 |
| 7741 | #define SQLITE_TESTCTRL_PRNG_RESET 7 /* NOT USED */ |
| 7742 | #define SQLITE_TESTCTRL_SEEK_COUNT 7 |
| 7743 | #define SQLITE_TESTCTRL_BITVEC_TEST 8 |
| 7744 | #define SQLITE_TESTCTRL_FAULT_INSTALL 9 |
| 7745 | #define SQLITE_TESTCTRL_BENIGN_MALLOC_HOOKS 10 |
| 7746 | #define SQLITE_TESTCTRL_PENDING_BYTE 11 |
| 7747 | #define SQLITE_TESTCTRL_ASSERT 12 |
| @@ -7762,11 +7762,12 @@ | |
| 7762 | #define SQLITE_TESTCTRL_IMPOSTER 25 |
| 7763 | #define SQLITE_TESTCTRL_PARSER_COVERAGE 26 |
| 7764 | #define SQLITE_TESTCTRL_RESULT_INTREAL 27 |
| 7765 | #define SQLITE_TESTCTRL_PRNG_SEED 28 |
| 7766 | #define SQLITE_TESTCTRL_EXTRA_SCHEMA_CHECKS 29 |
| 7767 | #define SQLITE_TESTCTRL_LAST 29 /* Largest TESTCTRL */ |
| 7768 | |
| 7769 | /* |
| 7770 | ** CAPI3REF: SQL Keyword Checking |
| 7771 | ** |
| 7772 | ** These routines provide access to the set of SQL language keywords |
| 7773 |
| --- src/sqlite3.h | |
| +++ src/sqlite3.h | |
| @@ -123,11 +123,11 @@ | |
| 123 | ** [sqlite3_libversion_number()], [sqlite3_sourceid()], |
| 124 | ** [sqlite_version()] and [sqlite_source_id()]. |
| 125 | */ |
| 126 | #define SQLITE_VERSION "3.34.0" |
| 127 | #define SQLITE_VERSION_NUMBER 3034000 |
| 128 | #define SQLITE_SOURCE_ID "2020-12-01 16:14:00 a26b6597e3ae272231b96f9982c3bcc17ddec2f2b6eb4df06a224b91089fed5b" |
| 129 | |
| 130 | /* |
| 131 | ** CAPI3REF: Run-Time Library Version Numbers |
| 132 | ** KEYWORDS: sqlite3_version sqlite3_sourceid |
| 133 | ** |
| @@ -502,10 +502,11 @@ | |
| 502 | #define SQLITE_IOERR_AUTH (SQLITE_IOERR | (28<<8)) |
| 503 | #define SQLITE_IOERR_BEGIN_ATOMIC (SQLITE_IOERR | (29<<8)) |
| 504 | #define SQLITE_IOERR_COMMIT_ATOMIC (SQLITE_IOERR | (30<<8)) |
| 505 | #define SQLITE_IOERR_ROLLBACK_ATOMIC (SQLITE_IOERR | (31<<8)) |
| 506 | #define SQLITE_IOERR_DATA (SQLITE_IOERR | (32<<8)) |
| 507 | #define SQLITE_IOERR_CORRUPTFS (SQLITE_IOERR | (33<<8)) |
| 508 | #define SQLITE_LOCKED_SHAREDCACHE (SQLITE_LOCKED | (1<<8)) |
| 509 | #define SQLITE_LOCKED_VTAB (SQLITE_LOCKED | (2<<8)) |
| 510 | #define SQLITE_BUSY_RECOVERY (SQLITE_BUSY | (1<<8)) |
| 511 | #define SQLITE_BUSY_SNAPSHOT (SQLITE_BUSY | (2<<8)) |
| 512 | #define SQLITE_BUSY_TIMEOUT (SQLITE_BUSY | (3<<8)) |
| @@ -6190,11 +6191,11 @@ | |
| 6191 | ** CAPI3REF: Determine the transaction state of a database |
| 6192 | ** METHOD: sqlite3 |
| 6193 | ** |
| 6194 | ** ^The sqlite3_txn_state(D,S) interface returns the current |
| 6195 | ** [transaction state] of schema S in database connection D. ^If S is NULL, |
| 6196 | ** then the highest transaction state of any schema on database connection D |
| 6197 | ** is returned. Transaction states are (in order of lowest to highest): |
| 6198 | ** <ol> |
| 6199 | ** <li value="0"> SQLITE_TXN_NONE |
| 6200 | ** <li value="1"> SQLITE_TXN_READ |
| 6201 | ** <li value="2"> SQLITE_TXN_WRITE |
| @@ -7737,11 +7738,10 @@ | |
| 7738 | */ |
| 7739 | #define SQLITE_TESTCTRL_FIRST 5 |
| 7740 | #define SQLITE_TESTCTRL_PRNG_SAVE 5 |
| 7741 | #define SQLITE_TESTCTRL_PRNG_RESTORE 6 |
| 7742 | #define SQLITE_TESTCTRL_PRNG_RESET 7 /* NOT USED */ |
| 7743 | #define SQLITE_TESTCTRL_BITVEC_TEST 8 |
| 7744 | #define SQLITE_TESTCTRL_FAULT_INSTALL 9 |
| 7745 | #define SQLITE_TESTCTRL_BENIGN_MALLOC_HOOKS 10 |
| 7746 | #define SQLITE_TESTCTRL_PENDING_BYTE 11 |
| 7747 | #define SQLITE_TESTCTRL_ASSERT 12 |
| @@ -7762,11 +7762,12 @@ | |
| 7762 | #define SQLITE_TESTCTRL_IMPOSTER 25 |
| 7763 | #define SQLITE_TESTCTRL_PARSER_COVERAGE 26 |
| 7764 | #define SQLITE_TESTCTRL_RESULT_INTREAL 27 |
| 7765 | #define SQLITE_TESTCTRL_PRNG_SEED 28 |
| 7766 | #define SQLITE_TESTCTRL_EXTRA_SCHEMA_CHECKS 29 |
| 7767 | #define SQLITE_TESTCTRL_SEEK_COUNT 30 |
| 7768 | #define SQLITE_TESTCTRL_LAST 30 /* Largest TESTCTRL */ |
| 7769 | |
| 7770 | /* |
| 7771 | ** CAPI3REF: SQL Keyword Checking |
| 7772 | ** |
| 7773 | ** These routines provide access to the set of SQL language keywords |
| 7774 |
+4
-2
| --- src/style.c | ||
| +++ src/style.c | ||
| @@ -1204,14 +1204,16 @@ | ||
| 1204 | 1204 | @ <pre> |
| 1205 | 1205 | @ %h(blob_str(&g.httpHeader)) |
| 1206 | 1206 | @ </pre> |
| 1207 | 1207 | } |
| 1208 | 1208 | } |
| 1209 | - style_finish_page("error"); | |
| 1210 | - if( zErr ){ | |
| 1209 | + if( zErr && zErr[0] ){ | |
| 1210 | + style_finish_page("error"); | |
| 1211 | 1211 | cgi_reply(); |
| 1212 | 1212 | fossil_exit(1); |
| 1213 | + }else{ | |
| 1214 | + style_finish_page("test"); | |
| 1213 | 1215 | } |
| 1214 | 1216 | } |
| 1215 | 1217 | |
| 1216 | 1218 | /* |
| 1217 | 1219 | ** Generate a Not Yet Implemented error page. |
| 1218 | 1220 |
| --- src/style.c | |
| +++ src/style.c | |
| @@ -1204,14 +1204,16 @@ | |
| 1204 | @ <pre> |
| 1205 | @ %h(blob_str(&g.httpHeader)) |
| 1206 | @ </pre> |
| 1207 | } |
| 1208 | } |
| 1209 | style_finish_page("error"); |
| 1210 | if( zErr ){ |
| 1211 | cgi_reply(); |
| 1212 | fossil_exit(1); |
| 1213 | } |
| 1214 | } |
| 1215 | |
| 1216 | /* |
| 1217 | ** Generate a Not Yet Implemented error page. |
| 1218 |
| --- src/style.c | |
| +++ src/style.c | |
| @@ -1204,14 +1204,16 @@ | |
| 1204 | @ <pre> |
| 1205 | @ %h(blob_str(&g.httpHeader)) |
| 1206 | @ </pre> |
| 1207 | } |
| 1208 | } |
| 1209 | if( zErr && zErr[0] ){ |
| 1210 | style_finish_page("error"); |
| 1211 | cgi_reply(); |
| 1212 | fossil_exit(1); |
| 1213 | }else{ |
| 1214 | style_finish_page("test"); |
| 1215 | } |
| 1216 | } |
| 1217 | |
| 1218 | /* |
| 1219 | ** Generate a Not Yet Implemented error page. |
| 1220 |
+126
-24
| --- src/timeline.c | ||
| +++ src/timeline.c | ||
| @@ -595,25 +595,32 @@ | ||
| 595 | 595 | if( zType[0]!='c' ){ |
| 596 | 596 | /* Comments for anything other than a check-in are generated by |
| 597 | 597 | ** "fossil rebuild" and expect to be rendered as text/x-fossil-wiki */ |
| 598 | 598 | if( zType[0]=='w' ){ |
| 599 | 599 | const char *zCom = blob_str(&comment); |
| 600 | - char *zWiki; | |
| 600 | + /* Except, the comments generated by "fossil rebuild" for a wiki | |
| 601 | + ** page edit consist of a single character '-', '+', or ':' (to | |
| 602 | + ** indicate "deleted", "added", or "edited") followed by the | |
| 603 | + ** raw wiki page name. We have to generate an appropriate | |
| 604 | + ** comment on-the-fly | |
| 605 | + */ | |
| 601 | 606 | wiki_hyperlink_override(zUuid); |
| 602 | - if( (tmFlags & TIMELINE_REFS)!=0 | |
| 603 | - && (zWiki = strstr(zCom,"wiki"))!=0 | |
| 604 | - ){ | |
| 605 | - /* The TIMELINE_REFS flag causes timeline comments of the | |
| 606 | - ** form "Changes to wiki..." or "Added wiki" to be changed | |
| 607 | - ** into just "Wiki..." */ | |
| 608 | - Blob rcom; | |
| 609 | - blob_init(&rcom, 0, 0); | |
| 610 | - blob_appendf(&rcom, "W%s", zWiki+1); | |
| 611 | - wiki_convert(&rcom, 0, WIKI_INLINE); | |
| 612 | - blob_reset(&rcom); | |
| 607 | + if( zCom[0]=='-' ){ | |
| 608 | + @ Deleted wiki page "%z(href("%R/whistory?name=%t",zCom+1))\ | |
| 609 | + @ %h(zCom+1)</a>" | |
| 610 | + }else if( (tmFlags & TIMELINE_REFS)!=0 | |
| 611 | + && (zCom[0]=='+' || zCom[0]==':') ){ | |
| 612 | + @ Wiki page "%z(href("%R/wiki?name=%t",zCom+1))%h(zCom+1)</a>" | |
| 613 | + }else if( zCom[0]=='+' ){ | |
| 614 | + @ Added wiki page "%z(href("%R/wiki?name=%t",zCom+1))%h(zCom+1)</a>" | |
| 615 | + }else if( zCom[0]==':' ){ | |
| 616 | + @ Changes to wiki page "%z(href("%R/wiki?name=%t",zCom+1))\ | |
| 617 | + @ %h(zCom+1)</a>" | |
| 613 | 618 | }else{ |
| 614 | - wiki_convert(&comment, 0, WIKI_INLINE); | |
| 619 | + /* Legacy EVENT table entry that needs to be rebuilt */ | |
| 620 | + @ Changes to a wiki page → Obsolete EVENT table information. | |
| 621 | + @ Run "fossil rebuild" on the repository. | |
| 615 | 622 | } |
| 616 | 623 | wiki_hyperlink_override(0); |
| 617 | 624 | }else{ |
| 618 | 625 | wiki_convert(&comment, 0, WIKI_INLINE); |
| 619 | 626 | } |
| @@ -1727,19 +1734,26 @@ | ||
| 1727 | 1734 | int you_rid = name_to_typed_rid(P("you"),"ci");/* you= for common ancst */ |
| 1728 | 1735 | int pd_rid; |
| 1729 | 1736 | double rBefore, rAfter, rCirca; /* Boundary times */ |
| 1730 | 1737 | const char *z; |
| 1731 | 1738 | char *zOlderButton = 0; /* URL for Older button at the bottom */ |
| 1739 | + char *zOlderButtonLabel = 0; /* Label for the Older Button */ | |
| 1732 | 1740 | char *zNewerButton = 0; /* URL for Newer button at the top */ |
| 1741 | + char *zNewerButtonLabel = 0; /* Label for the Newer button */ | |
| 1733 | 1742 | int selectedRid = 0; /* Show a highlight on this RID */ |
| 1734 | 1743 | int secondaryRid = 0; /* Show secondary highlight */ |
| 1735 | 1744 | int disableY = 0; /* Disable type selector on submenu */ |
| 1736 | 1745 | int advancedMenu = 0; /* Use the advanced menu design */ |
| 1737 | 1746 | char *zPlural; /* Ending for plural forms */ |
| 1738 | 1747 | int showCherrypicks = 1; /* True to show cherrypick merges */ |
| 1748 | + int haveParameterN; /* True if n= query parameter present */ | |
| 1749 | + | |
| 1750 | + url_initialize(&url, "timeline"); | |
| 1751 | + cgi_query_parameters_to_url(&url); | |
| 1739 | 1752 | |
| 1740 | 1753 | /* Set number of rows to display */ |
| 1754 | + haveParameterN = P("n")!=0; | |
| 1741 | 1755 | cookie_read_parameter("n","n"); |
| 1742 | 1756 | z = P("n"); |
| 1743 | 1757 | if( z==0 ) z = db_get("timeline-default-length",0); |
| 1744 | 1758 | if( z ){ |
| 1745 | 1759 | if( fossil_strcmp(z,"all")==0 ){ |
| @@ -1797,12 +1811,10 @@ | ||
| 1797 | 1811 | } |
| 1798 | 1812 | if( zType[0]=='a' || zType[0]=='c' ){ |
| 1799 | 1813 | cookie_write_parameter("y","y",zType); |
| 1800 | 1814 | } |
| 1801 | 1815 | cookie_render(); |
| 1802 | - url_initialize(&url, "timeline"); | |
| 1803 | - cgi_query_parameters_to_url(&url); | |
| 1804 | 1816 | |
| 1805 | 1817 | /* Convert the cf=FILEHASH query parameter into a c=CHECKINHASH value */ |
| 1806 | 1818 | if( P("cf")!=0 ){ |
| 1807 | 1819 | zCirca = db_text(0, |
| 1808 | 1820 | "SELECT (SELECT uuid FROM blob WHERE rid=mlink.mid)" |
| @@ -2064,11 +2076,10 @@ | ||
| 2064 | 2076 | " WHERE mlink.mid=x" |
| 2065 | 2077 | " AND mlink.fnid=filename.fnid AND %s)", |
| 2066 | 2078 | glob_expr("filename.name", zChng) |
| 2067 | 2079 | ); |
| 2068 | 2080 | } |
| 2069 | -// tmFlags |= TIMELINE_DISJOINT; | |
| 2070 | 2081 | tmFlags |= TIMELINE_XMERGE | TIMELINE_FILLGAPS; |
| 2071 | 2082 | db_multi_exec("%s", blob_sql_text(&sql)); |
| 2072 | 2083 | if( advancedMenu ){ |
| 2073 | 2084 | style_submenu_checkbox("v", "Files", (zType[0]!='a' && zType[0]!='c'),0); |
| 2074 | 2085 | } |
| @@ -2118,10 +2129,11 @@ | ||
| 2118 | 2129 | db_multi_exec("DELETE FROM ok"); |
| 2119 | 2130 | } |
| 2120 | 2131 | if( p_rid ){ |
| 2121 | 2132 | zBackTo = P("bt"); |
| 2122 | 2133 | ridBackTo = zBackTo ? name_to_typed_rid(zBackTo,"ci") : 0; |
| 2134 | + if( !haveParameterN ) nEntry = 0; | |
| 2123 | 2135 | compute_ancestors(p_rid, nEntry==0 ? 0 : nEntry+1, 0, ridBackTo); |
| 2124 | 2136 | np = db_int(0, "SELECT count(*)-1 FROM ok"); |
| 2125 | 2137 | if( np>0 || nd==0 ){ |
| 2126 | 2138 | if( nd>0 ) blob_appendf(&desc, " and "); |
| 2127 | 2139 | blob_appendf(&desc, "%d ancestor%s", np, (1==np)?"":"s"); |
| @@ -2216,27 +2228,57 @@ | ||
| 2216 | 2228 | } |
| 2217 | 2229 | if( bisectLocal || zBisect!=0 ){ |
| 2218 | 2230 | blob_append_sql(&cond, " AND event.objid IN (SELECT rid FROM bilog) "); |
| 2219 | 2231 | } |
| 2220 | 2232 | if( zYearMonth ){ |
| 2233 | + char *zNext; | |
| 2221 | 2234 | zYearMonth = timeline_expand_datetime(zYearMonth); |
| 2235 | + if( strlen(zYearMonth)>7 ){ | |
| 2236 | + zYearMonth = mprintf("%.7s", zYearMonth); | |
| 2237 | + } | |
| 2238 | + if( db_int(0,"SELECT julianday('%q-01') IS NULL", zYearMonth) ){ | |
| 2239 | + zYearMonth = db_text(0, "SELECT strftime('%%Y-%%m','now');"); | |
| 2240 | + } | |
| 2241 | + zNext = db_text(0, "SELECT strftime('%%Y-%%m','%q-01','+1 month');", | |
| 2242 | + zYearMonth); | |
| 2243 | + if( db_int(0, | |
| 2244 | + "SELECT EXISTS (SELECT 1 FROM event CROSS JOIN blob" | |
| 2245 | + " WHERE blob.rid=event.objid AND mtime>=julianday('%q-01')%s)", | |
| 2246 | + zNext, blob_sql_text(&cond)) | |
| 2247 | + ){ | |
| 2248 | + zNewerButton = fossil_strdup(url_render(&url, "ym", zNext, 0, 0)); | |
| 2249 | + zNewerButtonLabel = "Following month"; | |
| 2250 | + } | |
| 2251 | + fossil_free(zNext); | |
| 2252 | + zNext = db_text(0, "SELECT strftime('%%Y-%%m','%q-01','-1 month');", | |
| 2253 | + zYearMonth); | |
| 2254 | + if( db_int(0, | |
| 2255 | + "SELECT EXISTS (SELECT 1 FROM event CROSS JOIN blob" | |
| 2256 | + " WHERE blob.rid=event.objid AND mtime<julianday('%q-01')%s)", | |
| 2257 | + zYearMonth, blob_sql_text(&cond)) | |
| 2258 | + ){ | |
| 2259 | + zOlderButton = fossil_strdup(url_render(&url, "ym", zNext, 0, 0)); | |
| 2260 | + zOlderButtonLabel = "Previous month"; | |
| 2261 | + } | |
| 2262 | + fossil_free(zNext); | |
| 2222 | 2263 | blob_append_sql(&cond, " AND %Q=strftime('%%Y-%%m',event.mtime) ", |
| 2223 | 2264 | zYearMonth); |
| 2265 | + nEntry = -1; | |
| 2224 | 2266 | } |
| 2225 | 2267 | else if( zYearWeek ){ |
| 2226 | - char *z; | |
| 2268 | + char *z, *zNext; | |
| 2227 | 2269 | zYearWeek = timeline_expand_datetime(zYearWeek); |
| 2228 | 2270 | z = db_text(0, "SELECT strftime('%%Y-%%W',%Q)", zYearWeek); |
| 2229 | 2271 | if( z && z[0] ){ |
| 2230 | 2272 | zYearWeekStart = db_text(0, "SELECT date(%Q,'-6 days','weekday 1')", |
| 2231 | 2273 | zYearWeek); |
| 2232 | 2274 | zYearWeek = z; |
| 2233 | 2275 | }else{ |
| 2234 | 2276 | if( strlen(zYearWeek)==7 ){ |
| 2235 | 2277 | zYearWeekStart = db_text(0, |
| 2236 | - "SELECT date('%.4q-01-01','+%d days','weekday 1')", | |
| 2237 | - zYearWeek, atoi(zYearWeek+5)*7); | |
| 2278 | + "SELECT date('%.4q-01-01','%+d days','weekday 1')", | |
| 2279 | + zYearWeek, atoi(zYearWeek+5)*7-6); | |
| 2238 | 2280 | }else{ |
| 2239 | 2281 | zYearWeekStart = 0; |
| 2240 | 2282 | } |
| 2241 | 2283 | if( zYearWeekStart==0 || zYearWeekStart[0]==0 ){ |
| 2242 | 2284 | zYearWeekStart = db_text(0, |
| @@ -2243,20 +2285,61 @@ | ||
| 2243 | 2285 | "SELECT date('now','-6 days','weekday 1');"); |
| 2244 | 2286 | zYearWeek = db_text(0, |
| 2245 | 2287 | "SELECT strftime('%%Y-%%W','now','-6 days','weekday 1')"); |
| 2246 | 2288 | } |
| 2247 | 2289 | } |
| 2290 | + zNext = db_text(0, "SELECT date(%Q,'+7 day');", zYearWeekStart); | |
| 2291 | + if( db_int(0, | |
| 2292 | + "SELECT EXISTS (SELECT 1 FROM event CROSS JOIN blob" | |
| 2293 | + " WHERE blob.rid=event.objid AND mtime>=julianday(%Q)%s)", | |
| 2294 | + zNext, blob_sql_text(&cond)) | |
| 2295 | + ){ | |
| 2296 | + zNewerButton = fossil_strdup(url_render(&url, "yw", zNext, 0, 0)); | |
| 2297 | + zNewerButtonLabel = "Following week"; | |
| 2298 | + } | |
| 2299 | + fossil_free(zNext); | |
| 2300 | + zNext = db_text(0, "SELECT date(%Q,'-7 days');", zYearWeekStart); | |
| 2301 | + if( db_int(0, | |
| 2302 | + "SELECT EXISTS (SELECT 1 FROM event CROSS JOIN blob" | |
| 2303 | + " WHERE blob.rid=event.objid AND mtime<julianday(%Q)%s)", | |
| 2304 | + zYearWeekStart, blob_sql_text(&cond)) | |
| 2305 | + ){ | |
| 2306 | + zOlderButton = fossil_strdup(url_render(&url, "yw", zNext, 0, 0)); | |
| 2307 | + zOlderButtonLabel = "Previous week"; | |
| 2308 | + } | |
| 2309 | + fossil_free(zNext); | |
| 2248 | 2310 | blob_append_sql(&cond, " AND %Q=strftime('%%Y-%%W',event.mtime) ", |
| 2249 | 2311 | zYearWeek); |
| 2250 | 2312 | nEntry = -1; |
| 2251 | 2313 | } |
| 2252 | 2314 | else if( zDay ){ |
| 2315 | + char *zNext; | |
| 2253 | 2316 | zDay = timeline_expand_datetime(zDay); |
| 2254 | 2317 | zDay = db_text(0, "SELECT date(%Q)", zDay); |
| 2255 | 2318 | if( zDay==0 || zDay[0]==0 ){ |
| 2256 | 2319 | zDay = db_text(0, "SELECT date('now')"); |
| 2257 | 2320 | } |
| 2321 | + zNext = db_text(0, "SELECT date(%Q,'+1 day');", zDay); | |
| 2322 | + if( db_int(0, | |
| 2323 | + "SELECT EXISTS (SELECT 1 FROM event CROSS JOIN blob" | |
| 2324 | + " WHERE blob.rid=event.objid AND mtime>=julianday(%Q)%s)", | |
| 2325 | + zNext, blob_sql_text(&cond)) | |
| 2326 | + ){ | |
| 2327 | + zNewerButton = fossil_strdup(url_render(&url, "ymd", zNext, 0, 0)); | |
| 2328 | + zNewerButtonLabel = "Following day"; | |
| 2329 | + } | |
| 2330 | + fossil_free(zNext); | |
| 2331 | + zNext = db_text(0, "SELECT date(%Q,'-1 day');", zDay); | |
| 2332 | + if( db_int(0, | |
| 2333 | + "SELECT EXISTS (SELECT 1 FROM event CROSS JOIN blob" | |
| 2334 | + " WHERE blob.rid=event.objid AND mtime<julianday(%Q)%s)", | |
| 2335 | + zDay, blob_sql_text(&cond)) | |
| 2336 | + ){ | |
| 2337 | + zOlderButton = fossil_strdup(url_render(&url, "ymd", zNext, 0, 0)); | |
| 2338 | + zOlderButtonLabel = "Previous day"; | |
| 2339 | + } | |
| 2340 | + fossil_free(zNext); | |
| 2258 | 2341 | blob_append_sql(&cond, " AND %Q=date(event.mtime) ", |
| 2259 | 2342 | zDay); |
| 2260 | 2343 | nEntry = -1; |
| 2261 | 2344 | } |
| 2262 | 2345 | else if( zNDays ){ |
| @@ -2452,11 +2535,12 @@ | ||
| 2452 | 2535 | db_multi_exec("%s", blob_sql_text(&sql)); |
| 2453 | 2536 | |
| 2454 | 2537 | n = db_int(0, "SELECT count(*) FROM timeline WHERE etype!='div' /*scan*/"); |
| 2455 | 2538 | zPlural = n==1 ? "" : "s"; |
| 2456 | 2539 | if( zYearMonth ){ |
| 2457 | - blob_appendf(&desc, "%d %s%s for %h", n, zEType, zPlural, zYearMonth); | |
| 2540 | + blob_appendf(&desc, "%d %s%s for the month beginning %h-01", | |
| 2541 | + n, zEType, zPlural, zYearMonth); | |
| 2458 | 2542 | }else if( zYearWeek ){ |
| 2459 | 2543 | blob_appendf(&desc, "%d %s%s for week %h beginning on %h", |
| 2460 | 2544 | n, zEType, zPlural, zYearWeek, zYearWeekStart); |
| 2461 | 2545 | }else if( zDay ){ |
| 2462 | 2546 | blob_appendf(&desc, "%d %s%s occurring on %h", n, zEType, zPlural, zDay); |
| @@ -2544,10 +2628,11 @@ | ||
| 2544 | 2628 | "SELECT EXISTS (SELECT 1 FROM event CROSS JOIN blob" |
| 2545 | 2629 | " WHERE blob.rid=event.objid AND mtime<=%.17g%s)", |
| 2546 | 2630 | rDate-ONE_SECOND, blob_sql_text(&cond)) |
| 2547 | 2631 | ){ |
| 2548 | 2632 | zOlderButton = fossil_strdup(url_render(&url, "b", zDate, "a", 0)); |
| 2633 | + zOlderButtonLabel = "More"; | |
| 2549 | 2634 | } |
| 2550 | 2635 | free(zDate); |
| 2551 | 2636 | } |
| 2552 | 2637 | zDate = db_text(0, "SELECT max(timestamp) FROM timeline /*scan*/"); |
| 2553 | 2638 | if( (!zDate || !zDate[0]) && ( zAfter || zBefore ) ){ |
| @@ -2559,10 +2644,11 @@ | ||
| 2559 | 2644 | "SELECT EXISTS (SELECT 1 FROM event CROSS JOIN blob" |
| 2560 | 2645 | " WHERE blob.rid=event.objid AND mtime>=%.17g%s)", |
| 2561 | 2646 | rDate+ONE_SECOND, blob_sql_text(&cond)) |
| 2562 | 2647 | ){ |
| 2563 | 2648 | zNewerButton = fossil_strdup(url_render(&url, "a", zDate, "b", 0)); |
| 2649 | + zNewerButtonLabel = "More"; | |
| 2564 | 2650 | } |
| 2565 | 2651 | free(zDate); |
| 2566 | 2652 | } |
| 2567 | 2653 | if( advancedMenu ){ |
| 2568 | 2654 | if( zType[0]=='a' || zType[0]=='c' ){ |
| @@ -2628,17 +2714,19 @@ | ||
| 2628 | 2714 | if( zError ){ |
| 2629 | 2715 | @ <p class="generalError">%h(zError)</p> |
| 2630 | 2716 | } |
| 2631 | 2717 | |
| 2632 | 2718 | if( zNewerButton ){ |
| 2633 | - @ %z(chref("button","%z",zNewerButton))More ↑</a> | |
| 2719 | + @ %z(chref("button","%s",zNewerButton))%h(zNewerButtonLabel)\ | |
| 2720 | + @ ↑</a> | |
| 2634 | 2721 | } |
| 2635 | 2722 | www_print_timeline(&q, tmFlags, zThisUser, zThisTag, zBrName, |
| 2636 | 2723 | selectedRid, secondaryRid, 0); |
| 2637 | 2724 | db_finalize(&q); |
| 2638 | 2725 | if( zOlderButton ){ |
| 2639 | - @ %z(chref("button","%z",zOlderButton))More ↓</a> | |
| 2726 | + @ %z(chref("button","%s",zOlderButton))%h(zOlderButtonLabel)\ | |
| 2727 | + @ ↓</a> | |
| 2640 | 2728 | } |
| 2641 | 2729 | document_emit_js(/*handles pikchrs rendered above*/); |
| 2642 | 2730 | style_finish_page("timeline"); |
| 2643 | 2731 | } |
| 2644 | 2732 | |
| @@ -2662,10 +2750,11 @@ | ||
| 2662 | 2750 | ** 3. Comment string and user |
| 2663 | 2751 | ** 4. Number of non-merge children |
| 2664 | 2752 | ** 5. Number of parents |
| 2665 | 2753 | ** 6. mtime |
| 2666 | 2754 | ** 7. branch |
| 2755 | +** 8. event-type: 'ci', 'w', 't', 'f', and so forth. | |
| 2667 | 2756 | */ |
| 2668 | 2757 | void print_timeline(Stmt *q, int nLimit, int width, int verboseFlag){ |
| 2669 | 2758 | int nAbsLimit = (nLimit >= 0) ? nLimit : -nLimit; |
| 2670 | 2759 | int nLine = 0; |
| 2671 | 2760 | int nEntry = 0; |
| @@ -2686,10 +2775,11 @@ | ||
| 2686 | 2775 | const char *zId = db_column_text(q, 1); |
| 2687 | 2776 | const char *zDate = db_column_text(q, 2); |
| 2688 | 2777 | const char *zCom = db_column_text(q, 3); |
| 2689 | 2778 | int nChild = db_column_int(q, 4); |
| 2690 | 2779 | int nParent = db_column_int(q, 5); |
| 2780 | + const char *zType = db_column_text(q, 8); | |
| 2691 | 2781 | char *zFree = 0; |
| 2692 | 2782 | int n = 0; |
| 2693 | 2783 | char zPrefix[80]; |
| 2694 | 2784 | |
| 2695 | 2785 | if( nAbsLimit!=0 ){ |
| @@ -2729,11 +2819,22 @@ | ||
| 2729 | 2819 | } |
| 2730 | 2820 | if( content_is_private(rid) ){ |
| 2731 | 2821 | sqlite3_snprintf(sizeof(zPrefix)-n, &zPrefix[n], "*UNPUBLISHED* "); |
| 2732 | 2822 | n += strlen(zPrefix+n); |
| 2733 | 2823 | } |
| 2734 | - zFree = mprintf("[%S] %s%s", zId, zPrefix, zCom); | |
| 2824 | + if( zType[0]=='w' && (zCom[0]=='+' || zCom[0]=='-' || zCom[0]==':') ){ | |
| 2825 | + /* Special processing for Wiki comments */ | |
| 2826 | + if( zCom[0]=='+' ){ | |
| 2827 | + zFree = mprintf("[%S] Add wiki page \"%s\"", zId, zCom+1); | |
| 2828 | + }else if( zCom[0]=='-' ){ | |
| 2829 | + zFree = mprintf("[%S] Delete wiki page \"%s\"", zId, zCom+1); | |
| 2830 | + }else{ | |
| 2831 | + zFree = mprintf("[%S] Edit to wiki page \"%s\"", zId, zCom+1); | |
| 2832 | + } | |
| 2833 | + }else{ | |
| 2834 | + zFree = mprintf("[%S] %s%s", zId, zPrefix, zCom); | |
| 2835 | + } | |
| 2735 | 2836 | /* record another X lines */ |
| 2736 | 2837 | nLine += comment_print(zFree, zCom, 9, width, get_comment_format()); |
| 2737 | 2838 | fossil_free(zFree); |
| 2738 | 2839 | |
| 2739 | 2840 | if(verboseFlag){ |
| @@ -2799,11 +2900,12 @@ | ||
| 2799 | 2900 | @ || ')' as comment, |
| 2800 | 2901 | @ (SELECT count(*) FROM plink WHERE pid=blob.rid AND isprim) |
| 2801 | 2902 | @ AS primPlinkCount, |
| 2802 | 2903 | @ (SELECT count(*) FROM plink WHERE cid=blob.rid) AS plinkCount, |
| 2803 | 2904 | @ event.mtime AS mtime, |
| 2804 | - @ tagxref.value AS branch | |
| 2905 | + @ tagxref.value AS branch, | |
| 2906 | + @ event.type | |
| 2805 | 2907 | @ FROM tag CROSS JOIN event CROSS JOIN blob |
| 2806 | 2908 | @ LEFT JOIN tagxref ON tagxref.tagid=tag.tagid |
| 2807 | 2909 | @ AND tagxref.tagtype>0 |
| 2808 | 2910 | @ AND tagxref.rid=blob.rid |
| 2809 | 2911 | @ WHERE blob.rid=event.objid |
| 2810 | 2912 |
| --- src/timeline.c | |
| +++ src/timeline.c | |
| @@ -595,25 +595,32 @@ | |
| 595 | if( zType[0]!='c' ){ |
| 596 | /* Comments for anything other than a check-in are generated by |
| 597 | ** "fossil rebuild" and expect to be rendered as text/x-fossil-wiki */ |
| 598 | if( zType[0]=='w' ){ |
| 599 | const char *zCom = blob_str(&comment); |
| 600 | char *zWiki; |
| 601 | wiki_hyperlink_override(zUuid); |
| 602 | if( (tmFlags & TIMELINE_REFS)!=0 |
| 603 | && (zWiki = strstr(zCom,"wiki"))!=0 |
| 604 | ){ |
| 605 | /* The TIMELINE_REFS flag causes timeline comments of the |
| 606 | ** form "Changes to wiki..." or "Added wiki" to be changed |
| 607 | ** into just "Wiki..." */ |
| 608 | Blob rcom; |
| 609 | blob_init(&rcom, 0, 0); |
| 610 | blob_appendf(&rcom, "W%s", zWiki+1); |
| 611 | wiki_convert(&rcom, 0, WIKI_INLINE); |
| 612 | blob_reset(&rcom); |
| 613 | }else{ |
| 614 | wiki_convert(&comment, 0, WIKI_INLINE); |
| 615 | } |
| 616 | wiki_hyperlink_override(0); |
| 617 | }else{ |
| 618 | wiki_convert(&comment, 0, WIKI_INLINE); |
| 619 | } |
| @@ -1727,19 +1734,26 @@ | |
| 1727 | int you_rid = name_to_typed_rid(P("you"),"ci");/* you= for common ancst */ |
| 1728 | int pd_rid; |
| 1729 | double rBefore, rAfter, rCirca; /* Boundary times */ |
| 1730 | const char *z; |
| 1731 | char *zOlderButton = 0; /* URL for Older button at the bottom */ |
| 1732 | char *zNewerButton = 0; /* URL for Newer button at the top */ |
| 1733 | int selectedRid = 0; /* Show a highlight on this RID */ |
| 1734 | int secondaryRid = 0; /* Show secondary highlight */ |
| 1735 | int disableY = 0; /* Disable type selector on submenu */ |
| 1736 | int advancedMenu = 0; /* Use the advanced menu design */ |
| 1737 | char *zPlural; /* Ending for plural forms */ |
| 1738 | int showCherrypicks = 1; /* True to show cherrypick merges */ |
| 1739 | |
| 1740 | /* Set number of rows to display */ |
| 1741 | cookie_read_parameter("n","n"); |
| 1742 | z = P("n"); |
| 1743 | if( z==0 ) z = db_get("timeline-default-length",0); |
| 1744 | if( z ){ |
| 1745 | if( fossil_strcmp(z,"all")==0 ){ |
| @@ -1797,12 +1811,10 @@ | |
| 1797 | } |
| 1798 | if( zType[0]=='a' || zType[0]=='c' ){ |
| 1799 | cookie_write_parameter("y","y",zType); |
| 1800 | } |
| 1801 | cookie_render(); |
| 1802 | url_initialize(&url, "timeline"); |
| 1803 | cgi_query_parameters_to_url(&url); |
| 1804 | |
| 1805 | /* Convert the cf=FILEHASH query parameter into a c=CHECKINHASH value */ |
| 1806 | if( P("cf")!=0 ){ |
| 1807 | zCirca = db_text(0, |
| 1808 | "SELECT (SELECT uuid FROM blob WHERE rid=mlink.mid)" |
| @@ -2064,11 +2076,10 @@ | |
| 2064 | " WHERE mlink.mid=x" |
| 2065 | " AND mlink.fnid=filename.fnid AND %s)", |
| 2066 | glob_expr("filename.name", zChng) |
| 2067 | ); |
| 2068 | } |
| 2069 | // tmFlags |= TIMELINE_DISJOINT; |
| 2070 | tmFlags |= TIMELINE_XMERGE | TIMELINE_FILLGAPS; |
| 2071 | db_multi_exec("%s", blob_sql_text(&sql)); |
| 2072 | if( advancedMenu ){ |
| 2073 | style_submenu_checkbox("v", "Files", (zType[0]!='a' && zType[0]!='c'),0); |
| 2074 | } |
| @@ -2118,10 +2129,11 @@ | |
| 2118 | db_multi_exec("DELETE FROM ok"); |
| 2119 | } |
| 2120 | if( p_rid ){ |
| 2121 | zBackTo = P("bt"); |
| 2122 | ridBackTo = zBackTo ? name_to_typed_rid(zBackTo,"ci") : 0; |
| 2123 | compute_ancestors(p_rid, nEntry==0 ? 0 : nEntry+1, 0, ridBackTo); |
| 2124 | np = db_int(0, "SELECT count(*)-1 FROM ok"); |
| 2125 | if( np>0 || nd==0 ){ |
| 2126 | if( nd>0 ) blob_appendf(&desc, " and "); |
| 2127 | blob_appendf(&desc, "%d ancestor%s", np, (1==np)?"":"s"); |
| @@ -2216,27 +2228,57 @@ | |
| 2216 | } |
| 2217 | if( bisectLocal || zBisect!=0 ){ |
| 2218 | blob_append_sql(&cond, " AND event.objid IN (SELECT rid FROM bilog) "); |
| 2219 | } |
| 2220 | if( zYearMonth ){ |
| 2221 | zYearMonth = timeline_expand_datetime(zYearMonth); |
| 2222 | blob_append_sql(&cond, " AND %Q=strftime('%%Y-%%m',event.mtime) ", |
| 2223 | zYearMonth); |
| 2224 | } |
| 2225 | else if( zYearWeek ){ |
| 2226 | char *z; |
| 2227 | zYearWeek = timeline_expand_datetime(zYearWeek); |
| 2228 | z = db_text(0, "SELECT strftime('%%Y-%%W',%Q)", zYearWeek); |
| 2229 | if( z && z[0] ){ |
| 2230 | zYearWeekStart = db_text(0, "SELECT date(%Q,'-6 days','weekday 1')", |
| 2231 | zYearWeek); |
| 2232 | zYearWeek = z; |
| 2233 | }else{ |
| 2234 | if( strlen(zYearWeek)==7 ){ |
| 2235 | zYearWeekStart = db_text(0, |
| 2236 | "SELECT date('%.4q-01-01','+%d days','weekday 1')", |
| 2237 | zYearWeek, atoi(zYearWeek+5)*7); |
| 2238 | }else{ |
| 2239 | zYearWeekStart = 0; |
| 2240 | } |
| 2241 | if( zYearWeekStart==0 || zYearWeekStart[0]==0 ){ |
| 2242 | zYearWeekStart = db_text(0, |
| @@ -2243,20 +2285,61 @@ | |
| 2243 | "SELECT date('now','-6 days','weekday 1');"); |
| 2244 | zYearWeek = db_text(0, |
| 2245 | "SELECT strftime('%%Y-%%W','now','-6 days','weekday 1')"); |
| 2246 | } |
| 2247 | } |
| 2248 | blob_append_sql(&cond, " AND %Q=strftime('%%Y-%%W',event.mtime) ", |
| 2249 | zYearWeek); |
| 2250 | nEntry = -1; |
| 2251 | } |
| 2252 | else if( zDay ){ |
| 2253 | zDay = timeline_expand_datetime(zDay); |
| 2254 | zDay = db_text(0, "SELECT date(%Q)", zDay); |
| 2255 | if( zDay==0 || zDay[0]==0 ){ |
| 2256 | zDay = db_text(0, "SELECT date('now')"); |
| 2257 | } |
| 2258 | blob_append_sql(&cond, " AND %Q=date(event.mtime) ", |
| 2259 | zDay); |
| 2260 | nEntry = -1; |
| 2261 | } |
| 2262 | else if( zNDays ){ |
| @@ -2452,11 +2535,12 @@ | |
| 2452 | db_multi_exec("%s", blob_sql_text(&sql)); |
| 2453 | |
| 2454 | n = db_int(0, "SELECT count(*) FROM timeline WHERE etype!='div' /*scan*/"); |
| 2455 | zPlural = n==1 ? "" : "s"; |
| 2456 | if( zYearMonth ){ |
| 2457 | blob_appendf(&desc, "%d %s%s for %h", n, zEType, zPlural, zYearMonth); |
| 2458 | }else if( zYearWeek ){ |
| 2459 | blob_appendf(&desc, "%d %s%s for week %h beginning on %h", |
| 2460 | n, zEType, zPlural, zYearWeek, zYearWeekStart); |
| 2461 | }else if( zDay ){ |
| 2462 | blob_appendf(&desc, "%d %s%s occurring on %h", n, zEType, zPlural, zDay); |
| @@ -2544,10 +2628,11 @@ | |
| 2544 | "SELECT EXISTS (SELECT 1 FROM event CROSS JOIN blob" |
| 2545 | " WHERE blob.rid=event.objid AND mtime<=%.17g%s)", |
| 2546 | rDate-ONE_SECOND, blob_sql_text(&cond)) |
| 2547 | ){ |
| 2548 | zOlderButton = fossil_strdup(url_render(&url, "b", zDate, "a", 0)); |
| 2549 | } |
| 2550 | free(zDate); |
| 2551 | } |
| 2552 | zDate = db_text(0, "SELECT max(timestamp) FROM timeline /*scan*/"); |
| 2553 | if( (!zDate || !zDate[0]) && ( zAfter || zBefore ) ){ |
| @@ -2559,10 +2644,11 @@ | |
| 2559 | "SELECT EXISTS (SELECT 1 FROM event CROSS JOIN blob" |
| 2560 | " WHERE blob.rid=event.objid AND mtime>=%.17g%s)", |
| 2561 | rDate+ONE_SECOND, blob_sql_text(&cond)) |
| 2562 | ){ |
| 2563 | zNewerButton = fossil_strdup(url_render(&url, "a", zDate, "b", 0)); |
| 2564 | } |
| 2565 | free(zDate); |
| 2566 | } |
| 2567 | if( advancedMenu ){ |
| 2568 | if( zType[0]=='a' || zType[0]=='c' ){ |
| @@ -2628,17 +2714,19 @@ | |
| 2628 | if( zError ){ |
| 2629 | @ <p class="generalError">%h(zError)</p> |
| 2630 | } |
| 2631 | |
| 2632 | if( zNewerButton ){ |
| 2633 | @ %z(chref("button","%z",zNewerButton))More ↑</a> |
| 2634 | } |
| 2635 | www_print_timeline(&q, tmFlags, zThisUser, zThisTag, zBrName, |
| 2636 | selectedRid, secondaryRid, 0); |
| 2637 | db_finalize(&q); |
| 2638 | if( zOlderButton ){ |
| 2639 | @ %z(chref("button","%z",zOlderButton))More ↓</a> |
| 2640 | } |
| 2641 | document_emit_js(/*handles pikchrs rendered above*/); |
| 2642 | style_finish_page("timeline"); |
| 2643 | } |
| 2644 | |
| @@ -2662,10 +2750,11 @@ | |
| 2662 | ** 3. Comment string and user |
| 2663 | ** 4. Number of non-merge children |
| 2664 | ** 5. Number of parents |
| 2665 | ** 6. mtime |
| 2666 | ** 7. branch |
| 2667 | */ |
| 2668 | void print_timeline(Stmt *q, int nLimit, int width, int verboseFlag){ |
| 2669 | int nAbsLimit = (nLimit >= 0) ? nLimit : -nLimit; |
| 2670 | int nLine = 0; |
| 2671 | int nEntry = 0; |
| @@ -2686,10 +2775,11 @@ | |
| 2686 | const char *zId = db_column_text(q, 1); |
| 2687 | const char *zDate = db_column_text(q, 2); |
| 2688 | const char *zCom = db_column_text(q, 3); |
| 2689 | int nChild = db_column_int(q, 4); |
| 2690 | int nParent = db_column_int(q, 5); |
| 2691 | char *zFree = 0; |
| 2692 | int n = 0; |
| 2693 | char zPrefix[80]; |
| 2694 | |
| 2695 | if( nAbsLimit!=0 ){ |
| @@ -2729,11 +2819,22 @@ | |
| 2729 | } |
| 2730 | if( content_is_private(rid) ){ |
| 2731 | sqlite3_snprintf(sizeof(zPrefix)-n, &zPrefix[n], "*UNPUBLISHED* "); |
| 2732 | n += strlen(zPrefix+n); |
| 2733 | } |
| 2734 | zFree = mprintf("[%S] %s%s", zId, zPrefix, zCom); |
| 2735 | /* record another X lines */ |
| 2736 | nLine += comment_print(zFree, zCom, 9, width, get_comment_format()); |
| 2737 | fossil_free(zFree); |
| 2738 | |
| 2739 | if(verboseFlag){ |
| @@ -2799,11 +2900,12 @@ | |
| 2799 | @ || ')' as comment, |
| 2800 | @ (SELECT count(*) FROM plink WHERE pid=blob.rid AND isprim) |
| 2801 | @ AS primPlinkCount, |
| 2802 | @ (SELECT count(*) FROM plink WHERE cid=blob.rid) AS plinkCount, |
| 2803 | @ event.mtime AS mtime, |
| 2804 | @ tagxref.value AS branch |
| 2805 | @ FROM tag CROSS JOIN event CROSS JOIN blob |
| 2806 | @ LEFT JOIN tagxref ON tagxref.tagid=tag.tagid |
| 2807 | @ AND tagxref.tagtype>0 |
| 2808 | @ AND tagxref.rid=blob.rid |
| 2809 | @ WHERE blob.rid=event.objid |
| 2810 |
| --- src/timeline.c | |
| +++ src/timeline.c | |
| @@ -595,25 +595,32 @@ | |
| 595 | if( zType[0]!='c' ){ |
| 596 | /* Comments for anything other than a check-in are generated by |
| 597 | ** "fossil rebuild" and expect to be rendered as text/x-fossil-wiki */ |
| 598 | if( zType[0]=='w' ){ |
| 599 | const char *zCom = blob_str(&comment); |
| 600 | /* Except, the comments generated by "fossil rebuild" for a wiki |
| 601 | ** page edit consist of a single character '-', '+', or ':' (to |
| 602 | ** indicate "deleted", "added", or "edited") followed by the |
| 603 | ** raw wiki page name. We have to generate an appropriate |
| 604 | ** comment on-the-fly |
| 605 | */ |
| 606 | wiki_hyperlink_override(zUuid); |
| 607 | if( zCom[0]=='-' ){ |
| 608 | @ Deleted wiki page "%z(href("%R/whistory?name=%t",zCom+1))\ |
| 609 | @ %h(zCom+1)</a>" |
| 610 | }else if( (tmFlags & TIMELINE_REFS)!=0 |
| 611 | && (zCom[0]=='+' || zCom[0]==':') ){ |
| 612 | @ Wiki page "%z(href("%R/wiki?name=%t",zCom+1))%h(zCom+1)</a>" |
| 613 | }else if( zCom[0]=='+' ){ |
| 614 | @ Added wiki page "%z(href("%R/wiki?name=%t",zCom+1))%h(zCom+1)</a>" |
| 615 | }else if( zCom[0]==':' ){ |
| 616 | @ Changes to wiki page "%z(href("%R/wiki?name=%t",zCom+1))\ |
| 617 | @ %h(zCom+1)</a>" |
| 618 | }else{ |
| 619 | /* Legacy EVENT table entry that needs to be rebuilt */ |
| 620 | @ Changes to a wiki page → Obsolete EVENT table information. |
| 621 | @ Run "fossil rebuild" on the repository. |
| 622 | } |
| 623 | wiki_hyperlink_override(0); |
| 624 | }else{ |
| 625 | wiki_convert(&comment, 0, WIKI_INLINE); |
| 626 | } |
| @@ -1727,19 +1734,26 @@ | |
| 1734 | int you_rid = name_to_typed_rid(P("you"),"ci");/* you= for common ancst */ |
| 1735 | int pd_rid; |
| 1736 | double rBefore, rAfter, rCirca; /* Boundary times */ |
| 1737 | const char *z; |
| 1738 | char *zOlderButton = 0; /* URL for Older button at the bottom */ |
| 1739 | char *zOlderButtonLabel = 0; /* Label for the Older Button */ |
| 1740 | char *zNewerButton = 0; /* URL for Newer button at the top */ |
| 1741 | char *zNewerButtonLabel = 0; /* Label for the Newer button */ |
| 1742 | int selectedRid = 0; /* Show a highlight on this RID */ |
| 1743 | int secondaryRid = 0; /* Show secondary highlight */ |
| 1744 | int disableY = 0; /* Disable type selector on submenu */ |
| 1745 | int advancedMenu = 0; /* Use the advanced menu design */ |
| 1746 | char *zPlural; /* Ending for plural forms */ |
| 1747 | int showCherrypicks = 1; /* True to show cherrypick merges */ |
| 1748 | int haveParameterN; /* True if n= query parameter present */ |
| 1749 | |
| 1750 | url_initialize(&url, "timeline"); |
| 1751 | cgi_query_parameters_to_url(&url); |
| 1752 | |
| 1753 | /* Set number of rows to display */ |
| 1754 | haveParameterN = P("n")!=0; |
| 1755 | cookie_read_parameter("n","n"); |
| 1756 | z = P("n"); |
| 1757 | if( z==0 ) z = db_get("timeline-default-length",0); |
| 1758 | if( z ){ |
| 1759 | if( fossil_strcmp(z,"all")==0 ){ |
| @@ -1797,12 +1811,10 @@ | |
| 1811 | } |
| 1812 | if( zType[0]=='a' || zType[0]=='c' ){ |
| 1813 | cookie_write_parameter("y","y",zType); |
| 1814 | } |
| 1815 | cookie_render(); |
| 1816 | |
| 1817 | /* Convert the cf=FILEHASH query parameter into a c=CHECKINHASH value */ |
| 1818 | if( P("cf")!=0 ){ |
| 1819 | zCirca = db_text(0, |
| 1820 | "SELECT (SELECT uuid FROM blob WHERE rid=mlink.mid)" |
| @@ -2064,11 +2076,10 @@ | |
| 2076 | " WHERE mlink.mid=x" |
| 2077 | " AND mlink.fnid=filename.fnid AND %s)", |
| 2078 | glob_expr("filename.name", zChng) |
| 2079 | ); |
| 2080 | } |
| 2081 | tmFlags |= TIMELINE_XMERGE | TIMELINE_FILLGAPS; |
| 2082 | db_multi_exec("%s", blob_sql_text(&sql)); |
| 2083 | if( advancedMenu ){ |
| 2084 | style_submenu_checkbox("v", "Files", (zType[0]!='a' && zType[0]!='c'),0); |
| 2085 | } |
| @@ -2118,10 +2129,11 @@ | |
| 2129 | db_multi_exec("DELETE FROM ok"); |
| 2130 | } |
| 2131 | if( p_rid ){ |
| 2132 | zBackTo = P("bt"); |
| 2133 | ridBackTo = zBackTo ? name_to_typed_rid(zBackTo,"ci") : 0; |
| 2134 | if( !haveParameterN ) nEntry = 0; |
| 2135 | compute_ancestors(p_rid, nEntry==0 ? 0 : nEntry+1, 0, ridBackTo); |
| 2136 | np = db_int(0, "SELECT count(*)-1 FROM ok"); |
| 2137 | if( np>0 || nd==0 ){ |
| 2138 | if( nd>0 ) blob_appendf(&desc, " and "); |
| 2139 | blob_appendf(&desc, "%d ancestor%s", np, (1==np)?"":"s"); |
| @@ -2216,27 +2228,57 @@ | |
| 2228 | } |
| 2229 | if( bisectLocal || zBisect!=0 ){ |
| 2230 | blob_append_sql(&cond, " AND event.objid IN (SELECT rid FROM bilog) "); |
| 2231 | } |
| 2232 | if( zYearMonth ){ |
| 2233 | char *zNext; |
| 2234 | zYearMonth = timeline_expand_datetime(zYearMonth); |
| 2235 | if( strlen(zYearMonth)>7 ){ |
| 2236 | zYearMonth = mprintf("%.7s", zYearMonth); |
| 2237 | } |
| 2238 | if( db_int(0,"SELECT julianday('%q-01') IS NULL", zYearMonth) ){ |
| 2239 | zYearMonth = db_text(0, "SELECT strftime('%%Y-%%m','now');"); |
| 2240 | } |
| 2241 | zNext = db_text(0, "SELECT strftime('%%Y-%%m','%q-01','+1 month');", |
| 2242 | zYearMonth); |
| 2243 | if( db_int(0, |
| 2244 | "SELECT EXISTS (SELECT 1 FROM event CROSS JOIN blob" |
| 2245 | " WHERE blob.rid=event.objid AND mtime>=julianday('%q-01')%s)", |
| 2246 | zNext, blob_sql_text(&cond)) |
| 2247 | ){ |
| 2248 | zNewerButton = fossil_strdup(url_render(&url, "ym", zNext, 0, 0)); |
| 2249 | zNewerButtonLabel = "Following month"; |
| 2250 | } |
| 2251 | fossil_free(zNext); |
| 2252 | zNext = db_text(0, "SELECT strftime('%%Y-%%m','%q-01','-1 month');", |
| 2253 | zYearMonth); |
| 2254 | if( db_int(0, |
| 2255 | "SELECT EXISTS (SELECT 1 FROM event CROSS JOIN blob" |
| 2256 | " WHERE blob.rid=event.objid AND mtime<julianday('%q-01')%s)", |
| 2257 | zYearMonth, blob_sql_text(&cond)) |
| 2258 | ){ |
| 2259 | zOlderButton = fossil_strdup(url_render(&url, "ym", zNext, 0, 0)); |
| 2260 | zOlderButtonLabel = "Previous month"; |
| 2261 | } |
| 2262 | fossil_free(zNext); |
| 2263 | blob_append_sql(&cond, " AND %Q=strftime('%%Y-%%m',event.mtime) ", |
| 2264 | zYearMonth); |
| 2265 | nEntry = -1; |
| 2266 | } |
| 2267 | else if( zYearWeek ){ |
| 2268 | char *z, *zNext; |
| 2269 | zYearWeek = timeline_expand_datetime(zYearWeek); |
| 2270 | z = db_text(0, "SELECT strftime('%%Y-%%W',%Q)", zYearWeek); |
| 2271 | if( z && z[0] ){ |
| 2272 | zYearWeekStart = db_text(0, "SELECT date(%Q,'-6 days','weekday 1')", |
| 2273 | zYearWeek); |
| 2274 | zYearWeek = z; |
| 2275 | }else{ |
| 2276 | if( strlen(zYearWeek)==7 ){ |
| 2277 | zYearWeekStart = db_text(0, |
| 2278 | "SELECT date('%.4q-01-01','%+d days','weekday 1')", |
| 2279 | zYearWeek, atoi(zYearWeek+5)*7-6); |
| 2280 | }else{ |
| 2281 | zYearWeekStart = 0; |
| 2282 | } |
| 2283 | if( zYearWeekStart==0 || zYearWeekStart[0]==0 ){ |
| 2284 | zYearWeekStart = db_text(0, |
| @@ -2243,20 +2285,61 @@ | |
| 2285 | "SELECT date('now','-6 days','weekday 1');"); |
| 2286 | zYearWeek = db_text(0, |
| 2287 | "SELECT strftime('%%Y-%%W','now','-6 days','weekday 1')"); |
| 2288 | } |
| 2289 | } |
| 2290 | zNext = db_text(0, "SELECT date(%Q,'+7 day');", zYearWeekStart); |
| 2291 | if( db_int(0, |
| 2292 | "SELECT EXISTS (SELECT 1 FROM event CROSS JOIN blob" |
| 2293 | " WHERE blob.rid=event.objid AND mtime>=julianday(%Q)%s)", |
| 2294 | zNext, blob_sql_text(&cond)) |
| 2295 | ){ |
| 2296 | zNewerButton = fossil_strdup(url_render(&url, "yw", zNext, 0, 0)); |
| 2297 | zNewerButtonLabel = "Following week"; |
| 2298 | } |
| 2299 | fossil_free(zNext); |
| 2300 | zNext = db_text(0, "SELECT date(%Q,'-7 days');", zYearWeekStart); |
| 2301 | if( db_int(0, |
| 2302 | "SELECT EXISTS (SELECT 1 FROM event CROSS JOIN blob" |
| 2303 | " WHERE blob.rid=event.objid AND mtime<julianday(%Q)%s)", |
| 2304 | zYearWeekStart, blob_sql_text(&cond)) |
| 2305 | ){ |
| 2306 | zOlderButton = fossil_strdup(url_render(&url, "yw", zNext, 0, 0)); |
| 2307 | zOlderButtonLabel = "Previous week"; |
| 2308 | } |
| 2309 | fossil_free(zNext); |
| 2310 | blob_append_sql(&cond, " AND %Q=strftime('%%Y-%%W',event.mtime) ", |
| 2311 | zYearWeek); |
| 2312 | nEntry = -1; |
| 2313 | } |
| 2314 | else if( zDay ){ |
| 2315 | char *zNext; |
| 2316 | zDay = timeline_expand_datetime(zDay); |
| 2317 | zDay = db_text(0, "SELECT date(%Q)", zDay); |
| 2318 | if( zDay==0 || zDay[0]==0 ){ |
| 2319 | zDay = db_text(0, "SELECT date('now')"); |
| 2320 | } |
| 2321 | zNext = db_text(0, "SELECT date(%Q,'+1 day');", zDay); |
| 2322 | if( db_int(0, |
| 2323 | "SELECT EXISTS (SELECT 1 FROM event CROSS JOIN blob" |
| 2324 | " WHERE blob.rid=event.objid AND mtime>=julianday(%Q)%s)", |
| 2325 | zNext, blob_sql_text(&cond)) |
| 2326 | ){ |
| 2327 | zNewerButton = fossil_strdup(url_render(&url, "ymd", zNext, 0, 0)); |
| 2328 | zNewerButtonLabel = "Following day"; |
| 2329 | } |
| 2330 | fossil_free(zNext); |
| 2331 | zNext = db_text(0, "SELECT date(%Q,'-1 day');", zDay); |
| 2332 | if( db_int(0, |
| 2333 | "SELECT EXISTS (SELECT 1 FROM event CROSS JOIN blob" |
| 2334 | " WHERE blob.rid=event.objid AND mtime<julianday(%Q)%s)", |
| 2335 | zDay, blob_sql_text(&cond)) |
| 2336 | ){ |
| 2337 | zOlderButton = fossil_strdup(url_render(&url, "ymd", zNext, 0, 0)); |
| 2338 | zOlderButtonLabel = "Previous day"; |
| 2339 | } |
| 2340 | fossil_free(zNext); |
| 2341 | blob_append_sql(&cond, " AND %Q=date(event.mtime) ", |
| 2342 | zDay); |
| 2343 | nEntry = -1; |
| 2344 | } |
| 2345 | else if( zNDays ){ |
| @@ -2452,11 +2535,12 @@ | |
| 2535 | db_multi_exec("%s", blob_sql_text(&sql)); |
| 2536 | |
| 2537 | n = db_int(0, "SELECT count(*) FROM timeline WHERE etype!='div' /*scan*/"); |
| 2538 | zPlural = n==1 ? "" : "s"; |
| 2539 | if( zYearMonth ){ |
| 2540 | blob_appendf(&desc, "%d %s%s for the month beginning %h-01", |
| 2541 | n, zEType, zPlural, zYearMonth); |
| 2542 | }else if( zYearWeek ){ |
| 2543 | blob_appendf(&desc, "%d %s%s for week %h beginning on %h", |
| 2544 | n, zEType, zPlural, zYearWeek, zYearWeekStart); |
| 2545 | }else if( zDay ){ |
| 2546 | blob_appendf(&desc, "%d %s%s occurring on %h", n, zEType, zPlural, zDay); |
| @@ -2544,10 +2628,11 @@ | |
| 2628 | "SELECT EXISTS (SELECT 1 FROM event CROSS JOIN blob" |
| 2629 | " WHERE blob.rid=event.objid AND mtime<=%.17g%s)", |
| 2630 | rDate-ONE_SECOND, blob_sql_text(&cond)) |
| 2631 | ){ |
| 2632 | zOlderButton = fossil_strdup(url_render(&url, "b", zDate, "a", 0)); |
| 2633 | zOlderButtonLabel = "More"; |
| 2634 | } |
| 2635 | free(zDate); |
| 2636 | } |
| 2637 | zDate = db_text(0, "SELECT max(timestamp) FROM timeline /*scan*/"); |
| 2638 | if( (!zDate || !zDate[0]) && ( zAfter || zBefore ) ){ |
| @@ -2559,10 +2644,11 @@ | |
| 2644 | "SELECT EXISTS (SELECT 1 FROM event CROSS JOIN blob" |
| 2645 | " WHERE blob.rid=event.objid AND mtime>=%.17g%s)", |
| 2646 | rDate+ONE_SECOND, blob_sql_text(&cond)) |
| 2647 | ){ |
| 2648 | zNewerButton = fossil_strdup(url_render(&url, "a", zDate, "b", 0)); |
| 2649 | zNewerButtonLabel = "More"; |
| 2650 | } |
| 2651 | free(zDate); |
| 2652 | } |
| 2653 | if( advancedMenu ){ |
| 2654 | if( zType[0]=='a' || zType[0]=='c' ){ |
| @@ -2628,17 +2714,19 @@ | |
| 2714 | if( zError ){ |
| 2715 | @ <p class="generalError">%h(zError)</p> |
| 2716 | } |
| 2717 | |
| 2718 | if( zNewerButton ){ |
| 2719 | @ %z(chref("button","%s",zNewerButton))%h(zNewerButtonLabel)\ |
| 2720 | @ ↑</a> |
| 2721 | } |
| 2722 | www_print_timeline(&q, tmFlags, zThisUser, zThisTag, zBrName, |
| 2723 | selectedRid, secondaryRid, 0); |
| 2724 | db_finalize(&q); |
| 2725 | if( zOlderButton ){ |
| 2726 | @ %z(chref("button","%s",zOlderButton))%h(zOlderButtonLabel)\ |
| 2727 | @ ↓</a> |
| 2728 | } |
| 2729 | document_emit_js(/*handles pikchrs rendered above*/); |
| 2730 | style_finish_page("timeline"); |
| 2731 | } |
| 2732 | |
| @@ -2662,10 +2750,11 @@ | |
| 2750 | ** 3. Comment string and user |
| 2751 | ** 4. Number of non-merge children |
| 2752 | ** 5. Number of parents |
| 2753 | ** 6. mtime |
| 2754 | ** 7. branch |
| 2755 | ** 8. event-type: 'ci', 'w', 't', 'f', and so forth. |
| 2756 | */ |
| 2757 | void print_timeline(Stmt *q, int nLimit, int width, int verboseFlag){ |
| 2758 | int nAbsLimit = (nLimit >= 0) ? nLimit : -nLimit; |
| 2759 | int nLine = 0; |
| 2760 | int nEntry = 0; |
| @@ -2686,10 +2775,11 @@ | |
| 2775 | const char *zId = db_column_text(q, 1); |
| 2776 | const char *zDate = db_column_text(q, 2); |
| 2777 | const char *zCom = db_column_text(q, 3); |
| 2778 | int nChild = db_column_int(q, 4); |
| 2779 | int nParent = db_column_int(q, 5); |
| 2780 | const char *zType = db_column_text(q, 8); |
| 2781 | char *zFree = 0; |
| 2782 | int n = 0; |
| 2783 | char zPrefix[80]; |
| 2784 | |
| 2785 | if( nAbsLimit!=0 ){ |
| @@ -2729,11 +2819,22 @@ | |
| 2819 | } |
| 2820 | if( content_is_private(rid) ){ |
| 2821 | sqlite3_snprintf(sizeof(zPrefix)-n, &zPrefix[n], "*UNPUBLISHED* "); |
| 2822 | n += strlen(zPrefix+n); |
| 2823 | } |
| 2824 | if( zType[0]=='w' && (zCom[0]=='+' || zCom[0]=='-' || zCom[0]==':') ){ |
| 2825 | /* Special processing for Wiki comments */ |
| 2826 | if( zCom[0]=='+' ){ |
| 2827 | zFree = mprintf("[%S] Add wiki page \"%s\"", zId, zCom+1); |
| 2828 | }else if( zCom[0]=='-' ){ |
| 2829 | zFree = mprintf("[%S] Delete wiki page \"%s\"", zId, zCom+1); |
| 2830 | }else{ |
| 2831 | zFree = mprintf("[%S] Edit to wiki page \"%s\"", zId, zCom+1); |
| 2832 | } |
| 2833 | }else{ |
| 2834 | zFree = mprintf("[%S] %s%s", zId, zPrefix, zCom); |
| 2835 | } |
| 2836 | /* record another X lines */ |
| 2837 | nLine += comment_print(zFree, zCom, 9, width, get_comment_format()); |
| 2838 | fossil_free(zFree); |
| 2839 | |
| 2840 | if(verboseFlag){ |
| @@ -2799,11 +2900,12 @@ | |
| 2900 | @ || ')' as comment, |
| 2901 | @ (SELECT count(*) FROM plink WHERE pid=blob.rid AND isprim) |
| 2902 | @ AS primPlinkCount, |
| 2903 | @ (SELECT count(*) FROM plink WHERE cid=blob.rid) AS plinkCount, |
| 2904 | @ event.mtime AS mtime, |
| 2905 | @ tagxref.value AS branch, |
| 2906 | @ event.type |
| 2907 | @ FROM tag CROSS JOIN event CROSS JOIN blob |
| 2908 | @ LEFT JOIN tagxref ON tagxref.tagid=tag.tagid |
| 2909 | @ AND tagxref.tagtype>0 |
| 2910 | @ AND tagxref.rid=blob.rid |
| 2911 | @ WHERE blob.rid=event.objid |
| 2912 |
+1
| --- src/tkt.c | ||
| +++ src/tkt.c | ||
| @@ -446,10 +446,11 @@ | ||
| 446 | 446 | ){ |
| 447 | 447 | goto ticket_schema_error; |
| 448 | 448 | } |
| 449 | 449 | break; |
| 450 | 450 | } |
| 451 | + case SQLITE_FUNCTION: | |
| 451 | 452 | case SQLITE_REINDEX: |
| 452 | 453 | case SQLITE_TRANSACTION: |
| 453 | 454 | case SQLITE_READ: { |
| 454 | 455 | break; |
| 455 | 456 | } |
| 456 | 457 |
| --- src/tkt.c | |
| +++ src/tkt.c | |
| @@ -446,10 +446,11 @@ | |
| 446 | ){ |
| 447 | goto ticket_schema_error; |
| 448 | } |
| 449 | break; |
| 450 | } |
| 451 | case SQLITE_REINDEX: |
| 452 | case SQLITE_TRANSACTION: |
| 453 | case SQLITE_READ: { |
| 454 | break; |
| 455 | } |
| 456 |
| --- src/tkt.c | |
| +++ src/tkt.c | |
| @@ -446,10 +446,11 @@ | |
| 446 | ){ |
| 447 | goto ticket_schema_error; |
| 448 | } |
| 449 | break; |
| 450 | } |
| 451 | case SQLITE_FUNCTION: |
| 452 | case SQLITE_REINDEX: |
| 453 | case SQLITE_TRANSACTION: |
| 454 | case SQLITE_READ: { |
| 455 | break; |
| 456 | } |
| 457 |
+1
-1
| --- src/tktsetup.c | ||
| +++ src/tktsetup.c | ||
| @@ -364,11 +364,11 @@ | ||
| 364 | 364 | @ <td colspan="3"> |
| 365 | 365 | @ Enter a detailed description of the problem. |
| 366 | 366 | @ For code defects, be sure to provide details on exactly how |
| 367 | 367 | @ the problem can be reproduced. Provide as much detail as |
| 368 | 368 | @ possible. Format: |
| 369 | -@ <th1>combobox mutype {HTML {[links only]} Markdown {Plain Text} Wiki}} 1</th1> | |
| 369 | +@ <th1>combobox mutype {HTML {[links only]} Markdown {Plain Text} Wiki} 1</th1> | |
| 370 | 370 | @ <br /> |
| 371 | 371 | @ <th1>set nline [linecount $comment 50 10]</th1> |
| 372 | 372 | @ <textarea name="icomment" cols="80" rows="$nline" |
| 373 | 373 | @ wrap="virtual" class="wikiedit">$<icomment></textarea><br /> |
| 374 | 374 | @ </tr> |
| 375 | 375 |
| --- src/tktsetup.c | |
| +++ src/tktsetup.c | |
| @@ -364,11 +364,11 @@ | |
| 364 | @ <td colspan="3"> |
| 365 | @ Enter a detailed description of the problem. |
| 366 | @ For code defects, be sure to provide details on exactly how |
| 367 | @ the problem can be reproduced. Provide as much detail as |
| 368 | @ possible. Format: |
| 369 | @ <th1>combobox mutype {HTML {[links only]} Markdown {Plain Text} Wiki}} 1</th1> |
| 370 | @ <br /> |
| 371 | @ <th1>set nline [linecount $comment 50 10]</th1> |
| 372 | @ <textarea name="icomment" cols="80" rows="$nline" |
| 373 | @ wrap="virtual" class="wikiedit">$<icomment></textarea><br /> |
| 374 | @ </tr> |
| 375 |
| --- src/tktsetup.c | |
| +++ src/tktsetup.c | |
| @@ -364,11 +364,11 @@ | |
| 364 | @ <td colspan="3"> |
| 365 | @ Enter a detailed description of the problem. |
| 366 | @ For code defects, be sure to provide details on exactly how |
| 367 | @ the problem can be reproduced. Provide as much detail as |
| 368 | @ possible. Format: |
| 369 | @ <th1>combobox mutype {HTML {[links only]} Markdown {Plain Text} Wiki} 1</th1> |
| 370 | @ <br /> |
| 371 | @ <th1>set nline [linecount $comment 50 10]</th1> |
| 372 | @ <textarea name="icomment" cols="80" rows="$nline" |
| 373 | @ wrap="virtual" class="wikiedit">$<icomment></textarea><br /> |
| 374 | @ </tr> |
| 375 |
+1
-1
| --- src/update.c | ||
| +++ src/update.c | ||
| @@ -595,11 +595,11 @@ | ||
| 595 | 595 | if( dryRunFlag ){ |
| 596 | 596 | db_end_transaction(1); /* With --dry-run, rollback changes */ |
| 597 | 597 | }else{ |
| 598 | 598 | char *zPwd; |
| 599 | 599 | ensure_empty_dirs_created(1); |
| 600 | - sqlite3_create_function(g.db, "rmdir", 1, SQLITE_UTF8, 0, | |
| 600 | + sqlite3_create_function(g.db, "rmdir", 1, SQLITE_UTF8|SQLITE_DIRECTONLY, 0, | |
| 601 | 601 | file_rmdir_sql_function, 0, 0); |
| 602 | 602 | zPwd = file_getcwd(0,0); |
| 603 | 603 | db_multi_exec( |
| 604 | 604 | "SELECT rmdir(%Q||name) FROM dir_to_delete" |
| 605 | 605 | " WHERE (%Q||name)<>%Q ORDER BY name DESC", |
| 606 | 606 |
| --- src/update.c | |
| +++ src/update.c | |
| @@ -595,11 +595,11 @@ | |
| 595 | if( dryRunFlag ){ |
| 596 | db_end_transaction(1); /* With --dry-run, rollback changes */ |
| 597 | }else{ |
| 598 | char *zPwd; |
| 599 | ensure_empty_dirs_created(1); |
| 600 | sqlite3_create_function(g.db, "rmdir", 1, SQLITE_UTF8, 0, |
| 601 | file_rmdir_sql_function, 0, 0); |
| 602 | zPwd = file_getcwd(0,0); |
| 603 | db_multi_exec( |
| 604 | "SELECT rmdir(%Q||name) FROM dir_to_delete" |
| 605 | " WHERE (%Q||name)<>%Q ORDER BY name DESC", |
| 606 |
| --- src/update.c | |
| +++ src/update.c | |
| @@ -595,11 +595,11 @@ | |
| 595 | if( dryRunFlag ){ |
| 596 | db_end_transaction(1); /* With --dry-run, rollback changes */ |
| 597 | }else{ |
| 598 | char *zPwd; |
| 599 | ensure_empty_dirs_created(1); |
| 600 | sqlite3_create_function(g.db, "rmdir", 1, SQLITE_UTF8|SQLITE_DIRECTONLY, 0, |
| 601 | file_rmdir_sql_function, 0, 0); |
| 602 | zPwd = file_getcwd(0,0); |
| 603 | db_multi_exec( |
| 604 | "SELECT rmdir(%Q||name) FROM dir_to_delete" |
| 605 | " WHERE (%Q||name)<>%Q ORDER BY name DESC", |
| 606 |
+1
-1
| --- src/wiki.c | ||
| +++ src/wiki.c | ||
| @@ -1299,11 +1299,11 @@ | ||
| 1299 | 1299 | well_formed_wiki_name_rules(); |
| 1300 | 1300 | CX("</div>"/*#wikiedit-tab-save*/); |
| 1301 | 1301 | } |
| 1302 | 1302 | builtin_fossil_js_bundle_or("fetch", "dom", "tabs", "confirmer", |
| 1303 | 1303 | "storage", "popupwidget", "copybutton", |
| 1304 | - "pikchr", 0); | |
| 1304 | + "pikchr", NULL); | |
| 1305 | 1305 | builtin_request_js("sbsdiff.js"); |
| 1306 | 1306 | builtin_request_js("fossil.page.wikiedit.js"); |
| 1307 | 1307 | builtin_fulfill_js_requests(); |
| 1308 | 1308 | /* Dynamically populate the editor... */ |
| 1309 | 1309 | style_script_begin(__FILE__,__LINE__); |
| 1310 | 1310 |
| --- src/wiki.c | |
| +++ src/wiki.c | |
| @@ -1299,11 +1299,11 @@ | |
| 1299 | well_formed_wiki_name_rules(); |
| 1300 | CX("</div>"/*#wikiedit-tab-save*/); |
| 1301 | } |
| 1302 | builtin_fossil_js_bundle_or("fetch", "dom", "tabs", "confirmer", |
| 1303 | "storage", "popupwidget", "copybutton", |
| 1304 | "pikchr", 0); |
| 1305 | builtin_request_js("sbsdiff.js"); |
| 1306 | builtin_request_js("fossil.page.wikiedit.js"); |
| 1307 | builtin_fulfill_js_requests(); |
| 1308 | /* Dynamically populate the editor... */ |
| 1309 | style_script_begin(__FILE__,__LINE__); |
| 1310 |
| --- src/wiki.c | |
| +++ src/wiki.c | |
| @@ -1299,11 +1299,11 @@ | |
| 1299 | well_formed_wiki_name_rules(); |
| 1300 | CX("</div>"/*#wikiedit-tab-save*/); |
| 1301 | } |
| 1302 | builtin_fossil_js_bundle_or("fetch", "dom", "tabs", "confirmer", |
| 1303 | "storage", "popupwidget", "copybutton", |
| 1304 | "pikchr", NULL); |
| 1305 | builtin_request_js("sbsdiff.js"); |
| 1306 | builtin_request_js("fossil.page.wikiedit.js"); |
| 1307 | builtin_fulfill_js_requests(); |
| 1308 | /* Dynamically populate the editor... */ |
| 1309 | style_script_begin(__FILE__,__LINE__); |
| 1310 |
+2
-2
| --- www/backup.md | ||
| +++ www/backup.md | ||
| @@ -248,11 +248,11 @@ | ||
| 248 | 248 | care of. If all variables defined in earlier scripts are available, then |
| 249 | 249 | restoration is: |
| 250 | 250 | |
| 251 | 251 | ``` |
| 252 | 252 | openssl enc -d -aes-256-cbc -pbkdf2 -iter 52830 -pass pass:"$pass" -in "$gd" | |
| 253 | - xz -d | fossil --no-repository ~/museum/restored-repo.fossil | |
| 253 | + xz -d | fossil sql --no-repository ~/museum/restored-repo.fossil | |
| 254 | 254 | ``` |
| 255 | 255 | |
| 256 | 256 | We changed the `-e` to `-d` on the `openssl` command to get decryption, |
| 257 | 257 | and we changed the `-out` to `-in` so it reads from the encrypted backup |
| 258 | 258 | file and writes the result to stdout. |
| @@ -265,11 +265,11 @@ | ||
| 265 | 265 | restoration: |
| 266 | 266 | Fossil serves as a dogfooding project for SQLite, |
| 267 | 267 | often making use of the latest features, so it is quite likely that a given |
| 268 | 268 | random `sqlite3` binary in your `PATH` will be unable to understand the |
| 269 | 269 | file created by “`fossil sql .dump`”! The tricky bit is, you can’t just |
| 270 | -pipe the decrpted SQL dump into `fossil sql`, because on startup, Fossil | |
| 270 | +pipe the decrypted SQL dump into `fossil sql`, because on startup, Fossil | |
| 271 | 271 | normally goes looking for tables created by `fossil init`, and it won’t |
| 272 | 272 | find them in a newly-created repo DB. We get around this by passing |
| 273 | 273 | the `--no-repository` flag, which suppresses this behavior. Doing it |
| 274 | 274 | this way saves you from needing to go and build a matching version of |
| 275 | 275 | `sqlite3` just to restore the backup. |
| 276 | 276 |
| --- www/backup.md | |
| +++ www/backup.md | |
| @@ -248,11 +248,11 @@ | |
| 248 | care of. If all variables defined in earlier scripts are available, then |
| 249 | restoration is: |
| 250 | |
| 251 | ``` |
| 252 | openssl enc -d -aes-256-cbc -pbkdf2 -iter 52830 -pass pass:"$pass" -in "$gd" | |
| 253 | xz -d | fossil --no-repository ~/museum/restored-repo.fossil |
| 254 | ``` |
| 255 | |
| 256 | We changed the `-e` to `-d` on the `openssl` command to get decryption, |
| 257 | and we changed the `-out` to `-in` so it reads from the encrypted backup |
| 258 | file and writes the result to stdout. |
| @@ -265,11 +265,11 @@ | |
| 265 | restoration: |
| 266 | Fossil serves as a dogfooding project for SQLite, |
| 267 | often making use of the latest features, so it is quite likely that a given |
| 268 | random `sqlite3` binary in your `PATH` will be unable to understand the |
| 269 | file created by “`fossil sql .dump`”! The tricky bit is, you can’t just |
| 270 | pipe the decrpted SQL dump into `fossil sql`, because on startup, Fossil |
| 271 | normally goes looking for tables created by `fossil init`, and it won’t |
| 272 | find them in a newly-created repo DB. We get around this by passing |
| 273 | the `--no-repository` flag, which suppresses this behavior. Doing it |
| 274 | this way saves you from needing to go and build a matching version of |
| 275 | `sqlite3` just to restore the backup. |
| 276 |
| --- www/backup.md | |
| +++ www/backup.md | |
| @@ -248,11 +248,11 @@ | |
| 248 | care of. If all variables defined in earlier scripts are available, then |
| 249 | restoration is: |
| 250 | |
| 251 | ``` |
| 252 | openssl enc -d -aes-256-cbc -pbkdf2 -iter 52830 -pass pass:"$pass" -in "$gd" | |
| 253 | xz -d | fossil sql --no-repository ~/museum/restored-repo.fossil |
| 254 | ``` |
| 255 | |
| 256 | We changed the `-e` to `-d` on the `openssl` command to get decryption, |
| 257 | and we changed the `-out` to `-in` so it reads from the encrypted backup |
| 258 | file and writes the result to stdout. |
| @@ -265,11 +265,11 @@ | |
| 265 | restoration: |
| 266 | Fossil serves as a dogfooding project for SQLite, |
| 267 | often making use of the latest features, so it is quite likely that a given |
| 268 | random `sqlite3` binary in your `PATH` will be unable to understand the |
| 269 | file created by “`fossil sql .dump`”! The tricky bit is, you can’t just |
| 270 | pipe the decrypted SQL dump into `fossil sql`, because on startup, Fossil |
| 271 | normally goes looking for tables created by `fossil init`, and it won’t |
| 272 | find them in a newly-created repo DB. We get around this by passing |
| 273 | the `--no-repository` flag, which suppresses this behavior. Doing it |
| 274 | this way saves you from needing to go and build a matching version of |
| 275 | `sqlite3` just to restore the backup. |
| 276 |
+18
| --- www/changes.wiki | ||
| +++ www/changes.wiki | ||
| @@ -6,11 +6,29 @@ | ||
| 6 | 6 | * The "[/help?cmd=clone|fossil clone]" command is enhanced so that |
| 7 | 7 | if the repository filename is omitted, an appropriate name is derived |
| 8 | 8 | from the remote URL and the newly cloned repo is opened. This makes |
| 9 | 9 | the clone command work more like Git, thus making it easier for |
| 10 | 10 | people transitioning from Git. |
| 11 | + * Changed the way that wiki edits are stored in the EVENT table of the | |
| 12 | + database, for more flexibility and better features. | |
| 13 | + <b>Run "fossil rebuild" to take advantage of this change</b>. | |
| 11 | 14 | * Add the "contact" sub-command to [/help?cmd=user|fossil user]. |
| 15 | + * Added commands "[/help?cmd=all|fossil all git export]" and | |
| 16 | + "[/help?cmd=all|fossil all git status]". | |
| 17 | + * Improvements to the "[/sitemap]" page. Add subpages | |
| 18 | + [/sitemap-timeline] and [/sitemap-test]. | |
| 19 | + * Better text position in cylinder objects of Pikchr diagrams. | |
| 20 | + * New "details.txt" settings available to custom skins to better control | |
| 21 | + the rendering of Pikchr diagrams: | |
| 22 | + <ul> | |
| 23 | + <li> pikchr-foreground | |
| 24 | + <li> pikchr-scale | |
| 25 | + <li> pikchr-fontscale | |
| 26 | + </ul> | |
| 27 | + * Allow the use of SQL functions inside the ticket table definition | |
| 28 | + for custom ticket configurations. | |
| 29 | + * Countless improvements and enhancements to the documentation | |
| 12 | 30 | |
| 13 | 31 | <a name='v2_13'></a> |
| 14 | 32 | <h2>Changes for Version 2.13 (2020-11-01)</h2> |
| 15 | 33 | |
| 16 | 34 | * Added support for [./interwiki.md|interwiki links]. |
| 17 | 35 |
| --- www/changes.wiki | |
| +++ www/changes.wiki | |
| @@ -6,11 +6,29 @@ | |
| 6 | * The "[/help?cmd=clone|fossil clone]" command is enhanced so that |
| 7 | if the repository filename is omitted, an appropriate name is derived |
| 8 | from the remote URL and the newly cloned repo is opened. This makes |
| 9 | the clone command work more like Git, thus making it easier for |
| 10 | people transitioning from Git. |
| 11 | * Add the "contact" sub-command to [/help?cmd=user|fossil user]. |
| 12 | |
| 13 | <a name='v2_13'></a> |
| 14 | <h2>Changes for Version 2.13 (2020-11-01)</h2> |
| 15 | |
| 16 | * Added support for [./interwiki.md|interwiki links]. |
| 17 |
| --- www/changes.wiki | |
| +++ www/changes.wiki | |
| @@ -6,11 +6,29 @@ | |
| 6 | * The "[/help?cmd=clone|fossil clone]" command is enhanced so that |
| 7 | if the repository filename is omitted, an appropriate name is derived |
| 8 | from the remote URL and the newly cloned repo is opened. This makes |
| 9 | the clone command work more like Git, thus making it easier for |
| 10 | people transitioning from Git. |
| 11 | * Changed the way that wiki edits are stored in the EVENT table of the |
| 12 | database, for more flexibility and better features. |
| 13 | <b>Run "fossil rebuild" to take advantage of this change</b>. |
| 14 | * Add the "contact" sub-command to [/help?cmd=user|fossil user]. |
| 15 | * Added commands "[/help?cmd=all|fossil all git export]" and |
| 16 | "[/help?cmd=all|fossil all git status]". |
| 17 | * Improvements to the "[/sitemap]" page. Add subpages |
| 18 | [/sitemap-timeline] and [/sitemap-test]. |
| 19 | * Better text position in cylinder objects of Pikchr diagrams. |
| 20 | * New "details.txt" settings available to custom skins to better control |
| 21 | the rendering of Pikchr diagrams: |
| 22 | <ul> |
| 23 | <li> pikchr-foreground |
| 24 | <li> pikchr-scale |
| 25 | <li> pikchr-fontscale |
| 26 | </ul> |
| 27 | * Allow the use of SQL functions inside the ticket table definition |
| 28 | for custom ticket configurations. |
| 29 | * Countless improvements and enhancements to the documentation |
| 30 | |
| 31 | <a name='v2_13'></a> |
| 32 | <h2>Changes for Version 2.13 (2020-11-01)</h2> |
| 33 | |
| 34 | * Added support for [./interwiki.md|interwiki links]. |
| 35 |
+26
-38
| --- www/ckout-workflows.md | ||
| +++ www/ckout-workflows.md | ||
| @@ -2,12 +2,11 @@ | ||
| 2 | 2 | |
| 3 | 3 | Because Fossil separates the concept of “check-out directory” from |
| 4 | 4 | “repository DB file,” it gives you the freedom to choose from several |
| 5 | 5 | working styles. Contrast Git, where the two concepts are normally |
| 6 | 6 | intermingled in a single working directory, which strongly encourages |
| 7 | -the “update in place” working style, leaving its `git-worktree` feature | |
| 8 | -underutilized. | |
| 7 | +the “update in place” working style. | |
| 9 | 8 | |
| 10 | 9 | |
| 11 | 10 | ## <a id="mcw"></a> Multiple-Checkout Workflow |
| 12 | 11 | |
| 13 | 12 | With Fossil, it is routine to have multiple check-outs from the same |
| @@ -48,13 +47,13 @@ | ||
| 48 | 47 | |
| 49 | 48 | Each check-out operates independently of the others. |
| 50 | 49 | |
| 51 | 50 | This multiple-checkouts working style is especially useful when Fossil stores source code in programming languages |
| 52 | 51 | where there is a “build” step that transforms source files into files |
| 53 | -you actually run or distribute. With Git’s typical switch-in-place workflow, | |
| 54 | -you have to rebuild all outputs from the source files | |
| 55 | -that differ between those versions whenever you switch versions. In the above Fossil working model, | |
| 52 | +you actually run or distribute. Contrast a switch-in-place workflow, | |
| 53 | +where you have to rebuild all outputs from the source files | |
| 54 | +that differ between those versions whenever you switch versions. In the above model, | |
| 56 | 55 | you switch versions with a “`cd`” command instead, so that you only have |
| 57 | 56 | to rebuild outputs from files you yourself change. |
| 58 | 57 | |
| 59 | 58 | This style is also useful when a check-out directory may be tied up with |
| 60 | 59 | some long-running process, as with the “test” example above, where you |
| @@ -72,13 +71,13 @@ | ||
| 72 | 71 | Nevertheless, it is possible to work in a more typical Git sort of |
| 73 | 72 | style, switching between versions in a single check-out directory. |
| 74 | 73 | |
| 75 | 74 | #### <a id="idiomatic"></a> The Idiomatic Fossil Way |
| 76 | 75 | |
| 77 | -With the clone done as in [the prior section](#mdw), the most idiomatic | |
| 78 | -way is as follows: | |
| 76 | +The most idiomatic way is as follows: | |
| 79 | 77 | |
| 78 | + fossil clone https://example.com/repo /path/to/repo.fossil | |
| 80 | 79 | mkdir work-dir |
| 81 | 80 | cd work-dir |
| 82 | 81 | fossil open /path/to/repo.fossil |
| 83 | 82 | ...work on trunk... |
| 84 | 83 | |
| @@ -87,56 +86,44 @@ | ||
| 87 | 86 | |
| 88 | 87 | Basically, you replace the `cd` commands in the multiple checkouts |
| 89 | 88 | workflow above with `fossil up` commands. |
| 90 | 89 | |
| 91 | 90 | |
| 92 | -#### <a id="open"></a> The Clone-and-Open Way | |
| 91 | +#### <a id="open"></a> Opening a Repository by URI | |
| 93 | 92 | |
| 94 | -In Fossil 2.12, we added a feature that allows you to get closer to | |
| 95 | -Git’s single-step clone-and-open behavior: | |
| 93 | +In Fossil 2.12, we added a feature to simplify the single-worktree use | |
| 94 | +case: | |
| 96 | 95 | |
| 97 | 96 | mkdir work-dir |
| 98 | 97 | cd work-dir |
| 99 | 98 | fossil open https://example.com/repo |
| 100 | 99 | |
| 101 | 100 | Now you have “trunk” open in `work-dir`, with the repo file stored as |
| 102 | 101 | `repo.fossil` in that same directory. |
| 103 | 102 | |
| 104 | -The use of [`fossil open`][open] here instead of [`fossil clone`][clone] | |
| 105 | -is likely to surprise a Git user. When we were [discussing][caod] | |
| 106 | -this, we considered following the Git command style, but we decided | |
| 107 | -against it because it goes against this core Fossil design principle: | |
| 108 | -given that the Fossil repo is separate from the check-out, why would you | |
| 109 | -expect asking for a repo clone to also create a check-out directory for | |
| 110 | -you? We view commingled repository + check-out as a design error in | |
| 111 | -Git, so why would we repeat the error? | |
| 112 | - | |
| 113 | -To see why we see this behavior is error-prone, consider that | |
| 114 | -`git clean` must have an exception to avoid nuking the `.git` directory. | |
| 115 | -We had to add that complication to `fossil clean` when we added the | |
| 116 | -`fossil open URI` feature: it won’t nuke the repo DB file. | |
| 117 | - | |
| 118 | -[clone]: /help?cmd=clone | |
| 119 | -[open]: /help?cmd=open | |
| 120 | - | |
| 121 | - | |
| 122 | -#### <a id="clone"></a> The Git Clone Way | |
| 123 | - | |
| 124 | -This feature didn’t placate many Git fans, though, so with Fossil 2.14 — | |
| 125 | -currently unreleased — we now allow this: | |
| 103 | +Users of Git may be surprised that it doesn’t create a directory for you | |
| 104 | +and that you `cd` into it *before* the clone-and-open step, not after. | |
| 105 | +This is because we’re overloading the “open” command, which already had | |
| 106 | +the behavior of opening into the current working directory. Changing it | |
| 107 | +to behave like `git clone` would therefore make the behavior surprising | |
| 108 | +to Fossil users. (See [our discussions][caod] if you want the full | |
| 109 | +details.) | |
| 110 | + | |
| 111 | + | |
| 112 | +#### <a id="clone"></a> Git-Like Clone-and-Open | |
| 113 | + | |
| 114 | +With Fossil 2.14 — currently unreleased — we added a more Git-like | |
| 115 | +alternative: | |
| 126 | 116 | |
| 127 | 117 | fossil clone https://fossil-scm.org/fossil |
| 118 | + cd fossil | |
| 128 | 119 | |
| 129 | 120 | This results in a `fossil.fossil` repo DB file and a `fossil/` working |
| 130 | 121 | directory. |
| 131 | 122 | |
| 132 | 123 | Note that our `clone URI` behavior does not commingle the repo and |
| 133 | -check-out, solving our major problem with the Git design, though we | |
| 134 | -still believe it to be confusing to have “clone” be part of “open,” and | |
| 135 | -still more confusing to have “open” part of “clone.” We prefer keeping | |
| 136 | -these operations entirely separate, either as at the [top of this | |
| 137 | -section](#scw) or [as in the prior one](#mcw). Still, please yourself. | |
| 124 | +check-out, solving our major problem with the Git design. | |
| 138 | 125 | |
| 139 | 126 | If you want the repo to be named something else, adjust the URL: |
| 140 | 127 | |
| 141 | 128 | fossil clone https://fossil-scm.org/fossil/fsl |
| 142 | 129 | |
| @@ -149,8 +136,9 @@ | ||
| 149 | 136 | fossil clone https://dev.example.com/repo/my-project |
| 150 | 137 | |
| 151 | 138 | The `/repo` addition is the key: whatever comes after is used as the |
| 152 | 139 | repository name. [See the docs][clone] for more details. |
| 153 | 140 | |
| 154 | -[caod]: https://fossil-scm.org/forum/forumpost/3f143cec74 | |
| 141 | +[caod]: https://fossil-scm.org/forum/forumpost/3f143cec74 | |
| 142 | +[clone]: /help?cmd=clone | |
| 155 | 143 | |
| 156 | 144 | <div style="height:50em" id="this-space-intentionally-left-blank"></div> |
| 157 | 145 |
| --- www/ckout-workflows.md | |
| +++ www/ckout-workflows.md | |
| @@ -2,12 +2,11 @@ | |
| 2 | |
| 3 | Because Fossil separates the concept of “check-out directory” from |
| 4 | “repository DB file,” it gives you the freedom to choose from several |
| 5 | working styles. Contrast Git, where the two concepts are normally |
| 6 | intermingled in a single working directory, which strongly encourages |
| 7 | the “update in place” working style, leaving its `git-worktree` feature |
| 8 | underutilized. |
| 9 | |
| 10 | |
| 11 | ## <a id="mcw"></a> Multiple-Checkout Workflow |
| 12 | |
| 13 | With Fossil, it is routine to have multiple check-outs from the same |
| @@ -48,13 +47,13 @@ | |
| 48 | |
| 49 | Each check-out operates independently of the others. |
| 50 | |
| 51 | This multiple-checkouts working style is especially useful when Fossil stores source code in programming languages |
| 52 | where there is a “build” step that transforms source files into files |
| 53 | you actually run or distribute. With Git’s typical switch-in-place workflow, |
| 54 | you have to rebuild all outputs from the source files |
| 55 | that differ between those versions whenever you switch versions. In the above Fossil working model, |
| 56 | you switch versions with a “`cd`” command instead, so that you only have |
| 57 | to rebuild outputs from files you yourself change. |
| 58 | |
| 59 | This style is also useful when a check-out directory may be tied up with |
| 60 | some long-running process, as with the “test” example above, where you |
| @@ -72,13 +71,13 @@ | |
| 72 | Nevertheless, it is possible to work in a more typical Git sort of |
| 73 | style, switching between versions in a single check-out directory. |
| 74 | |
| 75 | #### <a id="idiomatic"></a> The Idiomatic Fossil Way |
| 76 | |
| 77 | With the clone done as in [the prior section](#mdw), the most idiomatic |
| 78 | way is as follows: |
| 79 | |
| 80 | mkdir work-dir |
| 81 | cd work-dir |
| 82 | fossil open /path/to/repo.fossil |
| 83 | ...work on trunk... |
| 84 | |
| @@ -87,56 +86,44 @@ | |
| 87 | |
| 88 | Basically, you replace the `cd` commands in the multiple checkouts |
| 89 | workflow above with `fossil up` commands. |
| 90 | |
| 91 | |
| 92 | #### <a id="open"></a> The Clone-and-Open Way |
| 93 | |
| 94 | In Fossil 2.12, we added a feature that allows you to get closer to |
| 95 | Git’s single-step clone-and-open behavior: |
| 96 | |
| 97 | mkdir work-dir |
| 98 | cd work-dir |
| 99 | fossil open https://example.com/repo |
| 100 | |
| 101 | Now you have “trunk” open in `work-dir`, with the repo file stored as |
| 102 | `repo.fossil` in that same directory. |
| 103 | |
| 104 | The use of [`fossil open`][open] here instead of [`fossil clone`][clone] |
| 105 | is likely to surprise a Git user. When we were [discussing][caod] |
| 106 | this, we considered following the Git command style, but we decided |
| 107 | against it because it goes against this core Fossil design principle: |
| 108 | given that the Fossil repo is separate from the check-out, why would you |
| 109 | expect asking for a repo clone to also create a check-out directory for |
| 110 | you? We view commingled repository + check-out as a design error in |
| 111 | Git, so why would we repeat the error? |
| 112 | |
| 113 | To see why we see this behavior is error-prone, consider that |
| 114 | `git clean` must have an exception to avoid nuking the `.git` directory. |
| 115 | We had to add that complication to `fossil clean` when we added the |
| 116 | `fossil open URI` feature: it won’t nuke the repo DB file. |
| 117 | |
| 118 | [clone]: /help?cmd=clone |
| 119 | [open]: /help?cmd=open |
| 120 | |
| 121 | |
| 122 | #### <a id="clone"></a> The Git Clone Way |
| 123 | |
| 124 | This feature didn’t placate many Git fans, though, so with Fossil 2.14 — |
| 125 | currently unreleased — we now allow this: |
| 126 | |
| 127 | fossil clone https://fossil-scm.org/fossil |
| 128 | |
| 129 | This results in a `fossil.fossil` repo DB file and a `fossil/` working |
| 130 | directory. |
| 131 | |
| 132 | Note that our `clone URI` behavior does not commingle the repo and |
| 133 | check-out, solving our major problem with the Git design, though we |
| 134 | still believe it to be confusing to have “clone” be part of “open,” and |
| 135 | still more confusing to have “open” part of “clone.” We prefer keeping |
| 136 | these operations entirely separate, either as at the [top of this |
| 137 | section](#scw) or [as in the prior one](#mcw). Still, please yourself. |
| 138 | |
| 139 | If you want the repo to be named something else, adjust the URL: |
| 140 | |
| 141 | fossil clone https://fossil-scm.org/fossil/fsl |
| 142 | |
| @@ -149,8 +136,9 @@ | |
| 149 | fossil clone https://dev.example.com/repo/my-project |
| 150 | |
| 151 | The `/repo` addition is the key: whatever comes after is used as the |
| 152 | repository name. [See the docs][clone] for more details. |
| 153 | |
| 154 | [caod]: https://fossil-scm.org/forum/forumpost/3f143cec74 |
| 155 | |
| 156 | <div style="height:50em" id="this-space-intentionally-left-blank"></div> |
| 157 |
| --- www/ckout-workflows.md | |
| +++ www/ckout-workflows.md | |
| @@ -2,12 +2,11 @@ | |
| 2 | |
| 3 | Because Fossil separates the concept of “check-out directory” from |
| 4 | “repository DB file,” it gives you the freedom to choose from several |
| 5 | working styles. Contrast Git, where the two concepts are normally |
| 6 | intermingled in a single working directory, which strongly encourages |
| 7 | the “update in place” working style. |
| 8 | |
| 9 | |
| 10 | ## <a id="mcw"></a> Multiple-Checkout Workflow |
| 11 | |
| 12 | With Fossil, it is routine to have multiple check-outs from the same |
| @@ -48,13 +47,13 @@ | |
| 47 | |
| 48 | Each check-out operates independently of the others. |
| 49 | |
| 50 | This multiple-checkouts working style is especially useful when Fossil stores source code in programming languages |
| 51 | where there is a “build” step that transforms source files into files |
| 52 | you actually run or distribute. Contrast a switch-in-place workflow, |
| 53 | where you have to rebuild all outputs from the source files |
| 54 | that differ between those versions whenever you switch versions. In the above model, |
| 55 | you switch versions with a “`cd`” command instead, so that you only have |
| 56 | to rebuild outputs from files you yourself change. |
| 57 | |
| 58 | This style is also useful when a check-out directory may be tied up with |
| 59 | some long-running process, as with the “test” example above, where you |
| @@ -72,13 +71,13 @@ | |
| 71 | Nevertheless, it is possible to work in a more typical Git sort of |
| 72 | style, switching between versions in a single check-out directory. |
| 73 | |
| 74 | #### <a id="idiomatic"></a> The Idiomatic Fossil Way |
| 75 | |
| 76 | The most idiomatic way is as follows: |
| 77 | |
| 78 | fossil clone https://example.com/repo /path/to/repo.fossil |
| 79 | mkdir work-dir |
| 80 | cd work-dir |
| 81 | fossil open /path/to/repo.fossil |
| 82 | ...work on trunk... |
| 83 | |
| @@ -87,56 +86,44 @@ | |
| 86 | |
| 87 | Basically, you replace the `cd` commands in the multiple checkouts |
| 88 | workflow above with `fossil up` commands. |
| 89 | |
| 90 | |
| 91 | #### <a id="open"></a> Opening a Repository by URI |
| 92 | |
| 93 | In Fossil 2.12, we added a feature to simplify the single-worktree use |
| 94 | case: |
| 95 | |
| 96 | mkdir work-dir |
| 97 | cd work-dir |
| 98 | fossil open https://example.com/repo |
| 99 | |
| 100 | Now you have “trunk” open in `work-dir`, with the repo file stored as |
| 101 | `repo.fossil` in that same directory. |
| 102 | |
| 103 | Users of Git may be surprised that it doesn’t create a directory for you |
| 104 | and that you `cd` into it *before* the clone-and-open step, not after. |
| 105 | This is because we’re overloading the “open” command, which already had |
| 106 | the behavior of opening into the current working directory. Changing it |
| 107 | to behave like `git clone` would therefore make the behavior surprising |
| 108 | to Fossil users. (See [our discussions][caod] if you want the full |
| 109 | details.) |
| 110 | |
| 111 | |
| 112 | #### <a id="clone"></a> Git-Like Clone-and-Open |
| 113 | |
| 114 | With Fossil 2.14 — currently unreleased — we added a more Git-like |
| 115 | alternative: |
| 116 | |
| 117 | fossil clone https://fossil-scm.org/fossil |
| 118 | cd fossil |
| 119 | |
| 120 | This results in a `fossil.fossil` repo DB file and a `fossil/` working |
| 121 | directory. |
| 122 | |
| 123 | Note that our `clone URI` behavior does not commingle the repo and |
| 124 | check-out, solving our major problem with the Git design. |
| 125 | |
| 126 | If you want the repo to be named something else, adjust the URL: |
| 127 | |
| 128 | fossil clone https://fossil-scm.org/fossil/fsl |
| 129 | |
| @@ -149,8 +136,9 @@ | |
| 136 | fossil clone https://dev.example.com/repo/my-project |
| 137 | |
| 138 | The `/repo` addition is the key: whatever comes after is used as the |
| 139 | repository name. [See the docs][clone] for more details. |
| 140 | |
| 141 | [caod]: https://fossil-scm.org/forum/forumpost/3f143cec74 |
| 142 | [clone]: /help?cmd=clone |
| 143 | |
| 144 | <div style="height:50em" id="this-space-intentionally-left-blank"></div> |
| 145 |
+1
-1
| --- www/embeddeddoc.wiki | ||
| +++ www/embeddeddoc.wiki | ||
| @@ -56,11 +56,11 @@ | ||
| 56 | 56 | pull the documentation file from the local source tree on disk, not |
| 57 | 57 | from the any check-in. The "<b>ckout</b>" keyword |
| 58 | 58 | only works when you start your server using the |
| 59 | 59 | "[/help?cmd=server|fossil server]" or "[/help?cmd=ui|fossil ui]" |
| 60 | 60 | commands. The "/doc/ckout" URL is intended to show a preview of |
| 61 | -the documentation you are currently editing but have not yet you checked in. | |
| 61 | +the documentation you are currently editing but have not yet checked in. | |
| 62 | 62 | |
| 63 | 63 | The original designed purpose of the "ckout" feature is to allow the |
| 64 | 64 | user to preview local changes to documentation before committing the |
| 65 | 65 | change. This is an important facility, since unlike other document |
| 66 | 66 | languages like HTML, there is still a lot of variation among rendering |
| 67 | 67 |
| --- www/embeddeddoc.wiki | |
| +++ www/embeddeddoc.wiki | |
| @@ -56,11 +56,11 @@ | |
| 56 | pull the documentation file from the local source tree on disk, not |
| 57 | from the any check-in. The "<b>ckout</b>" keyword |
| 58 | only works when you start your server using the |
| 59 | "[/help?cmd=server|fossil server]" or "[/help?cmd=ui|fossil ui]" |
| 60 | commands. The "/doc/ckout" URL is intended to show a preview of |
| 61 | the documentation you are currently editing but have not yet you checked in. |
| 62 | |
| 63 | The original designed purpose of the "ckout" feature is to allow the |
| 64 | user to preview local changes to documentation before committing the |
| 65 | change. This is an important facility, since unlike other document |
| 66 | languages like HTML, there is still a lot of variation among rendering |
| 67 |
| --- www/embeddeddoc.wiki | |
| +++ www/embeddeddoc.wiki | |
| @@ -56,11 +56,11 @@ | |
| 56 | pull the documentation file from the local source tree on disk, not |
| 57 | from the any check-in. The "<b>ckout</b>" keyword |
| 58 | only works when you start your server using the |
| 59 | "[/help?cmd=server|fossil server]" or "[/help?cmd=ui|fossil ui]" |
| 60 | commands. The "/doc/ckout" URL is intended to show a preview of |
| 61 | the documentation you are currently editing but have not yet checked in. |
| 62 | |
| 63 | The original designed purpose of the "ckout" feature is to allow the |
| 64 | user to preview local changes to documentation before committing the |
| 65 | change. This is an important facility, since unlike other document |
| 66 | languages like HTML, there is still a lot of variation among rendering |
| 67 |
+6
-6
| --- www/fossil-v-git.wiki | ||
| +++ www/fossil-v-git.wiki | ||
| @@ -253,22 +253,22 @@ | ||
| 253 | 253 | |
| 254 | 254 | With Git, one can easily locate the ancestors of a particular check-in |
| 255 | 255 | by following the pointers embedded in the check-in object, but it is |
| 256 | 256 | difficult to go the other direction and locate the descendants of a |
| 257 | 257 | check-in. It is so difficult, in fact, that neither native Git nor |
| 258 | -GitHub provide this capability short of | |
| 259 | -[http://catb.org/jargon/html/G/grovel.html|groveling] the | |
| 258 | +GitHub provide this capability short of crawling the | |
| 260 | 259 | [https://www.git-scm.com/docs/git-log|commit log]. With Git, if you |
| 261 | 260 | are looking at some historical check-in then you cannot ask "What came |
| 262 | 261 | next?" or "What are the children of this check-in?" |
| 263 | 262 | |
| 264 | 263 | Fossil, on the other hand, parses essential information about check-ins |
| 265 | 264 | (parents, children, committers, comments, files changed, etc.) into a |
| 266 | 265 | relational database that can easily be queried using concise SQL |
| 267 | 266 | statements to find both ancestors and descendants of a check-in. This is |
| 268 | 267 | the hybrid data model mentioned above: Fossil manages your check-in and |
| 269 | -other data in a NoSQL block chain structured data store, but that's backed | |
| 268 | +other data in a NoSQL [https://en.wikipedia.org/wiki/Merkle_tree | | |
| 269 | +Merkle tree] structured data store, but that's backed | |
| 270 | 270 | by a set of relational lookup tables for quick indexing into that |
| 271 | 271 | artifact store. (See "[./theory1.wiki|Thoughts On The Design Of The |
| 272 | 272 | Fossil DVCS]" for more details.) |
| 273 | 273 | |
| 274 | 274 | Leaf check-ins in Git that lack a "ref" become "detached," making them |
| @@ -276,11 +276,11 @@ | ||
| 276 | 276 | [http://gitfaq.org/articles/what-is-a-detached-head.html|detached head |
| 277 | 277 | state] problem has caused untold grief for |
| 278 | 278 | [https://www.google.com/search?q=git+detached+head+state | a huge number |
| 279 | 279 | of Git users]. With |
| 280 | 280 | Fossil, detached heads are simply impossible because we can always find |
| 281 | -our way back into the block chain using one or more of the relational | |
| 281 | +our way back into the Merkle tree using one or more of the relational | |
| 282 | 282 | indices it automatically manages for you. |
| 283 | 283 | |
| 284 | 284 | This design difference shows up in several other places within each |
| 285 | 285 | tool. It is why Fossil's [/help?cmd=timeline|timeline] is generally more |
| 286 | 286 | detailed yet more clear than those available in Git front-ends. |
| @@ -906,11 +906,11 @@ | ||
| 906 | 906 | Almost three years after Fossil solved this problem, the |
| 907 | 907 | [https://sha-mbles.github.io/ | SHAmbles attack] was published, further |
| 908 | 908 | weakening the case for continuing to use SHA-1. |
| 909 | 909 | |
| 910 | 910 | The practical impact of attacks like SHAttered and SHAmbles on the |
| 911 | -Git and Fossil blockchains isn't clear, but you want to have your repositories | |
| 911 | +Git and Fossil Merkle trees isn't clear, but you want to have your repositories | |
| 912 | 912 | moved over to a stronger hash algorithm before someone figures out how |
| 913 | 913 | to make use of the weaknesses in the old one. Fossil has had this covered |
| 914 | 914 | for years now, so that the solution is now almost universally deployed. |
| 915 | 915 | |
| 916 | 916 | <hr/> |
| @@ -936,11 +936,11 @@ | ||
| 936 | 936 | isn't normally synchronized with a "<tt>fossil clone</tt>" command unless |
| 937 | 937 | you add the "-u" option. (See "[./aboutdownload.wiki|How the |
| 938 | 938 | Download Page Works]" for details.) There may also be some purely |
| 939 | 939 | static elements of the web site served via D. Richard Hipp's own |
| 940 | 940 | lightweight web server, |
| 941 | - <tt>[https://sqlite.org/docsrc/doc/trunk/misc/althttpd.md|althttpd]</tt>, | |
| 941 | + <tt>[https://sqlite.org/althttpd/|althttpd]</tt>, | |
| 942 | 942 | which is configured as a front end to Fossil running in CGI mode on |
| 943 | 943 | these sites. |
| 944 | 944 | |
| 945 | 945 | <li><p>That estimate is based on pricing at Digital Ocean in |
| 946 | 946 | mid-2019: Fossil will run just fine on the smallest instance they |
| 947 | 947 |
| --- www/fossil-v-git.wiki | |
| +++ www/fossil-v-git.wiki | |
| @@ -253,22 +253,22 @@ | |
| 253 | |
| 254 | With Git, one can easily locate the ancestors of a particular check-in |
| 255 | by following the pointers embedded in the check-in object, but it is |
| 256 | difficult to go the other direction and locate the descendants of a |
| 257 | check-in. It is so difficult, in fact, that neither native Git nor |
| 258 | GitHub provide this capability short of |
| 259 | [http://catb.org/jargon/html/G/grovel.html|groveling] the |
| 260 | [https://www.git-scm.com/docs/git-log|commit log]. With Git, if you |
| 261 | are looking at some historical check-in then you cannot ask "What came |
| 262 | next?" or "What are the children of this check-in?" |
| 263 | |
| 264 | Fossil, on the other hand, parses essential information about check-ins |
| 265 | (parents, children, committers, comments, files changed, etc.) into a |
| 266 | relational database that can easily be queried using concise SQL |
| 267 | statements to find both ancestors and descendants of a check-in. This is |
| 268 | the hybrid data model mentioned above: Fossil manages your check-in and |
| 269 | other data in a NoSQL block chain structured data store, but that's backed |
| 270 | by a set of relational lookup tables for quick indexing into that |
| 271 | artifact store. (See "[./theory1.wiki|Thoughts On The Design Of The |
| 272 | Fossil DVCS]" for more details.) |
| 273 | |
| 274 | Leaf check-ins in Git that lack a "ref" become "detached," making them |
| @@ -276,11 +276,11 @@ | |
| 276 | [http://gitfaq.org/articles/what-is-a-detached-head.html|detached head |
| 277 | state] problem has caused untold grief for |
| 278 | [https://www.google.com/search?q=git+detached+head+state | a huge number |
| 279 | of Git users]. With |
| 280 | Fossil, detached heads are simply impossible because we can always find |
| 281 | our way back into the block chain using one or more of the relational |
| 282 | indices it automatically manages for you. |
| 283 | |
| 284 | This design difference shows up in several other places within each |
| 285 | tool. It is why Fossil's [/help?cmd=timeline|timeline] is generally more |
| 286 | detailed yet more clear than those available in Git front-ends. |
| @@ -906,11 +906,11 @@ | |
| 906 | Almost three years after Fossil solved this problem, the |
| 907 | [https://sha-mbles.github.io/ | SHAmbles attack] was published, further |
| 908 | weakening the case for continuing to use SHA-1. |
| 909 | |
| 910 | The practical impact of attacks like SHAttered and SHAmbles on the |
| 911 | Git and Fossil blockchains isn't clear, but you want to have your repositories |
| 912 | moved over to a stronger hash algorithm before someone figures out how |
| 913 | to make use of the weaknesses in the old one. Fossil has had this covered |
| 914 | for years now, so that the solution is now almost universally deployed. |
| 915 | |
| 916 | <hr/> |
| @@ -936,11 +936,11 @@ | |
| 936 | isn't normally synchronized with a "<tt>fossil clone</tt>" command unless |
| 937 | you add the "-u" option. (See "[./aboutdownload.wiki|How the |
| 938 | Download Page Works]" for details.) There may also be some purely |
| 939 | static elements of the web site served via D. Richard Hipp's own |
| 940 | lightweight web server, |
| 941 | <tt>[https://sqlite.org/docsrc/doc/trunk/misc/althttpd.md|althttpd]</tt>, |
| 942 | which is configured as a front end to Fossil running in CGI mode on |
| 943 | these sites. |
| 944 | |
| 945 | <li><p>That estimate is based on pricing at Digital Ocean in |
| 946 | mid-2019: Fossil will run just fine on the smallest instance they |
| 947 |
| --- www/fossil-v-git.wiki | |
| +++ www/fossil-v-git.wiki | |
| @@ -253,22 +253,22 @@ | |
| 253 | |
| 254 | With Git, one can easily locate the ancestors of a particular check-in |
| 255 | by following the pointers embedded in the check-in object, but it is |
| 256 | difficult to go the other direction and locate the descendants of a |
| 257 | check-in. It is so difficult, in fact, that neither native Git nor |
| 258 | GitHub provide this capability short of crawling the |
| 259 | [https://www.git-scm.com/docs/git-log|commit log]. With Git, if you |
| 260 | are looking at some historical check-in then you cannot ask "What came |
| 261 | next?" or "What are the children of this check-in?" |
| 262 | |
| 263 | Fossil, on the other hand, parses essential information about check-ins |
| 264 | (parents, children, committers, comments, files changed, etc.) into a |
| 265 | relational database that can easily be queried using concise SQL |
| 266 | statements to find both ancestors and descendants of a check-in. This is |
| 267 | the hybrid data model mentioned above: Fossil manages your check-in and |
| 268 | other data in a NoSQL [https://en.wikipedia.org/wiki/Merkle_tree | |
| 269 | Merkle tree] structured data store, but that's backed |
| 270 | by a set of relational lookup tables for quick indexing into that |
| 271 | artifact store. (See "[./theory1.wiki|Thoughts On The Design Of The |
| 272 | Fossil DVCS]" for more details.) |
| 273 | |
| 274 | Leaf check-ins in Git that lack a "ref" become "detached," making them |
| @@ -276,11 +276,11 @@ | |
| 276 | [http://gitfaq.org/articles/what-is-a-detached-head.html|detached head |
| 277 | state] problem has caused untold grief for |
| 278 | [https://www.google.com/search?q=git+detached+head+state | a huge number |
| 279 | of Git users]. With |
| 280 | Fossil, detached heads are simply impossible because we can always find |
| 281 | our way back into the Merkle tree using one or more of the relational |
| 282 | indices it automatically manages for you. |
| 283 | |
| 284 | This design difference shows up in several other places within each |
| 285 | tool. It is why Fossil's [/help?cmd=timeline|timeline] is generally more |
| 286 | detailed yet more clear than those available in Git front-ends. |
| @@ -906,11 +906,11 @@ | |
| 906 | Almost three years after Fossil solved this problem, the |
| 907 | [https://sha-mbles.github.io/ | SHAmbles attack] was published, further |
| 908 | weakening the case for continuing to use SHA-1. |
| 909 | |
| 910 | The practical impact of attacks like SHAttered and SHAmbles on the |
| 911 | Git and Fossil Merkle trees isn't clear, but you want to have your repositories |
| 912 | moved over to a stronger hash algorithm before someone figures out how |
| 913 | to make use of the weaknesses in the old one. Fossil has had this covered |
| 914 | for years now, so that the solution is now almost universally deployed. |
| 915 | |
| 916 | <hr/> |
| @@ -936,11 +936,11 @@ | |
| 936 | isn't normally synchronized with a "<tt>fossil clone</tt>" command unless |
| 937 | you add the "-u" option. (See "[./aboutdownload.wiki|How the |
| 938 | Download Page Works]" for details.) There may also be some purely |
| 939 | static elements of the web site served via D. Richard Hipp's own |
| 940 | lightweight web server, |
| 941 | <tt>[https://sqlite.org/althttpd/|althttpd]</tt>, |
| 942 | which is configured as a front end to Fossil running in CGI mode on |
| 943 | these sites. |
| 944 | |
| 945 | <li><p>That estimate is based on pricing at Digital Ocean in |
| 946 | mid-2019: Fossil will run just fine on the smallest instance they |
| 947 |
+18
-10
| --- www/gitusers.md | ||
| +++ www/gitusers.md | ||
| @@ -687,20 +687,28 @@ | ||
| 687 | 687 | view][infow], though. |
| 688 | 688 | |
| 689 | 689 | |
| 690 | 690 | #### <a name="dstat"></a> Diff Statistics |
| 691 | 691 | |
| 692 | -Fossil doesn’t have an internal equivalent to commands like | |
| 693 | -`git show --stat`, but it’s easily remedied by using | |
| 694 | -[the widely-available `diffstat` tool][dst]: | |
| 695 | - | |
| 696 | - fossil diff -i --from 2020-04-01 | diffstat | |
| 697 | - | |
| 698 | -We gave the `-i` flag here to force Fossil to use its internal diff | |
| 699 | -implementation, bypassing [your local `diff-command` setting][dcset]. | |
| 700 | -If you had that set to [`colordiff`][cdiff], for example, its output | |
| 701 | -would confuse `diffstat`. | |
| 692 | +Fossil’s closest internal equivalent to commands like | |
| 693 | +`git show --stat` is: | |
| 694 | + | |
| 695 | + fossil diff -i --from 2020-04-01 --numstat | |
| 696 | + | |
| 697 | +The `--numstat` output is a bit cryptic, so we recommend delegating | |
| 698 | +this task to [the widely-available `diffstat` tool][dst]: | |
| 699 | + | |
| 700 | + fossil diff -i -N --from 2020-04-01 | diffstat | |
| 701 | + | |
| 702 | +We gave the `-i` flag in both cases to force Fossil to use its internal | |
| 703 | +diff implementation, bypassing [your local `diff-command` setting][dcset]. | |
| 704 | +The `--numstat` option has no effect when you have an external diff | |
| 705 | +command set, and some diff command alternatives like | |
| 706 | +[`colordiff`][cdiff] produce output that confuses `diffstat`. | |
| 707 | + | |
| 708 | +If you leave off the `-N` flag in the second example, the `diffstat` | |
| 709 | +output won’t include info about any newly-added files. | |
| 702 | 710 | |
| 703 | 711 | [cdiff]: https://www.colordiff.org/ |
| 704 | 712 | [dcset]: https://fossil-scm.org/home/help?cmd=diff-command |
| 705 | 713 | [dst]: https://invisible-island.net/diffstat/diffstat.html |
| 706 | 714 | |
| 707 | 715 |
| --- www/gitusers.md | |
| +++ www/gitusers.md | |
| @@ -687,20 +687,28 @@ | |
| 687 | view][infow], though. |
| 688 | |
| 689 | |
| 690 | #### <a name="dstat"></a> Diff Statistics |
| 691 | |
| 692 | Fossil doesn’t have an internal equivalent to commands like |
| 693 | `git show --stat`, but it’s easily remedied by using |
| 694 | [the widely-available `diffstat` tool][dst]: |
| 695 | |
| 696 | fossil diff -i --from 2020-04-01 | diffstat |
| 697 | |
| 698 | We gave the `-i` flag here to force Fossil to use its internal diff |
| 699 | implementation, bypassing [your local `diff-command` setting][dcset]. |
| 700 | If you had that set to [`colordiff`][cdiff], for example, its output |
| 701 | would confuse `diffstat`. |
| 702 | |
| 703 | [cdiff]: https://www.colordiff.org/ |
| 704 | [dcset]: https://fossil-scm.org/home/help?cmd=diff-command |
| 705 | [dst]: https://invisible-island.net/diffstat/diffstat.html |
| 706 | |
| 707 |
| --- www/gitusers.md | |
| +++ www/gitusers.md | |
| @@ -687,20 +687,28 @@ | |
| 687 | view][infow], though. |
| 688 | |
| 689 | |
| 690 | #### <a name="dstat"></a> Diff Statistics |
| 691 | |
| 692 | Fossil’s closest internal equivalent to commands like |
| 693 | `git show --stat` is: |
| 694 | |
| 695 | fossil diff -i --from 2020-04-01 --numstat |
| 696 | |
| 697 | The `--numstat` output is a bit cryptic, so we recommend delegating |
| 698 | this task to [the widely-available `diffstat` tool][dst]: |
| 699 | |
| 700 | fossil diff -i -N --from 2020-04-01 | diffstat |
| 701 | |
| 702 | We gave the `-i` flag in both cases to force Fossil to use its internal |
| 703 | diff implementation, bypassing [your local `diff-command` setting][dcset]. |
| 704 | The `--numstat` option has no effect when you have an external diff |
| 705 | command set, and some diff command alternatives like |
| 706 | [`colordiff`][cdiff] produce output that confuses `diffstat`. |
| 707 | |
| 708 | If you leave off the `-N` flag in the second example, the `diffstat` |
| 709 | output won’t include info about any newly-added files. |
| 710 | |
| 711 | [cdiff]: https://www.colordiff.org/ |
| 712 | [dcset]: https://fossil-scm.org/home/help?cmd=diff-command |
| 713 | [dst]: https://invisible-island.net/diffstat/diffstat.html |
| 714 | |
| 715 |
+1
| --- www/mkindex.tcl | ||
| +++ www/mkindex.tcl | ||
| @@ -148,10 +148,11 @@ | ||
| 148 | 148 | <li> <a href='history.md'>Purpose and History of Fossil</a> |
| 149 | 149 | <li> <a href='build.wiki'>Compiling and installing Fossil</a> |
| 150 | 150 | <li> <a href='../COPYRIGHT-BSD2.txt'>License</a> |
| 151 | 151 | <li> <a href='userlinks.wiki'>Miscellaneous Docs for Fossil Users</a> |
| 152 | 152 | <li> <a href='hacker-howto.wiki'>Fossil Developer's Guide</a> |
| 153 | +<li> <a href='$ROOT/wiki?name=To+Do+List'>To Do List (Wiki)</a> | |
| 153 | 154 | <li> <a href='http://www.fossil-scm.org/schimpf-book/home'>Jim Schimpf's |
| 154 | 155 | book</a> |
| 155 | 156 | </ul> |
| 156 | 157 | <a name="pindex"></a> |
| 157 | 158 | <h2>Permuted Index:</h2> |
| 158 | 159 |
| --- www/mkindex.tcl | |
| +++ www/mkindex.tcl | |
| @@ -148,10 +148,11 @@ | |
| 148 | <li> <a href='history.md'>Purpose and History of Fossil</a> |
| 149 | <li> <a href='build.wiki'>Compiling and installing Fossil</a> |
| 150 | <li> <a href='../COPYRIGHT-BSD2.txt'>License</a> |
| 151 | <li> <a href='userlinks.wiki'>Miscellaneous Docs for Fossil Users</a> |
| 152 | <li> <a href='hacker-howto.wiki'>Fossil Developer's Guide</a> |
| 153 | <li> <a href='http://www.fossil-scm.org/schimpf-book/home'>Jim Schimpf's |
| 154 | book</a> |
| 155 | </ul> |
| 156 | <a name="pindex"></a> |
| 157 | <h2>Permuted Index:</h2> |
| 158 |
| --- www/mkindex.tcl | |
| +++ www/mkindex.tcl | |
| @@ -148,10 +148,11 @@ | |
| 148 | <li> <a href='history.md'>Purpose and History of Fossil</a> |
| 149 | <li> <a href='build.wiki'>Compiling and installing Fossil</a> |
| 150 | <li> <a href='../COPYRIGHT-BSD2.txt'>License</a> |
| 151 | <li> <a href='userlinks.wiki'>Miscellaneous Docs for Fossil Users</a> |
| 152 | <li> <a href='hacker-howto.wiki'>Fossil Developer's Guide</a> |
| 153 | <li> <a href='$ROOT/wiki?name=To+Do+List'>To Do List (Wiki)</a> |
| 154 | <li> <a href='http://www.fossil-scm.org/schimpf-book/home'>Jim Schimpf's |
| 155 | book</a> |
| 156 | </ul> |
| 157 | <a name="pindex"></a> |
| 158 | <h2>Permuted Index:</h2> |
| 159 |
| --- www/permutedindex.html | ||
| +++ www/permutedindex.html | ||
| @@ -13,10 +13,11 @@ | ||
| 13 | 13 | <li> <a href='history.md'>Purpose and History of Fossil</a> |
| 14 | 14 | <li> <a href='build.wiki'>Compiling and installing Fossil</a> |
| 15 | 15 | <li> <a href='../COPYRIGHT-BSD2.txt'>License</a> |
| 16 | 16 | <li> <a href='userlinks.wiki'>Miscellaneous Docs for Fossil Users</a> |
| 17 | 17 | <li> <a href='hacker-howto.wiki'>Fossil Developer's Guide</a> |
| 18 | +<li> <a href='$ROOT/wiki?name=To+Do+List'>To Do List (Wiki)</a> | |
| 18 | 19 | <li> <a href='http://www.fossil-scm.org/schimpf-book/home'>Jim Schimpf's |
| 19 | 20 | book</a> |
| 20 | 21 | </ul> |
| 21 | 22 | <a name="pindex"></a> |
| 22 | 23 | <h2>Permuted Index:</h2> |
| 23 | 24 |
| --- www/permutedindex.html | |
| +++ www/permutedindex.html | |
| @@ -13,10 +13,11 @@ | |
| 13 | <li> <a href='history.md'>Purpose and History of Fossil</a> |
| 14 | <li> <a href='build.wiki'>Compiling and installing Fossil</a> |
| 15 | <li> <a href='../COPYRIGHT-BSD2.txt'>License</a> |
| 16 | <li> <a href='userlinks.wiki'>Miscellaneous Docs for Fossil Users</a> |
| 17 | <li> <a href='hacker-howto.wiki'>Fossil Developer's Guide</a> |
| 18 | <li> <a href='http://www.fossil-scm.org/schimpf-book/home'>Jim Schimpf's |
| 19 | book</a> |
| 20 | </ul> |
| 21 | <a name="pindex"></a> |
| 22 | <h2>Permuted Index:</h2> |
| 23 |
| --- www/permutedindex.html | |
| +++ www/permutedindex.html | |
| @@ -13,10 +13,11 @@ | |
| 13 | <li> <a href='history.md'>Purpose and History of Fossil</a> |
| 14 | <li> <a href='build.wiki'>Compiling and installing Fossil</a> |
| 15 | <li> <a href='../COPYRIGHT-BSD2.txt'>License</a> |
| 16 | <li> <a href='userlinks.wiki'>Miscellaneous Docs for Fossil Users</a> |
| 17 | <li> <a href='hacker-howto.wiki'>Fossil Developer's Guide</a> |
| 18 | <li> <a href='$ROOT/wiki?name=To+Do+List'>To Do List (Wiki)</a> |
| 19 | <li> <a href='http://www.fossil-scm.org/schimpf-book/home'>Jim Schimpf's |
| 20 | book</a> |
| 21 | </ul> |
| 22 | <a name="pindex"></a> |
| 23 | <h2>Permuted Index:</h2> |
| 24 |
+28
-45
| --- www/rebaseharm.md | ||
| +++ www/rebaseharm.md | ||
| @@ -297,63 +297,46 @@ | ||
| 297 | 297 | ## <a name="lying"></a>5.0 Rebasing is lying about the project history |
| 298 | 298 | |
| 299 | 299 | By discarding parentage information, rebase attempts to deceive the |
| 300 | 300 | reader about how the code actually came together. |
| 301 | 301 | |
| 302 | -The [Git rebase documentation][gitrebase] admits as much. They acknowledge | |
| 303 | -that when you view a repository as record of what actually happened, | |
| 304 | -doing a rebase is "blasphemous" and "you're _lying_ about what | |
| 305 | -actually happened", but then goes on to justify rebase as follows: | |
| 306 | - | |
| 307 | -> | |
| 308 | -_"The opposing point of view is that the commit history is the **story of | |
| 309 | -how your project was made.** You wouldn't publish the first draft of a | |
| 310 | -book, and the manual for how to maintain your software deserves careful | |
| 311 | -editing. This is the camp that uses tools like rebase and filter-branch | |
| 312 | -to tell the story in the way that's best for future readers."_ | |
| 313 | - | |
| 314 | -This counter-argument assumes you must | |
| 315 | -change history in order to enhance readability, which is not true. | |
| 316 | - | |
| 317 | -In fairness to the Git documentation authors, changing the | |
| 318 | -project history appears to be the only way to make editorial | |
| 319 | -changes in Git. | |
| 320 | -But it does not have to be that way. | |
| 321 | -Fossil demonstrates how "the story of your project" | |
| 322 | -can be enhanced without changing the actual history | |
| 323 | -by allowing users to: | |
| 302 | +You may be tempted to dismiss this as an anti-Git opinion on a Fossil | |
| 303 | +web site, but it’s spelled out just like that [in the Git rebase | |
| 304 | +documentation][gitrebase]. It speaks of “lying,” “telling stories,” | |
| 305 | +and “blasphemy.” | |
| 306 | + | |
| 307 | +That section of the Git docs is contrasting rebase with merge, which we | |
| 308 | +cover [above](#cap-loss), but Git’s rebase feature is more than just an | |
| 309 | +alternative to merging: it also provides mechanisms for changing the | |
| 310 | +project history in order to make editorial changes. Fossil shows that | |
| 311 | +you can get similar effects without modifying historical records, | |
| 312 | +allowing users to: | |
| 324 | 313 | |
| 325 | 314 | 1. Edit check-in comments to fix typos or enhance clarity |
| 326 | 315 | 2. Attach supplemental notes to check-ins or whole branches |
| 327 | - 3. Cross-reference check-ins with each other, or with | |
| 328 | - wiki, tickets, forum posts, and/or embedded documentation | |
| 329 | - 4. Hide ill-conceived or now-unused branches from routine display | |
| 330 | - 5. Fix faulty check-in date/times resulting from misconfigured | |
| 316 | + 3. Hide ill-conceived or now-unused branches from routine display | |
| 317 | + 4. Fix faulty check-in date/times resulting from misconfigured | |
| 331 | 318 | system clocks |
| 319 | + 5. Cross-reference check-ins with each other, or with | |
| 320 | + wiki, tickets, forum posts, and/or embedded documentation | |
| 332 | 321 | |
| 333 | 322 | …and so forth. |
| 334 | 323 | |
| 335 | -These changes are accomplished not by removing or modifying existing | |
| 324 | +Fossil allows all of this not by removing or modifying existing | |
| 336 | 325 | repository entries, but rather by adding new supplemental records. |
| 337 | -The original incorrect or unclear inputs are preserved and are | |
| 338 | -readily accessible. The original history is preserved. | |
| 339 | -But for routine display purposes, the more | |
| 340 | -readable edited presentation is provided. | |
| 341 | - | |
| 342 | -A repository can be a true and accurate | |
| 343 | -representation of history even without getting everything perfect | |
| 344 | -on the first draft. Those are not contradictory goals, at least | |
| 345 | -not in theory. | |
| 346 | - | |
| 347 | -Unfortunately, Git does not currently provide the ability to add | |
| 348 | -corrections or clarifications or supplimental notes to historical check-ins. | |
| 349 | -Hence, once again, | |
| 350 | -rebase can be seen as an attempt to work around limitations | |
| 351 | -of Git. Git could be enhanced to support editorial changes | |
| 352 | -to check-ins. | |
| 353 | -Wouldn't it be better to fix the version control tool | |
| 354 | -rather than requiring users to fabricate a fictitious project history? | |
| 326 | +Fossil keeps the original incorrect or unclear inputs and makes them | |
| 327 | +readily accessible, preserving the original historical record. Fossil | |
| 328 | +doesn’t make the user tell counter-factual “stories,” it only allows the | |
| 329 | +user to provide annotations to provide a more readable edited | |
| 330 | +presentation for routine display purposes. | |
| 331 | + | |
| 332 | +Git needs rebase because it lacks these annotation facilities. Rather | |
| 333 | +than consider rebase a desirable feature missing in Fossil, ask instead | |
| 334 | +why Git lacks support for making editorial changes to check-ins without | |
| 335 | +modifyihng history? Wouldn't it be better to fix the version control | |
| 336 | +tool rather than requiring users to fabricate a fictitious project | |
| 337 | +history? | |
| 355 | 338 | |
| 356 | 339 | ## <a name="collapsing"></a>6.0 Collapsing check-ins throws away valuable information |
| 357 | 340 | |
| 358 | 341 | One of the oft-cited advantages of rebasing in Git is that it lets you |
| 359 | 342 | collapse multiple check-ins down to a single check-in to make the |
| 360 | 343 |
| --- www/rebaseharm.md | |
| +++ www/rebaseharm.md | |
| @@ -297,63 +297,46 @@ | |
| 297 | ## <a name="lying"></a>5.0 Rebasing is lying about the project history |
| 298 | |
| 299 | By discarding parentage information, rebase attempts to deceive the |
| 300 | reader about how the code actually came together. |
| 301 | |
| 302 | The [Git rebase documentation][gitrebase] admits as much. They acknowledge |
| 303 | that when you view a repository as record of what actually happened, |
| 304 | doing a rebase is "blasphemous" and "you're _lying_ about what |
| 305 | actually happened", but then goes on to justify rebase as follows: |
| 306 | |
| 307 | > |
| 308 | _"The opposing point of view is that the commit history is the **story of |
| 309 | how your project was made.** You wouldn't publish the first draft of a |
| 310 | book, and the manual for how to maintain your software deserves careful |
| 311 | editing. This is the camp that uses tools like rebase and filter-branch |
| 312 | to tell the story in the way that's best for future readers."_ |
| 313 | |
| 314 | This counter-argument assumes you must |
| 315 | change history in order to enhance readability, which is not true. |
| 316 | |
| 317 | In fairness to the Git documentation authors, changing the |
| 318 | project history appears to be the only way to make editorial |
| 319 | changes in Git. |
| 320 | But it does not have to be that way. |
| 321 | Fossil demonstrates how "the story of your project" |
| 322 | can be enhanced without changing the actual history |
| 323 | by allowing users to: |
| 324 | |
| 325 | 1. Edit check-in comments to fix typos or enhance clarity |
| 326 | 2. Attach supplemental notes to check-ins or whole branches |
| 327 | 3. Cross-reference check-ins with each other, or with |
| 328 | wiki, tickets, forum posts, and/or embedded documentation |
| 329 | 4. Hide ill-conceived or now-unused branches from routine display |
| 330 | 5. Fix faulty check-in date/times resulting from misconfigured |
| 331 | system clocks |
| 332 | |
| 333 | …and so forth. |
| 334 | |
| 335 | These changes are accomplished not by removing or modifying existing |
| 336 | repository entries, but rather by adding new supplemental records. |
| 337 | The original incorrect or unclear inputs are preserved and are |
| 338 | readily accessible. The original history is preserved. |
| 339 | But for routine display purposes, the more |
| 340 | readable edited presentation is provided. |
| 341 | |
| 342 | A repository can be a true and accurate |
| 343 | representation of history even without getting everything perfect |
| 344 | on the first draft. Those are not contradictory goals, at least |
| 345 | not in theory. |
| 346 | |
| 347 | Unfortunately, Git does not currently provide the ability to add |
| 348 | corrections or clarifications or supplimental notes to historical check-ins. |
| 349 | Hence, once again, |
| 350 | rebase can be seen as an attempt to work around limitations |
| 351 | of Git. Git could be enhanced to support editorial changes |
| 352 | to check-ins. |
| 353 | Wouldn't it be better to fix the version control tool |
| 354 | rather than requiring users to fabricate a fictitious project history? |
| 355 | |
| 356 | ## <a name="collapsing"></a>6.0 Collapsing check-ins throws away valuable information |
| 357 | |
| 358 | One of the oft-cited advantages of rebasing in Git is that it lets you |
| 359 | collapse multiple check-ins down to a single check-in to make the |
| 360 |
| --- www/rebaseharm.md | |
| +++ www/rebaseharm.md | |
| @@ -297,63 +297,46 @@ | |
| 297 | ## <a name="lying"></a>5.0 Rebasing is lying about the project history |
| 298 | |
| 299 | By discarding parentage information, rebase attempts to deceive the |
| 300 | reader about how the code actually came together. |
| 301 | |
| 302 | You may be tempted to dismiss this as an anti-Git opinion on a Fossil |
| 303 | web site, but it’s spelled out just like that [in the Git rebase |
| 304 | documentation][gitrebase]. It speaks of “lying,” “telling stories,” |
| 305 | and “blasphemy.” |
| 306 | |
| 307 | That section of the Git docs is contrasting rebase with merge, which we |
| 308 | cover [above](#cap-loss), but Git’s rebase feature is more than just an |
| 309 | alternative to merging: it also provides mechanisms for changing the |
| 310 | project history in order to make editorial changes. Fossil shows that |
| 311 | you can get similar effects without modifying historical records, |
| 312 | allowing users to: |
| 313 | |
| 314 | 1. Edit check-in comments to fix typos or enhance clarity |
| 315 | 2. Attach supplemental notes to check-ins or whole branches |
| 316 | 3. Hide ill-conceived or now-unused branches from routine display |
| 317 | 4. Fix faulty check-in date/times resulting from misconfigured |
| 318 | system clocks |
| 319 | 5. Cross-reference check-ins with each other, or with |
| 320 | wiki, tickets, forum posts, and/or embedded documentation |
| 321 | |
| 322 | …and so forth. |
| 323 | |
| 324 | Fossil allows all of this not by removing or modifying existing |
| 325 | repository entries, but rather by adding new supplemental records. |
| 326 | Fossil keeps the original incorrect or unclear inputs and makes them |
| 327 | readily accessible, preserving the original historical record. Fossil |
| 328 | doesn’t make the user tell counter-factual “stories,” it only allows the |
| 329 | user to provide annotations to provide a more readable edited |
| 330 | presentation for routine display purposes. |
| 331 | |
| 332 | Git needs rebase because it lacks these annotation facilities. Rather |
| 333 | than consider rebase a desirable feature missing in Fossil, ask instead |
| 334 | why Git lacks support for making editorial changes to check-ins without |
| 335 | modifyihng history? Wouldn't it be better to fix the version control |
| 336 | tool rather than requiring users to fabricate a fictitious project |
| 337 | history? |
| 338 | |
| 339 | ## <a name="collapsing"></a>6.0 Collapsing check-ins throws away valuable information |
| 340 | |
| 341 | One of the oft-cited advantages of rebasing in Git is that it lets you |
| 342 | collapse multiple check-ins down to a single check-in to make the |
| 343 |
+28
-45
| --- www/rebaseharm.md | ||
| +++ www/rebaseharm.md | ||
| @@ -297,63 +297,46 @@ | ||
| 297 | 297 | ## <a name="lying"></a>5.0 Rebasing is lying about the project history |
| 298 | 298 | |
| 299 | 299 | By discarding parentage information, rebase attempts to deceive the |
| 300 | 300 | reader about how the code actually came together. |
| 301 | 301 | |
| 302 | -The [Git rebase documentation][gitrebase] admits as much. They acknowledge | |
| 303 | -that when you view a repository as record of what actually happened, | |
| 304 | -doing a rebase is "blasphemous" and "you're _lying_ about what | |
| 305 | -actually happened", but then goes on to justify rebase as follows: | |
| 306 | - | |
| 307 | -> | |
| 308 | -_"The opposing point of view is that the commit history is the **story of | |
| 309 | -how your project was made.** You wouldn't publish the first draft of a | |
| 310 | -book, and the manual for how to maintain your software deserves careful | |
| 311 | -editing. This is the camp that uses tools like rebase and filter-branch | |
| 312 | -to tell the story in the way that's best for future readers."_ | |
| 313 | - | |
| 314 | -This counter-argument assumes you must | |
| 315 | -change history in order to enhance readability, which is not true. | |
| 316 | - | |
| 317 | -In fairness to the Git documentation authors, changing the | |
| 318 | -project history appears to be the only way to make editorial | |
| 319 | -changes in Git. | |
| 320 | -But it does not have to be that way. | |
| 321 | -Fossil demonstrates how "the story of your project" | |
| 322 | -can be enhanced without changing the actual history | |
| 323 | -by allowing users to: | |
| 302 | +You may be tempted to dismiss this as an anti-Git opinion on a Fossil | |
| 303 | +web site, but it’s spelled out just like that [in the Git rebase | |
| 304 | +documentation][gitrebase]. It speaks of “lying,” “telling stories,” | |
| 305 | +and “blasphemy.” | |
| 306 | + | |
| 307 | +That section of the Git docs is contrasting rebase with merge, which we | |
| 308 | +cover [above](#cap-loss), but Git’s rebase feature is more than just an | |
| 309 | +alternative to merging: it also provides mechanisms for changing the | |
| 310 | +project history in order to make editorial changes. Fossil shows that | |
| 311 | +you can get similar effects without modifying historical records, | |
| 312 | +allowing users to: | |
| 324 | 313 | |
| 325 | 314 | 1. Edit check-in comments to fix typos or enhance clarity |
| 326 | 315 | 2. Attach supplemental notes to check-ins or whole branches |
| 327 | - 3. Cross-reference check-ins with each other, or with | |
| 328 | - wiki, tickets, forum posts, and/or embedded documentation | |
| 329 | - 4. Hide ill-conceived or now-unused branches from routine display | |
| 330 | - 5. Fix faulty check-in date/times resulting from misconfigured | |
| 316 | + 3. Hide ill-conceived or now-unused branches from routine display | |
| 317 | + 4. Fix faulty check-in date/times resulting from misconfigured | |
| 331 | 318 | system clocks |
| 319 | + 5. Cross-reference check-ins with each other, or with | |
| 320 | + wiki, tickets, forum posts, and/or embedded documentation | |
| 332 | 321 | |
| 333 | 322 | …and so forth. |
| 334 | 323 | |
| 335 | -These changes are accomplished not by removing or modifying existing | |
| 324 | +Fossil allows all of this not by removing or modifying existing | |
| 336 | 325 | repository entries, but rather by adding new supplemental records. |
| 337 | -The original incorrect or unclear inputs are preserved and are | |
| 338 | -readily accessible. The original history is preserved. | |
| 339 | -But for routine display purposes, the more | |
| 340 | -readable edited presentation is provided. | |
| 341 | - | |
| 342 | -A repository can be a true and accurate | |
| 343 | -representation of history even without getting everything perfect | |
| 344 | -on the first draft. Those are not contradictory goals, at least | |
| 345 | -not in theory. | |
| 346 | - | |
| 347 | -Unfortunately, Git does not currently provide the ability to add | |
| 348 | -corrections or clarifications or supplimental notes to historical check-ins. | |
| 349 | -Hence, once again, | |
| 350 | -rebase can be seen as an attempt to work around limitations | |
| 351 | -of Git. Git could be enhanced to support editorial changes | |
| 352 | -to check-ins. | |
| 353 | -Wouldn't it be better to fix the version control tool | |
| 354 | -rather than requiring users to fabricate a fictitious project history? | |
| 326 | +Fossil keeps the original incorrect or unclear inputs and makes them | |
| 327 | +readily accessible, preserving the original historical record. Fossil | |
| 328 | +doesn’t make the user tell counter-factual “stories,” it only allows the | |
| 329 | +user to provide annotations to provide a more readable edited | |
| 330 | +presentation for routine display purposes. | |
| 331 | + | |
| 332 | +Git needs rebase because it lacks these annotation facilities. Rather | |
| 333 | +than consider rebase a desirable feature missing in Fossil, ask instead | |
| 334 | +why Git lacks support for making editorial changes to check-ins without | |
| 335 | +modifyihng history? Wouldn't it be better to fix the version control | |
| 336 | +tool rather than requiring users to fabricate a fictitious project | |
| 337 | +history? | |
| 355 | 338 | |
| 356 | 339 | ## <a name="collapsing"></a>6.0 Collapsing check-ins throws away valuable information |
| 357 | 340 | |
| 358 | 341 | One of the oft-cited advantages of rebasing in Git is that it lets you |
| 359 | 342 | collapse multiple check-ins down to a single check-in to make the |
| 360 | 343 |
| --- www/rebaseharm.md | |
| +++ www/rebaseharm.md | |
| @@ -297,63 +297,46 @@ | |
| 297 | ## <a name="lying"></a>5.0 Rebasing is lying about the project history |
| 298 | |
| 299 | By discarding parentage information, rebase attempts to deceive the |
| 300 | reader about how the code actually came together. |
| 301 | |
| 302 | The [Git rebase documentation][gitrebase] admits as much. They acknowledge |
| 303 | that when you view a repository as record of what actually happened, |
| 304 | doing a rebase is "blasphemous" and "you're _lying_ about what |
| 305 | actually happened", but then goes on to justify rebase as follows: |
| 306 | |
| 307 | > |
| 308 | _"The opposing point of view is that the commit history is the **story of |
| 309 | how your project was made.** You wouldn't publish the first draft of a |
| 310 | book, and the manual for how to maintain your software deserves careful |
| 311 | editing. This is the camp that uses tools like rebase and filter-branch |
| 312 | to tell the story in the way that's best for future readers."_ |
| 313 | |
| 314 | This counter-argument assumes you must |
| 315 | change history in order to enhance readability, which is not true. |
| 316 | |
| 317 | In fairness to the Git documentation authors, changing the |
| 318 | project history appears to be the only way to make editorial |
| 319 | changes in Git. |
| 320 | But it does not have to be that way. |
| 321 | Fossil demonstrates how "the story of your project" |
| 322 | can be enhanced without changing the actual history |
| 323 | by allowing users to: |
| 324 | |
| 325 | 1. Edit check-in comments to fix typos or enhance clarity |
| 326 | 2. Attach supplemental notes to check-ins or whole branches |
| 327 | 3. Cross-reference check-ins with each other, or with |
| 328 | wiki, tickets, forum posts, and/or embedded documentation |
| 329 | 4. Hide ill-conceived or now-unused branches from routine display |
| 330 | 5. Fix faulty check-in date/times resulting from misconfigured |
| 331 | system clocks |
| 332 | |
| 333 | …and so forth. |
| 334 | |
| 335 | These changes are accomplished not by removing or modifying existing |
| 336 | repository entries, but rather by adding new supplemental records. |
| 337 | The original incorrect or unclear inputs are preserved and are |
| 338 | readily accessible. The original history is preserved. |
| 339 | But for routine display purposes, the more |
| 340 | readable edited presentation is provided. |
| 341 | |
| 342 | A repository can be a true and accurate |
| 343 | representation of history even without getting everything perfect |
| 344 | on the first draft. Those are not contradictory goals, at least |
| 345 | not in theory. |
| 346 | |
| 347 | Unfortunately, Git does not currently provide the ability to add |
| 348 | corrections or clarifications or supplimental notes to historical check-ins. |
| 349 | Hence, once again, |
| 350 | rebase can be seen as an attempt to work around limitations |
| 351 | of Git. Git could be enhanced to support editorial changes |
| 352 | to check-ins. |
| 353 | Wouldn't it be better to fix the version control tool |
| 354 | rather than requiring users to fabricate a fictitious project history? |
| 355 | |
| 356 | ## <a name="collapsing"></a>6.0 Collapsing check-ins throws away valuable information |
| 357 | |
| 358 | One of the oft-cited advantages of rebasing in Git is that it lets you |
| 359 | collapse multiple check-ins down to a single check-in to make the |
| 360 |
| --- www/rebaseharm.md | |
| +++ www/rebaseharm.md | |
| @@ -297,63 +297,46 @@ | |
| 297 | ## <a name="lying"></a>5.0 Rebasing is lying about the project history |
| 298 | |
| 299 | By discarding parentage information, rebase attempts to deceive the |
| 300 | reader about how the code actually came together. |
| 301 | |
| 302 | You may be tempted to dismiss this as an anti-Git opinion on a Fossil |
| 303 | web site, but it’s spelled out just like that [in the Git rebase |
| 304 | documentation][gitrebase]. It speaks of “lying,” “telling stories,” |
| 305 | and “blasphemy.” |
| 306 | |
| 307 | That section of the Git docs is contrasting rebase with merge, which we |
| 308 | cover [above](#cap-loss), but Git’s rebase feature is more than just an |
| 309 | alternative to merging: it also provides mechanisms for changing the |
| 310 | project history in order to make editorial changes. Fossil shows that |
| 311 | you can get similar effects without modifying historical records, |
| 312 | allowing users to: |
| 313 | |
| 314 | 1. Edit check-in comments to fix typos or enhance clarity |
| 315 | 2. Attach supplemental notes to check-ins or whole branches |
| 316 | 3. Hide ill-conceived or now-unused branches from routine display |
| 317 | 4. Fix faulty check-in date/times resulting from misconfigured |
| 318 | system clocks |
| 319 | 5. Cross-reference check-ins with each other, or with |
| 320 | wiki, tickets, forum posts, and/or embedded documentation |
| 321 | |
| 322 | …and so forth. |
| 323 | |
| 324 | Fossil allows all of this not by removing or modifying existing |
| 325 | repository entries, but rather by adding new supplemental records. |
| 326 | Fossil keeps the original incorrect or unclear inputs and makes them |
| 327 | readily accessible, preserving the original historical record. Fossil |
| 328 | doesn’t make the user tell counter-factual “stories,” it only allows the |
| 329 | user to provide annotations to provide a more readable edited |
| 330 | presentation for routine display purposes. |
| 331 | |
| 332 | Git needs rebase because it lacks these annotation facilities. Rather |
| 333 | than consider rebase a desirable feature missing in Fossil, ask instead |
| 334 | why Git lacks support for making editorial changes to check-ins without |
| 335 | modifyihng history? Wouldn't it be better to fix the version control |
| 336 | tool rather than requiring users to fabricate a fictitious project |
| 337 | history? |
| 338 | |
| 339 | ## <a name="collapsing"></a>6.0 Collapsing check-ins throws away valuable information |
| 340 | |
| 341 | One of the oft-cited advantages of rebasing in Git is that it lets you |
| 342 | collapse multiple check-ins down to a single check-in to make the |
| 343 |
+1
-1
| --- www/server/any/althttpd.md | ||
| +++ www/server/any/althttpd.md | ||
| @@ -36,7 +36,7 @@ | ||
| 36 | 36 | you created to start using your Fossil server. |
| 37 | 37 | |
| 38 | 38 | *[Return to the top-level Fossil server article.](../)* |
| 39 | 39 | |
| 40 | 40 | |
| 41 | -[althttpd]: https://sqlite.org/docsrc/doc/trunk/misc/althttpd.md | |
| 41 | +[althttpd]: https://sqlite.org/althttpd/ | |
| 42 | 42 | [cgi]: ../../cgi.wiki |
| 43 | 43 |
| --- www/server/any/althttpd.md | |
| +++ www/server/any/althttpd.md | |
| @@ -36,7 +36,7 @@ | |
| 36 | you created to start using your Fossil server. |
| 37 | |
| 38 | *[Return to the top-level Fossil server article.](../)* |
| 39 | |
| 40 | |
| 41 | [althttpd]: https://sqlite.org/docsrc/doc/trunk/misc/althttpd.md |
| 42 | [cgi]: ../../cgi.wiki |
| 43 |
| --- www/server/any/althttpd.md | |
| +++ www/server/any/althttpd.md | |
| @@ -36,7 +36,7 @@ | |
| 36 | you created to start using your Fossil server. |
| 37 | |
| 38 | *[Return to the top-level Fossil server article.](../)* |
| 39 | |
| 40 | |
| 41 | [althttpd]: https://sqlite.org/althttpd/ |
| 42 | [cgi]: ../../cgi.wiki |
| 43 |
+29
-8
| --- www/server/debian/nginx.md | ||
| +++ www/server/debian/nginx.md | ||
| @@ -217,25 +217,46 @@ | ||
| 217 | 217 | The most common thing people get wrong when hand-rolling a configuration |
| 218 | 218 | like this is to get the slashes wrong. Fossil is sensitive to this. For |
| 219 | 219 | instance, Fossil will not collapse double slashes down to a single |
| 220 | 220 | slash, as some other HTTP servers will. |
| 221 | 221 | |
| 222 | + | |
| 223 | +## <a name="large-uv"></a> Allowing Large Unversioned Files | |
| 224 | + | |
| 225 | +By default, nginx only accepts HTTP messages [up to a | |
| 226 | +meg](http://nginx.org/en/docs/http/ngx_http_core_module.html#client_max_body_size) | |
| 227 | +in size. Fossil chunks its sync protocol such that this is not normally | |
| 228 | +a problem, but when sending [unversioned content][uv], it uses a single | |
| 229 | +message for the entire file. Therefore, if you will be storing files | |
| 230 | +larger than this limit as unversioned content, you need to raise the | |
| 231 | +limit. Within the `location` block: | |
| 232 | + | |
| 233 | + # Allow large unversioned file uploads, such as PDFs | |
| 234 | + client_max_body_size 20M; | |
| 235 | + | |
| 236 | +[uv]: ../../unvers.wiki | |
| 237 | + | |
| 222 | 238 | |
| 223 | 239 | ## <a name="fail2ban"></a> Integrating `fail2ban` |
| 224 | 240 | |
| 225 | -You can have `fail2ban` recognize attacks and automatically block them, | |
| 226 | -but the stock configuration doesn’t work with our Fossil setup above, so | |
| 227 | -we have to do a bit of local adjustment. | |
| 241 | +One of the nice things that falls out of proxying Fossil behind nginx is | |
| 242 | +that it makes it easier to configure `fail2ban` to recognize attacks on | |
| 243 | +Fossil and automatically block them. Fossil logs the sorts of errors we | |
| 244 | +want to detect, but it does so in places like the repository’s admin | |
| 245 | +log, a SQL table, which `fail2ban` doesn’t know how to query. By putting | |
| 246 | +Fossil behind an nginx proxy, we convert these failures to log file | |
| 247 | +form, which `fail2ban` is designed to handle. | |
| 228 | 248 | |
| 229 | -First, install it: | |
| 249 | +First, install `fail2ban`, if you haven’t already: | |
| 230 | 250 | |
| 231 | 251 | sudo apt install fail2ban |
| 232 | 252 | |
| 233 | -Out of the box, you get SSH monitoring only. There are nginx monitors | |
| 234 | -included with the package, but they don’t look in the right places for | |
| 235 | -the right things. We’d like it to react to Fossil `/login` failures, for | |
| 236 | -example. Put the following into | |
| 253 | +We’d like `fail2ban` to react to Fossil `/login` failures. The stock | |
| 254 | +configuration of `fail2ban` only detects a few common sorts of SSH | |
| 255 | +attacks by default, and its included (but disabled) nginx attack | |
| 256 | +detectors don’t include one that knows how to detect an attack on | |
| 257 | +Fossil. We have to teach it by putting the following into | |
| 237 | 258 | `/etc/fail2ban/filter.d/nginx-fossil-login.conf`: |
| 238 | 259 | |
| 239 | 260 | [Definition] |
| 240 | 261 | failregex = ^<HOST> - .*POST .*/login HTTP/..." 401 |
| 241 | 262 | |
| 242 | 263 |
| --- www/server/debian/nginx.md | |
| +++ www/server/debian/nginx.md | |
| @@ -217,25 +217,46 @@ | |
| 217 | The most common thing people get wrong when hand-rolling a configuration |
| 218 | like this is to get the slashes wrong. Fossil is sensitive to this. For |
| 219 | instance, Fossil will not collapse double slashes down to a single |
| 220 | slash, as some other HTTP servers will. |
| 221 | |
| 222 | |
| 223 | ## <a name="fail2ban"></a> Integrating `fail2ban` |
| 224 | |
| 225 | You can have `fail2ban` recognize attacks and automatically block them, |
| 226 | but the stock configuration doesn’t work with our Fossil setup above, so |
| 227 | we have to do a bit of local adjustment. |
| 228 | |
| 229 | First, install it: |
| 230 | |
| 231 | sudo apt install fail2ban |
| 232 | |
| 233 | Out of the box, you get SSH monitoring only. There are nginx monitors |
| 234 | included with the package, but they don’t look in the right places for |
| 235 | the right things. We’d like it to react to Fossil `/login` failures, for |
| 236 | example. Put the following into |
| 237 | `/etc/fail2ban/filter.d/nginx-fossil-login.conf`: |
| 238 | |
| 239 | [Definition] |
| 240 | failregex = ^<HOST> - .*POST .*/login HTTP/..." 401 |
| 241 | |
| 242 |
| --- www/server/debian/nginx.md | |
| +++ www/server/debian/nginx.md | |
| @@ -217,25 +217,46 @@ | |
| 217 | The most common thing people get wrong when hand-rolling a configuration |
| 218 | like this is to get the slashes wrong. Fossil is sensitive to this. For |
| 219 | instance, Fossil will not collapse double slashes down to a single |
| 220 | slash, as some other HTTP servers will. |
| 221 | |
| 222 | |
| 223 | ## <a name="large-uv"></a> Allowing Large Unversioned Files |
| 224 | |
| 225 | By default, nginx only accepts HTTP messages [up to a |
| 226 | meg](http://nginx.org/en/docs/http/ngx_http_core_module.html#client_max_body_size) |
| 227 | in size. Fossil chunks its sync protocol such that this is not normally |
| 228 | a problem, but when sending [unversioned content][uv], it uses a single |
| 229 | message for the entire file. Therefore, if you will be storing files |
| 230 | larger than this limit as unversioned content, you need to raise the |
| 231 | limit. Within the `location` block: |
| 232 | |
| 233 | # Allow large unversioned file uploads, such as PDFs |
| 234 | client_max_body_size 20M; |
| 235 | |
| 236 | [uv]: ../../unvers.wiki |
| 237 | |
| 238 | |
| 239 | ## <a name="fail2ban"></a> Integrating `fail2ban` |
| 240 | |
| 241 | One of the nice things that falls out of proxying Fossil behind nginx is |
| 242 | that it makes it easier to configure `fail2ban` to recognize attacks on |
| 243 | Fossil and automatically block them. Fossil logs the sorts of errors we |
| 244 | want to detect, but it does so in places like the repository’s admin |
| 245 | log, a SQL table, which `fail2ban` doesn’t know how to query. By putting |
| 246 | Fossil behind an nginx proxy, we convert these failures to log file |
| 247 | form, which `fail2ban` is designed to handle. |
| 248 | |
| 249 | First, install `fail2ban`, if you haven’t already: |
| 250 | |
| 251 | sudo apt install fail2ban |
| 252 | |
| 253 | We’d like `fail2ban` to react to Fossil `/login` failures. The stock |
| 254 | configuration of `fail2ban` only detects a few common sorts of SSH |
| 255 | attacks by default, and its included (but disabled) nginx attack |
| 256 | detectors don’t include one that knows how to detect an attack on |
| 257 | Fossil. We have to teach it by putting the following into |
| 258 | `/etc/fail2ban/filter.d/nginx-fossil-login.conf`: |
| 259 | |
| 260 | [Definition] |
| 261 | failregex = ^<HOST> - .*POST .*/login HTTP/..." 401 |
| 262 | |
| 263 |
| --- www/server/openbsd/fastcgi.md | ||
| +++ www/server/openbsd/fastcgi.md | ||
| @@ -159,15 +159,27 @@ | ||
| 159 | 159 | root "/acme" |
| 160 | 160 | request strip 2 |
| 161 | 161 | } |
| 162 | 162 | } |
| 163 | 163 | ``` |
| 164 | + | |
| 165 | +[The default limit][dlim] for HTTP messages in OpenBSD’s `httpd` server | |
| 166 | +is 1 MiB. Fossil chunks its sync protocol such that this is not | |
| 167 | +normally a problem, but when sending [unversioned content][uv], it uses | |
| 168 | +a single message for the entire file. Therefore, if you will be storing | |
| 169 | +files larger than this limit as unversioned content, you need to raise | |
| 170 | +the limit as we’ve done above with the “`connection max request body`” | |
| 171 | +setting, raising the limit to 100 MiB. | |
| 172 | + | |
| 173 | +[dlim]: https://man.openbsd.org/httpd.conf.5#connection | |
| 174 | +[uv]: ../../unvers.wiki | |
| 164 | 175 | |
| 165 | 176 | **NOTE:** If not already in possession of a HTTPS certificate, comment |
| 166 | 177 | out the `https` server block and proceed to securing a free |
| 167 | 178 | [Let's Encrypt Certificate](#letsencrypt); otherwise skip to |
| 168 | 179 | [Start `httpd`](#starthttpd). |
| 180 | + | |
| 169 | 181 | |
| 170 | 182 | ## <a name="letsencrypt"></a>Let's Encrypt Certificate |
| 171 | 183 | |
| 172 | 184 | In order for `httpd` to serve HTTPS, secure a free certificate from |
| 173 | 185 | Let's Encrypt using `acme-client`. Before issuing the request, however, |
| 174 | 186 |
| --- www/server/openbsd/fastcgi.md | |
| +++ www/server/openbsd/fastcgi.md | |
| @@ -159,15 +159,27 @@ | |
| 159 | root "/acme" |
| 160 | request strip 2 |
| 161 | } |
| 162 | } |
| 163 | ``` |
| 164 | |
| 165 | **NOTE:** If not already in possession of a HTTPS certificate, comment |
| 166 | out the `https` server block and proceed to securing a free |
| 167 | [Let's Encrypt Certificate](#letsencrypt); otherwise skip to |
| 168 | [Start `httpd`](#starthttpd). |
| 169 | |
| 170 | ## <a name="letsencrypt"></a>Let's Encrypt Certificate |
| 171 | |
| 172 | In order for `httpd` to serve HTTPS, secure a free certificate from |
| 173 | Let's Encrypt using `acme-client`. Before issuing the request, however, |
| 174 |
| --- www/server/openbsd/fastcgi.md | |
| +++ www/server/openbsd/fastcgi.md | |
| @@ -159,15 +159,27 @@ | |
| 159 | root "/acme" |
| 160 | request strip 2 |
| 161 | } |
| 162 | } |
| 163 | ``` |
| 164 | |
| 165 | [The default limit][dlim] for HTTP messages in OpenBSD’s `httpd` server |
| 166 | is 1 MiB. Fossil chunks its sync protocol such that this is not |
| 167 | normally a problem, but when sending [unversioned content][uv], it uses |
| 168 | a single message for the entire file. Therefore, if you will be storing |
| 169 | files larger than this limit as unversioned content, you need to raise |
| 170 | the limit as we’ve done above with the “`connection max request body`” |
| 171 | setting, raising the limit to 100 MiB. |
| 172 | |
| 173 | [dlim]: https://man.openbsd.org/httpd.conf.5#connection |
| 174 | [uv]: ../../unvers.wiki |
| 175 | |
| 176 | **NOTE:** If not already in possession of a HTTPS certificate, comment |
| 177 | out the `https` server block and proceed to securing a free |
| 178 | [Let's Encrypt Certificate](#letsencrypt); otherwise skip to |
| 179 | [Start `httpd`](#starthttpd). |
| 180 | |
| 181 | |
| 182 | ## <a name="letsencrypt"></a>Let's Encrypt Certificate |
| 183 | |
| 184 | In order for `httpd` to serve HTTPS, secure a free certificate from |
| 185 | Let's Encrypt using `acme-client`. Before issuing the request, however, |
| 186 |
+1
-1
| --- www/sync.wiki | ||
| +++ www/sync.wiki | ||
| @@ -12,11 +12,11 @@ | ||
| 12 | 12 | repositories so that all repositories have copies of all artifacts. Because |
| 13 | 13 | artifacts are unordered, the order in which artifacts are received |
| 14 | 14 | is unimportant. It is assumed that the hash names |
| 15 | 15 | of artifacts are unique - that every artifact has a different hash. |
| 16 | 16 | To a first approximation, synchronization proceeds by sharing lists |
| 17 | -hash values for available artifacts, then sharing the content of artifacts | |
| 17 | +of hashes for available artifacts, then sharing the content of artifacts | |
| 18 | 18 | whose names are missing from one side or the other of the connection. |
| 19 | 19 | In practice, a repository might contain millions of artifacts. The list of |
| 20 | 20 | hash names for this many artifacts can be large. So optimizations are |
| 21 | 21 | employed that usually reduce the number of hashes that need to be |
| 22 | 22 | shared to a few hundred.</p> |
| 23 | 23 |
| --- www/sync.wiki | |
| +++ www/sync.wiki | |
| @@ -12,11 +12,11 @@ | |
| 12 | repositories so that all repositories have copies of all artifacts. Because |
| 13 | artifacts are unordered, the order in which artifacts are received |
| 14 | is unimportant. It is assumed that the hash names |
| 15 | of artifacts are unique - that every artifact has a different hash. |
| 16 | To a first approximation, synchronization proceeds by sharing lists |
| 17 | hash values for available artifacts, then sharing the content of artifacts |
| 18 | whose names are missing from one side or the other of the connection. |
| 19 | In practice, a repository might contain millions of artifacts. The list of |
| 20 | hash names for this many artifacts can be large. So optimizations are |
| 21 | employed that usually reduce the number of hashes that need to be |
| 22 | shared to a few hundred.</p> |
| 23 |
| --- www/sync.wiki | |
| +++ www/sync.wiki | |
| @@ -12,11 +12,11 @@ | |
| 12 | repositories so that all repositories have copies of all artifacts. Because |
| 13 | artifacts are unordered, the order in which artifacts are received |
| 14 | is unimportant. It is assumed that the hash names |
| 15 | of artifacts are unique - that every artifact has a different hash. |
| 16 | To a first approximation, synchronization proceeds by sharing lists |
| 17 | of hashes for available artifacts, then sharing the content of artifacts |
| 18 | whose names are missing from one side or the other of the connection. |
| 19 | In practice, a repository might contain millions of artifacts. The list of |
| 20 | hash names for this many artifacts can be large. So optimizations are |
| 21 | employed that usually reduce the number of hashes that need to be |
| 22 | shared to a few hundred.</p> |
| 23 |