Fossil SCM
Merge from trunk
Commit
f445855917422ecaef494af93c839fd6e0f8f237fc836a53b461543552828751
Parent
528d62894782127…
35 files changed
+1
-1
+2
-2
+2
-2
+2
-2
+3
-1
+1
-1
+2
-2
+1
-1
+4
-1
+2
-2
+10
-10
+19
-4
+33
-1
+4
+8
-4
+14
-3
+5
-5
+42
-1
+5
-1
+5
-1
+1
-1
+6
-6
+147
-80
+1
-1
+11
+3
-3
+12
-2
+23
-2
+21
-4
+21
-4
+2
-2
+2
-2
+1
+1
+231
~
VERSION
~
skins/ardoise/header.txt
~
skins/blitz/header.txt
~
skins/bootstrap/header.txt
~
skins/darkmode/css.txt
~
skins/darkmode/header.txt
~
skins/default/css.txt
~
skins/default/header.txt
~
skins/xekri/css.txt
~
skins/xekri/header.txt
~
src/chat.c
~
src/chat.js
~
src/db.c
~
src/default.css
~
src/forum.c
~
src/http_ssl.c
~
src/info.c
~
src/pikchr.c
~
src/report.c
~
src/report.c
~
src/schema.c
~
src/shell.c
~
src/sqlite3.c
~
src/sqlite3.h
~
src/stat.c
~
src/sync.c
~
src/th_main.c
~
src/tkt.c
~
www/changes.wiki
~
www/changes.wiki
~
www/index.wiki
~
www/javascript.md
~
www/mkindex.tcl
~
www/permutedindex.html
~
www/whyallinone.md
M
VERSION
+1
-1
| --- VERSION | ||
| +++ VERSION | ||
| @@ -1,1 +1,1 @@ | ||
| 1 | -2.15 | |
| 1 | +2.16 | |
| 2 | 2 |
| --- VERSION | |
| +++ VERSION | |
| @@ -1,1 +1,1 @@ | |
| 1 | 2.15 |
| 2 |
| --- VERSION | |
| +++ VERSION | |
| @@ -1,1 +1,1 @@ | |
| 1 | 2.16 |
| 2 |
+2
-2
| --- skins/ardoise/header.txt | ||
| +++ skins/ardoise/header.txt | ||
| @@ -26,12 +26,12 @@ | ||
| 26 | 26 | html "<li><a id='hbbtn' href='$home/sitemap' aria-label='Site Map'>☰</a></li>\n" |
| 27 | 27 | builtin_request_js hbmenu.js |
| 28 | 28 | set once 1 |
| 29 | 29 | foreach {name url expr class} $mainmenu { |
| 30 | 30 | if {![capexpr $expr]} continue |
| 31 | - if {$once && [string match /$current_page* $url]} { | |
| 32 | - lappend class active | |
| 31 | + if {$once && [string match $url\[/?#\]* /$current_page/]} { | |
| 32 | + set class "$class active" | |
| 33 | 33 | set once 0 |
| 34 | 34 | } |
| 35 | 35 | html "<li class='$class'>" |
| 36 | 36 | if {[string match /* $url]} {set url $home$url} |
| 37 | 37 | html "<a href='$url'>$name</a></li>\n" |
| 38 | 38 |
| --- skins/ardoise/header.txt | |
| +++ skins/ardoise/header.txt | |
| @@ -26,12 +26,12 @@ | |
| 26 | html "<li><a id='hbbtn' href='$home/sitemap' aria-label='Site Map'>☰</a></li>\n" |
| 27 | builtin_request_js hbmenu.js |
| 28 | set once 1 |
| 29 | foreach {name url expr class} $mainmenu { |
| 30 | if {![capexpr $expr]} continue |
| 31 | if {$once && [string match /$current_page* $url]} { |
| 32 | lappend class active |
| 33 | set once 0 |
| 34 | } |
| 35 | html "<li class='$class'>" |
| 36 | if {[string match /* $url]} {set url $home$url} |
| 37 | html "<a href='$url'>$name</a></li>\n" |
| 38 |
| --- skins/ardoise/header.txt | |
| +++ skins/ardoise/header.txt | |
| @@ -26,12 +26,12 @@ | |
| 26 | html "<li><a id='hbbtn' href='$home/sitemap' aria-label='Site Map'>☰</a></li>\n" |
| 27 | builtin_request_js hbmenu.js |
| 28 | set once 1 |
| 29 | foreach {name url expr class} $mainmenu { |
| 30 | if {![capexpr $expr]} continue |
| 31 | if {$once && [string match $url\[/?#\]* /$current_page/]} { |
| 32 | set class "$class active" |
| 33 | set once 0 |
| 34 | } |
| 35 | html "<li class='$class'>" |
| 36 | if {[string match /* $url]} {set url $home$url} |
| 37 | html "<a href='$url'>$name</a></li>\n" |
| 38 |
+2
-2
| --- skins/blitz/header.txt | ||
| +++ skins/blitz/header.txt | ||
| @@ -27,12 +27,12 @@ | ||
| 27 | 27 | html "<li><a id='hbbtn' href='$home/sitemap' aria-label='Site Map'>☰</a></li>\n" |
| 28 | 28 | builtin_request_js hbmenu.js |
| 29 | 29 | set once 1 |
| 30 | 30 | foreach {name url expr class} $mainmenu { |
| 31 | 31 | if {![capexpr $expr]} continue |
| 32 | - if {$once && [string match /$current_page* $url]} { | |
| 33 | - lappend class active | |
| 32 | + if {$once && [string match $url\[/?#\]* /$current_page/]} { | |
| 33 | + set class "active $class" | |
| 34 | 34 | set once 0 |
| 35 | 35 | } |
| 36 | 36 | html "<li class='$class'>" |
| 37 | 37 | if {[string match /* $url]} {set url $home$url} |
| 38 | 38 | html "<a href='$url'>$name</a></li>\n" |
| 39 | 39 |
| --- skins/blitz/header.txt | |
| +++ skins/blitz/header.txt | |
| @@ -27,12 +27,12 @@ | |
| 27 | html "<li><a id='hbbtn' href='$home/sitemap' aria-label='Site Map'>☰</a></li>\n" |
| 28 | builtin_request_js hbmenu.js |
| 29 | set once 1 |
| 30 | foreach {name url expr class} $mainmenu { |
| 31 | if {![capexpr $expr]} continue |
| 32 | if {$once && [string match /$current_page* $url]} { |
| 33 | lappend class active |
| 34 | set once 0 |
| 35 | } |
| 36 | html "<li class='$class'>" |
| 37 | if {[string match /* $url]} {set url $home$url} |
| 38 | html "<a href='$url'>$name</a></li>\n" |
| 39 |
| --- skins/blitz/header.txt | |
| +++ skins/blitz/header.txt | |
| @@ -27,12 +27,12 @@ | |
| 27 | html "<li><a id='hbbtn' href='$home/sitemap' aria-label='Site Map'>☰</a></li>\n" |
| 28 | builtin_request_js hbmenu.js |
| 29 | set once 1 |
| 30 | foreach {name url expr class} $mainmenu { |
| 31 | if {![capexpr $expr]} continue |
| 32 | if {$once && [string match $url\[/?#\]* /$current_page/]} { |
| 33 | set class "active $class" |
| 34 | set once 0 |
| 35 | } |
| 36 | html "<li class='$class'>" |
| 37 | if {[string match /* $url]} {set url $home$url} |
| 38 | html "<a href='$url'>$name</a></li>\n" |
| 39 |
+2
-2
| --- skins/bootstrap/header.txt | ||
| +++ skins/bootstrap/header.txt | ||
| @@ -45,12 +45,12 @@ | ||
| 45 | 45 | set sitemap 0 |
| 46 | 46 | set is_index [expr [string compare [string range $current_page 0 4] "index"]==0] |
| 47 | 47 | set is_home [expr [string compare [string range $current_page 0 [expr [string length $index_page]-1] ] $index_page]==0] |
| 48 | 48 | foreach {name url expr class} $mainmenu { |
| 49 | 49 | if {![capexpr $expr]} continue |
| 50 | - if {$once && [string match /$current_page* $url]} { | |
| 51 | - lappend class active | |
| 50 | + if {$once && [string match $url\[/?#\]* /$current_page/]} { | |
| 51 | + set class "active $class" | |
| 52 | 52 | set once 0 |
| 53 | 53 | } |
| 54 | 54 | html "<li class='$class'>" |
| 55 | 55 | if {[string match /* $url]} {set url $home$url} |
| 56 | 56 | if {[string match *sitemap* $url]} {set sitemap 1} |
| 57 | 57 |
| --- skins/bootstrap/header.txt | |
| +++ skins/bootstrap/header.txt | |
| @@ -45,12 +45,12 @@ | |
| 45 | set sitemap 0 |
| 46 | set is_index [expr [string compare [string range $current_page 0 4] "index"]==0] |
| 47 | set is_home [expr [string compare [string range $current_page 0 [expr [string length $index_page]-1] ] $index_page]==0] |
| 48 | foreach {name url expr class} $mainmenu { |
| 49 | if {![capexpr $expr]} continue |
| 50 | if {$once && [string match /$current_page* $url]} { |
| 51 | lappend class active |
| 52 | set once 0 |
| 53 | } |
| 54 | html "<li class='$class'>" |
| 55 | if {[string match /* $url]} {set url $home$url} |
| 56 | if {[string match *sitemap* $url]} {set sitemap 1} |
| 57 |
| --- skins/bootstrap/header.txt | |
| +++ skins/bootstrap/header.txt | |
| @@ -45,12 +45,12 @@ | |
| 45 | set sitemap 0 |
| 46 | set is_index [expr [string compare [string range $current_page 0 4] "index"]==0] |
| 47 | set is_home [expr [string compare [string range $current_page 0 [expr [string length $index_page]-1] ] $index_page]==0] |
| 48 | foreach {name url expr class} $mainmenu { |
| 49 | if {![capexpr $expr]} continue |
| 50 | if {$once && [string match $url\[/?#\]* /$current_page/]} { |
| 51 | set class "active $class" |
| 52 | set once 0 |
| 53 | } |
| 54 | html "<li class='$class'>" |
| 55 | if {[string match /* $url]} {set url $home$url} |
| 56 | if {[string match *sitemap* $url]} {set sitemap 1} |
| 57 |
+3
-1
| --- skins/darkmode/css.txt | ||
| +++ skins/darkmode/css.txt | ||
| @@ -85,11 +85,13 @@ | ||
| 85 | 85 | div.mainmenu a, div.submenu a, |
| 86 | 86 | div.sectionmenu>a.button, div.submenu label, |
| 87 | 87 | div.footer a { |
| 88 | 88 | padding: 0.15em 0.5em; |
| 89 | 89 | } |
| 90 | -/* div.mainmenu a.active, FIXME: setting of .active is broken for some URIs */ | |
| 90 | +div.mainmenu a.active { | |
| 91 | + border-bottom: 1px solid #FF4500f0; | |
| 92 | +} | |
| 91 | 93 | a:hover, |
| 92 | 94 | a:visited:hover { |
| 93 | 95 | background-color: #FF4500f0; |
| 94 | 96 | color: rgba(24,24,24,0.8); |
| 95 | 97 | border-radius: 0.1em; |
| 96 | 98 |
| --- skins/darkmode/css.txt | |
| +++ skins/darkmode/css.txt | |
| @@ -85,11 +85,13 @@ | |
| 85 | div.mainmenu a, div.submenu a, |
| 86 | div.sectionmenu>a.button, div.submenu label, |
| 87 | div.footer a { |
| 88 | padding: 0.15em 0.5em; |
| 89 | } |
| 90 | /* div.mainmenu a.active, FIXME: setting of .active is broken for some URIs */ |
| 91 | a:hover, |
| 92 | a:visited:hover { |
| 93 | background-color: #FF4500f0; |
| 94 | color: rgba(24,24,24,0.8); |
| 95 | border-radius: 0.1em; |
| 96 |
| --- skins/darkmode/css.txt | |
| +++ skins/darkmode/css.txt | |
| @@ -85,11 +85,13 @@ | |
| 85 | div.mainmenu a, div.submenu a, |
| 86 | div.sectionmenu>a.button, div.submenu label, |
| 87 | div.footer a { |
| 88 | padding: 0.15em 0.5em; |
| 89 | } |
| 90 | div.mainmenu a.active { |
| 91 | border-bottom: 1px solid #FF4500f0; |
| 92 | } |
| 93 | a:hover, |
| 94 | a:visited:hover { |
| 95 | background-color: #FF4500f0; |
| 96 | color: rgba(24,24,24,0.8); |
| 97 | border-radius: 0.1em; |
| 98 |
+1
-1
| --- skins/darkmode/header.txt | ||
| +++ skins/darkmode/header.txt | ||
| @@ -17,11 +17,11 @@ | ||
| 17 | 17 | html "<a id='hbbtn' href='$home/sitemap' aria-label='Site Map'>☰</a>" |
| 18 | 18 | builtin_request_js hbmenu.js |
| 19 | 19 | foreach {name url expr class} $mainmenu { |
| 20 | 20 | if {![capexpr $expr]} continue |
| 21 | 21 | if {[string match /* $url]} { |
| 22 | - if {[string match /$current_page* $url]} { | |
| 22 | + if {[string match $url\[/?#\]* /$current_page/]} { | |
| 23 | 23 | set class "active $class" |
| 24 | 24 | } |
| 25 | 25 | set url $home$url |
| 26 | 26 | } |
| 27 | 27 | html "<a href='$url' class='$class'>$name</a>\n" |
| 28 | 28 |
| --- skins/darkmode/header.txt | |
| +++ skins/darkmode/header.txt | |
| @@ -17,11 +17,11 @@ | |
| 17 | html "<a id='hbbtn' href='$home/sitemap' aria-label='Site Map'>☰</a>" |
| 18 | builtin_request_js hbmenu.js |
| 19 | foreach {name url expr class} $mainmenu { |
| 20 | if {![capexpr $expr]} continue |
| 21 | if {[string match /* $url]} { |
| 22 | if {[string match /$current_page* $url]} { |
| 23 | set class "active $class" |
| 24 | } |
| 25 | set url $home$url |
| 26 | } |
| 27 | html "<a href='$url' class='$class'>$name</a>\n" |
| 28 |
| --- skins/darkmode/header.txt | |
| +++ skins/darkmode/header.txt | |
| @@ -17,11 +17,11 @@ | |
| 17 | html "<a id='hbbtn' href='$home/sitemap' aria-label='Site Map'>☰</a>" |
| 18 | builtin_request_js hbmenu.js |
| 19 | foreach {name url expr class} $mainmenu { |
| 20 | if {![capexpr $expr]} continue |
| 21 | if {[string match /* $url]} { |
| 22 | if {[string match $url\[/?#\]* /$current_page/]} { |
| 23 | set class "active $class" |
| 24 | } |
| 25 | set url $home$url |
| 26 | } |
| 27 | html "<a href='$url' class='$class'>$name</a>\n" |
| 28 |
+2
-2
| --- skins/default/css.txt | ||
| +++ skins/default/css.txt | ||
| @@ -267,11 +267,11 @@ | ||
| 267 | 267 | padding-top: 0px; |
| 268 | 268 | padding-bottom: 0px; |
| 269 | 269 | } |
| 270 | 270 | .status {padding-top: 0px;} |
| 271 | 271 | .mainmenu a { |
| 272 | - padding: 10px 10px; | |
| 272 | + padding: 8px 10px; | |
| 273 | 273 | } |
| 274 | 274 | .mainmenu { |
| 275 | 275 | padding: 10px; |
| 276 | 276 | } |
| 277 | 277 | } |
| @@ -285,11 +285,11 @@ | ||
| 285 | 285 | padding-top: 10px; |
| 286 | 286 | padding-bottom: 10px; |
| 287 | 287 | } |
| 288 | 288 | .status {padding-top: 30px;} |
| 289 | 289 | .mainmenu a { |
| 290 | - padding: 10px 20px; | |
| 290 | + padding: 8px 20px; | |
| 291 | 291 | } |
| 292 | 292 | .mainmenu { |
| 293 | 293 | padding: 10px; |
| 294 | 294 | } |
| 295 | 295 | } |
| 296 | 296 |
| --- skins/default/css.txt | |
| +++ skins/default/css.txt | |
| @@ -267,11 +267,11 @@ | |
| 267 | padding-top: 0px; |
| 268 | padding-bottom: 0px; |
| 269 | } |
| 270 | .status {padding-top: 0px;} |
| 271 | .mainmenu a { |
| 272 | padding: 10px 10px; |
| 273 | } |
| 274 | .mainmenu { |
| 275 | padding: 10px; |
| 276 | } |
| 277 | } |
| @@ -285,11 +285,11 @@ | |
| 285 | padding-top: 10px; |
| 286 | padding-bottom: 10px; |
| 287 | } |
| 288 | .status {padding-top: 30px;} |
| 289 | .mainmenu a { |
| 290 | padding: 10px 20px; |
| 291 | } |
| 292 | .mainmenu { |
| 293 | padding: 10px; |
| 294 | } |
| 295 | } |
| 296 |
| --- skins/default/css.txt | |
| +++ skins/default/css.txt | |
| @@ -267,11 +267,11 @@ | |
| 267 | padding-top: 0px; |
| 268 | padding-bottom: 0px; |
| 269 | } |
| 270 | .status {padding-top: 0px;} |
| 271 | .mainmenu a { |
| 272 | padding: 8px 10px; |
| 273 | } |
| 274 | .mainmenu { |
| 275 | padding: 10px; |
| 276 | } |
| 277 | } |
| @@ -285,11 +285,11 @@ | |
| 285 | padding-top: 10px; |
| 286 | padding-bottom: 10px; |
| 287 | } |
| 288 | .status {padding-top: 30px;} |
| 289 | .mainmenu a { |
| 290 | padding: 8px 20px; |
| 291 | } |
| 292 | .mainmenu { |
| 293 | padding: 10px; |
| 294 | } |
| 295 | } |
| 296 |
+1
-1
| --- skins/default/header.txt | ||
| +++ skins/default/header.txt | ||
| @@ -13,11 +13,11 @@ | ||
| 13 | 13 | html "<a id='hbbtn' href='$home/sitemap' aria-label='Site Map'>☰</a>" |
| 14 | 14 | builtin_request_js hbmenu.js |
| 15 | 15 | foreach {name url expr class} $mainmenu { |
| 16 | 16 | if {![capexpr $expr]} continue |
| 17 | 17 | if {[string match /* $url]} { |
| 18 | - if {[string match /$current_page* $url]} { | |
| 18 | + if {[string match $url\[/?#\]* /$current_page/]} { | |
| 19 | 19 | set class "active $class" |
| 20 | 20 | } |
| 21 | 21 | set url $home$url |
| 22 | 22 | } |
| 23 | 23 | html "<a href='$url' class='$class'>$name</a>\n" |
| 24 | 24 |
| --- skins/default/header.txt | |
| +++ skins/default/header.txt | |
| @@ -13,11 +13,11 @@ | |
| 13 | html "<a id='hbbtn' href='$home/sitemap' aria-label='Site Map'>☰</a>" |
| 14 | builtin_request_js hbmenu.js |
| 15 | foreach {name url expr class} $mainmenu { |
| 16 | if {![capexpr $expr]} continue |
| 17 | if {[string match /* $url]} { |
| 18 | if {[string match /$current_page* $url]} { |
| 19 | set class "active $class" |
| 20 | } |
| 21 | set url $home$url |
| 22 | } |
| 23 | html "<a href='$url' class='$class'>$name</a>\n" |
| 24 |
| --- skins/default/header.txt | |
| +++ skins/default/header.txt | |
| @@ -13,11 +13,11 @@ | |
| 13 | html "<a id='hbbtn' href='$home/sitemap' aria-label='Site Map'>☰</a>" |
| 14 | builtin_request_js hbmenu.js |
| 15 | foreach {name url expr class} $mainmenu { |
| 16 | if {![capexpr $expr]} continue |
| 17 | if {[string match /* $url]} { |
| 18 | if {[string match $url\[/?#\]* /$current_page/]} { |
| 19 | set class "active $class" |
| 20 | } |
| 21 | set url $home$url |
| 22 | } |
| 23 | html "<a href='$url' class='$class'>$name</a>\n" |
| 24 |
+4
-1
| --- skins/xekri/css.txt | ||
| +++ skins/xekri/css.txt | ||
| @@ -1124,11 +1124,14 @@ | ||
| 1124 | 1124 | .fossil-PopupWidget, |
| 1125 | 1125 | .fossil-tooltip.help-buttonlet-content { |
| 1126 | 1126 | background-color: #111; |
| 1127 | 1127 | border: 1px solid rgba(255,255,255,0.5); |
| 1128 | 1128 | } |
| 1129 | - | |
| 1129 | +.fossil-PopupWidget a, | |
| 1130 | +.fossil-PopupWidget a:visited { | |
| 1131 | + color: white; | |
| 1132 | +} | |
| 1130 | 1133 | div.forumSel { |
| 1131 | 1134 | background-color: #663399; |
| 1132 | 1135 | } |
| 1133 | 1136 | div.forumPostBody blockquote { |
| 1134 | 1137 | border-width: 1pt; |
| 1135 | 1138 |
| --- skins/xekri/css.txt | |
| +++ skins/xekri/css.txt | |
| @@ -1124,11 +1124,14 @@ | |
| 1124 | .fossil-PopupWidget, |
| 1125 | .fossil-tooltip.help-buttonlet-content { |
| 1126 | background-color: #111; |
| 1127 | border: 1px solid rgba(255,255,255,0.5); |
| 1128 | } |
| 1129 | |
| 1130 | div.forumSel { |
| 1131 | background-color: #663399; |
| 1132 | } |
| 1133 | div.forumPostBody blockquote { |
| 1134 | border-width: 1pt; |
| 1135 |
| --- skins/xekri/css.txt | |
| +++ skins/xekri/css.txt | |
| @@ -1124,11 +1124,14 @@ | |
| 1124 | .fossil-PopupWidget, |
| 1125 | .fossil-tooltip.help-buttonlet-content { |
| 1126 | background-color: #111; |
| 1127 | border: 1px solid rgba(255,255,255,0.5); |
| 1128 | } |
| 1129 | .fossil-PopupWidget a, |
| 1130 | .fossil-PopupWidget a:visited { |
| 1131 | color: white; |
| 1132 | } |
| 1133 | div.forumSel { |
| 1134 | background-color: #663399; |
| 1135 | } |
| 1136 | div.forumPostBody blockquote { |
| 1137 | border-width: 1pt; |
| 1138 |
+2
-2
| --- skins/xekri/header.txt | ||
| +++ skins/xekri/header.txt | ||
| @@ -94,12 +94,12 @@ | ||
| 94 | 94 | <div class="mainmenu"><th1> |
| 95 | 95 | set sitemap 0 |
| 96 | 96 | foreach {name url expr class} $mainmenu { |
| 97 | 97 | if {![capexpr $expr]} continue |
| 98 | 98 | if {[string match /* $url]} { |
| 99 | - if {[string match /$current_page* $url]} { | |
| 100 | - lappend class active | |
| 99 | + if {[string match $url\[/?#\]* /$current_page/]} { | |
| 100 | + set class "active $class" | |
| 101 | 101 | } |
| 102 | 102 | set url $home$url |
| 103 | 103 | } |
| 104 | 104 | html "<a href='$url' class='$class'>$name</a>\n" |
| 105 | 105 | if {[string match */sitemap $url]} {set sitemap 1} |
| 106 | 106 |
| --- skins/xekri/header.txt | |
| +++ skins/xekri/header.txt | |
| @@ -94,12 +94,12 @@ | |
| 94 | <div class="mainmenu"><th1> |
| 95 | set sitemap 0 |
| 96 | foreach {name url expr class} $mainmenu { |
| 97 | if {![capexpr $expr]} continue |
| 98 | if {[string match /* $url]} { |
| 99 | if {[string match /$current_page* $url]} { |
| 100 | lappend class active |
| 101 | } |
| 102 | set url $home$url |
| 103 | } |
| 104 | html "<a href='$url' class='$class'>$name</a>\n" |
| 105 | if {[string match */sitemap $url]} {set sitemap 1} |
| 106 |
| --- skins/xekri/header.txt | |
| +++ skins/xekri/header.txt | |
| @@ -94,12 +94,12 @@ | |
| 94 | <div class="mainmenu"><th1> |
| 95 | set sitemap 0 |
| 96 | foreach {name url expr class} $mainmenu { |
| 97 | if {![capexpr $expr]} continue |
| 98 | if {[string match /* $url]} { |
| 99 | if {[string match $url\[/?#\]* /$current_page/]} { |
| 100 | set class "active $class" |
| 101 | } |
| 102 | set url $home$url |
| 103 | } |
| 104 | html "<a href='$url' class='$class'>$name</a>\n" |
| 105 | if {[string match */sitemap $url]} {set sitemap 1} |
| 106 |
+10
-10
| --- src/chat.c | ||
| +++ src/chat.c | ||
| @@ -213,18 +213,18 @@ | ||
| 213 | 213 | /* Definition of repository tables used by chat |
| 214 | 214 | */ |
| 215 | 215 | static const char zChatSchema1[] = |
| 216 | 216 | @ CREATE TABLE repository.chat( |
| 217 | 217 | @ msgid INTEGER PRIMARY KEY AUTOINCREMENT, |
| 218 | -@ mtime JULIANDAY, -- Time for this entry - Julianday Zulu | |
| 219 | -@ lmtime TEXT, -- Localtime when message originally sent | |
| 220 | -@ xfrom TEXT, -- Login of the sender | |
| 221 | -@ xmsg TEXT, -- Raw, unformatted text of the message | |
| 222 | -@ fname TEXT, -- Filename of the uploaded file, or NULL | |
| 223 | -@ fmime TEXT, -- MIMEType of the upload file, or NULL | |
| 224 | -@ mdel INT, -- msgid of another message to delete | |
| 225 | -@ file BLOB -- Text of the uploaded file, or NULL | |
| 218 | +@ mtime JULIANDAY, -- Time for this entry - Julianday Zulu | |
| 219 | +@ lmtime TEXT, -- Client YYYY-MM-DDZHH:MM:SS when message originally sent | |
| 220 | +@ xfrom TEXT, -- Login of the sender | |
| 221 | +@ xmsg TEXT, -- Raw, unformatted text of the message | |
| 222 | +@ fname TEXT, -- Filename of the uploaded file, or NULL | |
| 223 | +@ fmime TEXT, -- MIMEType of the upload file, or NULL | |
| 224 | +@ mdel INT, -- msgid of another message to delete | |
| 225 | +@ file BLOB -- Text of the uploaded file, or NULL | |
| 226 | 226 | @ ); |
| 227 | 227 | ; |
| 228 | 228 | |
| 229 | 229 | |
| 230 | 230 | /* |
| @@ -483,12 +483,12 @@ | ||
| 483 | 483 | ** |
| 484 | 484 | ** | { |
| 485 | 485 | ** | "msgs":[ |
| 486 | 486 | ** | { |
| 487 | 487 | ** | "msgid": integer // message id |
| 488 | -** | "mtime": text // When sent: YYYY-MM-DD HH:MM:SS UTC | |
| 489 | -** | "lmtime: text // Localtime where the message was sent from | |
| 488 | +** | "mtime": text // When sent: YYYY-MM-DDTHH:MM:SSZ | |
| 489 | +** | "lmtime: text // Sender's client-side YYYY-MM-DDTHH:MM:SS | |
| 490 | 490 | ** | "xfrom": text // Login name of sender |
| 491 | 491 | ** | "uclr": text // Color string associated with the user |
| 492 | 492 | ** | "xmsg": text // HTML text of the message |
| 493 | 493 | ** | "fsize": integer // file attachment size in bytes |
| 494 | 494 | ** | "fname": text // Name of file attachment |
| 495 | 495 |
| --- src/chat.c | |
| +++ src/chat.c | |
| @@ -213,18 +213,18 @@ | |
| 213 | /* Definition of repository tables used by chat |
| 214 | */ |
| 215 | static const char zChatSchema1[] = |
| 216 | @ CREATE TABLE repository.chat( |
| 217 | @ msgid INTEGER PRIMARY KEY AUTOINCREMENT, |
| 218 | @ mtime JULIANDAY, -- Time for this entry - Julianday Zulu |
| 219 | @ lmtime TEXT, -- Localtime when message originally sent |
| 220 | @ xfrom TEXT, -- Login of the sender |
| 221 | @ xmsg TEXT, -- Raw, unformatted text of the message |
| 222 | @ fname TEXT, -- Filename of the uploaded file, or NULL |
| 223 | @ fmime TEXT, -- MIMEType of the upload file, or NULL |
| 224 | @ mdel INT, -- msgid of another message to delete |
| 225 | @ file BLOB -- Text of the uploaded file, or NULL |
| 226 | @ ); |
| 227 | ; |
| 228 | |
| 229 | |
| 230 | /* |
| @@ -483,12 +483,12 @@ | |
| 483 | ** |
| 484 | ** | { |
| 485 | ** | "msgs":[ |
| 486 | ** | { |
| 487 | ** | "msgid": integer // message id |
| 488 | ** | "mtime": text // When sent: YYYY-MM-DD HH:MM:SS UTC |
| 489 | ** | "lmtime: text // Localtime where the message was sent from |
| 490 | ** | "xfrom": text // Login name of sender |
| 491 | ** | "uclr": text // Color string associated with the user |
| 492 | ** | "xmsg": text // HTML text of the message |
| 493 | ** | "fsize": integer // file attachment size in bytes |
| 494 | ** | "fname": text // Name of file attachment |
| 495 |
| --- src/chat.c | |
| +++ src/chat.c | |
| @@ -213,18 +213,18 @@ | |
| 213 | /* Definition of repository tables used by chat |
| 214 | */ |
| 215 | static const char zChatSchema1[] = |
| 216 | @ CREATE TABLE repository.chat( |
| 217 | @ msgid INTEGER PRIMARY KEY AUTOINCREMENT, |
| 218 | @ mtime JULIANDAY, -- Time for this entry - Julianday Zulu |
| 219 | @ lmtime TEXT, -- Client YYYY-MM-DDZHH:MM:SS when message originally sent |
| 220 | @ xfrom TEXT, -- Login of the sender |
| 221 | @ xmsg TEXT, -- Raw, unformatted text of the message |
| 222 | @ fname TEXT, -- Filename of the uploaded file, or NULL |
| 223 | @ fmime TEXT, -- MIMEType of the upload file, or NULL |
| 224 | @ mdel INT, -- msgid of another message to delete |
| 225 | @ file BLOB -- Text of the uploaded file, or NULL |
| 226 | @ ); |
| 227 | ; |
| 228 | |
| 229 | |
| 230 | /* |
| @@ -483,12 +483,12 @@ | |
| 483 | ** |
| 484 | ** | { |
| 485 | ** | "msgs":[ |
| 486 | ** | { |
| 487 | ** | "msgid": integer // message id |
| 488 | ** | "mtime": text // When sent: YYYY-MM-DDTHH:MM:SSZ |
| 489 | ** | "lmtime: text // Sender's client-side YYYY-MM-DDTHH:MM:SS |
| 490 | ** | "xfrom": text // Login name of sender |
| 491 | ** | "uclr": text // Color string associated with the user |
| 492 | ** | "xmsg": text // HTML text of the message |
| 493 | ** | "fsize": integer // file attachment size in bytes |
| 494 | ** | "fname": text // Name of file attachment |
| 495 |
+19
-4
| --- src/chat.js | ||
| +++ src/chat.js | ||
| @@ -94,10 +94,12 @@ | ||
| 94 | 94 | return resized; |
| 95 | 95 | })(); |
| 96 | 96 | fossil.FRK = ForceResizeKludge/*for debugging*/; |
| 97 | 97 | const Chat = (function(){ |
| 98 | 98 | const cs = { |
| 99 | + verboseErrors: false /* if true then certain, mostly extraneous, | |
| 100 | + error messages may be sent to the console. */, | |
| 99 | 101 | e:{/*map of certain DOM elements.*/ |
| 100 | 102 | messageInjectPoint: E1('#message-inject-point'), |
| 101 | 103 | pageTitle: E1('head title'), |
| 102 | 104 | loadOlderToolbar: undefined /* the load-posts toolbar (dynamically created) */, |
| 103 | 105 | inputWrapper: E1("#chat-input-area"), |
| @@ -226,13 +228,13 @@ | ||
| 226 | 228 | }else{ |
| 227 | 229 | eMsg.scrollIntoView(false); |
| 228 | 230 | } |
| 229 | 231 | return this; |
| 230 | 232 | }, |
| 231 | - /* Injects element e as a new row in the chat, at the oldest end | |
| 232 | - of the list if atEnd is truthy, else at the newest end of the | |
| 233 | - list. */ | |
| 233 | + /* Injects DOM element e as a new row in the chat, at the oldest | |
| 234 | + end of the list if atEnd is truthy, else at the newest end of | |
| 235 | + the list. */ | |
| 234 | 236 | injectMessageElem: function f(e, atEnd){ |
| 235 | 237 | const mip = atEnd ? this.e.loadOlderToolbar : this.e.messageInjectPoint, |
| 236 | 238 | holder = this.e.messagesWrapper, |
| 237 | 239 | prevMessage = this.e.newestMessage; |
| 238 | 240 | if(atEnd){ |
| @@ -736,10 +738,23 @@ | ||
| 736 | 738 | btnDeleteGlobal.addEventListener('click', function(){ |
| 737 | 739 | self.hide(); |
| 738 | 740 | Chat.deleteMessage(eMsg); |
| 739 | 741 | }); |
| 740 | 742 | } |
| 743 | + if(eMsg.dataset.xfrom){ | |
| 744 | + /* Add a link to the /timeline filtered on this user. */ | |
| 745 | + const toolbar2 = D.addClass(D.div(), 'toolbar'); | |
| 746 | + D.append(this.e, toolbar2); | |
| 747 | + const timelineLink = D.attr( | |
| 748 | + D.a(F.repoUrl('timeline',{ | |
| 749 | + u: eMsg.dataset.xfrom, | |
| 750 | + y: 'a' | |
| 751 | + }), "User's Timeline"), | |
| 752 | + 'target', '_blank' | |
| 753 | + ); | |
| 754 | + D.append(toolbar2, timelineLink); | |
| 755 | + } | |
| 741 | 756 | }/*refresh()*/ |
| 742 | 757 | }); |
| 743 | 758 | f.popup.installHideHandlers(); |
| 744 | 759 | f.popup.hide = function(){ |
| 745 | 760 | delete this._eMsg; |
| @@ -1229,11 +1244,11 @@ | ||
| 1229 | 1244 | // Disable the ajax start/end handling for this long-polling op: |
| 1230 | 1245 | beforesend: function(){}, |
| 1231 | 1246 | aftersend: function(){}, |
| 1232 | 1247 | onerror:function(err){ |
| 1233 | 1248 | Chat._isBatchLoading = false; |
| 1234 | - console.error(err); | |
| 1249 | + if(Chat.verboseErrors) console.error(err); | |
| 1235 | 1250 | /* ^^^ we don't use Chat.reportError() here b/c the polling |
| 1236 | 1251 | fails exepectedly when it times out, but is then immediately |
| 1237 | 1252 | resumed, and reportError() produces a loud error message. */ |
| 1238 | 1253 | afterFetch(); |
| 1239 | 1254 | }, |
| 1240 | 1255 |
| --- src/chat.js | |
| +++ src/chat.js | |
| @@ -94,10 +94,12 @@ | |
| 94 | return resized; |
| 95 | })(); |
| 96 | fossil.FRK = ForceResizeKludge/*for debugging*/; |
| 97 | const Chat = (function(){ |
| 98 | const cs = { |
| 99 | e:{/*map of certain DOM elements.*/ |
| 100 | messageInjectPoint: E1('#message-inject-point'), |
| 101 | pageTitle: E1('head title'), |
| 102 | loadOlderToolbar: undefined /* the load-posts toolbar (dynamically created) */, |
| 103 | inputWrapper: E1("#chat-input-area"), |
| @@ -226,13 +228,13 @@ | |
| 226 | }else{ |
| 227 | eMsg.scrollIntoView(false); |
| 228 | } |
| 229 | return this; |
| 230 | }, |
| 231 | /* Injects element e as a new row in the chat, at the oldest end |
| 232 | of the list if atEnd is truthy, else at the newest end of the |
| 233 | list. */ |
| 234 | injectMessageElem: function f(e, atEnd){ |
| 235 | const mip = atEnd ? this.e.loadOlderToolbar : this.e.messageInjectPoint, |
| 236 | holder = this.e.messagesWrapper, |
| 237 | prevMessage = this.e.newestMessage; |
| 238 | if(atEnd){ |
| @@ -736,10 +738,23 @@ | |
| 736 | btnDeleteGlobal.addEventListener('click', function(){ |
| 737 | self.hide(); |
| 738 | Chat.deleteMessage(eMsg); |
| 739 | }); |
| 740 | } |
| 741 | }/*refresh()*/ |
| 742 | }); |
| 743 | f.popup.installHideHandlers(); |
| 744 | f.popup.hide = function(){ |
| 745 | delete this._eMsg; |
| @@ -1229,11 +1244,11 @@ | |
| 1229 | // Disable the ajax start/end handling for this long-polling op: |
| 1230 | beforesend: function(){}, |
| 1231 | aftersend: function(){}, |
| 1232 | onerror:function(err){ |
| 1233 | Chat._isBatchLoading = false; |
| 1234 | console.error(err); |
| 1235 | /* ^^^ we don't use Chat.reportError() here b/c the polling |
| 1236 | fails exepectedly when it times out, but is then immediately |
| 1237 | resumed, and reportError() produces a loud error message. */ |
| 1238 | afterFetch(); |
| 1239 | }, |
| 1240 |
| --- src/chat.js | |
| +++ src/chat.js | |
| @@ -94,10 +94,12 @@ | |
| 94 | return resized; |
| 95 | })(); |
| 96 | fossil.FRK = ForceResizeKludge/*for debugging*/; |
| 97 | const Chat = (function(){ |
| 98 | const cs = { |
| 99 | verboseErrors: false /* if true then certain, mostly extraneous, |
| 100 | error messages may be sent to the console. */, |
| 101 | e:{/*map of certain DOM elements.*/ |
| 102 | messageInjectPoint: E1('#message-inject-point'), |
| 103 | pageTitle: E1('head title'), |
| 104 | loadOlderToolbar: undefined /* the load-posts toolbar (dynamically created) */, |
| 105 | inputWrapper: E1("#chat-input-area"), |
| @@ -226,13 +228,13 @@ | |
| 228 | }else{ |
| 229 | eMsg.scrollIntoView(false); |
| 230 | } |
| 231 | return this; |
| 232 | }, |
| 233 | /* Injects DOM element e as a new row in the chat, at the oldest |
| 234 | end of the list if atEnd is truthy, else at the newest end of |
| 235 | the list. */ |
| 236 | injectMessageElem: function f(e, atEnd){ |
| 237 | const mip = atEnd ? this.e.loadOlderToolbar : this.e.messageInjectPoint, |
| 238 | holder = this.e.messagesWrapper, |
| 239 | prevMessage = this.e.newestMessage; |
| 240 | if(atEnd){ |
| @@ -736,10 +738,23 @@ | |
| 738 | btnDeleteGlobal.addEventListener('click', function(){ |
| 739 | self.hide(); |
| 740 | Chat.deleteMessage(eMsg); |
| 741 | }); |
| 742 | } |
| 743 | if(eMsg.dataset.xfrom){ |
| 744 | /* Add a link to the /timeline filtered on this user. */ |
| 745 | const toolbar2 = D.addClass(D.div(), 'toolbar'); |
| 746 | D.append(this.e, toolbar2); |
| 747 | const timelineLink = D.attr( |
| 748 | D.a(F.repoUrl('timeline',{ |
| 749 | u: eMsg.dataset.xfrom, |
| 750 | y: 'a' |
| 751 | }), "User's Timeline"), |
| 752 | 'target', '_blank' |
| 753 | ); |
| 754 | D.append(toolbar2, timelineLink); |
| 755 | } |
| 756 | }/*refresh()*/ |
| 757 | }); |
| 758 | f.popup.installHideHandlers(); |
| 759 | f.popup.hide = function(){ |
| 760 | delete this._eMsg; |
| @@ -1229,11 +1244,11 @@ | |
| 1244 | // Disable the ajax start/end handling for this long-polling op: |
| 1245 | beforesend: function(){}, |
| 1246 | aftersend: function(){}, |
| 1247 | onerror:function(err){ |
| 1248 | Chat._isBatchLoading = false; |
| 1249 | if(Chat.verboseErrors) console.error(err); |
| 1250 | /* ^^^ we don't use Chat.reportError() here b/c the polling |
| 1251 | fails exepectedly when it times out, but is then immediately |
| 1252 | resumed, and reportError() produces a loud error message. */ |
| 1253 | afterFetch(); |
| 1254 | }, |
| 1255 |
M
src/db.c
+33
-1
| --- src/db.c | ||
| +++ src/db.c | ||
| @@ -864,23 +864,55 @@ | ||
| 864 | 864 | db_exec(&err); |
| 865 | 865 | } |
| 866 | 866 | |
| 867 | 867 | /* |
| 868 | 868 | ** COMMAND: test-db-prepare |
| 869 | -** Usage: %fossil test-db-prepare ?OPTIONS? SQL | |
| 869 | +** Usage: %fossil test-db-prepare ?OPTIONS? SQL-STATEMENT | |
| 870 | +** | |
| 871 | +** Options: | |
| 872 | +** | |
| 873 | +** --auth-report Enable the ticket report query authorizer. | |
| 874 | +** --auth-ticket Enable the ticket schema query authorizer. | |
| 870 | 875 | ** |
| 871 | 876 | ** Invoke db_prepare() on the SQL input. Report any errors encountered. |
| 872 | 877 | ** This command is used to verify error detection logic in the db_prepare() |
| 873 | 878 | ** utility routine. |
| 874 | 879 | */ |
| 875 | 880 | void db_test_db_prepare(void){ |
| 881 | + const int fAuthReport = find_option("auth-report",0,0)!=0; | |
| 882 | + const int fAuthSchema = find_option("auth-ticket",0,0)!=0; | |
| 883 | + char * zReportErr = 0; /* auth-report error string. */ | |
| 884 | + int nSchemaErr = 0; /* Number of auth-ticket errors. */ | |
| 876 | 885 | Stmt err; |
| 886 | + | |
| 887 | + if(fAuthReport + fAuthSchema > 1){ | |
| 888 | + fossil_fatal("Only one of --auth-report or --auth-ticket " | |
| 889 | + "may be used."); | |
| 890 | + } | |
| 877 | 891 | db_find_and_open_repository(0,0); |
| 878 | 892 | verify_all_options(); |
| 879 | 893 | if( g.argc!=3 ) usage("?OPTIONS? SQL"); |
| 894 | + if(fAuthReport){ | |
| 895 | + report_restrict_sql(&zReportErr); | |
| 896 | + }else if(fAuthSchema){ | |
| 897 | + ticket_restrict_sql(&nSchemaErr); | |
| 898 | + } | |
| 880 | 899 | db_prepare(&err, "%s", g.argv[2]/*safe-for-%s*/); |
| 881 | 900 | db_finalize(&err); |
| 901 | + if(fAuthReport){ | |
| 902 | + report_unrestrict_sql(); | |
| 903 | + if(zReportErr){ | |
| 904 | + fossil_warning("Report authorizer error: %s\n", zReportErr); | |
| 905 | + fossil_free(zReportErr); | |
| 906 | + } | |
| 907 | + }else if(fAuthSchema){ | |
| 908 | + ticket_unrestrict_sql(); | |
| 909 | + if(nSchemaErr){ | |
| 910 | + fossil_warning("Ticket schema authorizer error count: %d\n", | |
| 911 | + nSchemaErr); | |
| 912 | + } | |
| 913 | + } | |
| 882 | 914 | } |
| 883 | 915 | |
| 884 | 916 | /* |
| 885 | 917 | ** Print the output of one or more SQL queries on standard output. |
| 886 | 918 | ** This routine is used for debugging purposes only. |
| 887 | 919 |
| --- src/db.c | |
| +++ src/db.c | |
| @@ -864,23 +864,55 @@ | |
| 864 | db_exec(&err); |
| 865 | } |
| 866 | |
| 867 | /* |
| 868 | ** COMMAND: test-db-prepare |
| 869 | ** Usage: %fossil test-db-prepare ?OPTIONS? SQL |
| 870 | ** |
| 871 | ** Invoke db_prepare() on the SQL input. Report any errors encountered. |
| 872 | ** This command is used to verify error detection logic in the db_prepare() |
| 873 | ** utility routine. |
| 874 | */ |
| 875 | void db_test_db_prepare(void){ |
| 876 | Stmt err; |
| 877 | db_find_and_open_repository(0,0); |
| 878 | verify_all_options(); |
| 879 | if( g.argc!=3 ) usage("?OPTIONS? SQL"); |
| 880 | db_prepare(&err, "%s", g.argv[2]/*safe-for-%s*/); |
| 881 | db_finalize(&err); |
| 882 | } |
| 883 | |
| 884 | /* |
| 885 | ** Print the output of one or more SQL queries on standard output. |
| 886 | ** This routine is used for debugging purposes only. |
| 887 |
| --- src/db.c | |
| +++ src/db.c | |
| @@ -864,23 +864,55 @@ | |
| 864 | db_exec(&err); |
| 865 | } |
| 866 | |
| 867 | /* |
| 868 | ** COMMAND: test-db-prepare |
| 869 | ** Usage: %fossil test-db-prepare ?OPTIONS? SQL-STATEMENT |
| 870 | ** |
| 871 | ** Options: |
| 872 | ** |
| 873 | ** --auth-report Enable the ticket report query authorizer. |
| 874 | ** --auth-ticket Enable the ticket schema query authorizer. |
| 875 | ** |
| 876 | ** Invoke db_prepare() on the SQL input. Report any errors encountered. |
| 877 | ** This command is used to verify error detection logic in the db_prepare() |
| 878 | ** utility routine. |
| 879 | */ |
| 880 | void db_test_db_prepare(void){ |
| 881 | const int fAuthReport = find_option("auth-report",0,0)!=0; |
| 882 | const int fAuthSchema = find_option("auth-ticket",0,0)!=0; |
| 883 | char * zReportErr = 0; /* auth-report error string. */ |
| 884 | int nSchemaErr = 0; /* Number of auth-ticket errors. */ |
| 885 | Stmt err; |
| 886 | |
| 887 | if(fAuthReport + fAuthSchema > 1){ |
| 888 | fossil_fatal("Only one of --auth-report or --auth-ticket " |
| 889 | "may be used."); |
| 890 | } |
| 891 | db_find_and_open_repository(0,0); |
| 892 | verify_all_options(); |
| 893 | if( g.argc!=3 ) usage("?OPTIONS? SQL"); |
| 894 | if(fAuthReport){ |
| 895 | report_restrict_sql(&zReportErr); |
| 896 | }else if(fAuthSchema){ |
| 897 | ticket_restrict_sql(&nSchemaErr); |
| 898 | } |
| 899 | db_prepare(&err, "%s", g.argv[2]/*safe-for-%s*/); |
| 900 | db_finalize(&err); |
| 901 | if(fAuthReport){ |
| 902 | report_unrestrict_sql(); |
| 903 | if(zReportErr){ |
| 904 | fossil_warning("Report authorizer error: %s\n", zReportErr); |
| 905 | fossil_free(zReportErr); |
| 906 | } |
| 907 | }else if(fAuthSchema){ |
| 908 | ticket_unrestrict_sql(); |
| 909 | if(nSchemaErr){ |
| 910 | fossil_warning("Ticket schema authorizer error count: %d\n", |
| 911 | nSchemaErr); |
| 912 | } |
| 913 | } |
| 914 | } |
| 915 | |
| 916 | /* |
| 917 | ** Print the output of one or more SQL queries on standard output. |
| 918 | ** This routine is used for debugging purposes only. |
| 919 |
+4
| --- src/default.css | ||
| +++ src/default.css | ||
| @@ -1287,10 +1287,14 @@ | ||
| 1287 | 1287 | /* problem: if we inherit the color it may either be |
| 1288 | 1288 | transparent or inherit translucency via the |
| 1289 | 1289 | skin, leaving it unreadable. Since we set the bg |
| 1290 | 1290 | color we must also set the fg color. */; |
| 1291 | 1291 | color: rgba(235, 235, 235, 0.9); |
| 1292 | +} | |
| 1293 | +.fossil-PopupWidget a, | |
| 1294 | +.fossil-PopupWidget a:visited { | |
| 1295 | + color: initial; | |
| 1292 | 1296 | } |
| 1293 | 1297 | .fossil-toast-message.error, |
| 1294 | 1298 | .fossil-toast-message.warning { |
| 1295 | 1299 | background: yellow; |
| 1296 | 1300 | } |
| 1297 | 1301 |
| --- src/default.css | |
| +++ src/default.css | |
| @@ -1287,10 +1287,14 @@ | |
| 1287 | /* problem: if we inherit the color it may either be |
| 1288 | transparent or inherit translucency via the |
| 1289 | skin, leaving it unreadable. Since we set the bg |
| 1290 | color we must also set the fg color. */; |
| 1291 | color: rgba(235, 235, 235, 0.9); |
| 1292 | } |
| 1293 | .fossil-toast-message.error, |
| 1294 | .fossil-toast-message.warning { |
| 1295 | background: yellow; |
| 1296 | } |
| 1297 |
| --- src/default.css | |
| +++ src/default.css | |
| @@ -1287,10 +1287,14 @@ | |
| 1287 | /* problem: if we inherit the color it may either be |
| 1288 | transparent or inherit translucency via the |
| 1289 | skin, leaving it unreadable. Since we set the bg |
| 1290 | color we must also set the fg color. */; |
| 1291 | color: rgba(235, 235, 235, 0.9); |
| 1292 | } |
| 1293 | .fossil-PopupWidget a, |
| 1294 | .fossil-PopupWidget a:visited { |
| 1295 | color: initial; |
| 1296 | } |
| 1297 | .fossil-toast-message.error, |
| 1298 | .fossil-toast-message.warning { |
| 1299 | background: yellow; |
| 1300 | } |
| 1301 |
+8
-4
| --- src/forum.c | ||
| +++ src/forum.c | ||
| @@ -546,16 +546,16 @@ | ||
| 546 | 546 | @ .%0*d(fossil_num_digits(p->nEdit))(p->pEditNext->rev)</a> |
| 547 | 547 | } |
| 548 | 548 | |
| 549 | 549 | /* Provide a link to select the individual post. */ |
| 550 | 550 | if( !bSelect ){ |
| 551 | - @ %z(href("%R/forumpost/%S?%s",p->zUuid,zQuery))[link]</a> | |
| 551 | + @ %z(href("%R/forumpost/%!S?%s",p->zUuid,zQuery))[link]</a> | |
| 552 | 552 | } |
| 553 | 553 | |
| 554 | 554 | /* Provide a link to the raw source code. */ |
| 555 | 555 | if( !bUnf ){ |
| 556 | - @ %z(href("%R/forumpost/%S?raw",p->zUuid))[source]</a> | |
| 556 | + @ %z(href("%R/forumpost/%!S?raw",p->zUuid))[source]</a> | |
| 557 | 557 | } |
| 558 | 558 | @ </h3> |
| 559 | 559 | } |
| 560 | 560 | |
| 561 | 561 | /* Check if this post is approved, also if it's by the current user. */ |
| @@ -1173,19 +1173,20 @@ | ||
| 1173 | 1173 | Manifest *pRootPost = 0; |
| 1174 | 1174 | const char *zMimetype = 0; |
| 1175 | 1175 | const char *zContent = 0; |
| 1176 | 1176 | const char *zTitle = 0; |
| 1177 | 1177 | char *zDate = 0; |
| 1178 | + const char *zFpid = PD("fpid",""); | |
| 1178 | 1179 | int isCsrfSafe; |
| 1179 | 1180 | int isDelete = 0; |
| 1180 | 1181 | |
| 1181 | 1182 | login_check_credentials(); |
| 1182 | 1183 | if( !g.perm.WrForum ){ |
| 1183 | 1184 | login_needed(g.anon.WrForum); |
| 1184 | 1185 | return; |
| 1185 | 1186 | } |
| 1186 | - fpid = symbolic_name_to_rid(PD("fpid",""), "f"); | |
| 1187 | + fpid = symbolic_name_to_rid(zFpid, "f"); | |
| 1187 | 1188 | if( fpid<=0 || (pPost = manifest_get(fpid, CFTYPE_FORUM, 0))==0 ){ |
| 1188 | 1189 | webpage_error("Missing or invalid fpid query parameter"); |
| 1189 | 1190 | } |
| 1190 | 1191 | froot = db_int(0, "SELECT froot FROM forumpost WHERE fpid=%d", fpid); |
| 1191 | 1192 | if( froot==0 || (pRootPost = manifest_get(froot, CFTYPE_FORUM, 0))==0 ){ |
| @@ -1296,11 +1297,14 @@ | ||
| 1296 | 1297 | zContent = PDT("content",""); |
| 1297 | 1298 | style_header("Reply"); |
| 1298 | 1299 | if( pRootPost->zThreadTitle ){ |
| 1299 | 1300 | @ <h1>Thread: %h(pRootPost->zThreadTitle)</h1> |
| 1300 | 1301 | } |
| 1301 | - @ <h2>Replying To:</h2> | |
| 1302 | + @ <h2>Replying To: | |
| 1303 | + @ <a href="%R/forumpost/%!S(zFpid)" target="_blank">%S(zFpid)</a> | |
| 1304 | + @ <a href="%R/forumpost/%!S(zFpid)?raw" target="_blank">[source]</a> | |
| 1305 | + @ </h2> | |
| 1302 | 1306 | zDate = db_text(0, "SELECT datetime(%.17g,toLocal())", pPost->rDate); |
| 1303 | 1307 | zDisplayName = display_name_from_login(pPost->zUser); |
| 1304 | 1308 | @ <h3 class='forumPostHdr'>By %s(zDisplayName) on %h(zDate)</h3> |
| 1305 | 1309 | fossil_free(zDisplayName); |
| 1306 | 1310 | fossil_free(zDate); |
| 1307 | 1311 |
| --- src/forum.c | |
| +++ src/forum.c | |
| @@ -546,16 +546,16 @@ | |
| 546 | @ .%0*d(fossil_num_digits(p->nEdit))(p->pEditNext->rev)</a> |
| 547 | } |
| 548 | |
| 549 | /* Provide a link to select the individual post. */ |
| 550 | if( !bSelect ){ |
| 551 | @ %z(href("%R/forumpost/%S?%s",p->zUuid,zQuery))[link]</a> |
| 552 | } |
| 553 | |
| 554 | /* Provide a link to the raw source code. */ |
| 555 | if( !bUnf ){ |
| 556 | @ %z(href("%R/forumpost/%S?raw",p->zUuid))[source]</a> |
| 557 | } |
| 558 | @ </h3> |
| 559 | } |
| 560 | |
| 561 | /* Check if this post is approved, also if it's by the current user. */ |
| @@ -1173,19 +1173,20 @@ | |
| 1173 | Manifest *pRootPost = 0; |
| 1174 | const char *zMimetype = 0; |
| 1175 | const char *zContent = 0; |
| 1176 | const char *zTitle = 0; |
| 1177 | char *zDate = 0; |
| 1178 | int isCsrfSafe; |
| 1179 | int isDelete = 0; |
| 1180 | |
| 1181 | login_check_credentials(); |
| 1182 | if( !g.perm.WrForum ){ |
| 1183 | login_needed(g.anon.WrForum); |
| 1184 | return; |
| 1185 | } |
| 1186 | fpid = symbolic_name_to_rid(PD("fpid",""), "f"); |
| 1187 | if( fpid<=0 || (pPost = manifest_get(fpid, CFTYPE_FORUM, 0))==0 ){ |
| 1188 | webpage_error("Missing or invalid fpid query parameter"); |
| 1189 | } |
| 1190 | froot = db_int(0, "SELECT froot FROM forumpost WHERE fpid=%d", fpid); |
| 1191 | if( froot==0 || (pRootPost = manifest_get(froot, CFTYPE_FORUM, 0))==0 ){ |
| @@ -1296,11 +1297,14 @@ | |
| 1296 | zContent = PDT("content",""); |
| 1297 | style_header("Reply"); |
| 1298 | if( pRootPost->zThreadTitle ){ |
| 1299 | @ <h1>Thread: %h(pRootPost->zThreadTitle)</h1> |
| 1300 | } |
| 1301 | @ <h2>Replying To:</h2> |
| 1302 | zDate = db_text(0, "SELECT datetime(%.17g,toLocal())", pPost->rDate); |
| 1303 | zDisplayName = display_name_from_login(pPost->zUser); |
| 1304 | @ <h3 class='forumPostHdr'>By %s(zDisplayName) on %h(zDate)</h3> |
| 1305 | fossil_free(zDisplayName); |
| 1306 | fossil_free(zDate); |
| 1307 |
| --- src/forum.c | |
| +++ src/forum.c | |
| @@ -546,16 +546,16 @@ | |
| 546 | @ .%0*d(fossil_num_digits(p->nEdit))(p->pEditNext->rev)</a> |
| 547 | } |
| 548 | |
| 549 | /* Provide a link to select the individual post. */ |
| 550 | if( !bSelect ){ |
| 551 | @ %z(href("%R/forumpost/%!S?%s",p->zUuid,zQuery))[link]</a> |
| 552 | } |
| 553 | |
| 554 | /* Provide a link to the raw source code. */ |
| 555 | if( !bUnf ){ |
| 556 | @ %z(href("%R/forumpost/%!S?raw",p->zUuid))[source]</a> |
| 557 | } |
| 558 | @ </h3> |
| 559 | } |
| 560 | |
| 561 | /* Check if this post is approved, also if it's by the current user. */ |
| @@ -1173,19 +1173,20 @@ | |
| 1173 | Manifest *pRootPost = 0; |
| 1174 | const char *zMimetype = 0; |
| 1175 | const char *zContent = 0; |
| 1176 | const char *zTitle = 0; |
| 1177 | char *zDate = 0; |
| 1178 | const char *zFpid = PD("fpid",""); |
| 1179 | int isCsrfSafe; |
| 1180 | int isDelete = 0; |
| 1181 | |
| 1182 | login_check_credentials(); |
| 1183 | if( !g.perm.WrForum ){ |
| 1184 | login_needed(g.anon.WrForum); |
| 1185 | return; |
| 1186 | } |
| 1187 | fpid = symbolic_name_to_rid(zFpid, "f"); |
| 1188 | if( fpid<=0 || (pPost = manifest_get(fpid, CFTYPE_FORUM, 0))==0 ){ |
| 1189 | webpage_error("Missing or invalid fpid query parameter"); |
| 1190 | } |
| 1191 | froot = db_int(0, "SELECT froot FROM forumpost WHERE fpid=%d", fpid); |
| 1192 | if( froot==0 || (pRootPost = manifest_get(froot, CFTYPE_FORUM, 0))==0 ){ |
| @@ -1296,11 +1297,14 @@ | |
| 1297 | zContent = PDT("content",""); |
| 1298 | style_header("Reply"); |
| 1299 | if( pRootPost->zThreadTitle ){ |
| 1300 | @ <h1>Thread: %h(pRootPost->zThreadTitle)</h1> |
| 1301 | } |
| 1302 | @ <h2>Replying To: |
| 1303 | @ <a href="%R/forumpost/%!S(zFpid)" target="_blank">%S(zFpid)</a> |
| 1304 | @ <a href="%R/forumpost/%!S(zFpid)?raw" target="_blank">[source]</a> |
| 1305 | @ </h2> |
| 1306 | zDate = db_text(0, "SELECT datetime(%.17g,toLocal())", pPost->rDate); |
| 1307 | zDisplayName = display_name_from_login(pPost->zUser); |
| 1308 | @ <h3 class='forumPostHdr'>By %s(zDisplayName) on %h(zDate)</h3> |
| 1309 | fossil_free(zDisplayName); |
| 1310 | fossil_free(zDate); |
| 1311 |
+14
-3
| --- src/http_ssl.c | ||
| +++ src/http_ssl.c | ||
| @@ -252,10 +252,11 @@ | ||
| 252 | 252 | ** |
| 253 | 253 | ** Return the number of errors. |
| 254 | 254 | */ |
| 255 | 255 | int ssl_open(UrlData *pUrlData){ |
| 256 | 256 | X509 *cert; |
| 257 | + const char *zRemoteHost; | |
| 257 | 258 | |
| 258 | 259 | ssl_global_init(); |
| 259 | 260 | if( pUrlData->useProxy ){ |
| 260 | 261 | int rc; |
| 261 | 262 | char *connStr = mprintf("%s:%d", g.url.name, pUrlData->port); |
| @@ -276,30 +277,40 @@ | ||
| 276 | 277 | |
| 277 | 278 | pUrlData->path = pUrlData->proxyUrlPath; |
| 278 | 279 | |
| 279 | 280 | iBio = BIO_new_ssl(sslCtx, 1); |
| 280 | 281 | BIO_push(iBio, sBio); |
| 282 | + zRemoteHost = pUrlData->hostname; | |
| 281 | 283 | }else{ |
| 282 | 284 | iBio = BIO_new_ssl_connect(sslCtx); |
| 285 | + zRemoteHost = pUrlData->name; | |
| 283 | 286 | } |
| 284 | 287 | if( iBio==NULL ) { |
| 285 | 288 | ssl_set_errmsg("SSL: cannot open SSL (%s)", |
| 286 | 289 | ERR_reason_error_string(ERR_get_error())); |
| 287 | 290 | return 1; |
| 288 | 291 | } |
| 289 | 292 | BIO_get_ssl(iBio, &ssl); |
| 290 | 293 | |
| 291 | 294 | #if (SSLEAY_VERSION_NUMBER >= 0x00908070) && !defined(OPENSSL_NO_TLSEXT) |
| 292 | - if( !SSL_set_tlsext_host_name(ssl, | |
| 293 | - (pUrlData->useProxy?pUrlData->hostname:pUrlData->name)) | |
| 294 | - ){ | |
| 295 | + if( !SSL_set_tlsext_host_name(ssl, zRemoteHost)){ | |
| 295 | 296 | fossil_warning("WARNING: failed to set server name indication (SNI), " |
| 296 | 297 | "continuing without it.\n"); |
| 297 | 298 | } |
| 298 | 299 | #endif |
| 299 | 300 | |
| 300 | 301 | SSL_set_mode(ssl, SSL_MODE_AUTO_RETRY); |
| 302 | +#if OPENSSL_VERSION_NUMBER >= 0x010002000 | |
| 303 | + if( !sslNoCertVerify ){ | |
| 304 | + X509_VERIFY_PARAM *param = 0; | |
| 305 | + param = SSL_get0_param(ssl); | |
| 306 | + if( !X509_VERIFY_PARAM_set1_host(param, zRemoteHost, strlen(zRemoteHost)) ){ | |
| 307 | + fossil_fatal("failed to set hostname."); | |
| 308 | + } | |
| 309 | + /* SSL_set_verify(ssl, SSL_VERIFY_PEER, 0); */ | |
| 310 | + } | |
| 311 | +#endif | |
| 301 | 312 | |
| 302 | 313 | if( !pUrlData->useProxy ){ |
| 303 | 314 | char *connStr = mprintf("%s:%d", pUrlData->name, pUrlData->port); |
| 304 | 315 | BIO_set_conn_hostname(iBio, connStr); |
| 305 | 316 | free(connStr); |
| 306 | 317 |
| --- src/http_ssl.c | |
| +++ src/http_ssl.c | |
| @@ -252,10 +252,11 @@ | |
| 252 | ** |
| 253 | ** Return the number of errors. |
| 254 | */ |
| 255 | int ssl_open(UrlData *pUrlData){ |
| 256 | X509 *cert; |
| 257 | |
| 258 | ssl_global_init(); |
| 259 | if( pUrlData->useProxy ){ |
| 260 | int rc; |
| 261 | char *connStr = mprintf("%s:%d", g.url.name, pUrlData->port); |
| @@ -276,30 +277,40 @@ | |
| 276 | |
| 277 | pUrlData->path = pUrlData->proxyUrlPath; |
| 278 | |
| 279 | iBio = BIO_new_ssl(sslCtx, 1); |
| 280 | BIO_push(iBio, sBio); |
| 281 | }else{ |
| 282 | iBio = BIO_new_ssl_connect(sslCtx); |
| 283 | } |
| 284 | if( iBio==NULL ) { |
| 285 | ssl_set_errmsg("SSL: cannot open SSL (%s)", |
| 286 | ERR_reason_error_string(ERR_get_error())); |
| 287 | return 1; |
| 288 | } |
| 289 | BIO_get_ssl(iBio, &ssl); |
| 290 | |
| 291 | #if (SSLEAY_VERSION_NUMBER >= 0x00908070) && !defined(OPENSSL_NO_TLSEXT) |
| 292 | if( !SSL_set_tlsext_host_name(ssl, |
| 293 | (pUrlData->useProxy?pUrlData->hostname:pUrlData->name)) |
| 294 | ){ |
| 295 | fossil_warning("WARNING: failed to set server name indication (SNI), " |
| 296 | "continuing without it.\n"); |
| 297 | } |
| 298 | #endif |
| 299 | |
| 300 | SSL_set_mode(ssl, SSL_MODE_AUTO_RETRY); |
| 301 | |
| 302 | if( !pUrlData->useProxy ){ |
| 303 | char *connStr = mprintf("%s:%d", pUrlData->name, pUrlData->port); |
| 304 | BIO_set_conn_hostname(iBio, connStr); |
| 305 | free(connStr); |
| 306 |
| --- src/http_ssl.c | |
| +++ src/http_ssl.c | |
| @@ -252,10 +252,11 @@ | |
| 252 | ** |
| 253 | ** Return the number of errors. |
| 254 | */ |
| 255 | int ssl_open(UrlData *pUrlData){ |
| 256 | X509 *cert; |
| 257 | const char *zRemoteHost; |
| 258 | |
| 259 | ssl_global_init(); |
| 260 | if( pUrlData->useProxy ){ |
| 261 | int rc; |
| 262 | char *connStr = mprintf("%s:%d", g.url.name, pUrlData->port); |
| @@ -276,30 +277,40 @@ | |
| 277 | |
| 278 | pUrlData->path = pUrlData->proxyUrlPath; |
| 279 | |
| 280 | iBio = BIO_new_ssl(sslCtx, 1); |
| 281 | BIO_push(iBio, sBio); |
| 282 | zRemoteHost = pUrlData->hostname; |
| 283 | }else{ |
| 284 | iBio = BIO_new_ssl_connect(sslCtx); |
| 285 | zRemoteHost = pUrlData->name; |
| 286 | } |
| 287 | if( iBio==NULL ) { |
| 288 | ssl_set_errmsg("SSL: cannot open SSL (%s)", |
| 289 | ERR_reason_error_string(ERR_get_error())); |
| 290 | return 1; |
| 291 | } |
| 292 | BIO_get_ssl(iBio, &ssl); |
| 293 | |
| 294 | #if (SSLEAY_VERSION_NUMBER >= 0x00908070) && !defined(OPENSSL_NO_TLSEXT) |
| 295 | if( !SSL_set_tlsext_host_name(ssl, zRemoteHost)){ |
| 296 | fossil_warning("WARNING: failed to set server name indication (SNI), " |
| 297 | "continuing without it.\n"); |
| 298 | } |
| 299 | #endif |
| 300 | |
| 301 | SSL_set_mode(ssl, SSL_MODE_AUTO_RETRY); |
| 302 | #if OPENSSL_VERSION_NUMBER >= 0x010002000 |
| 303 | if( !sslNoCertVerify ){ |
| 304 | X509_VERIFY_PARAM *param = 0; |
| 305 | param = SSL_get0_param(ssl); |
| 306 | if( !X509_VERIFY_PARAM_set1_host(param, zRemoteHost, strlen(zRemoteHost)) ){ |
| 307 | fossil_fatal("failed to set hostname."); |
| 308 | } |
| 309 | /* SSL_set_verify(ssl, SSL_VERIFY_PEER, 0); */ |
| 310 | } |
| 311 | #endif |
| 312 | |
| 313 | if( !pUrlData->useProxy ){ |
| 314 | char *connStr = mprintf("%s:%d", pUrlData->name, pUrlData->port); |
| 315 | BIO_set_conn_hostname(iBio, connStr); |
| 316 | free(connStr); |
| 317 |
+5
-5
| --- src/info.c | ||
| +++ src/info.c | ||
| @@ -2794,10 +2794,15 @@ | ||
| 2794 | 2794 | winfo_page(); |
| 2795 | 2795 | }else |
| 2796 | 2796 | if( db_exists("SELECT 1 FROM tagxref JOIN tag USING(tagid)" |
| 2797 | 2797 | " WHERE rid=%d AND tagname LIKE 'tkt-%%'", rid) ){ |
| 2798 | 2798 | tinfo_page(); |
| 2799 | + }else | |
| 2800 | + if( db_table_exists("repository","forumpost") | |
| 2801 | + && db_exists("SELECT 1 FROM forumpost WHERE fpid=%d", rid) | |
| 2802 | + ){ | |
| 2803 | + forumthread_page(); | |
| 2799 | 2804 | }else |
| 2800 | 2805 | if( db_exists("SELECT 1 FROM plink WHERE cid=%d", rid) ){ |
| 2801 | 2806 | ci_page(); |
| 2802 | 2807 | }else |
| 2803 | 2808 | if( db_exists("SELECT 1 FROM plink WHERE pid=%d", rid) ){ |
| @@ -2804,15 +2809,10 @@ | ||
| 2804 | 2809 | ci_page(); |
| 2805 | 2810 | }else |
| 2806 | 2811 | if( db_exists("SELECT 1 FROM attachment WHERE attachid=%d", rid) ){ |
| 2807 | 2812 | ainfo_page(); |
| 2808 | 2813 | }else |
| 2809 | - if( db_table_exists("repository","forumpost") | |
| 2810 | - && db_exists("SELECT 1 FROM forumpost WHERE fpid=%d", rid) | |
| 2811 | - ){ | |
| 2812 | - forumthread_page(); | |
| 2813 | - }else | |
| 2814 | 2814 | { |
| 2815 | 2815 | artifact_page(); |
| 2816 | 2816 | } |
| 2817 | 2817 | } |
| 2818 | 2818 | |
| 2819 | 2819 |
| --- src/info.c | |
| +++ src/info.c | |
| @@ -2794,10 +2794,15 @@ | |
| 2794 | winfo_page(); |
| 2795 | }else |
| 2796 | if( db_exists("SELECT 1 FROM tagxref JOIN tag USING(tagid)" |
| 2797 | " WHERE rid=%d AND tagname LIKE 'tkt-%%'", rid) ){ |
| 2798 | tinfo_page(); |
| 2799 | }else |
| 2800 | if( db_exists("SELECT 1 FROM plink WHERE cid=%d", rid) ){ |
| 2801 | ci_page(); |
| 2802 | }else |
| 2803 | if( db_exists("SELECT 1 FROM plink WHERE pid=%d", rid) ){ |
| @@ -2804,15 +2809,10 @@ | |
| 2804 | ci_page(); |
| 2805 | }else |
| 2806 | if( db_exists("SELECT 1 FROM attachment WHERE attachid=%d", rid) ){ |
| 2807 | ainfo_page(); |
| 2808 | }else |
| 2809 | if( db_table_exists("repository","forumpost") |
| 2810 | && db_exists("SELECT 1 FROM forumpost WHERE fpid=%d", rid) |
| 2811 | ){ |
| 2812 | forumthread_page(); |
| 2813 | }else |
| 2814 | { |
| 2815 | artifact_page(); |
| 2816 | } |
| 2817 | } |
| 2818 | |
| 2819 |
| --- src/info.c | |
| +++ src/info.c | |
| @@ -2794,10 +2794,15 @@ | |
| 2794 | winfo_page(); |
| 2795 | }else |
| 2796 | if( db_exists("SELECT 1 FROM tagxref JOIN tag USING(tagid)" |
| 2797 | " WHERE rid=%d AND tagname LIKE 'tkt-%%'", rid) ){ |
| 2798 | tinfo_page(); |
| 2799 | }else |
| 2800 | if( db_table_exists("repository","forumpost") |
| 2801 | && db_exists("SELECT 1 FROM forumpost WHERE fpid=%d", rid) |
| 2802 | ){ |
| 2803 | forumthread_page(); |
| 2804 | }else |
| 2805 | if( db_exists("SELECT 1 FROM plink WHERE cid=%d", rid) ){ |
| 2806 | ci_page(); |
| 2807 | }else |
| 2808 | if( db_exists("SELECT 1 FROM plink WHERE pid=%d", rid) ){ |
| @@ -2804,15 +2809,10 @@ | |
| 2809 | ci_page(); |
| 2810 | }else |
| 2811 | if( db_exists("SELECT 1 FROM attachment WHERE attachid=%d", rid) ){ |
| 2812 | ainfo_page(); |
| 2813 | }else |
| 2814 | { |
| 2815 | artifact_page(); |
| 2816 | } |
| 2817 | } |
| 2818 | |
| 2819 |
+42
-1
| --- src/pikchr.c | ||
| +++ src/pikchr.c | ||
| @@ -7308,10 +7308,28 @@ | ||
| 7308 | 7308 | }else{ |
| 7309 | 7309 | pToken->eType = T_LT; |
| 7310 | 7310 | return 1; |
| 7311 | 7311 | } |
| 7312 | 7312 | } |
| 7313 | + case 0xe2: { | |
| 7314 | + if( z[1]==0x86 ){ | |
| 7315 | + if( z[2]==0x90 ){ | |
| 7316 | + pToken->eType = T_LARROW; /* <- */ | |
| 7317 | + return 3; | |
| 7318 | + } | |
| 7319 | + if( z[2]==0x92 ){ | |
| 7320 | + pToken->eType = T_RARROW; /* -> */ | |
| 7321 | + return 3; | |
| 7322 | + } | |
| 7323 | + if( z[2]==0x94 ){ | |
| 7324 | + pToken->eType = T_LRARROW; /* <-> */ | |
| 7325 | + return 3; | |
| 7326 | + } | |
| 7327 | + } | |
| 7328 | + pToken->eType = T_ERROR; | |
| 7329 | + return 1; | |
| 7330 | + } | |
| 7313 | 7331 | case '{': { |
| 7314 | 7332 | int len, depth; |
| 7315 | 7333 | i = 1; |
| 7316 | 7334 | if( bAllowCodeBlock ){ |
| 7317 | 7335 | depth = 1; |
| @@ -7332,10 +7350,33 @@ | ||
| 7332 | 7350 | pToken->eType = T_ERROR; |
| 7333 | 7351 | return 1; |
| 7334 | 7352 | } |
| 7335 | 7353 | pToken->eType = T_CODEBLOCK; |
| 7336 | 7354 | return i; |
| 7355 | + } | |
| 7356 | + case '&': { | |
| 7357 | + static const struct { | |
| 7358 | + int nByte; /* Number of bytes in zEntity */ | |
| 7359 | + int eCode; /* Corresponding token code */ | |
| 7360 | + const char *zEntity; /* Name of the HTML entity */ | |
| 7361 | + } aEntity[] = { | |
| 7362 | + /* 123456789 1234567 */ | |
| 7363 | + { 6, T_RARROW, "→" }, /* Same as -> */ | |
| 7364 | + { 12, T_RARROW, "→" }, /* Same as -> */ | |
| 7365 | + { 6, T_LARROW, "←" }, /* Same as <- */ | |
| 7366 | + { 11, T_LARROW, "←" }, /* Same as <- */ | |
| 7367 | + { 16, T_LRARROW, "↔" }, /* Same as <-> */ | |
| 7368 | + }; | |
| 7369 | + unsigned int i; | |
| 7370 | + for(i=0; i<sizeof(aEntity)/sizeof(aEntity[0]); i++){ | |
| 7371 | + if( strncmp((const char*)z,aEntity[i].zEntity,aEntity[i].nByte)==0 ){ | |
| 7372 | + pToken->eType = aEntity[i].eCode; | |
| 7373 | + return aEntity[i].nByte; | |
| 7374 | + } | |
| 7375 | + } | |
| 7376 | + pToken->eType = T_ERROR; | |
| 7377 | + return 1; | |
| 7337 | 7378 | } |
| 7338 | 7379 | default: { |
| 7339 | 7380 | c = z[0]; |
| 7340 | 7381 | if( c=='.' ){ |
| 7341 | 7382 | unsigned char c1 = z[1]; |
| @@ -7960,6 +8001,6 @@ | ||
| 7960 | 8001 | |
| 7961 | 8002 | |
| 7962 | 8003 | #endif /* PIKCHR_TCL */ |
| 7963 | 8004 | |
| 7964 | 8005 | |
| 7965 | -#line 7990 "pikchr.c" | |
| 8006 | +#line 8031 "pikchr.c" | |
| 7966 | 8007 |
| --- src/pikchr.c | |
| +++ src/pikchr.c | |
| @@ -7308,10 +7308,28 @@ | |
| 7308 | }else{ |
| 7309 | pToken->eType = T_LT; |
| 7310 | return 1; |
| 7311 | } |
| 7312 | } |
| 7313 | case '{': { |
| 7314 | int len, depth; |
| 7315 | i = 1; |
| 7316 | if( bAllowCodeBlock ){ |
| 7317 | depth = 1; |
| @@ -7332,10 +7350,33 @@ | |
| 7332 | pToken->eType = T_ERROR; |
| 7333 | return 1; |
| 7334 | } |
| 7335 | pToken->eType = T_CODEBLOCK; |
| 7336 | return i; |
| 7337 | } |
| 7338 | default: { |
| 7339 | c = z[0]; |
| 7340 | if( c=='.' ){ |
| 7341 | unsigned char c1 = z[1]; |
| @@ -7960,6 +8001,6 @@ | |
| 7960 | |
| 7961 | |
| 7962 | #endif /* PIKCHR_TCL */ |
| 7963 | |
| 7964 | |
| 7965 | #line 7990 "pikchr.c" |
| 7966 |
| --- src/pikchr.c | |
| +++ src/pikchr.c | |
| @@ -7308,10 +7308,28 @@ | |
| 7308 | }else{ |
| 7309 | pToken->eType = T_LT; |
| 7310 | return 1; |
| 7311 | } |
| 7312 | } |
| 7313 | case 0xe2: { |
| 7314 | if( z[1]==0x86 ){ |
| 7315 | if( z[2]==0x90 ){ |
| 7316 | pToken->eType = T_LARROW; /* <- */ |
| 7317 | return 3; |
| 7318 | } |
| 7319 | if( z[2]==0x92 ){ |
| 7320 | pToken->eType = T_RARROW; /* -> */ |
| 7321 | return 3; |
| 7322 | } |
| 7323 | if( z[2]==0x94 ){ |
| 7324 | pToken->eType = T_LRARROW; /* <-> */ |
| 7325 | return 3; |
| 7326 | } |
| 7327 | } |
| 7328 | pToken->eType = T_ERROR; |
| 7329 | return 1; |
| 7330 | } |
| 7331 | case '{': { |
| 7332 | int len, depth; |
| 7333 | i = 1; |
| 7334 | if( bAllowCodeBlock ){ |
| 7335 | depth = 1; |
| @@ -7332,10 +7350,33 @@ | |
| 7350 | pToken->eType = T_ERROR; |
| 7351 | return 1; |
| 7352 | } |
| 7353 | pToken->eType = T_CODEBLOCK; |
| 7354 | return i; |
| 7355 | } |
| 7356 | case '&': { |
| 7357 | static const struct { |
| 7358 | int nByte; /* Number of bytes in zEntity */ |
| 7359 | int eCode; /* Corresponding token code */ |
| 7360 | const char *zEntity; /* Name of the HTML entity */ |
| 7361 | } aEntity[] = { |
| 7362 | /* 123456789 1234567 */ |
| 7363 | { 6, T_RARROW, "→" }, /* Same as -> */ |
| 7364 | { 12, T_RARROW, "→" }, /* Same as -> */ |
| 7365 | { 6, T_LARROW, "←" }, /* Same as <- */ |
| 7366 | { 11, T_LARROW, "←" }, /* Same as <- */ |
| 7367 | { 16, T_LRARROW, "↔" }, /* Same as <-> */ |
| 7368 | }; |
| 7369 | unsigned int i; |
| 7370 | for(i=0; i<sizeof(aEntity)/sizeof(aEntity[0]); i++){ |
| 7371 | if( strncmp((const char*)z,aEntity[i].zEntity,aEntity[i].nByte)==0 ){ |
| 7372 | pToken->eType = aEntity[i].eCode; |
| 7373 | return aEntity[i].nByte; |
| 7374 | } |
| 7375 | } |
| 7376 | pToken->eType = T_ERROR; |
| 7377 | return 1; |
| 7378 | } |
| 7379 | default: { |
| 7380 | c = z[0]; |
| 7381 | if( c=='.' ){ |
| 7382 | unsigned char c1 = z[1]; |
| @@ -7960,6 +8001,6 @@ | |
| 8001 | |
| 8002 | |
| 8003 | #endif /* PIKCHR_TCL */ |
| 8004 | |
| 8005 | |
| 8006 | #line 8031 "pikchr.c" |
| 8007 |
+5
-1
| --- src/report.c | ||
| +++ src/report.c | ||
| @@ -161,10 +161,13 @@ | ||
| 161 | 161 | /* |
| 162 | 162 | ** This is the SQLite authorizer callback used to make sure that the |
| 163 | 163 | ** SQL statements entered by users do not try to do anything untoward. |
| 164 | 164 | ** If anything suspicious is tried, set *(char**)pError to an error |
| 165 | 165 | ** message obtained from malloc. |
| 166 | +** | |
| 167 | +** Use the "fossil test-db-prepare --auth-report SQL" command to perform | |
| 168 | +** manual testing of this authorizer. | |
| 166 | 169 | */ |
| 167 | 170 | static int report_query_authorizer( |
| 168 | 171 | void *pError, |
| 169 | 172 | int code, |
| 170 | 173 | const char *zArg1, |
| @@ -240,11 +243,12 @@ | ||
| 240 | 243 | } |
| 241 | 244 | return rc; |
| 242 | 245 | } |
| 243 | 246 | |
| 244 | 247 | /* |
| 245 | -** Activate the query authorizer | |
| 248 | +** Activate the ticket report query authorizer. Must be followed by an | |
| 249 | +** eventual call to report_unrestrict_sql(). | |
| 246 | 250 | */ |
| 247 | 251 | void report_restrict_sql(char **pzErr){ |
| 248 | 252 | db_set_authorizer(report_query_authorizer,(void*)pzErr,"Ticket-Report"); |
| 249 | 253 | sqlite3_limit(g.db, SQLITE_LIMIT_VDBE_OP, 10000); |
| 250 | 254 | } |
| 251 | 255 |
| --- src/report.c | |
| +++ src/report.c | |
| @@ -161,10 +161,13 @@ | |
| 161 | /* |
| 162 | ** This is the SQLite authorizer callback used to make sure that the |
| 163 | ** SQL statements entered by users do not try to do anything untoward. |
| 164 | ** If anything suspicious is tried, set *(char**)pError to an error |
| 165 | ** message obtained from malloc. |
| 166 | */ |
| 167 | static int report_query_authorizer( |
| 168 | void *pError, |
| 169 | int code, |
| 170 | const char *zArg1, |
| @@ -240,11 +243,12 @@ | |
| 240 | } |
| 241 | return rc; |
| 242 | } |
| 243 | |
| 244 | /* |
| 245 | ** Activate the query authorizer |
| 246 | */ |
| 247 | void report_restrict_sql(char **pzErr){ |
| 248 | db_set_authorizer(report_query_authorizer,(void*)pzErr,"Ticket-Report"); |
| 249 | sqlite3_limit(g.db, SQLITE_LIMIT_VDBE_OP, 10000); |
| 250 | } |
| 251 |
| --- src/report.c | |
| +++ src/report.c | |
| @@ -161,10 +161,13 @@ | |
| 161 | /* |
| 162 | ** This is the SQLite authorizer callback used to make sure that the |
| 163 | ** SQL statements entered by users do not try to do anything untoward. |
| 164 | ** If anything suspicious is tried, set *(char**)pError to an error |
| 165 | ** message obtained from malloc. |
| 166 | ** |
| 167 | ** Use the "fossil test-db-prepare --auth-report SQL" command to perform |
| 168 | ** manual testing of this authorizer. |
| 169 | */ |
| 170 | static int report_query_authorizer( |
| 171 | void *pError, |
| 172 | int code, |
| 173 | const char *zArg1, |
| @@ -240,11 +243,12 @@ | |
| 243 | } |
| 244 | return rc; |
| 245 | } |
| 246 | |
| 247 | /* |
| 248 | ** Activate the ticket report query authorizer. Must be followed by an |
| 249 | ** eventual call to report_unrestrict_sql(). |
| 250 | */ |
| 251 | void report_restrict_sql(char **pzErr){ |
| 252 | db_set_authorizer(report_query_authorizer,(void*)pzErr,"Ticket-Report"); |
| 253 | sqlite3_limit(g.db, SQLITE_LIMIT_VDBE_OP, 10000); |
| 254 | } |
| 255 |
+5
-1
| --- src/report.c | ||
| +++ src/report.c | ||
| @@ -161,10 +161,13 @@ | ||
| 161 | 161 | /* |
| 162 | 162 | ** This is the SQLite authorizer callback used to make sure that the |
| 163 | 163 | ** SQL statements entered by users do not try to do anything untoward. |
| 164 | 164 | ** If anything suspicious is tried, set *(char**)pError to an error |
| 165 | 165 | ** message obtained from malloc. |
| 166 | +** | |
| 167 | +** Use the "fossil test-db-prepare --auth-report SQL" command to perform | |
| 168 | +** manual testing of this authorizer. | |
| 166 | 169 | */ |
| 167 | 170 | static int report_query_authorizer( |
| 168 | 171 | void *pError, |
| 169 | 172 | int code, |
| 170 | 173 | const char *zArg1, |
| @@ -240,11 +243,12 @@ | ||
| 240 | 243 | } |
| 241 | 244 | return rc; |
| 242 | 245 | } |
| 243 | 246 | |
| 244 | 247 | /* |
| 245 | -** Activate the query authorizer | |
| 248 | +** Activate the ticket report query authorizer. Must be followed by an | |
| 249 | +** eventual call to report_unrestrict_sql(). | |
| 246 | 250 | */ |
| 247 | 251 | void report_restrict_sql(char **pzErr){ |
| 248 | 252 | db_set_authorizer(report_query_authorizer,(void*)pzErr,"Ticket-Report"); |
| 249 | 253 | sqlite3_limit(g.db, SQLITE_LIMIT_VDBE_OP, 10000); |
| 250 | 254 | } |
| 251 | 255 |
| --- src/report.c | |
| +++ src/report.c | |
| @@ -161,10 +161,13 @@ | |
| 161 | /* |
| 162 | ** This is the SQLite authorizer callback used to make sure that the |
| 163 | ** SQL statements entered by users do not try to do anything untoward. |
| 164 | ** If anything suspicious is tried, set *(char**)pError to an error |
| 165 | ** message obtained from malloc. |
| 166 | */ |
| 167 | static int report_query_authorizer( |
| 168 | void *pError, |
| 169 | int code, |
| 170 | const char *zArg1, |
| @@ -240,11 +243,12 @@ | |
| 240 | } |
| 241 | return rc; |
| 242 | } |
| 243 | |
| 244 | /* |
| 245 | ** Activate the query authorizer |
| 246 | */ |
| 247 | void report_restrict_sql(char **pzErr){ |
| 248 | db_set_authorizer(report_query_authorizer,(void*)pzErr,"Ticket-Report"); |
| 249 | sqlite3_limit(g.db, SQLITE_LIMIT_VDBE_OP, 10000); |
| 250 | } |
| 251 |
| --- src/report.c | |
| +++ src/report.c | |
| @@ -161,10 +161,13 @@ | |
| 161 | /* |
| 162 | ** This is the SQLite authorizer callback used to make sure that the |
| 163 | ** SQL statements entered by users do not try to do anything untoward. |
| 164 | ** If anything suspicious is tried, set *(char**)pError to an error |
| 165 | ** message obtained from malloc. |
| 166 | ** |
| 167 | ** Use the "fossil test-db-prepare --auth-report SQL" command to perform |
| 168 | ** manual testing of this authorizer. |
| 169 | */ |
| 170 | static int report_query_authorizer( |
| 171 | void *pError, |
| 172 | int code, |
| 173 | const char *zArg1, |
| @@ -240,11 +243,12 @@ | |
| 243 | } |
| 244 | return rc; |
| 245 | } |
| 246 | |
| 247 | /* |
| 248 | ** Activate the ticket report query authorizer. Must be followed by an |
| 249 | ** eventual call to report_unrestrict_sql(). |
| 250 | */ |
| 251 | void report_restrict_sql(char **pzErr){ |
| 252 | db_set_authorizer(report_query_authorizer,(void*)pzErr,"Ticket-Report"); |
| 253 | sqlite3_limit(g.db, SQLITE_LIMIT_VDBE_OP, 10000); |
| 254 | } |
| 255 |
+1
-1
| --- src/schema.c | ||
| +++ src/schema.c | ||
| @@ -329,11 +329,11 @@ | ||
| 329 | 329 | @ rid INTEGER PRIMARY KEY -- Record ID of the phantom |
| 330 | 330 | @ ); |
| 331 | 331 | @ |
| 332 | 332 | @ -- A record of orphaned delta-manifests. An orphan is a delta-manifest |
| 333 | 333 | @ -- for which we have content, but its baseline-manifest is a phantom. |
| 334 | -@ -- We have to track all orphan maniftests so that when the baseline arrives, | |
| 334 | +@ -- We have to track all orphan manifests so that when the baseline arrives, | |
| 335 | 335 | @ -- we know to process the orphaned deltas. |
| 336 | 336 | @ CREATE TABLE orphan( |
| 337 | 337 | @ rid INTEGER PRIMARY KEY, -- Delta manifest with a phantom baseline |
| 338 | 338 | @ baseline INTEGER -- Phantom baseline of this orphan |
| 339 | 339 | @ ); |
| 340 | 340 |
| --- src/schema.c | |
| +++ src/schema.c | |
| @@ -329,11 +329,11 @@ | |
| 329 | @ rid INTEGER PRIMARY KEY -- Record ID of the phantom |
| 330 | @ ); |
| 331 | @ |
| 332 | @ -- A record of orphaned delta-manifests. An orphan is a delta-manifest |
| 333 | @ -- for which we have content, but its baseline-manifest is a phantom. |
| 334 | @ -- We have to track all orphan maniftests so that when the baseline arrives, |
| 335 | @ -- we know to process the orphaned deltas. |
| 336 | @ CREATE TABLE orphan( |
| 337 | @ rid INTEGER PRIMARY KEY, -- Delta manifest with a phantom baseline |
| 338 | @ baseline INTEGER -- Phantom baseline of this orphan |
| 339 | @ ); |
| 340 |
| --- src/schema.c | |
| +++ src/schema.c | |
| @@ -329,11 +329,11 @@ | |
| 329 | @ rid INTEGER PRIMARY KEY -- Record ID of the phantom |
| 330 | @ ); |
| 331 | @ |
| 332 | @ -- A record of orphaned delta-manifests. An orphan is a delta-manifest |
| 333 | @ -- for which we have content, but its baseline-manifest is a phantom. |
| 334 | @ -- We have to track all orphan manifests so that when the baseline arrives, |
| 335 | @ -- we know to process the orphaned deltas. |
| 336 | @ CREATE TABLE orphan( |
| 337 | @ rid INTEGER PRIMARY KEY, -- Delta manifest with a phantom baseline |
| 338 | @ baseline INTEGER -- Phantom baseline of this orphan |
| 339 | @ ); |
| 340 |
+6
-6
| --- src/shell.c | ||
| +++ src/shell.c | ||
| @@ -6204,11 +6204,11 @@ | ||
| 6204 | 6204 | } |
| 6205 | 6205 | } |
| 6206 | 6206 | } |
| 6207 | 6207 | if( pRe->aOp[x]==RE_OP_CC_EXC ) hit = !hit; |
| 6208 | 6208 | if( hit ) re_add_state(pNext, x+n); |
| 6209 | - break; | |
| 6209 | + break; | |
| 6210 | 6210 | } |
| 6211 | 6211 | } |
| 6212 | 6212 | } |
| 6213 | 6213 | } |
| 6214 | 6214 | for(i=0; i<pNext->nState; i++){ |
| @@ -6365,11 +6365,11 @@ | ||
| 6365 | 6365 | const char *zErr; |
| 6366 | 6366 | while( (c = p->xNextChar(&p->sIn))!=0 ){ |
| 6367 | 6367 | iStart = p->nState; |
| 6368 | 6368 | switch( c ){ |
| 6369 | 6369 | case '|': |
| 6370 | - case '$': | |
| 6370 | + case '$': | |
| 6371 | 6371 | case ')': { |
| 6372 | 6372 | p->sIn.i--; |
| 6373 | 6373 | return 0; |
| 6374 | 6374 | } |
| 6375 | 6375 | case '(': { |
| @@ -6381,11 +6381,11 @@ | ||
| 6381 | 6381 | } |
| 6382 | 6382 | case '.': { |
| 6383 | 6383 | if( rePeek(p)=='*' ){ |
| 6384 | 6384 | re_append(p, RE_OP_ANYSTAR, 0); |
| 6385 | 6385 | p->sIn.i++; |
| 6386 | - }else{ | |
| 6386 | + }else{ | |
| 6387 | 6387 | re_append(p, RE_OP_ANY, 0); |
| 6388 | 6388 | } |
| 6389 | 6389 | break; |
| 6390 | 6390 | } |
| 6391 | 6391 | case '*': { |
| @@ -6590,12 +6590,12 @@ | ||
| 6590 | 6590 | ** A REGEXP B |
| 6591 | 6591 | ** |
| 6592 | 6592 | ** is implemented as regexp(B,A). |
| 6593 | 6593 | */ |
| 6594 | 6594 | static void re_sql_func( |
| 6595 | - sqlite3_context *context, | |
| 6596 | - int argc, | |
| 6595 | + sqlite3_context *context, | |
| 6596 | + int argc, | |
| 6597 | 6597 | sqlite3_value **argv |
| 6598 | 6598 | ){ |
| 6599 | 6599 | ReCompiled *pRe; /* Compiled regular expression */ |
| 6600 | 6600 | const char *zPattern; /* The regular expression */ |
| 6601 | 6601 | const unsigned char *zStr;/* String being searched */ |
| @@ -14815,11 +14815,11 @@ | ||
| 14815 | 14815 | " Examples:", |
| 14816 | 14816 | " .ar -cf ARCHIVE foo bar # Create ARCHIVE from files foo and bar", |
| 14817 | 14817 | " .ar -tf ARCHIVE # List members of ARCHIVE", |
| 14818 | 14818 | " .ar -xvf ARCHIVE # Verbosely extract files from ARCHIVE", |
| 14819 | 14819 | " See also:", |
| 14820 | - " http://sqlite.org/cli.html#sqlar_archive_support", | |
| 14820 | + " http://sqlite.org/cli.html#sqlite_archive_support", | |
| 14821 | 14821 | #endif |
| 14822 | 14822 | #ifndef SQLITE_OMIT_AUTHORIZATION |
| 14823 | 14823 | ".auth ON|OFF Show authorizer callbacks", |
| 14824 | 14824 | #endif |
| 14825 | 14825 | ".backup ?DB? FILE Backup DB (default \"main\") to FILE", |
| 14826 | 14826 |
| --- src/shell.c | |
| +++ src/shell.c | |
| @@ -6204,11 +6204,11 @@ | |
| 6204 | } |
| 6205 | } |
| 6206 | } |
| 6207 | if( pRe->aOp[x]==RE_OP_CC_EXC ) hit = !hit; |
| 6208 | if( hit ) re_add_state(pNext, x+n); |
| 6209 | break; |
| 6210 | } |
| 6211 | } |
| 6212 | } |
| 6213 | } |
| 6214 | for(i=0; i<pNext->nState; i++){ |
| @@ -6365,11 +6365,11 @@ | |
| 6365 | const char *zErr; |
| 6366 | while( (c = p->xNextChar(&p->sIn))!=0 ){ |
| 6367 | iStart = p->nState; |
| 6368 | switch( c ){ |
| 6369 | case '|': |
| 6370 | case '$': |
| 6371 | case ')': { |
| 6372 | p->sIn.i--; |
| 6373 | return 0; |
| 6374 | } |
| 6375 | case '(': { |
| @@ -6381,11 +6381,11 @@ | |
| 6381 | } |
| 6382 | case '.': { |
| 6383 | if( rePeek(p)=='*' ){ |
| 6384 | re_append(p, RE_OP_ANYSTAR, 0); |
| 6385 | p->sIn.i++; |
| 6386 | }else{ |
| 6387 | re_append(p, RE_OP_ANY, 0); |
| 6388 | } |
| 6389 | break; |
| 6390 | } |
| 6391 | case '*': { |
| @@ -6590,12 +6590,12 @@ | |
| 6590 | ** A REGEXP B |
| 6591 | ** |
| 6592 | ** is implemented as regexp(B,A). |
| 6593 | */ |
| 6594 | static void re_sql_func( |
| 6595 | sqlite3_context *context, |
| 6596 | int argc, |
| 6597 | sqlite3_value **argv |
| 6598 | ){ |
| 6599 | ReCompiled *pRe; /* Compiled regular expression */ |
| 6600 | const char *zPattern; /* The regular expression */ |
| 6601 | const unsigned char *zStr;/* String being searched */ |
| @@ -14815,11 +14815,11 @@ | |
| 14815 | " Examples:", |
| 14816 | " .ar -cf ARCHIVE foo bar # Create ARCHIVE from files foo and bar", |
| 14817 | " .ar -tf ARCHIVE # List members of ARCHIVE", |
| 14818 | " .ar -xvf ARCHIVE # Verbosely extract files from ARCHIVE", |
| 14819 | " See also:", |
| 14820 | " http://sqlite.org/cli.html#sqlar_archive_support", |
| 14821 | #endif |
| 14822 | #ifndef SQLITE_OMIT_AUTHORIZATION |
| 14823 | ".auth ON|OFF Show authorizer callbacks", |
| 14824 | #endif |
| 14825 | ".backup ?DB? FILE Backup DB (default \"main\") to FILE", |
| 14826 |
| --- src/shell.c | |
| +++ src/shell.c | |
| @@ -6204,11 +6204,11 @@ | |
| 6204 | } |
| 6205 | } |
| 6206 | } |
| 6207 | if( pRe->aOp[x]==RE_OP_CC_EXC ) hit = !hit; |
| 6208 | if( hit ) re_add_state(pNext, x+n); |
| 6209 | break; |
| 6210 | } |
| 6211 | } |
| 6212 | } |
| 6213 | } |
| 6214 | for(i=0; i<pNext->nState; i++){ |
| @@ -6365,11 +6365,11 @@ | |
| 6365 | const char *zErr; |
| 6366 | while( (c = p->xNextChar(&p->sIn))!=0 ){ |
| 6367 | iStart = p->nState; |
| 6368 | switch( c ){ |
| 6369 | case '|': |
| 6370 | case '$': |
| 6371 | case ')': { |
| 6372 | p->sIn.i--; |
| 6373 | return 0; |
| 6374 | } |
| 6375 | case '(': { |
| @@ -6381,11 +6381,11 @@ | |
| 6381 | } |
| 6382 | case '.': { |
| 6383 | if( rePeek(p)=='*' ){ |
| 6384 | re_append(p, RE_OP_ANYSTAR, 0); |
| 6385 | p->sIn.i++; |
| 6386 | }else{ |
| 6387 | re_append(p, RE_OP_ANY, 0); |
| 6388 | } |
| 6389 | break; |
| 6390 | } |
| 6391 | case '*': { |
| @@ -6590,12 +6590,12 @@ | |
| 6590 | ** A REGEXP B |
| 6591 | ** |
| 6592 | ** is implemented as regexp(B,A). |
| 6593 | */ |
| 6594 | static void re_sql_func( |
| 6595 | sqlite3_context *context, |
| 6596 | int argc, |
| 6597 | sqlite3_value **argv |
| 6598 | ){ |
| 6599 | ReCompiled *pRe; /* Compiled regular expression */ |
| 6600 | const char *zPattern; /* The regular expression */ |
| 6601 | const unsigned char *zStr;/* String being searched */ |
| @@ -14815,11 +14815,11 @@ | |
| 14815 | " Examples:", |
| 14816 | " .ar -cf ARCHIVE foo bar # Create ARCHIVE from files foo and bar", |
| 14817 | " .ar -tf ARCHIVE # List members of ARCHIVE", |
| 14818 | " .ar -xvf ARCHIVE # Verbosely extract files from ARCHIVE", |
| 14819 | " See also:", |
| 14820 | " http://sqlite.org/cli.html#sqlite_archive_support", |
| 14821 | #endif |
| 14822 | #ifndef SQLITE_OMIT_AUTHORIZATION |
| 14823 | ".auth ON|OFF Show authorizer callbacks", |
| 14824 | #endif |
| 14825 | ".backup ?DB? FILE Backup DB (default \"main\") to FILE", |
| 14826 |
+147
-80
| --- src/sqlite3.c | ||
| +++ src/sqlite3.c | ||
| @@ -1205,11 +1205,11 @@ | ||
| 1205 | 1205 | ** [sqlite3_libversion_number()], [sqlite3_sourceid()], |
| 1206 | 1206 | ** [sqlite_version()] and [sqlite_source_id()]. |
| 1207 | 1207 | */ |
| 1208 | 1208 | #define SQLITE_VERSION "3.36.0" |
| 1209 | 1209 | #define SQLITE_VERSION_NUMBER 3036000 |
| 1210 | -#define SQLITE_SOURCE_ID "2021-06-07 00:41:18 2aa9368b63b42ac7c700516f109edcc6098c12b850eae591afed4e51a3f41819" | |
| 1210 | +#define SQLITE_SOURCE_ID "2021-06-14 20:41:20 e5a5acd6006133c5da4a7dd79726dbaa41c0d60ebeda890f848a6aafe5f9ef70" | |
| 1211 | 1211 | |
| 1212 | 1212 | /* |
| 1213 | 1213 | ** CAPI3REF: Run-Time Library Version Numbers |
| 1214 | 1214 | ** KEYWORDS: sqlite3_version sqlite3_sourceid |
| 1215 | 1215 | ** |
| @@ -16558,10 +16558,16 @@ | ||
| 16558 | 16558 | */ |
| 16559 | 16559 | #ifndef SET_FULLSYNC |
| 16560 | 16560 | # define SET_FULLSYNC(x,y) |
| 16561 | 16561 | #endif |
| 16562 | 16562 | |
| 16563 | +/* Maximum pathname length. Note: FILENAME_MAX defined by stdio.h | |
| 16564 | +*/ | |
| 16565 | +#ifndef SQLITE_MAX_PATHLEN | |
| 16566 | +# define SQLITE_MAX_PATHLEN FILENAME_MAX | |
| 16567 | +#endif | |
| 16568 | + | |
| 16563 | 16569 | /* |
| 16564 | 16570 | ** The default size of a disk sector |
| 16565 | 16571 | */ |
| 16566 | 16572 | #ifndef SQLITE_DEFAULT_SECTOR_SIZE |
| 16567 | 16573 | # define SQLITE_DEFAULT_SECTOR_SIZE 4096 |
| @@ -18795,10 +18801,11 @@ | ||
| 18795 | 18801 | #define SF_View 0x0200000 /* SELECT statement is a view */ |
| 18796 | 18802 | #define SF_NoopOrderBy 0x0400000 /* ORDER BY is ignored for this query */ |
| 18797 | 18803 | #define SF_UpdateFrom 0x0800000 /* Statement is an UPDATE...FROM */ |
| 18798 | 18804 | #define SF_PushDown 0x1000000 /* SELECT has be modified by push-down opt */ |
| 18799 | 18805 | #define SF_MultiPart 0x2000000 /* Has multiple incompatible PARTITIONs */ |
| 18806 | +#define SF_CopyCte 0x4000000 /* SELECT statement is a copy of a CTE */ | |
| 18800 | 18807 | |
| 18801 | 18808 | /* |
| 18802 | 18809 | ** The results of a SELECT can be distributed in several ways, as defined |
| 18803 | 18810 | ** by one of the following macros. The "SRT" prefix means "SELECT Result |
| 18804 | 18811 | ** Type". |
| @@ -20158,10 +20165,11 @@ | ||
| 20158 | 20165 | SQLITE_PRIVATE void sqlite3RegisterDateTimeFunctions(void); |
| 20159 | 20166 | SQLITE_PRIVATE void sqlite3RegisterPerConnectionBuiltinFunctions(sqlite3*); |
| 20160 | 20167 | SQLITE_PRIVATE int sqlite3SafetyCheckOk(sqlite3*); |
| 20161 | 20168 | SQLITE_PRIVATE int sqlite3SafetyCheckSickOrOk(sqlite3*); |
| 20162 | 20169 | SQLITE_PRIVATE void sqlite3ChangeCookie(Parse*, int); |
| 20170 | +SQLITE_PRIVATE With *sqlite3WithDup(sqlite3 *db, With *p); | |
| 20163 | 20171 | |
| 20164 | 20172 | #if !defined(SQLITE_OMIT_VIEW) && !defined(SQLITE_OMIT_TRIGGER) |
| 20165 | 20173 | SQLITE_PRIVATE void sqlite3MaterializeView(Parse*, Table*, Expr*, ExprList*,Expr*,int); |
| 20166 | 20174 | #endif |
| 20167 | 20175 | |
| @@ -20568,11 +20576,11 @@ | ||
| 20568 | 20576 | #ifndef SQLITE_OMIT_CTE |
| 20569 | 20577 | SQLITE_PRIVATE Cte *sqlite3CteNew(Parse*,Token*,ExprList*,Select*,u8); |
| 20570 | 20578 | SQLITE_PRIVATE void sqlite3CteDelete(sqlite3*,Cte*); |
| 20571 | 20579 | SQLITE_PRIVATE With *sqlite3WithAdd(Parse*,With*,Cte*); |
| 20572 | 20580 | SQLITE_PRIVATE void sqlite3WithDelete(sqlite3*,With*); |
| 20573 | -SQLITE_PRIVATE void sqlite3WithPush(Parse*, With*, u8); | |
| 20581 | +SQLITE_PRIVATE With *sqlite3WithPush(Parse*, With*, u8); | |
| 20574 | 20582 | #else |
| 20575 | 20583 | # define sqlite3CteNew(P,T,E,S) ((void*)0) |
| 20576 | 20584 | # define sqlite3CteDelete(D,C) |
| 20577 | 20585 | # define sqlite3CteWithAdd(P,W,C) ((void*)0) |
| 20578 | 20586 | # define sqlite3WithDelete(x,y) |
| @@ -21670,11 +21678,11 @@ | ||
| 21670 | 21678 | SQLITE_PRIVATE int sqlite3VdbeMemTooBig(Mem*); |
| 21671 | 21679 | SQLITE_PRIVATE int sqlite3VdbeMemCopy(Mem*, const Mem*); |
| 21672 | 21680 | SQLITE_PRIVATE void sqlite3VdbeMemShallowCopy(Mem*, const Mem*, int); |
| 21673 | 21681 | SQLITE_PRIVATE void sqlite3VdbeMemMove(Mem*, Mem*); |
| 21674 | 21682 | SQLITE_PRIVATE int sqlite3VdbeMemNulTerminate(Mem*); |
| 21675 | -SQLITE_PRIVATE int sqlite3VdbeMemSetStr(Mem*, const char*, int, u8, void(*)(void*)); | |
| 21683 | +SQLITE_PRIVATE int sqlite3VdbeMemSetStr(Mem*, const char*, i64, u8, void(*)(void*)); | |
| 21676 | 21684 | SQLITE_PRIVATE void sqlite3VdbeMemSetInt64(Mem*, i64); |
| 21677 | 21685 | #ifdef SQLITE_OMIT_FLOATING_POINT |
| 21678 | 21686 | # define sqlite3VdbeMemSetDouble sqlite3VdbeMemSetInt64 |
| 21679 | 21687 | #else |
| 21680 | 21688 | SQLITE_PRIVATE void sqlite3VdbeMemSetDouble(Mem*, double); |
| @@ -23667,10 +23675,12 @@ | ||
| 23667 | 23675 | zPathOut[0] = 0; |
| 23668 | 23676 | return pVfs->xFullPathname(pVfs, zPath, nPathOut, zPathOut); |
| 23669 | 23677 | } |
| 23670 | 23678 | #ifndef SQLITE_OMIT_LOAD_EXTENSION |
| 23671 | 23679 | SQLITE_PRIVATE void *sqlite3OsDlOpen(sqlite3_vfs *pVfs, const char *zPath){ |
| 23680 | + assert( zPath!=0 ); | |
| 23681 | + assert( strlen(zPath)<=SQLITE_MAX_PATHLEN ); /* tag-20210611-1 */ | |
| 23672 | 23682 | return pVfs->xDlOpen(pVfs, zPath); |
| 23673 | 23683 | } |
| 23674 | 23684 | SQLITE_PRIVATE void sqlite3OsDlError(sqlite3_vfs *pVfs, int nByte, char *zBufOut){ |
| 23675 | 23685 | pVfs->xDlError(pVfs, nByte, zBufOut); |
| 23676 | 23686 | } |
| @@ -66965,11 +66975,11 @@ | ||
| 66965 | 66975 | testcase( pc+size==usableSize ); |
| 66966 | 66976 | put2byte(pAddr, cbrk); |
| 66967 | 66977 | if( temp==0 ){ |
| 66968 | 66978 | if( cbrk==pc ) continue; |
| 66969 | 66979 | temp = sqlite3PagerTempSpace(pPage->pBt->pPager); |
| 66970 | - memcpy(&temp[iCellStart], &data[iCellStart], (cbrk+size) - iCellStart); | |
| 66980 | + memcpy(&temp[iCellStart], &data[iCellStart], usableSize - iCellStart); | |
| 66971 | 66981 | src = temp; |
| 66972 | 66982 | } |
| 66973 | 66983 | memcpy(&data[cbrk], &src[pc], size); |
| 66974 | 66984 | } |
| 66975 | 66985 | data[hdr+7] = 0; |
| @@ -78047,15 +78057,15 @@ | ||
| 78047 | 78057 | ** either case, SQLITE_TOOBIG is returned. |
| 78048 | 78058 | */ |
| 78049 | 78059 | SQLITE_PRIVATE int sqlite3VdbeMemSetStr( |
| 78050 | 78060 | Mem *pMem, /* Memory cell to set to string value */ |
| 78051 | 78061 | const char *z, /* String pointer */ |
| 78052 | - int n, /* Bytes in string, or negative */ | |
| 78062 | + i64 n, /* Bytes in string, or negative */ | |
| 78053 | 78063 | u8 enc, /* Encoding of z. 0 for BLOBs */ |
| 78054 | 78064 | void (*xDel)(void*) /* Destructor function */ |
| 78055 | 78065 | ){ |
| 78056 | - int nByte = n; /* New value for pMem->n */ | |
| 78066 | + i64 nByte = n; /* New value for pMem->n */ | |
| 78057 | 78067 | int iLimit; /* Maximum allowed string or blob size */ |
| 78058 | 78068 | u16 flags = 0; /* New value for pMem->flags */ |
| 78059 | 78069 | |
| 78060 | 78070 | assert( pMem->db==0 || sqlite3_mutex_held(pMem->db->mutex) ); |
| 78061 | 78071 | assert( !sqlite3VdbeMemIsRowSet(pMem) ); |
| @@ -78073,11 +78083,11 @@ | ||
| 78073 | 78083 | } |
| 78074 | 78084 | flags = (enc==0?MEM_Blob:MEM_Str); |
| 78075 | 78085 | if( nByte<0 ){ |
| 78076 | 78086 | assert( enc!=0 ); |
| 78077 | 78087 | if( enc==SQLITE_UTF8 ){ |
| 78078 | - nByte = 0x7fffffff & (int)strlen(z); | |
| 78088 | + nByte = strlen(z); | |
| 78079 | 78089 | }else{ |
| 78080 | 78090 | for(nByte=0; nByte<=iLimit && (z[nByte] | z[nByte+1]); nByte+=2){} |
| 78081 | 78091 | } |
| 78082 | 78092 | flags |= MEM_Term; |
| 78083 | 78093 | } |
| @@ -78085,11 +78095,11 @@ | ||
| 78085 | 78095 | /* The following block sets the new values of Mem.z and Mem.xDel. It |
| 78086 | 78096 | ** also sets a flag in local variable "flags" to indicate the memory |
| 78087 | 78097 | ** management (one of MEM_Dyn or MEM_Static). |
| 78088 | 78098 | */ |
| 78089 | 78099 | if( xDel==SQLITE_TRANSIENT ){ |
| 78090 | - u32 nAlloc = nByte; | |
| 78100 | + i64 nAlloc = nByte; | |
| 78091 | 78101 | if( flags&MEM_Term ){ |
| 78092 | 78102 | nAlloc += (enc==SQLITE_UTF8?1:2); |
| 78093 | 78103 | } |
| 78094 | 78104 | if( nByte>iLimit ){ |
| 78095 | 78105 | return sqlite3ErrorToParser(pMem->db, SQLITE_TOOBIG); |
| @@ -78111,11 +78121,11 @@ | ||
| 78111 | 78121 | pMem->xDel = xDel; |
| 78112 | 78122 | flags |= ((xDel==SQLITE_STATIC)?MEM_Static:MEM_Dyn); |
| 78113 | 78123 | } |
| 78114 | 78124 | } |
| 78115 | 78125 | |
| 78116 | - pMem->n = nByte; | |
| 78126 | + pMem->n = (int)(nByte & 0x7fffffff); | |
| 78117 | 78127 | pMem->flags = flags; |
| 78118 | 78128 | if( enc ){ |
| 78119 | 78129 | pMem->enc = enc; |
| 78120 | 78130 | #ifdef SQLITE_ENABLE_SESSION |
| 78121 | 78131 | }else if( pMem->db==0 ){ |
| @@ -78131,11 +78141,11 @@ | ||
| 78131 | 78141 | return SQLITE_NOMEM_BKPT; |
| 78132 | 78142 | } |
| 78133 | 78143 | #endif |
| 78134 | 78144 | |
| 78135 | 78145 | if( nByte>iLimit ){ |
| 78136 | - return SQLITE_TOOBIG; | |
| 78146 | + return sqlite3ErrorToParser(pMem->db, SQLITE_TOOBIG); | |
| 78137 | 78147 | } |
| 78138 | 78148 | |
| 78139 | 78149 | return SQLITE_OK; |
| 78140 | 78150 | } |
| 78141 | 78151 | |
| @@ -84527,11 +84537,11 @@ | ||
| 84527 | 84537 | }else if( xDel==SQLITE_TRANSIENT ){ |
| 84528 | 84538 | /* noop */ |
| 84529 | 84539 | }else{ |
| 84530 | 84540 | xDel((void*)p); |
| 84531 | 84541 | } |
| 84532 | - if( pCtx ) sqlite3_result_error_toobig(pCtx); | |
| 84542 | + sqlite3_result_error_toobig(pCtx); | |
| 84533 | 84543 | return SQLITE_TOOBIG; |
| 84534 | 84544 | } |
| 84535 | 84545 | SQLITE_API void sqlite3_result_blob( |
| 84536 | 84546 | sqlite3_context *pCtx, |
| 84537 | 84547 | const void *z, |
| @@ -85509,11 +85519,11 @@ | ||
| 85509 | 85519 | */ |
| 85510 | 85520 | static int bindText( |
| 85511 | 85521 | sqlite3_stmt *pStmt, /* The statement to bind against */ |
| 85512 | 85522 | int i, /* Index of the parameter to bind */ |
| 85513 | 85523 | const void *zData, /* Pointer to the data to be bound */ |
| 85514 | - int nData, /* Number of bytes of data to be bound */ | |
| 85524 | + i64 nData, /* Number of bytes of data to be bound */ | |
| 85515 | 85525 | void (*xDel)(void*), /* Destructor for the data */ |
| 85516 | 85526 | u8 encoding /* Encoding for the data */ |
| 85517 | 85527 | ){ |
| 85518 | 85528 | Vdbe *p = (Vdbe *)pStmt; |
| 85519 | 85529 | Mem *pVar; |
| @@ -85561,15 +85571,11 @@ | ||
| 85561 | 85571 | const void *zData, |
| 85562 | 85572 | sqlite3_uint64 nData, |
| 85563 | 85573 | void (*xDel)(void*) |
| 85564 | 85574 | ){ |
| 85565 | 85575 | assert( xDel!=SQLITE_DYNAMIC ); |
| 85566 | - if( nData>0x7fffffff ){ | |
| 85567 | - return invokeValueDestructor(zData, xDel, 0); | |
| 85568 | - }else{ | |
| 85569 | - return bindText(pStmt, i, zData, (int)nData, xDel, 0); | |
| 85570 | - } | |
| 85576 | + return bindText(pStmt, i, zData, nData, xDel, 0); | |
| 85571 | 85577 | } |
| 85572 | 85578 | SQLITE_API int sqlite3_bind_double(sqlite3_stmt *pStmt, int i, double rValue){ |
| 85573 | 85579 | int rc; |
| 85574 | 85580 | Vdbe *p = (Vdbe *)pStmt; |
| 85575 | 85581 | rc = vdbeUnbind(p, i); |
| @@ -85635,16 +85641,12 @@ | ||
| 85635 | 85641 | sqlite3_uint64 nData, |
| 85636 | 85642 | void (*xDel)(void*), |
| 85637 | 85643 | unsigned char enc |
| 85638 | 85644 | ){ |
| 85639 | 85645 | assert( xDel!=SQLITE_DYNAMIC ); |
| 85640 | - if( nData>0x7fffffff ){ | |
| 85641 | - return invokeValueDestructor(zData, xDel, 0); | |
| 85642 | - }else{ | |
| 85643 | - if( enc==SQLITE_UTF16 ) enc = SQLITE_UTF16NATIVE; | |
| 85644 | - return bindText(pStmt, i, zData, (int)nData, xDel, enc); | |
| 85645 | - } | |
| 85646 | + if( enc==SQLITE_UTF16 ) enc = SQLITE_UTF16NATIVE; | |
| 85647 | + return bindText(pStmt, i, zData, nData, xDel, enc); | |
| 85646 | 85648 | } |
| 85647 | 85649 | #ifndef SQLITE_OMIT_UTF16 |
| 85648 | 85650 | SQLITE_API int sqlite3_bind_text16( |
| 85649 | 85651 | sqlite3_stmt *pStmt, |
| 85650 | 85652 | int i, |
| @@ -90957,11 +90959,12 @@ | ||
| 90957 | 90959 | assert( pOp[1].opcode==OP_SeekGE ); |
| 90958 | 90960 | |
| 90959 | 90961 | /* pOp->p2 points to the first instruction past the OP_IdxGT that |
| 90960 | 90962 | ** follows the OP_SeekGE. */ |
| 90961 | 90963 | assert( pOp->p2>=(int)(pOp-aOp)+2 ); |
| 90962 | - assert( aOp[pOp->p2-1].opcode==OP_IdxGT ); | |
| 90964 | + assert( aOp[pOp->p2-1].opcode==OP_IdxGT || aOp[pOp->p2-1].opcode==OP_IdxGE ); | |
| 90965 | + testcase( aOp[pOp->p2-1].opcode==OP_IdxGE ); | |
| 90963 | 90966 | assert( pOp[1].p1==aOp[pOp->p2-1].p1 ); |
| 90964 | 90967 | assert( pOp[1].p2==aOp[pOp->p2-1].p2 ); |
| 90965 | 90968 | assert( pOp[1].p3==aOp[pOp->p2-1].p3 ); |
| 90966 | 90969 | |
| 90967 | 90970 | assert( pOp->p1>0 ); |
| @@ -92847,11 +92850,13 @@ | ||
| 92847 | 92850 | } |
| 92848 | 92851 | #endif |
| 92849 | 92852 | |
| 92850 | 92853 | iDb = pOp->p1; |
| 92851 | 92854 | assert( iDb>=0 && iDb<db->nDb ); |
| 92852 | - assert( DbHasProperty(db, iDb, DB_SchemaLoaded) || db->mallocFailed ); | |
| 92855 | + assert( DbHasProperty(db, iDb, DB_SchemaLoaded) | |
| 92856 | + || db->mallocFailed | |
| 92857 | + || (CORRUPT_DB && (db->flags & SQLITE_NoSchemaError)!=0) ); | |
| 92853 | 92858 | |
| 92854 | 92859 | #ifndef SQLITE_OMIT_ALTERTABLE |
| 92855 | 92860 | if( pOp->p4.z==0 ){ |
| 92856 | 92861 | sqlite3SchemaClear(db->aDb[iDb].pSchema); |
| 92857 | 92862 | db->mDbFlags &= ~DBFLAG_SchemaKnownOk; |
| @@ -102622,11 +102627,11 @@ | ||
| 102622 | 102627 | ** Create and return a deep copy of the object passed as the second |
| 102623 | 102628 | ** argument. If an OOM condition is encountered, NULL is returned |
| 102624 | 102629 | ** and the db->mallocFailed flag set. |
| 102625 | 102630 | */ |
| 102626 | 102631 | #ifndef SQLITE_OMIT_CTE |
| 102627 | -static With *withDup(sqlite3 *db, With *p){ | |
| 102632 | +SQLITE_PRIVATE With *sqlite3WithDup(sqlite3 *db, With *p){ | |
| 102628 | 102633 | With *pRet = 0; |
| 102629 | 102634 | if( p ){ |
| 102630 | 102635 | sqlite3_int64 nByte = sizeof(*p) + sizeof(p->a[0]) * (p->nCte-1); |
| 102631 | 102636 | pRet = sqlite3DbMallocZero(db, nByte); |
| 102632 | 102637 | if( pRet ){ |
| @@ -102640,11 +102645,11 @@ | ||
| 102640 | 102645 | } |
| 102641 | 102646 | } |
| 102642 | 102647 | return pRet; |
| 102643 | 102648 | } |
| 102644 | 102649 | #else |
| 102645 | -# define withDup(x,y) 0 | |
| 102650 | +# define sqlite3WithDup(x,y) 0 | |
| 102646 | 102651 | #endif |
| 102647 | 102652 | |
| 102648 | 102653 | #ifndef SQLITE_OMIT_WINDOWFUNC |
| 102649 | 102654 | /* |
| 102650 | 102655 | ** The gatherSelectWindows() procedure and its helper routine |
| @@ -102844,11 +102849,11 @@ | ||
| 102844 | 102849 | pNew->iOffset = 0; |
| 102845 | 102850 | pNew->selFlags = p->selFlags & ~SF_UsesEphemeral; |
| 102846 | 102851 | pNew->addrOpenEphm[0] = -1; |
| 102847 | 102852 | pNew->addrOpenEphm[1] = -1; |
| 102848 | 102853 | pNew->nSelectRow = p->nSelectRow; |
| 102849 | - pNew->pWith = withDup(db, p->pWith); | |
| 102854 | + pNew->pWith = sqlite3WithDup(db, p->pWith); | |
| 102850 | 102855 | #ifndef SQLITE_OMIT_WINDOWFUNC |
| 102851 | 102856 | pNew->pWin = 0; |
| 102852 | 102857 | pNew->pWinDefn = sqlite3WindowListDup(db, p->pWinDefn); |
| 102853 | 102858 | if( p->pWin && db->mallocFailed==0 ) gatherSelectWindows(pNew); |
| 102854 | 102859 | #endif |
| @@ -108184,19 +108189,34 @@ | ||
| 108184 | 108189 | ** to select statement pSelect. |
| 108185 | 108190 | */ |
| 108186 | 108191 | static void renameWalkWith(Walker *pWalker, Select *pSelect){ |
| 108187 | 108192 | With *pWith = pSelect->pWith; |
| 108188 | 108193 | if( pWith ){ |
| 108194 | + Parse *pParse = pWalker->pParse; | |
| 108189 | 108195 | int i; |
| 108196 | + With *pCopy = 0; | |
| 108197 | + assert( pWith->nCte>0 ); | |
| 108198 | + if( (pWith->a[0].pSelect->selFlags & SF_Expanded)==0 ){ | |
| 108199 | + /* Push a copy of the With object onto the with-stack. We use a copy | |
| 108200 | + ** here as the original will be expanded and resolved (flags SF_Expanded | |
| 108201 | + ** and SF_Resolved) below. And the parser code that uses the with-stack | |
| 108202 | + ** fails if the Select objects on it have already been expanded and | |
| 108203 | + ** resolved. */ | |
| 108204 | + pCopy = sqlite3WithDup(pParse->db, pWith); | |
| 108205 | + pCopy = sqlite3WithPush(pParse, pCopy, 1); | |
| 108206 | + } | |
| 108190 | 108207 | for(i=0; i<pWith->nCte; i++){ |
| 108191 | 108208 | Select *p = pWith->a[i].pSelect; |
| 108192 | 108209 | NameContext sNC; |
| 108193 | 108210 | memset(&sNC, 0, sizeof(sNC)); |
| 108194 | - sNC.pParse = pWalker->pParse; | |
| 108195 | - sqlite3SelectPrep(sNC.pParse, p, &sNC); | |
| 108211 | + sNC.pParse = pParse; | |
| 108212 | + if( pCopy ) sqlite3SelectPrep(sNC.pParse, p, &sNC); | |
| 108196 | 108213 | sqlite3WalkSelect(pWalker, p); |
| 108197 | - sqlite3RenameExprlistUnmap(pWalker->pParse, pWith->a[i].pCols); | |
| 108214 | + sqlite3RenameExprlistUnmap(pParse, pWith->a[i].pCols); | |
| 108215 | + } | |
| 108216 | + if( pCopy && pParse->pWith==pCopy ){ | |
| 108217 | + pParse->pWith = pCopy->pOuter; | |
| 108198 | 108218 | } |
| 108199 | 108219 | } |
| 108200 | 108220 | } |
| 108201 | 108221 | |
| 108202 | 108222 | /* |
| @@ -108219,11 +108239,15 @@ | ||
| 108219 | 108239 | */ |
| 108220 | 108240 | static int renameUnmapSelectCb(Walker *pWalker, Select *p){ |
| 108221 | 108241 | Parse *pParse = pWalker->pParse; |
| 108222 | 108242 | int i; |
| 108223 | 108243 | if( pParse->nErr ) return WRC_Abort; |
| 108224 | - if( p->selFlags & SF_View ) return WRC_Prune; | |
| 108244 | + if( p->selFlags & (SF_View|SF_CopyCte) ){ | |
| 108245 | + testcase( pSelect->selFlags & SF_View ); | |
| 108246 | + testcase( pSelect->selFlags & SF_CopyCte ); | |
| 108247 | + return WRC_Prune; | |
| 108248 | + } | |
| 108225 | 108249 | if( ALWAYS(p->pEList) ){ |
| 108226 | 108250 | ExprList *pList = p->pEList; |
| 108227 | 108251 | for(i=0; i<pList->nExpr; i++){ |
| 108228 | 108252 | if( pList->a[i].zEName && pList->a[i].eEName==ENAME_NAME ){ |
| 108229 | 108253 | sqlite3RenameTokenRemap(pParse, 0, (void*)pList->a[i].zEName); |
| @@ -108327,11 +108351,15 @@ | ||
| 108327 | 108351 | ** This is a Walker select callback. It does nothing. It is only required |
| 108328 | 108352 | ** because without a dummy callback, sqlite3WalkExpr() and similar do not |
| 108329 | 108353 | ** descend into sub-select statements. |
| 108330 | 108354 | */ |
| 108331 | 108355 | static int renameColumnSelectCb(Walker *pWalker, Select *p){ |
| 108332 | - if( p->selFlags & SF_View ) return WRC_Prune; | |
| 108356 | + if( p->selFlags & (SF_View|SF_CopyCte) ){ | |
| 108357 | + testcase( pSelect->selFlags & SF_View ); | |
| 108358 | + testcase( pSelect->selFlags & SF_CopyCte ); | |
| 108359 | + return WRC_Prune; | |
| 108360 | + } | |
| 108333 | 108361 | renameWalkWith(pWalker, p); |
| 108334 | 108362 | return WRC_Continue; |
| 108335 | 108363 | } |
| 108336 | 108364 | |
| 108337 | 108365 | /* |
| @@ -108963,11 +108991,15 @@ | ||
| 108963 | 108991 | */ |
| 108964 | 108992 | static int renameTableSelectCb(Walker *pWalker, Select *pSelect){ |
| 108965 | 108993 | int i; |
| 108966 | 108994 | RenameCtx *p = pWalker->u.pRename; |
| 108967 | 108995 | SrcList *pSrc = pSelect->pSrc; |
| 108968 | - if( pSelect->selFlags & SF_View ) return WRC_Prune; | |
| 108996 | + if( pSelect->selFlags & (SF_View|SF_CopyCte) ){ | |
| 108997 | + testcase( pSelect->selFlags & SF_View ); | |
| 108998 | + testcase( pSelect->selFlags & SF_CopyCte ); | |
| 108999 | + return WRC_Prune; | |
| 109000 | + } | |
| 108969 | 109001 | if( NEVER(pSrc==0) ){ |
| 108970 | 109002 | assert( pWalker->pParse->db->mallocFailed ); |
| 108971 | 109003 | return WRC_Abort; |
| 108972 | 109004 | } |
| 108973 | 109005 | for(i=0; i<pSrc->nSrc; i++){ |
| @@ -116705,11 +116737,11 @@ | ||
| 116705 | 116737 | const char *zDb = db->aDb[iDb].zDbSName; |
| 116706 | 116738 | const char *zTab = SCHEMA_TABLE(iDb); |
| 116707 | 116739 | if( sqlite3AuthCheck(pParse, SQLITE_DELETE, zTab, 0, zDb) ){ |
| 116708 | 116740 | goto exit_drop_index; |
| 116709 | 116741 | } |
| 116710 | - if( !OMIT_TEMPDB && iDb ) code = SQLITE_DROP_TEMP_INDEX; | |
| 116742 | + if( !OMIT_TEMPDB && iDb==1 ) code = SQLITE_DROP_TEMP_INDEX; | |
| 116711 | 116743 | if( sqlite3AuthCheck(pParse, code, pIndex->zName, pTab->zName, zDb) ){ |
| 116712 | 116744 | goto exit_drop_index; |
| 116713 | 116745 | } |
| 116714 | 116746 | } |
| 116715 | 116747 | #endif |
| @@ -119237,17 +119269,19 @@ | ||
| 119237 | 119269 | ){ |
| 119238 | 119270 | /* This column was already computed by the previous index */ |
| 119239 | 119271 | continue; |
| 119240 | 119272 | } |
| 119241 | 119273 | sqlite3ExprCodeLoadIndexColumn(pParse, pIdx, iDataCur, j, regBase+j); |
| 119242 | - /* If the column affinity is REAL but the number is an integer, then it | |
| 119243 | - ** might be stored in the table as an integer (using a compact | |
| 119244 | - ** representation) then converted to REAL by an OP_RealAffinity opcode. | |
| 119245 | - ** But we are getting ready to store this value back into an index, where | |
| 119246 | - ** it should be converted by to INTEGER again. So omit the OP_RealAffinity | |
| 119247 | - ** opcode if it is present */ | |
| 119248 | - sqlite3VdbeDeletePriorOpcode(v, OP_RealAffinity); | |
| 119274 | + if( pIdx->aiColumn[j]>=0 ){ | |
| 119275 | + /* If the column affinity is REAL but the number is an integer, then it | |
| 119276 | + ** might be stored in the table as an integer (using a compact | |
| 119277 | + ** representation) then converted to REAL by an OP_RealAffinity opcode. | |
| 119278 | + ** But we are getting ready to store this value back into an index, where | |
| 119279 | + ** it should be converted by to INTEGER again. So omit the | |
| 119280 | + ** OP_RealAffinity opcode if it is present */ | |
| 119281 | + sqlite3VdbeDeletePriorOpcode(v, OP_RealAffinity); | |
| 119282 | + } | |
| 119249 | 119283 | } |
| 119250 | 119284 | if( regOut ){ |
| 119251 | 119285 | sqlite3VdbeAddOp3(v, OP_MakeRecord, regBase, nCol, regOut); |
| 119252 | 119286 | } |
| 119253 | 119287 | sqlite3ReleaseTempRange(pParse, regBase, nCol); |
| @@ -127401,11 +127435,11 @@ | ||
| 127401 | 127435 | sqlite3_loadext_entry xInit; |
| 127402 | 127436 | char *zErrmsg = 0; |
| 127403 | 127437 | const char *zEntry; |
| 127404 | 127438 | char *zAltEntry = 0; |
| 127405 | 127439 | void **aHandle; |
| 127406 | - u64 nMsg = 300 + sqlite3Strlen30(zFile); | |
| 127440 | + u64 nMsg = strlen(zFile); | |
| 127407 | 127441 | int ii; |
| 127408 | 127442 | int rc; |
| 127409 | 127443 | |
| 127410 | 127444 | /* Shared library endings to try if zFile cannot be loaded as written */ |
| 127411 | 127445 | static const char *azEndings[] = { |
| @@ -127435,30 +127469,26 @@ | ||
| 127435 | 127469 | return SQLITE_ERROR; |
| 127436 | 127470 | } |
| 127437 | 127471 | |
| 127438 | 127472 | zEntry = zProc ? zProc : "sqlite3_extension_init"; |
| 127439 | 127473 | |
| 127474 | + /* tag-20210611-1. Some dlopen() implementations will segfault if given | |
| 127475 | + ** an oversize filename. Most filesystems have a pathname limit of 4K, | |
| 127476 | + ** so limit the extension filename length to about twice that. | |
| 127477 | + ** https://sqlite.org/forum/forumpost/08a0d6d9bf */ | |
| 127478 | + if( nMsg>SQLITE_MAX_PATHLEN ) goto extension_not_found; | |
| 127479 | + | |
| 127440 | 127480 | handle = sqlite3OsDlOpen(pVfs, zFile); |
| 127441 | 127481 | #if SQLITE_OS_UNIX || SQLITE_OS_WIN |
| 127442 | 127482 | for(ii=0; ii<ArraySize(azEndings) && handle==0; ii++){ |
| 127443 | 127483 | char *zAltFile = sqlite3_mprintf("%s.%s", zFile, azEndings[ii]); |
| 127444 | 127484 | if( zAltFile==0 ) return SQLITE_NOMEM_BKPT; |
| 127445 | 127485 | handle = sqlite3OsDlOpen(pVfs, zAltFile); |
| 127446 | 127486 | sqlite3_free(zAltFile); |
| 127447 | 127487 | } |
| 127448 | 127488 | #endif |
| 127449 | - if( handle==0 ){ | |
| 127450 | - if( pzErrMsg ){ | |
| 127451 | - *pzErrMsg = zErrmsg = sqlite3_malloc64(nMsg); | |
| 127452 | - if( zErrmsg ){ | |
| 127453 | - sqlite3_snprintf(nMsg, zErrmsg, | |
| 127454 | - "unable to open shared library [%s]", zFile); | |
| 127455 | - sqlite3OsDlError(pVfs, nMsg-1, zErrmsg); | |
| 127456 | - } | |
| 127457 | - } | |
| 127458 | - return SQLITE_ERROR; | |
| 127459 | - } | |
| 127489 | + if( handle==0 ) goto extension_not_found; | |
| 127460 | 127490 | xInit = (sqlite3_loadext_entry)sqlite3OsDlSym(pVfs, handle, zEntry); |
| 127461 | 127491 | |
| 127462 | 127492 | /* If no entry point was specified and the default legacy |
| 127463 | 127493 | ** entry point name "sqlite3_extension_init" was not found, then |
| 127464 | 127494 | ** construct an entry point name "sqlite3_X_init" where the X is |
| @@ -127491,14 +127521,15 @@ | ||
| 127491 | 127521 | zEntry = zAltEntry; |
| 127492 | 127522 | xInit = (sqlite3_loadext_entry)sqlite3OsDlSym(pVfs, handle, zEntry); |
| 127493 | 127523 | } |
| 127494 | 127524 | if( xInit==0 ){ |
| 127495 | 127525 | if( pzErrMsg ){ |
| 127496 | - nMsg += sqlite3Strlen30(zEntry); | |
| 127526 | + nMsg += strlen(zEntry) + 300; | |
| 127497 | 127527 | *pzErrMsg = zErrmsg = sqlite3_malloc64(nMsg); |
| 127498 | 127528 | if( zErrmsg ){ |
| 127499 | - sqlite3_snprintf(nMsg, zErrmsg, | |
| 127529 | + assert( nMsg<0x7fffffff ); /* zErrmsg would be NULL if not so */ | |
| 127530 | + sqlite3_snprintf((int)nMsg, zErrmsg, | |
| 127500 | 127531 | "no entry point [%s] in shared library [%s]", zEntry, zFile); |
| 127501 | 127532 | sqlite3OsDlError(pVfs, nMsg-1, zErrmsg); |
| 127502 | 127533 | } |
| 127503 | 127534 | } |
| 127504 | 127535 | sqlite3OsDlClose(pVfs, handle); |
| @@ -127528,10 +127559,23 @@ | ||
| 127528 | 127559 | sqlite3DbFree(db, db->aExtension); |
| 127529 | 127560 | db->aExtension = aHandle; |
| 127530 | 127561 | |
| 127531 | 127562 | db->aExtension[db->nExtension++] = handle; |
| 127532 | 127563 | return SQLITE_OK; |
| 127564 | + | |
| 127565 | +extension_not_found: | |
| 127566 | + if( pzErrMsg ){ | |
| 127567 | + nMsg += 300; | |
| 127568 | + *pzErrMsg = zErrmsg = sqlite3_malloc64(nMsg); | |
| 127569 | + if( zErrmsg ){ | |
| 127570 | + assert( nMsg<0x7fffffff ); /* zErrmsg would be NULL if not so */ | |
| 127571 | + sqlite3_snprintf((int)nMsg, zErrmsg, | |
| 127572 | + "unable to open shared library [%.*s]", SQLITE_MAX_PATHLEN, zFile); | |
| 127573 | + sqlite3OsDlError(pVfs, nMsg-1, zErrmsg); | |
| 127574 | + } | |
| 127575 | + } | |
| 127576 | + return SQLITE_ERROR; | |
| 127533 | 127577 | } |
| 127534 | 127578 | SQLITE_API int sqlite3_load_extension( |
| 127535 | 127579 | sqlite3 *db, /* Load the extension into this database connection */ |
| 127536 | 127580 | const char *zFile, /* Name of the shared library containing extension */ |
| 127537 | 127581 | const char *zProc, /* Entry point. Use "sqlite3_extension_init" if 0 */ |
| @@ -131368,13 +131412,15 @@ | ||
| 131368 | 131412 | if( rc==SQLITE_OK ){ |
| 131369 | 131413 | sqlite3AnalysisLoad(db, iDb); |
| 131370 | 131414 | } |
| 131371 | 131415 | #endif |
| 131372 | 131416 | } |
| 131417 | + assert( pDb == &(db->aDb[iDb]) ); | |
| 131373 | 131418 | if( db->mallocFailed ){ |
| 131374 | 131419 | rc = SQLITE_NOMEM_BKPT; |
| 131375 | 131420 | sqlite3ResetAllSchemasOfConnection(db); |
| 131421 | + pDb = &db->aDb[iDb]; | |
| 131376 | 131422 | }else |
| 131377 | 131423 | if( rc==SQLITE_OK || (db->flags&SQLITE_NoSchemaError)){ |
| 131378 | 131424 | /* Hack: If the SQLITE_NoSchemaError flag is set, then consider |
| 131379 | 131425 | ** the schema loaded, even if errors (other than OOM) occurred. In |
| 131380 | 131426 | ** this situation the current sqlite3_prepare() operation will fail, |
| @@ -136426,10 +136472,11 @@ | ||
| 136426 | 136472 | ** a known value due to WHERE clause constraints of the form COLUMN=VALUE. |
| 136427 | 136473 | */ |
| 136428 | 136474 | typedef struct WhereConst WhereConst; |
| 136429 | 136475 | struct WhereConst { |
| 136430 | 136476 | Parse *pParse; /* Parsing context */ |
| 136477 | + u8 *pOomFault; /* Pointer to pParse->db->mallocFailed */ | |
| 136431 | 136478 | int nConst; /* Number for COLUMN=CONSTANT terms */ |
| 136432 | 136479 | int nChng; /* Number of times a constant is propagated */ |
| 136433 | 136480 | int bHasAffBlob; /* At least one column in apExpr[] as affinity BLOB */ |
| 136434 | 136481 | Expr **apExpr; /* [i*2] is COLUMN and [i*2+1] is VALUE */ |
| 136435 | 136482 | }; |
| @@ -136525,10 +136572,11 @@ | ||
| 136525 | 136572 | WhereConst *pConst, |
| 136526 | 136573 | Expr *pExpr, |
| 136527 | 136574 | int bIgnoreAffBlob |
| 136528 | 136575 | ){ |
| 136529 | 136576 | int i; |
| 136577 | + if( pConst->pOomFault[0] ) return WRC_Prune; | |
| 136530 | 136578 | if( pExpr->op!=TK_COLUMN ) return WRC_Continue; |
| 136531 | 136579 | if( ExprHasProperty(pExpr, EP_FixedCol|EP_FromJoin) ){ |
| 136532 | 136580 | testcase( ExprHasProperty(pExpr, EP_FixedCol) ); |
| 136533 | 136581 | testcase( ExprHasProperty(pExpr, EP_FromJoin) ); |
| 136534 | 136582 | return WRC_Continue; |
| @@ -136545,10 +136593,11 @@ | ||
| 136545 | 136593 | pConst->nChng++; |
| 136546 | 136594 | ExprClearProperty(pExpr, EP_Leaf); |
| 136547 | 136595 | ExprSetProperty(pExpr, EP_FixedCol); |
| 136548 | 136596 | assert( pExpr->pLeft==0 ); |
| 136549 | 136597 | pExpr->pLeft = sqlite3ExprDup(pConst->pParse->db, pConst->apExpr[i*2+1], 0); |
| 136598 | + if( pConst->pParse->db->mallocFailed ) return WRC_Prune; | |
| 136550 | 136599 | break; |
| 136551 | 136600 | } |
| 136552 | 136601 | return WRC_Prune; |
| 136553 | 136602 | } |
| 136554 | 136603 | |
| @@ -136577,10 +136626,11 @@ | ||
| 136577 | 136626 | if( pConst->bHasAffBlob ){ |
| 136578 | 136627 | if( (pExpr->op>=TK_EQ && pExpr->op<=TK_GE) |
| 136579 | 136628 | || pExpr->op==TK_IS |
| 136580 | 136629 | ){ |
| 136581 | 136630 | propagateConstantExprRewriteOne(pConst, pExpr->pLeft, 0); |
| 136631 | + if( pConst->pOomFault[0] ) return WRC_Prune; | |
| 136582 | 136632 | if( sqlite3ExprAffinity(pExpr->pLeft)!=SQLITE_AFF_TEXT ){ |
| 136583 | 136633 | propagateConstantExprRewriteOne(pConst, pExpr->pRight, 0); |
| 136584 | 136634 | } |
| 136585 | 136635 | } |
| 136586 | 136636 | } |
| @@ -136644,10 +136694,11 @@ | ||
| 136644 | 136694 | ){ |
| 136645 | 136695 | WhereConst x; |
| 136646 | 136696 | Walker w; |
| 136647 | 136697 | int nChng = 0; |
| 136648 | 136698 | x.pParse = pParse; |
| 136699 | + x.pOomFault = &pParse->db->mallocFailed; | |
| 136649 | 136700 | do{ |
| 136650 | 136701 | x.nConst = 0; |
| 136651 | 136702 | x.nChng = 0; |
| 136652 | 136703 | x.apExpr = 0; |
| 136653 | 136704 | x.bHasAffBlob = 0; |
| @@ -137099,25 +137150,33 @@ | ||
| 137099 | 137150 | ** onto the top of the stack. If argument bFree is true, then this |
| 137100 | 137151 | ** WITH clause will never be popped from the stack but should instead |
| 137101 | 137152 | ** be freed along with the Parse object. In other cases, when |
| 137102 | 137153 | ** bFree==0, the With object will be freed along with the SELECT |
| 137103 | 137154 | ** statement with which it is associated. |
| 137155 | +** | |
| 137156 | +** This routine returns a copy of pWith. Or, if bFree is true and | |
| 137157 | +** the pWith object is destroyed immediately due to an OOM condition, | |
| 137158 | +** then this routine return NULL. | |
| 137159 | +** | |
| 137160 | +** If bFree is true, do not continue to use the pWith pointer after | |
| 137161 | +** calling this routine, Instead, use only the return value. | |
| 137104 | 137162 | */ |
| 137105 | -SQLITE_PRIVATE void sqlite3WithPush(Parse *pParse, With *pWith, u8 bFree){ | |
| 137163 | +SQLITE_PRIVATE With *sqlite3WithPush(Parse *pParse, With *pWith, u8 bFree){ | |
| 137106 | 137164 | if( pWith ){ |
| 137165 | + if( bFree ){ | |
| 137166 | + pWith = (With*)sqlite3ParserAddCleanup(pParse, | |
| 137167 | + (void(*)(sqlite3*,void*))sqlite3WithDelete, | |
| 137168 | + pWith); | |
| 137169 | + if( pWith==0 ) return 0; | |
| 137170 | + } | |
| 137107 | 137171 | if( pParse->nErr==0 ){ |
| 137108 | 137172 | assert( pParse->pWith!=pWith ); |
| 137109 | 137173 | pWith->pOuter = pParse->pWith; |
| 137110 | 137174 | pParse->pWith = pWith; |
| 137111 | 137175 | } |
| 137112 | - if( bFree ){ | |
| 137113 | - sqlite3ParserAddCleanup(pParse, | |
| 137114 | - (void(*)(sqlite3*,void*))sqlite3WithDelete, | |
| 137115 | - pWith); | |
| 137116 | - testcase( pParse->earlyCleanup ); | |
| 137117 | - } | |
| 137118 | 137176 | } |
| 137177 | + return pWith; | |
| 137119 | 137178 | } |
| 137120 | 137179 | |
| 137121 | 137180 | /* |
| 137122 | 137181 | ** This function checks if argument pFrom refers to a CTE declared by |
| 137123 | 137182 | ** a WITH clause on the stack currently maintained by the parser (on the |
| @@ -137206,10 +137265,11 @@ | ||
| 137206 | 137265 | pTab->iPKey = -1; |
| 137207 | 137266 | pTab->nRowLogEst = 200; assert( 200==sqlite3LogEst(1048576) ); |
| 137208 | 137267 | pTab->tabFlags |= TF_Ephemeral | TF_NoVisibleRowid; |
| 137209 | 137268 | pFrom->pSelect = sqlite3SelectDup(db, pCte->pSelect, 0); |
| 137210 | 137269 | if( db->mallocFailed ) return 2; |
| 137270 | + pFrom->pSelect->selFlags |= SF_CopyCte; | |
| 137211 | 137271 | assert( pFrom->pSelect ); |
| 137212 | 137272 | pFrom->fg.isCte = 1; |
| 137213 | 137273 | pFrom->u2.pCteUse = pCteUse; |
| 137214 | 137274 | pCteUse->nUse++; |
| 137215 | 137275 | if( pCteUse->nUse>=2 && pCteUse->eM10d==M10d_Any ){ |
| @@ -137474,10 +137534,11 @@ | ||
| 137474 | 137534 | ){ |
| 137475 | 137535 | sqlite3ErrorMsg(pParse, "access to view \"%s\" prohibited", |
| 137476 | 137536 | pTab->zName); |
| 137477 | 137537 | } |
| 137478 | 137538 | #ifndef SQLITE_OMIT_VIRTUALTABLE |
| 137539 | + assert( SQLITE_VTABRISK_Normal==1 && SQLITE_VTABRISK_High==2 ); | |
| 137479 | 137540 | if( IsVirtual(pTab) |
| 137480 | 137541 | && pFrom->fg.fromDDL |
| 137481 | 137542 | && ALWAYS(pTab->pVTable!=0) |
| 137482 | 137543 | && pTab->pVTable->eVtabRisk > ((db->flags & SQLITE_TrustedSchema)!=0) |
| 137483 | 137544 | ){ |
| @@ -183221,12 +183282,12 @@ | ||
| 183221 | 183282 | int nPrev, /* Size of buffer zPrev in bytes */ |
| 183222 | 183283 | const char *zNext, /* Buffer containing next term */ |
| 183223 | 183284 | int nNext /* Size of buffer zNext in bytes */ |
| 183224 | 183285 | ){ |
| 183225 | 183286 | int n; |
| 183226 | - UNUSED_PARAMETER(nNext); | |
| 183227 | - for(n=0; n<nPrev && zPrev[n]==zNext[n]; n++); | |
| 183287 | + for(n=0; n<nPrev && n<nNext && zPrev[n]==zNext[n]; n++); | |
| 183288 | + assert_fts3_nc( n<nNext ); | |
| 183228 | 183289 | return n; |
| 183229 | 183290 | } |
| 183230 | 183291 | |
| 183231 | 183292 | /* |
| 183232 | 183293 | ** Add term zTerm to the SegmentNode. It is guaranteed that zTerm is larger |
| @@ -184221,11 +184282,11 @@ | ||
| 184221 | 184282 | iDelta = (i64)((u64)iDocid - (u64)iPrev); |
| 184222 | 184283 | } |
| 184223 | 184284 | |
| 184224 | 184285 | nByte = sqlite3Fts3VarintLen(iDelta) + (isRequirePos?nList+1:0); |
| 184225 | 184286 | |
| 184226 | - rc = fts3GrowSegReaderBuffer(pCsr, nByte+nDoclist); | |
| 184287 | + rc = fts3GrowSegReaderBuffer(pCsr, nByte+nDoclist+FTS3_NODE_PADDING); | |
| 184227 | 184288 | if( rc ) return rc; |
| 184228 | 184289 | |
| 184229 | 184290 | if( isFirst ){ |
| 184230 | 184291 | char *a = &pCsr->aBuffer[nDoclist]; |
| 184231 | 184292 | int nWrite; |
| @@ -216164,10 +216225,11 @@ | ||
| 216164 | 216225 | return 1; |
| 216165 | 216226 | }else{ |
| 216166 | 216227 | i64 iOff = *piOff; |
| 216167 | 216228 | int iVal; |
| 216168 | 216229 | fts5FastGetVarint32(a, i, iVal); |
| 216230 | + assert( iVal>=0 ); | |
| 216169 | 216231 | if( iVal<=1 ){ |
| 216170 | 216232 | if( iVal==0 ){ |
| 216171 | 216233 | *pi = i; |
| 216172 | 216234 | return 0; |
| 216173 | 216235 | } |
| @@ -216177,13 +216239,16 @@ | ||
| 216177 | 216239 | if( iVal<2 ){ |
| 216178 | 216240 | /* This is a corrupt record. So stop parsing it here. */ |
| 216179 | 216241 | *piOff = -1; |
| 216180 | 216242 | return 1; |
| 216181 | 216243 | } |
| 216244 | + *piOff = iOff + ((iVal-2) & 0x7FFFFFFF); | |
| 216245 | + }else{ | |
| 216246 | + *piOff = (iOff & (i64)0x7FFFFFFF<<32)+((iOff + (iVal-2)) & 0x7FFFFFFF); | |
| 216182 | 216247 | } |
| 216183 | - *piOff = iOff + ((iVal-2) & 0x7FFFFFFF); | |
| 216184 | 216248 | *pi = i; |
| 216249 | + assert( *piOff>=iOff ); | |
| 216185 | 216250 | return 0; |
| 216186 | 216251 | } |
| 216187 | 216252 | } |
| 216188 | 216253 | |
| 216189 | 216254 | |
| @@ -216218,18 +216283,20 @@ | ||
| 216218 | 216283 | static void sqlite3Fts5PoslistSafeAppend( |
| 216219 | 216284 | Fts5Buffer *pBuf, |
| 216220 | 216285 | i64 *piPrev, |
| 216221 | 216286 | i64 iPos |
| 216222 | 216287 | ){ |
| 216223 | - static const i64 colmask = ((i64)(0x7FFFFFFF)) << 32; | |
| 216224 | - if( (iPos & colmask) != (*piPrev & colmask) ){ | |
| 216225 | - pBuf->p[pBuf->n++] = 1; | |
| 216226 | - pBuf->n += sqlite3Fts5PutVarint(&pBuf->p[pBuf->n], (iPos>>32)); | |
| 216227 | - *piPrev = (iPos & colmask); | |
| 216228 | - } | |
| 216229 | - pBuf->n += sqlite3Fts5PutVarint(&pBuf->p[pBuf->n], (iPos-*piPrev)+2); | |
| 216230 | - *piPrev = iPos; | |
| 216288 | + if( iPos>=*piPrev ){ | |
| 216289 | + static const i64 colmask = ((i64)(0x7FFFFFFF)) << 32; | |
| 216290 | + if( (iPos & colmask) != (*piPrev & colmask) ){ | |
| 216291 | + pBuf->p[pBuf->n++] = 1; | |
| 216292 | + pBuf->n += sqlite3Fts5PutVarint(&pBuf->p[pBuf->n], (iPos>>32)); | |
| 216293 | + *piPrev = (iPos & colmask); | |
| 216294 | + } | |
| 216295 | + pBuf->n += sqlite3Fts5PutVarint(&pBuf->p[pBuf->n], (iPos-*piPrev)+2); | |
| 216296 | + *piPrev = iPos; | |
| 216297 | + } | |
| 216231 | 216298 | } |
| 216232 | 216299 | |
| 216233 | 216300 | static int sqlite3Fts5PoslistWriterAppend( |
| 216234 | 216301 | Fts5Buffer *pBuf, |
| 216235 | 216302 | Fts5PoslistWriter *pWriter, |
| @@ -224157,11 +224224,11 @@ | ||
| 224157 | 224224 | pIter->base.nData = p-aCopy; |
| 224158 | 224225 | return; |
| 224159 | 224226 | } |
| 224160 | 224227 | fts5BufferSafeAppendBlob(&pIter->poslist, aCopy, p-aCopy); |
| 224161 | 224228 | } |
| 224162 | - if( p==pEnd ){ | |
| 224229 | + if( p>=pEnd ){ | |
| 224163 | 224230 | pIter->base.pData = pIter->poslist.p; |
| 224164 | 224231 | pIter->base.nData = pIter->poslist.n; |
| 224165 | 224232 | return; |
| 224166 | 224233 | } |
| 224167 | 224234 | aCopy = p++; |
| @@ -225953,11 +226020,11 @@ | ||
| 225953 | 226020 | Fts5Buffer *p1, /* First list to merge */ |
| 225954 | 226021 | int nBuf, /* Number of buffers in array aBuf[] */ |
| 225955 | 226022 | Fts5Buffer *aBuf /* Other lists to merge in */ |
| 225956 | 226023 | ){ |
| 225957 | 226024 | #define fts5PrefixMergerNextPosition(p) \ |
| 225958 | - sqlite3Fts5PoslistNext64((p)->aPos,(p)->iter.nPoslist,&(p)->iOff,&(p)->iPos); | |
| 226025 | + sqlite3Fts5PoslistNext64((p)->aPos,(p)->iter.nPoslist,&(p)->iOff,&(p)->iPos) | |
| 225959 | 226026 | #define FTS5_MERGE_NLIST 16 |
| 225960 | 226027 | PrefixMerger aMerger[FTS5_MERGE_NLIST]; |
| 225961 | 226028 | PrefixMerger *pHead = 0; |
| 225962 | 226029 | int i; |
| 225963 | 226030 | int nOut = 0; |
| @@ -230501,11 +230568,11 @@ | ||
| 230501 | 230568 | int nArg, /* Number of args */ |
| 230502 | 230569 | sqlite3_value **apUnused /* Function arguments */ |
| 230503 | 230570 | ){ |
| 230504 | 230571 | assert( nArg==0 ); |
| 230505 | 230572 | UNUSED_PARAM2(nArg, apUnused); |
| 230506 | - sqlite3_result_text(pCtx, "fts5: 2021-06-04 23:26:56 1c71de43dbc68002c4a6229e7efffb019655baff67a51fe922571fe420c95835", -1, SQLITE_TRANSIENT); | |
| 230573 | + sqlite3_result_text(pCtx, "fts5: 2021-06-14 20:41:20 e5a5acd6006133c5da4a7dd79726dbaa41c0d60ebeda890f848a6aafe5f9ef70", -1, SQLITE_TRANSIENT); | |
| 230507 | 230574 | } |
| 230508 | 230575 | |
| 230509 | 230576 | /* |
| 230510 | 230577 | ** Return true if zName is the extension on one of the shadow tables used |
| 230511 | 230578 | ** by this module. |
| @@ -235427,12 +235494,12 @@ | ||
| 235427 | 235494 | } |
| 235428 | 235495 | #endif /* SQLITE_CORE */ |
| 235429 | 235496 | #endif /* !defined(SQLITE_CORE) || defined(SQLITE_ENABLE_STMTVTAB) */ |
| 235430 | 235497 | |
| 235431 | 235498 | /************** End of stmt.c ************************************************/ |
| 235432 | -#if __LINE__!=235432 | |
| 235499 | +#if __LINE__!=235499 | |
| 235433 | 235500 | #undef SQLITE_SOURCE_ID |
| 235434 | -#define SQLITE_SOURCE_ID "2021-06-07 00:41:18 2aa9368b63b42ac7c700516f109edcc6098c12b850eae591afed4e51a3f4alt2" | |
| 235501 | +#define SQLITE_SOURCE_ID "2021-06-14 20:41:20 e5a5acd6006133c5da4a7dd79726dbaa41c0d60ebeda890f848a6aafe5f9alt2" | |
| 235435 | 235502 | #endif |
| 235436 | 235503 | /* Return the source-id for this library */ |
| 235437 | 235504 | SQLITE_API const char *sqlite3_sourceid(void){ return SQLITE_SOURCE_ID; } |
| 235438 | 235505 | /************************** End of sqlite3.c ******************************/ |
| 235439 | 235506 |
| --- src/sqlite3.c | |
| +++ src/sqlite3.c | |
| @@ -1205,11 +1205,11 @@ | |
| 1205 | ** [sqlite3_libversion_number()], [sqlite3_sourceid()], |
| 1206 | ** [sqlite_version()] and [sqlite_source_id()]. |
| 1207 | */ |
| 1208 | #define SQLITE_VERSION "3.36.0" |
| 1209 | #define SQLITE_VERSION_NUMBER 3036000 |
| 1210 | #define SQLITE_SOURCE_ID "2021-06-07 00:41:18 2aa9368b63b42ac7c700516f109edcc6098c12b850eae591afed4e51a3f41819" |
| 1211 | |
| 1212 | /* |
| 1213 | ** CAPI3REF: Run-Time Library Version Numbers |
| 1214 | ** KEYWORDS: sqlite3_version sqlite3_sourceid |
| 1215 | ** |
| @@ -16558,10 +16558,16 @@ | |
| 16558 | */ |
| 16559 | #ifndef SET_FULLSYNC |
| 16560 | # define SET_FULLSYNC(x,y) |
| 16561 | #endif |
| 16562 | |
| 16563 | /* |
| 16564 | ** The default size of a disk sector |
| 16565 | */ |
| 16566 | #ifndef SQLITE_DEFAULT_SECTOR_SIZE |
| 16567 | # define SQLITE_DEFAULT_SECTOR_SIZE 4096 |
| @@ -18795,10 +18801,11 @@ | |
| 18795 | #define SF_View 0x0200000 /* SELECT statement is a view */ |
| 18796 | #define SF_NoopOrderBy 0x0400000 /* ORDER BY is ignored for this query */ |
| 18797 | #define SF_UpdateFrom 0x0800000 /* Statement is an UPDATE...FROM */ |
| 18798 | #define SF_PushDown 0x1000000 /* SELECT has be modified by push-down opt */ |
| 18799 | #define SF_MultiPart 0x2000000 /* Has multiple incompatible PARTITIONs */ |
| 18800 | |
| 18801 | /* |
| 18802 | ** The results of a SELECT can be distributed in several ways, as defined |
| 18803 | ** by one of the following macros. The "SRT" prefix means "SELECT Result |
| 18804 | ** Type". |
| @@ -20158,10 +20165,11 @@ | |
| 20158 | SQLITE_PRIVATE void sqlite3RegisterDateTimeFunctions(void); |
| 20159 | SQLITE_PRIVATE void sqlite3RegisterPerConnectionBuiltinFunctions(sqlite3*); |
| 20160 | SQLITE_PRIVATE int sqlite3SafetyCheckOk(sqlite3*); |
| 20161 | SQLITE_PRIVATE int sqlite3SafetyCheckSickOrOk(sqlite3*); |
| 20162 | SQLITE_PRIVATE void sqlite3ChangeCookie(Parse*, int); |
| 20163 | |
| 20164 | #if !defined(SQLITE_OMIT_VIEW) && !defined(SQLITE_OMIT_TRIGGER) |
| 20165 | SQLITE_PRIVATE void sqlite3MaterializeView(Parse*, Table*, Expr*, ExprList*,Expr*,int); |
| 20166 | #endif |
| 20167 | |
| @@ -20568,11 +20576,11 @@ | |
| 20568 | #ifndef SQLITE_OMIT_CTE |
| 20569 | SQLITE_PRIVATE Cte *sqlite3CteNew(Parse*,Token*,ExprList*,Select*,u8); |
| 20570 | SQLITE_PRIVATE void sqlite3CteDelete(sqlite3*,Cte*); |
| 20571 | SQLITE_PRIVATE With *sqlite3WithAdd(Parse*,With*,Cte*); |
| 20572 | SQLITE_PRIVATE void sqlite3WithDelete(sqlite3*,With*); |
| 20573 | SQLITE_PRIVATE void sqlite3WithPush(Parse*, With*, u8); |
| 20574 | #else |
| 20575 | # define sqlite3CteNew(P,T,E,S) ((void*)0) |
| 20576 | # define sqlite3CteDelete(D,C) |
| 20577 | # define sqlite3CteWithAdd(P,W,C) ((void*)0) |
| 20578 | # define sqlite3WithDelete(x,y) |
| @@ -21670,11 +21678,11 @@ | |
| 21670 | SQLITE_PRIVATE int sqlite3VdbeMemTooBig(Mem*); |
| 21671 | SQLITE_PRIVATE int sqlite3VdbeMemCopy(Mem*, const Mem*); |
| 21672 | SQLITE_PRIVATE void sqlite3VdbeMemShallowCopy(Mem*, const Mem*, int); |
| 21673 | SQLITE_PRIVATE void sqlite3VdbeMemMove(Mem*, Mem*); |
| 21674 | SQLITE_PRIVATE int sqlite3VdbeMemNulTerminate(Mem*); |
| 21675 | SQLITE_PRIVATE int sqlite3VdbeMemSetStr(Mem*, const char*, int, u8, void(*)(void*)); |
| 21676 | SQLITE_PRIVATE void sqlite3VdbeMemSetInt64(Mem*, i64); |
| 21677 | #ifdef SQLITE_OMIT_FLOATING_POINT |
| 21678 | # define sqlite3VdbeMemSetDouble sqlite3VdbeMemSetInt64 |
| 21679 | #else |
| 21680 | SQLITE_PRIVATE void sqlite3VdbeMemSetDouble(Mem*, double); |
| @@ -23667,10 +23675,12 @@ | |
| 23667 | zPathOut[0] = 0; |
| 23668 | return pVfs->xFullPathname(pVfs, zPath, nPathOut, zPathOut); |
| 23669 | } |
| 23670 | #ifndef SQLITE_OMIT_LOAD_EXTENSION |
| 23671 | SQLITE_PRIVATE void *sqlite3OsDlOpen(sqlite3_vfs *pVfs, const char *zPath){ |
| 23672 | return pVfs->xDlOpen(pVfs, zPath); |
| 23673 | } |
| 23674 | SQLITE_PRIVATE void sqlite3OsDlError(sqlite3_vfs *pVfs, int nByte, char *zBufOut){ |
| 23675 | pVfs->xDlError(pVfs, nByte, zBufOut); |
| 23676 | } |
| @@ -66965,11 +66975,11 @@ | |
| 66965 | testcase( pc+size==usableSize ); |
| 66966 | put2byte(pAddr, cbrk); |
| 66967 | if( temp==0 ){ |
| 66968 | if( cbrk==pc ) continue; |
| 66969 | temp = sqlite3PagerTempSpace(pPage->pBt->pPager); |
| 66970 | memcpy(&temp[iCellStart], &data[iCellStart], (cbrk+size) - iCellStart); |
| 66971 | src = temp; |
| 66972 | } |
| 66973 | memcpy(&data[cbrk], &src[pc], size); |
| 66974 | } |
| 66975 | data[hdr+7] = 0; |
| @@ -78047,15 +78057,15 @@ | |
| 78047 | ** either case, SQLITE_TOOBIG is returned. |
| 78048 | */ |
| 78049 | SQLITE_PRIVATE int sqlite3VdbeMemSetStr( |
| 78050 | Mem *pMem, /* Memory cell to set to string value */ |
| 78051 | const char *z, /* String pointer */ |
| 78052 | int n, /* Bytes in string, or negative */ |
| 78053 | u8 enc, /* Encoding of z. 0 for BLOBs */ |
| 78054 | void (*xDel)(void*) /* Destructor function */ |
| 78055 | ){ |
| 78056 | int nByte = n; /* New value for pMem->n */ |
| 78057 | int iLimit; /* Maximum allowed string or blob size */ |
| 78058 | u16 flags = 0; /* New value for pMem->flags */ |
| 78059 | |
| 78060 | assert( pMem->db==0 || sqlite3_mutex_held(pMem->db->mutex) ); |
| 78061 | assert( !sqlite3VdbeMemIsRowSet(pMem) ); |
| @@ -78073,11 +78083,11 @@ | |
| 78073 | } |
| 78074 | flags = (enc==0?MEM_Blob:MEM_Str); |
| 78075 | if( nByte<0 ){ |
| 78076 | assert( enc!=0 ); |
| 78077 | if( enc==SQLITE_UTF8 ){ |
| 78078 | nByte = 0x7fffffff & (int)strlen(z); |
| 78079 | }else{ |
| 78080 | for(nByte=0; nByte<=iLimit && (z[nByte] | z[nByte+1]); nByte+=2){} |
| 78081 | } |
| 78082 | flags |= MEM_Term; |
| 78083 | } |
| @@ -78085,11 +78095,11 @@ | |
| 78085 | /* The following block sets the new values of Mem.z and Mem.xDel. It |
| 78086 | ** also sets a flag in local variable "flags" to indicate the memory |
| 78087 | ** management (one of MEM_Dyn or MEM_Static). |
| 78088 | */ |
| 78089 | if( xDel==SQLITE_TRANSIENT ){ |
| 78090 | u32 nAlloc = nByte; |
| 78091 | if( flags&MEM_Term ){ |
| 78092 | nAlloc += (enc==SQLITE_UTF8?1:2); |
| 78093 | } |
| 78094 | if( nByte>iLimit ){ |
| 78095 | return sqlite3ErrorToParser(pMem->db, SQLITE_TOOBIG); |
| @@ -78111,11 +78121,11 @@ | |
| 78111 | pMem->xDel = xDel; |
| 78112 | flags |= ((xDel==SQLITE_STATIC)?MEM_Static:MEM_Dyn); |
| 78113 | } |
| 78114 | } |
| 78115 | |
| 78116 | pMem->n = nByte; |
| 78117 | pMem->flags = flags; |
| 78118 | if( enc ){ |
| 78119 | pMem->enc = enc; |
| 78120 | #ifdef SQLITE_ENABLE_SESSION |
| 78121 | }else if( pMem->db==0 ){ |
| @@ -78131,11 +78141,11 @@ | |
| 78131 | return SQLITE_NOMEM_BKPT; |
| 78132 | } |
| 78133 | #endif |
| 78134 | |
| 78135 | if( nByte>iLimit ){ |
| 78136 | return SQLITE_TOOBIG; |
| 78137 | } |
| 78138 | |
| 78139 | return SQLITE_OK; |
| 78140 | } |
| 78141 | |
| @@ -84527,11 +84537,11 @@ | |
| 84527 | }else if( xDel==SQLITE_TRANSIENT ){ |
| 84528 | /* noop */ |
| 84529 | }else{ |
| 84530 | xDel((void*)p); |
| 84531 | } |
| 84532 | if( pCtx ) sqlite3_result_error_toobig(pCtx); |
| 84533 | return SQLITE_TOOBIG; |
| 84534 | } |
| 84535 | SQLITE_API void sqlite3_result_blob( |
| 84536 | sqlite3_context *pCtx, |
| 84537 | const void *z, |
| @@ -85509,11 +85519,11 @@ | |
| 85509 | */ |
| 85510 | static int bindText( |
| 85511 | sqlite3_stmt *pStmt, /* The statement to bind against */ |
| 85512 | int i, /* Index of the parameter to bind */ |
| 85513 | const void *zData, /* Pointer to the data to be bound */ |
| 85514 | int nData, /* Number of bytes of data to be bound */ |
| 85515 | void (*xDel)(void*), /* Destructor for the data */ |
| 85516 | u8 encoding /* Encoding for the data */ |
| 85517 | ){ |
| 85518 | Vdbe *p = (Vdbe *)pStmt; |
| 85519 | Mem *pVar; |
| @@ -85561,15 +85571,11 @@ | |
| 85561 | const void *zData, |
| 85562 | sqlite3_uint64 nData, |
| 85563 | void (*xDel)(void*) |
| 85564 | ){ |
| 85565 | assert( xDel!=SQLITE_DYNAMIC ); |
| 85566 | if( nData>0x7fffffff ){ |
| 85567 | return invokeValueDestructor(zData, xDel, 0); |
| 85568 | }else{ |
| 85569 | return bindText(pStmt, i, zData, (int)nData, xDel, 0); |
| 85570 | } |
| 85571 | } |
| 85572 | SQLITE_API int sqlite3_bind_double(sqlite3_stmt *pStmt, int i, double rValue){ |
| 85573 | int rc; |
| 85574 | Vdbe *p = (Vdbe *)pStmt; |
| 85575 | rc = vdbeUnbind(p, i); |
| @@ -85635,16 +85641,12 @@ | |
| 85635 | sqlite3_uint64 nData, |
| 85636 | void (*xDel)(void*), |
| 85637 | unsigned char enc |
| 85638 | ){ |
| 85639 | assert( xDel!=SQLITE_DYNAMIC ); |
| 85640 | if( nData>0x7fffffff ){ |
| 85641 | return invokeValueDestructor(zData, xDel, 0); |
| 85642 | }else{ |
| 85643 | if( enc==SQLITE_UTF16 ) enc = SQLITE_UTF16NATIVE; |
| 85644 | return bindText(pStmt, i, zData, (int)nData, xDel, enc); |
| 85645 | } |
| 85646 | } |
| 85647 | #ifndef SQLITE_OMIT_UTF16 |
| 85648 | SQLITE_API int sqlite3_bind_text16( |
| 85649 | sqlite3_stmt *pStmt, |
| 85650 | int i, |
| @@ -90957,11 +90959,12 @@ | |
| 90957 | assert( pOp[1].opcode==OP_SeekGE ); |
| 90958 | |
| 90959 | /* pOp->p2 points to the first instruction past the OP_IdxGT that |
| 90960 | ** follows the OP_SeekGE. */ |
| 90961 | assert( pOp->p2>=(int)(pOp-aOp)+2 ); |
| 90962 | assert( aOp[pOp->p2-1].opcode==OP_IdxGT ); |
| 90963 | assert( pOp[1].p1==aOp[pOp->p2-1].p1 ); |
| 90964 | assert( pOp[1].p2==aOp[pOp->p2-1].p2 ); |
| 90965 | assert( pOp[1].p3==aOp[pOp->p2-1].p3 ); |
| 90966 | |
| 90967 | assert( pOp->p1>0 ); |
| @@ -92847,11 +92850,13 @@ | |
| 92847 | } |
| 92848 | #endif |
| 92849 | |
| 92850 | iDb = pOp->p1; |
| 92851 | assert( iDb>=0 && iDb<db->nDb ); |
| 92852 | assert( DbHasProperty(db, iDb, DB_SchemaLoaded) || db->mallocFailed ); |
| 92853 | |
| 92854 | #ifndef SQLITE_OMIT_ALTERTABLE |
| 92855 | if( pOp->p4.z==0 ){ |
| 92856 | sqlite3SchemaClear(db->aDb[iDb].pSchema); |
| 92857 | db->mDbFlags &= ~DBFLAG_SchemaKnownOk; |
| @@ -102622,11 +102627,11 @@ | |
| 102622 | ** Create and return a deep copy of the object passed as the second |
| 102623 | ** argument. If an OOM condition is encountered, NULL is returned |
| 102624 | ** and the db->mallocFailed flag set. |
| 102625 | */ |
| 102626 | #ifndef SQLITE_OMIT_CTE |
| 102627 | static With *withDup(sqlite3 *db, With *p){ |
| 102628 | With *pRet = 0; |
| 102629 | if( p ){ |
| 102630 | sqlite3_int64 nByte = sizeof(*p) + sizeof(p->a[0]) * (p->nCte-1); |
| 102631 | pRet = sqlite3DbMallocZero(db, nByte); |
| 102632 | if( pRet ){ |
| @@ -102640,11 +102645,11 @@ | |
| 102640 | } |
| 102641 | } |
| 102642 | return pRet; |
| 102643 | } |
| 102644 | #else |
| 102645 | # define withDup(x,y) 0 |
| 102646 | #endif |
| 102647 | |
| 102648 | #ifndef SQLITE_OMIT_WINDOWFUNC |
| 102649 | /* |
| 102650 | ** The gatherSelectWindows() procedure and its helper routine |
| @@ -102844,11 +102849,11 @@ | |
| 102844 | pNew->iOffset = 0; |
| 102845 | pNew->selFlags = p->selFlags & ~SF_UsesEphemeral; |
| 102846 | pNew->addrOpenEphm[0] = -1; |
| 102847 | pNew->addrOpenEphm[1] = -1; |
| 102848 | pNew->nSelectRow = p->nSelectRow; |
| 102849 | pNew->pWith = withDup(db, p->pWith); |
| 102850 | #ifndef SQLITE_OMIT_WINDOWFUNC |
| 102851 | pNew->pWin = 0; |
| 102852 | pNew->pWinDefn = sqlite3WindowListDup(db, p->pWinDefn); |
| 102853 | if( p->pWin && db->mallocFailed==0 ) gatherSelectWindows(pNew); |
| 102854 | #endif |
| @@ -108184,19 +108189,34 @@ | |
| 108184 | ** to select statement pSelect. |
| 108185 | */ |
| 108186 | static void renameWalkWith(Walker *pWalker, Select *pSelect){ |
| 108187 | With *pWith = pSelect->pWith; |
| 108188 | if( pWith ){ |
| 108189 | int i; |
| 108190 | for(i=0; i<pWith->nCte; i++){ |
| 108191 | Select *p = pWith->a[i].pSelect; |
| 108192 | NameContext sNC; |
| 108193 | memset(&sNC, 0, sizeof(sNC)); |
| 108194 | sNC.pParse = pWalker->pParse; |
| 108195 | sqlite3SelectPrep(sNC.pParse, p, &sNC); |
| 108196 | sqlite3WalkSelect(pWalker, p); |
| 108197 | sqlite3RenameExprlistUnmap(pWalker->pParse, pWith->a[i].pCols); |
| 108198 | } |
| 108199 | } |
| 108200 | } |
| 108201 | |
| 108202 | /* |
| @@ -108219,11 +108239,15 @@ | |
| 108219 | */ |
| 108220 | static int renameUnmapSelectCb(Walker *pWalker, Select *p){ |
| 108221 | Parse *pParse = pWalker->pParse; |
| 108222 | int i; |
| 108223 | if( pParse->nErr ) return WRC_Abort; |
| 108224 | if( p->selFlags & SF_View ) return WRC_Prune; |
| 108225 | if( ALWAYS(p->pEList) ){ |
| 108226 | ExprList *pList = p->pEList; |
| 108227 | for(i=0; i<pList->nExpr; i++){ |
| 108228 | if( pList->a[i].zEName && pList->a[i].eEName==ENAME_NAME ){ |
| 108229 | sqlite3RenameTokenRemap(pParse, 0, (void*)pList->a[i].zEName); |
| @@ -108327,11 +108351,15 @@ | |
| 108327 | ** This is a Walker select callback. It does nothing. It is only required |
| 108328 | ** because without a dummy callback, sqlite3WalkExpr() and similar do not |
| 108329 | ** descend into sub-select statements. |
| 108330 | */ |
| 108331 | static int renameColumnSelectCb(Walker *pWalker, Select *p){ |
| 108332 | if( p->selFlags & SF_View ) return WRC_Prune; |
| 108333 | renameWalkWith(pWalker, p); |
| 108334 | return WRC_Continue; |
| 108335 | } |
| 108336 | |
| 108337 | /* |
| @@ -108963,11 +108991,15 @@ | |
| 108963 | */ |
| 108964 | static int renameTableSelectCb(Walker *pWalker, Select *pSelect){ |
| 108965 | int i; |
| 108966 | RenameCtx *p = pWalker->u.pRename; |
| 108967 | SrcList *pSrc = pSelect->pSrc; |
| 108968 | if( pSelect->selFlags & SF_View ) return WRC_Prune; |
| 108969 | if( NEVER(pSrc==0) ){ |
| 108970 | assert( pWalker->pParse->db->mallocFailed ); |
| 108971 | return WRC_Abort; |
| 108972 | } |
| 108973 | for(i=0; i<pSrc->nSrc; i++){ |
| @@ -116705,11 +116737,11 @@ | |
| 116705 | const char *zDb = db->aDb[iDb].zDbSName; |
| 116706 | const char *zTab = SCHEMA_TABLE(iDb); |
| 116707 | if( sqlite3AuthCheck(pParse, SQLITE_DELETE, zTab, 0, zDb) ){ |
| 116708 | goto exit_drop_index; |
| 116709 | } |
| 116710 | if( !OMIT_TEMPDB && iDb ) code = SQLITE_DROP_TEMP_INDEX; |
| 116711 | if( sqlite3AuthCheck(pParse, code, pIndex->zName, pTab->zName, zDb) ){ |
| 116712 | goto exit_drop_index; |
| 116713 | } |
| 116714 | } |
| 116715 | #endif |
| @@ -119237,17 +119269,19 @@ | |
| 119237 | ){ |
| 119238 | /* This column was already computed by the previous index */ |
| 119239 | continue; |
| 119240 | } |
| 119241 | sqlite3ExprCodeLoadIndexColumn(pParse, pIdx, iDataCur, j, regBase+j); |
| 119242 | /* If the column affinity is REAL but the number is an integer, then it |
| 119243 | ** might be stored in the table as an integer (using a compact |
| 119244 | ** representation) then converted to REAL by an OP_RealAffinity opcode. |
| 119245 | ** But we are getting ready to store this value back into an index, where |
| 119246 | ** it should be converted by to INTEGER again. So omit the OP_RealAffinity |
| 119247 | ** opcode if it is present */ |
| 119248 | sqlite3VdbeDeletePriorOpcode(v, OP_RealAffinity); |
| 119249 | } |
| 119250 | if( regOut ){ |
| 119251 | sqlite3VdbeAddOp3(v, OP_MakeRecord, regBase, nCol, regOut); |
| 119252 | } |
| 119253 | sqlite3ReleaseTempRange(pParse, regBase, nCol); |
| @@ -127401,11 +127435,11 @@ | |
| 127401 | sqlite3_loadext_entry xInit; |
| 127402 | char *zErrmsg = 0; |
| 127403 | const char *zEntry; |
| 127404 | char *zAltEntry = 0; |
| 127405 | void **aHandle; |
| 127406 | u64 nMsg = 300 + sqlite3Strlen30(zFile); |
| 127407 | int ii; |
| 127408 | int rc; |
| 127409 | |
| 127410 | /* Shared library endings to try if zFile cannot be loaded as written */ |
| 127411 | static const char *azEndings[] = { |
| @@ -127435,30 +127469,26 @@ | |
| 127435 | return SQLITE_ERROR; |
| 127436 | } |
| 127437 | |
| 127438 | zEntry = zProc ? zProc : "sqlite3_extension_init"; |
| 127439 | |
| 127440 | handle = sqlite3OsDlOpen(pVfs, zFile); |
| 127441 | #if SQLITE_OS_UNIX || SQLITE_OS_WIN |
| 127442 | for(ii=0; ii<ArraySize(azEndings) && handle==0; ii++){ |
| 127443 | char *zAltFile = sqlite3_mprintf("%s.%s", zFile, azEndings[ii]); |
| 127444 | if( zAltFile==0 ) return SQLITE_NOMEM_BKPT; |
| 127445 | handle = sqlite3OsDlOpen(pVfs, zAltFile); |
| 127446 | sqlite3_free(zAltFile); |
| 127447 | } |
| 127448 | #endif |
| 127449 | if( handle==0 ){ |
| 127450 | if( pzErrMsg ){ |
| 127451 | *pzErrMsg = zErrmsg = sqlite3_malloc64(nMsg); |
| 127452 | if( zErrmsg ){ |
| 127453 | sqlite3_snprintf(nMsg, zErrmsg, |
| 127454 | "unable to open shared library [%s]", zFile); |
| 127455 | sqlite3OsDlError(pVfs, nMsg-1, zErrmsg); |
| 127456 | } |
| 127457 | } |
| 127458 | return SQLITE_ERROR; |
| 127459 | } |
| 127460 | xInit = (sqlite3_loadext_entry)sqlite3OsDlSym(pVfs, handle, zEntry); |
| 127461 | |
| 127462 | /* If no entry point was specified and the default legacy |
| 127463 | ** entry point name "sqlite3_extension_init" was not found, then |
| 127464 | ** construct an entry point name "sqlite3_X_init" where the X is |
| @@ -127491,14 +127521,15 @@ | |
| 127491 | zEntry = zAltEntry; |
| 127492 | xInit = (sqlite3_loadext_entry)sqlite3OsDlSym(pVfs, handle, zEntry); |
| 127493 | } |
| 127494 | if( xInit==0 ){ |
| 127495 | if( pzErrMsg ){ |
| 127496 | nMsg += sqlite3Strlen30(zEntry); |
| 127497 | *pzErrMsg = zErrmsg = sqlite3_malloc64(nMsg); |
| 127498 | if( zErrmsg ){ |
| 127499 | sqlite3_snprintf(nMsg, zErrmsg, |
| 127500 | "no entry point [%s] in shared library [%s]", zEntry, zFile); |
| 127501 | sqlite3OsDlError(pVfs, nMsg-1, zErrmsg); |
| 127502 | } |
| 127503 | } |
| 127504 | sqlite3OsDlClose(pVfs, handle); |
| @@ -127528,10 +127559,23 @@ | |
| 127528 | sqlite3DbFree(db, db->aExtension); |
| 127529 | db->aExtension = aHandle; |
| 127530 | |
| 127531 | db->aExtension[db->nExtension++] = handle; |
| 127532 | return SQLITE_OK; |
| 127533 | } |
| 127534 | SQLITE_API int sqlite3_load_extension( |
| 127535 | sqlite3 *db, /* Load the extension into this database connection */ |
| 127536 | const char *zFile, /* Name of the shared library containing extension */ |
| 127537 | const char *zProc, /* Entry point. Use "sqlite3_extension_init" if 0 */ |
| @@ -131368,13 +131412,15 @@ | |
| 131368 | if( rc==SQLITE_OK ){ |
| 131369 | sqlite3AnalysisLoad(db, iDb); |
| 131370 | } |
| 131371 | #endif |
| 131372 | } |
| 131373 | if( db->mallocFailed ){ |
| 131374 | rc = SQLITE_NOMEM_BKPT; |
| 131375 | sqlite3ResetAllSchemasOfConnection(db); |
| 131376 | }else |
| 131377 | if( rc==SQLITE_OK || (db->flags&SQLITE_NoSchemaError)){ |
| 131378 | /* Hack: If the SQLITE_NoSchemaError flag is set, then consider |
| 131379 | ** the schema loaded, even if errors (other than OOM) occurred. In |
| 131380 | ** this situation the current sqlite3_prepare() operation will fail, |
| @@ -136426,10 +136472,11 @@ | |
| 136426 | ** a known value due to WHERE clause constraints of the form COLUMN=VALUE. |
| 136427 | */ |
| 136428 | typedef struct WhereConst WhereConst; |
| 136429 | struct WhereConst { |
| 136430 | Parse *pParse; /* Parsing context */ |
| 136431 | int nConst; /* Number for COLUMN=CONSTANT terms */ |
| 136432 | int nChng; /* Number of times a constant is propagated */ |
| 136433 | int bHasAffBlob; /* At least one column in apExpr[] as affinity BLOB */ |
| 136434 | Expr **apExpr; /* [i*2] is COLUMN and [i*2+1] is VALUE */ |
| 136435 | }; |
| @@ -136525,10 +136572,11 @@ | |
| 136525 | WhereConst *pConst, |
| 136526 | Expr *pExpr, |
| 136527 | int bIgnoreAffBlob |
| 136528 | ){ |
| 136529 | int i; |
| 136530 | if( pExpr->op!=TK_COLUMN ) return WRC_Continue; |
| 136531 | if( ExprHasProperty(pExpr, EP_FixedCol|EP_FromJoin) ){ |
| 136532 | testcase( ExprHasProperty(pExpr, EP_FixedCol) ); |
| 136533 | testcase( ExprHasProperty(pExpr, EP_FromJoin) ); |
| 136534 | return WRC_Continue; |
| @@ -136545,10 +136593,11 @@ | |
| 136545 | pConst->nChng++; |
| 136546 | ExprClearProperty(pExpr, EP_Leaf); |
| 136547 | ExprSetProperty(pExpr, EP_FixedCol); |
| 136548 | assert( pExpr->pLeft==0 ); |
| 136549 | pExpr->pLeft = sqlite3ExprDup(pConst->pParse->db, pConst->apExpr[i*2+1], 0); |
| 136550 | break; |
| 136551 | } |
| 136552 | return WRC_Prune; |
| 136553 | } |
| 136554 | |
| @@ -136577,10 +136626,11 @@ | |
| 136577 | if( pConst->bHasAffBlob ){ |
| 136578 | if( (pExpr->op>=TK_EQ && pExpr->op<=TK_GE) |
| 136579 | || pExpr->op==TK_IS |
| 136580 | ){ |
| 136581 | propagateConstantExprRewriteOne(pConst, pExpr->pLeft, 0); |
| 136582 | if( sqlite3ExprAffinity(pExpr->pLeft)!=SQLITE_AFF_TEXT ){ |
| 136583 | propagateConstantExprRewriteOne(pConst, pExpr->pRight, 0); |
| 136584 | } |
| 136585 | } |
| 136586 | } |
| @@ -136644,10 +136694,11 @@ | |
| 136644 | ){ |
| 136645 | WhereConst x; |
| 136646 | Walker w; |
| 136647 | int nChng = 0; |
| 136648 | x.pParse = pParse; |
| 136649 | do{ |
| 136650 | x.nConst = 0; |
| 136651 | x.nChng = 0; |
| 136652 | x.apExpr = 0; |
| 136653 | x.bHasAffBlob = 0; |
| @@ -137099,25 +137150,33 @@ | |
| 137099 | ** onto the top of the stack. If argument bFree is true, then this |
| 137100 | ** WITH clause will never be popped from the stack but should instead |
| 137101 | ** be freed along with the Parse object. In other cases, when |
| 137102 | ** bFree==0, the With object will be freed along with the SELECT |
| 137103 | ** statement with which it is associated. |
| 137104 | */ |
| 137105 | SQLITE_PRIVATE void sqlite3WithPush(Parse *pParse, With *pWith, u8 bFree){ |
| 137106 | if( pWith ){ |
| 137107 | if( pParse->nErr==0 ){ |
| 137108 | assert( pParse->pWith!=pWith ); |
| 137109 | pWith->pOuter = pParse->pWith; |
| 137110 | pParse->pWith = pWith; |
| 137111 | } |
| 137112 | if( bFree ){ |
| 137113 | sqlite3ParserAddCleanup(pParse, |
| 137114 | (void(*)(sqlite3*,void*))sqlite3WithDelete, |
| 137115 | pWith); |
| 137116 | testcase( pParse->earlyCleanup ); |
| 137117 | } |
| 137118 | } |
| 137119 | } |
| 137120 | |
| 137121 | /* |
| 137122 | ** This function checks if argument pFrom refers to a CTE declared by |
| 137123 | ** a WITH clause on the stack currently maintained by the parser (on the |
| @@ -137206,10 +137265,11 @@ | |
| 137206 | pTab->iPKey = -1; |
| 137207 | pTab->nRowLogEst = 200; assert( 200==sqlite3LogEst(1048576) ); |
| 137208 | pTab->tabFlags |= TF_Ephemeral | TF_NoVisibleRowid; |
| 137209 | pFrom->pSelect = sqlite3SelectDup(db, pCte->pSelect, 0); |
| 137210 | if( db->mallocFailed ) return 2; |
| 137211 | assert( pFrom->pSelect ); |
| 137212 | pFrom->fg.isCte = 1; |
| 137213 | pFrom->u2.pCteUse = pCteUse; |
| 137214 | pCteUse->nUse++; |
| 137215 | if( pCteUse->nUse>=2 && pCteUse->eM10d==M10d_Any ){ |
| @@ -137474,10 +137534,11 @@ | |
| 137474 | ){ |
| 137475 | sqlite3ErrorMsg(pParse, "access to view \"%s\" prohibited", |
| 137476 | pTab->zName); |
| 137477 | } |
| 137478 | #ifndef SQLITE_OMIT_VIRTUALTABLE |
| 137479 | if( IsVirtual(pTab) |
| 137480 | && pFrom->fg.fromDDL |
| 137481 | && ALWAYS(pTab->pVTable!=0) |
| 137482 | && pTab->pVTable->eVtabRisk > ((db->flags & SQLITE_TrustedSchema)!=0) |
| 137483 | ){ |
| @@ -183221,12 +183282,12 @@ | |
| 183221 | int nPrev, /* Size of buffer zPrev in bytes */ |
| 183222 | const char *zNext, /* Buffer containing next term */ |
| 183223 | int nNext /* Size of buffer zNext in bytes */ |
| 183224 | ){ |
| 183225 | int n; |
| 183226 | UNUSED_PARAMETER(nNext); |
| 183227 | for(n=0; n<nPrev && zPrev[n]==zNext[n]; n++); |
| 183228 | return n; |
| 183229 | } |
| 183230 | |
| 183231 | /* |
| 183232 | ** Add term zTerm to the SegmentNode. It is guaranteed that zTerm is larger |
| @@ -184221,11 +184282,11 @@ | |
| 184221 | iDelta = (i64)((u64)iDocid - (u64)iPrev); |
| 184222 | } |
| 184223 | |
| 184224 | nByte = sqlite3Fts3VarintLen(iDelta) + (isRequirePos?nList+1:0); |
| 184225 | |
| 184226 | rc = fts3GrowSegReaderBuffer(pCsr, nByte+nDoclist); |
| 184227 | if( rc ) return rc; |
| 184228 | |
| 184229 | if( isFirst ){ |
| 184230 | char *a = &pCsr->aBuffer[nDoclist]; |
| 184231 | int nWrite; |
| @@ -216164,10 +216225,11 @@ | |
| 216164 | return 1; |
| 216165 | }else{ |
| 216166 | i64 iOff = *piOff; |
| 216167 | int iVal; |
| 216168 | fts5FastGetVarint32(a, i, iVal); |
| 216169 | if( iVal<=1 ){ |
| 216170 | if( iVal==0 ){ |
| 216171 | *pi = i; |
| 216172 | return 0; |
| 216173 | } |
| @@ -216177,13 +216239,16 @@ | |
| 216177 | if( iVal<2 ){ |
| 216178 | /* This is a corrupt record. So stop parsing it here. */ |
| 216179 | *piOff = -1; |
| 216180 | return 1; |
| 216181 | } |
| 216182 | } |
| 216183 | *piOff = iOff + ((iVal-2) & 0x7FFFFFFF); |
| 216184 | *pi = i; |
| 216185 | return 0; |
| 216186 | } |
| 216187 | } |
| 216188 | |
| 216189 | |
| @@ -216218,18 +216283,20 @@ | |
| 216218 | static void sqlite3Fts5PoslistSafeAppend( |
| 216219 | Fts5Buffer *pBuf, |
| 216220 | i64 *piPrev, |
| 216221 | i64 iPos |
| 216222 | ){ |
| 216223 | static const i64 colmask = ((i64)(0x7FFFFFFF)) << 32; |
| 216224 | if( (iPos & colmask) != (*piPrev & colmask) ){ |
| 216225 | pBuf->p[pBuf->n++] = 1; |
| 216226 | pBuf->n += sqlite3Fts5PutVarint(&pBuf->p[pBuf->n], (iPos>>32)); |
| 216227 | *piPrev = (iPos & colmask); |
| 216228 | } |
| 216229 | pBuf->n += sqlite3Fts5PutVarint(&pBuf->p[pBuf->n], (iPos-*piPrev)+2); |
| 216230 | *piPrev = iPos; |
| 216231 | } |
| 216232 | |
| 216233 | static int sqlite3Fts5PoslistWriterAppend( |
| 216234 | Fts5Buffer *pBuf, |
| 216235 | Fts5PoslistWriter *pWriter, |
| @@ -224157,11 +224224,11 @@ | |
| 224157 | pIter->base.nData = p-aCopy; |
| 224158 | return; |
| 224159 | } |
| 224160 | fts5BufferSafeAppendBlob(&pIter->poslist, aCopy, p-aCopy); |
| 224161 | } |
| 224162 | if( p==pEnd ){ |
| 224163 | pIter->base.pData = pIter->poslist.p; |
| 224164 | pIter->base.nData = pIter->poslist.n; |
| 224165 | return; |
| 224166 | } |
| 224167 | aCopy = p++; |
| @@ -225953,11 +226020,11 @@ | |
| 225953 | Fts5Buffer *p1, /* First list to merge */ |
| 225954 | int nBuf, /* Number of buffers in array aBuf[] */ |
| 225955 | Fts5Buffer *aBuf /* Other lists to merge in */ |
| 225956 | ){ |
| 225957 | #define fts5PrefixMergerNextPosition(p) \ |
| 225958 | sqlite3Fts5PoslistNext64((p)->aPos,(p)->iter.nPoslist,&(p)->iOff,&(p)->iPos); |
| 225959 | #define FTS5_MERGE_NLIST 16 |
| 225960 | PrefixMerger aMerger[FTS5_MERGE_NLIST]; |
| 225961 | PrefixMerger *pHead = 0; |
| 225962 | int i; |
| 225963 | int nOut = 0; |
| @@ -230501,11 +230568,11 @@ | |
| 230501 | int nArg, /* Number of args */ |
| 230502 | sqlite3_value **apUnused /* Function arguments */ |
| 230503 | ){ |
| 230504 | assert( nArg==0 ); |
| 230505 | UNUSED_PARAM2(nArg, apUnused); |
| 230506 | sqlite3_result_text(pCtx, "fts5: 2021-06-04 23:26:56 1c71de43dbc68002c4a6229e7efffb019655baff67a51fe922571fe420c95835", -1, SQLITE_TRANSIENT); |
| 230507 | } |
| 230508 | |
| 230509 | /* |
| 230510 | ** Return true if zName is the extension on one of the shadow tables used |
| 230511 | ** by this module. |
| @@ -235427,12 +235494,12 @@ | |
| 235427 | } |
| 235428 | #endif /* SQLITE_CORE */ |
| 235429 | #endif /* !defined(SQLITE_CORE) || defined(SQLITE_ENABLE_STMTVTAB) */ |
| 235430 | |
| 235431 | /************** End of stmt.c ************************************************/ |
| 235432 | #if __LINE__!=235432 |
| 235433 | #undef SQLITE_SOURCE_ID |
| 235434 | #define SQLITE_SOURCE_ID "2021-06-07 00:41:18 2aa9368b63b42ac7c700516f109edcc6098c12b850eae591afed4e51a3f4alt2" |
| 235435 | #endif |
| 235436 | /* Return the source-id for this library */ |
| 235437 | SQLITE_API const char *sqlite3_sourceid(void){ return SQLITE_SOURCE_ID; } |
| 235438 | /************************** End of sqlite3.c ******************************/ |
| 235439 |
| --- src/sqlite3.c | |
| +++ src/sqlite3.c | |
| @@ -1205,11 +1205,11 @@ | |
| 1205 | ** [sqlite3_libversion_number()], [sqlite3_sourceid()], |
| 1206 | ** [sqlite_version()] and [sqlite_source_id()]. |
| 1207 | */ |
| 1208 | #define SQLITE_VERSION "3.36.0" |
| 1209 | #define SQLITE_VERSION_NUMBER 3036000 |
| 1210 | #define SQLITE_SOURCE_ID "2021-06-14 20:41:20 e5a5acd6006133c5da4a7dd79726dbaa41c0d60ebeda890f848a6aafe5f9ef70" |
| 1211 | |
| 1212 | /* |
| 1213 | ** CAPI3REF: Run-Time Library Version Numbers |
| 1214 | ** KEYWORDS: sqlite3_version sqlite3_sourceid |
| 1215 | ** |
| @@ -16558,10 +16558,16 @@ | |
| 16558 | */ |
| 16559 | #ifndef SET_FULLSYNC |
| 16560 | # define SET_FULLSYNC(x,y) |
| 16561 | #endif |
| 16562 | |
| 16563 | /* Maximum pathname length. Note: FILENAME_MAX defined by stdio.h |
| 16564 | */ |
| 16565 | #ifndef SQLITE_MAX_PATHLEN |
| 16566 | # define SQLITE_MAX_PATHLEN FILENAME_MAX |
| 16567 | #endif |
| 16568 | |
| 16569 | /* |
| 16570 | ** The default size of a disk sector |
| 16571 | */ |
| 16572 | #ifndef SQLITE_DEFAULT_SECTOR_SIZE |
| 16573 | # define SQLITE_DEFAULT_SECTOR_SIZE 4096 |
| @@ -18795,10 +18801,11 @@ | |
| 18801 | #define SF_View 0x0200000 /* SELECT statement is a view */ |
| 18802 | #define SF_NoopOrderBy 0x0400000 /* ORDER BY is ignored for this query */ |
| 18803 | #define SF_UpdateFrom 0x0800000 /* Statement is an UPDATE...FROM */ |
| 18804 | #define SF_PushDown 0x1000000 /* SELECT has be modified by push-down opt */ |
| 18805 | #define SF_MultiPart 0x2000000 /* Has multiple incompatible PARTITIONs */ |
| 18806 | #define SF_CopyCte 0x4000000 /* SELECT statement is a copy of a CTE */ |
| 18807 | |
| 18808 | /* |
| 18809 | ** The results of a SELECT can be distributed in several ways, as defined |
| 18810 | ** by one of the following macros. The "SRT" prefix means "SELECT Result |
| 18811 | ** Type". |
| @@ -20158,10 +20165,11 @@ | |
| 20165 | SQLITE_PRIVATE void sqlite3RegisterDateTimeFunctions(void); |
| 20166 | SQLITE_PRIVATE void sqlite3RegisterPerConnectionBuiltinFunctions(sqlite3*); |
| 20167 | SQLITE_PRIVATE int sqlite3SafetyCheckOk(sqlite3*); |
| 20168 | SQLITE_PRIVATE int sqlite3SafetyCheckSickOrOk(sqlite3*); |
| 20169 | SQLITE_PRIVATE void sqlite3ChangeCookie(Parse*, int); |
| 20170 | SQLITE_PRIVATE With *sqlite3WithDup(sqlite3 *db, With *p); |
| 20171 | |
| 20172 | #if !defined(SQLITE_OMIT_VIEW) && !defined(SQLITE_OMIT_TRIGGER) |
| 20173 | SQLITE_PRIVATE void sqlite3MaterializeView(Parse*, Table*, Expr*, ExprList*,Expr*,int); |
| 20174 | #endif |
| 20175 | |
| @@ -20568,11 +20576,11 @@ | |
| 20576 | #ifndef SQLITE_OMIT_CTE |
| 20577 | SQLITE_PRIVATE Cte *sqlite3CteNew(Parse*,Token*,ExprList*,Select*,u8); |
| 20578 | SQLITE_PRIVATE void sqlite3CteDelete(sqlite3*,Cte*); |
| 20579 | SQLITE_PRIVATE With *sqlite3WithAdd(Parse*,With*,Cte*); |
| 20580 | SQLITE_PRIVATE void sqlite3WithDelete(sqlite3*,With*); |
| 20581 | SQLITE_PRIVATE With *sqlite3WithPush(Parse*, With*, u8); |
| 20582 | #else |
| 20583 | # define sqlite3CteNew(P,T,E,S) ((void*)0) |
| 20584 | # define sqlite3CteDelete(D,C) |
| 20585 | # define sqlite3CteWithAdd(P,W,C) ((void*)0) |
| 20586 | # define sqlite3WithDelete(x,y) |
| @@ -21670,11 +21678,11 @@ | |
| 21678 | SQLITE_PRIVATE int sqlite3VdbeMemTooBig(Mem*); |
| 21679 | SQLITE_PRIVATE int sqlite3VdbeMemCopy(Mem*, const Mem*); |
| 21680 | SQLITE_PRIVATE void sqlite3VdbeMemShallowCopy(Mem*, const Mem*, int); |
| 21681 | SQLITE_PRIVATE void sqlite3VdbeMemMove(Mem*, Mem*); |
| 21682 | SQLITE_PRIVATE int sqlite3VdbeMemNulTerminate(Mem*); |
| 21683 | SQLITE_PRIVATE int sqlite3VdbeMemSetStr(Mem*, const char*, i64, u8, void(*)(void*)); |
| 21684 | SQLITE_PRIVATE void sqlite3VdbeMemSetInt64(Mem*, i64); |
| 21685 | #ifdef SQLITE_OMIT_FLOATING_POINT |
| 21686 | # define sqlite3VdbeMemSetDouble sqlite3VdbeMemSetInt64 |
| 21687 | #else |
| 21688 | SQLITE_PRIVATE void sqlite3VdbeMemSetDouble(Mem*, double); |
| @@ -23667,10 +23675,12 @@ | |
| 23675 | zPathOut[0] = 0; |
| 23676 | return pVfs->xFullPathname(pVfs, zPath, nPathOut, zPathOut); |
| 23677 | } |
| 23678 | #ifndef SQLITE_OMIT_LOAD_EXTENSION |
| 23679 | SQLITE_PRIVATE void *sqlite3OsDlOpen(sqlite3_vfs *pVfs, const char *zPath){ |
| 23680 | assert( zPath!=0 ); |
| 23681 | assert( strlen(zPath)<=SQLITE_MAX_PATHLEN ); /* tag-20210611-1 */ |
| 23682 | return pVfs->xDlOpen(pVfs, zPath); |
| 23683 | } |
| 23684 | SQLITE_PRIVATE void sqlite3OsDlError(sqlite3_vfs *pVfs, int nByte, char *zBufOut){ |
| 23685 | pVfs->xDlError(pVfs, nByte, zBufOut); |
| 23686 | } |
| @@ -66965,11 +66975,11 @@ | |
| 66975 | testcase( pc+size==usableSize ); |
| 66976 | put2byte(pAddr, cbrk); |
| 66977 | if( temp==0 ){ |
| 66978 | if( cbrk==pc ) continue; |
| 66979 | temp = sqlite3PagerTempSpace(pPage->pBt->pPager); |
| 66980 | memcpy(&temp[iCellStart], &data[iCellStart], usableSize - iCellStart); |
| 66981 | src = temp; |
| 66982 | } |
| 66983 | memcpy(&data[cbrk], &src[pc], size); |
| 66984 | } |
| 66985 | data[hdr+7] = 0; |
| @@ -78047,15 +78057,15 @@ | |
| 78057 | ** either case, SQLITE_TOOBIG is returned. |
| 78058 | */ |
| 78059 | SQLITE_PRIVATE int sqlite3VdbeMemSetStr( |
| 78060 | Mem *pMem, /* Memory cell to set to string value */ |
| 78061 | const char *z, /* String pointer */ |
| 78062 | i64 n, /* Bytes in string, or negative */ |
| 78063 | u8 enc, /* Encoding of z. 0 for BLOBs */ |
| 78064 | void (*xDel)(void*) /* Destructor function */ |
| 78065 | ){ |
| 78066 | i64 nByte = n; /* New value for pMem->n */ |
| 78067 | int iLimit; /* Maximum allowed string or blob size */ |
| 78068 | u16 flags = 0; /* New value for pMem->flags */ |
| 78069 | |
| 78070 | assert( pMem->db==0 || sqlite3_mutex_held(pMem->db->mutex) ); |
| 78071 | assert( !sqlite3VdbeMemIsRowSet(pMem) ); |
| @@ -78073,11 +78083,11 @@ | |
| 78083 | } |
| 78084 | flags = (enc==0?MEM_Blob:MEM_Str); |
| 78085 | if( nByte<0 ){ |
| 78086 | assert( enc!=0 ); |
| 78087 | if( enc==SQLITE_UTF8 ){ |
| 78088 | nByte = strlen(z); |
| 78089 | }else{ |
| 78090 | for(nByte=0; nByte<=iLimit && (z[nByte] | z[nByte+1]); nByte+=2){} |
| 78091 | } |
| 78092 | flags |= MEM_Term; |
| 78093 | } |
| @@ -78085,11 +78095,11 @@ | |
| 78095 | /* The following block sets the new values of Mem.z and Mem.xDel. It |
| 78096 | ** also sets a flag in local variable "flags" to indicate the memory |
| 78097 | ** management (one of MEM_Dyn or MEM_Static). |
| 78098 | */ |
| 78099 | if( xDel==SQLITE_TRANSIENT ){ |
| 78100 | i64 nAlloc = nByte; |
| 78101 | if( flags&MEM_Term ){ |
| 78102 | nAlloc += (enc==SQLITE_UTF8?1:2); |
| 78103 | } |
| 78104 | if( nByte>iLimit ){ |
| 78105 | return sqlite3ErrorToParser(pMem->db, SQLITE_TOOBIG); |
| @@ -78111,11 +78121,11 @@ | |
| 78121 | pMem->xDel = xDel; |
| 78122 | flags |= ((xDel==SQLITE_STATIC)?MEM_Static:MEM_Dyn); |
| 78123 | } |
| 78124 | } |
| 78125 | |
| 78126 | pMem->n = (int)(nByte & 0x7fffffff); |
| 78127 | pMem->flags = flags; |
| 78128 | if( enc ){ |
| 78129 | pMem->enc = enc; |
| 78130 | #ifdef SQLITE_ENABLE_SESSION |
| 78131 | }else if( pMem->db==0 ){ |
| @@ -78131,11 +78141,11 @@ | |
| 78141 | return SQLITE_NOMEM_BKPT; |
| 78142 | } |
| 78143 | #endif |
| 78144 | |
| 78145 | if( nByte>iLimit ){ |
| 78146 | return sqlite3ErrorToParser(pMem->db, SQLITE_TOOBIG); |
| 78147 | } |
| 78148 | |
| 78149 | return SQLITE_OK; |
| 78150 | } |
| 78151 | |
| @@ -84527,11 +84537,11 @@ | |
| 84537 | }else if( xDel==SQLITE_TRANSIENT ){ |
| 84538 | /* noop */ |
| 84539 | }else{ |
| 84540 | xDel((void*)p); |
| 84541 | } |
| 84542 | sqlite3_result_error_toobig(pCtx); |
| 84543 | return SQLITE_TOOBIG; |
| 84544 | } |
| 84545 | SQLITE_API void sqlite3_result_blob( |
| 84546 | sqlite3_context *pCtx, |
| 84547 | const void *z, |
| @@ -85509,11 +85519,11 @@ | |
| 85519 | */ |
| 85520 | static int bindText( |
| 85521 | sqlite3_stmt *pStmt, /* The statement to bind against */ |
| 85522 | int i, /* Index of the parameter to bind */ |
| 85523 | const void *zData, /* Pointer to the data to be bound */ |
| 85524 | i64 nData, /* Number of bytes of data to be bound */ |
| 85525 | void (*xDel)(void*), /* Destructor for the data */ |
| 85526 | u8 encoding /* Encoding for the data */ |
| 85527 | ){ |
| 85528 | Vdbe *p = (Vdbe *)pStmt; |
| 85529 | Mem *pVar; |
| @@ -85561,15 +85571,11 @@ | |
| 85571 | const void *zData, |
| 85572 | sqlite3_uint64 nData, |
| 85573 | void (*xDel)(void*) |
| 85574 | ){ |
| 85575 | assert( xDel!=SQLITE_DYNAMIC ); |
| 85576 | return bindText(pStmt, i, zData, nData, xDel, 0); |
| 85577 | } |
| 85578 | SQLITE_API int sqlite3_bind_double(sqlite3_stmt *pStmt, int i, double rValue){ |
| 85579 | int rc; |
| 85580 | Vdbe *p = (Vdbe *)pStmt; |
| 85581 | rc = vdbeUnbind(p, i); |
| @@ -85635,16 +85641,12 @@ | |
| 85641 | sqlite3_uint64 nData, |
| 85642 | void (*xDel)(void*), |
| 85643 | unsigned char enc |
| 85644 | ){ |
| 85645 | assert( xDel!=SQLITE_DYNAMIC ); |
| 85646 | if( enc==SQLITE_UTF16 ) enc = SQLITE_UTF16NATIVE; |
| 85647 | return bindText(pStmt, i, zData, nData, xDel, enc); |
| 85648 | } |
| 85649 | #ifndef SQLITE_OMIT_UTF16 |
| 85650 | SQLITE_API int sqlite3_bind_text16( |
| 85651 | sqlite3_stmt *pStmt, |
| 85652 | int i, |
| @@ -90957,11 +90959,12 @@ | |
| 90959 | assert( pOp[1].opcode==OP_SeekGE ); |
| 90960 | |
| 90961 | /* pOp->p2 points to the first instruction past the OP_IdxGT that |
| 90962 | ** follows the OP_SeekGE. */ |
| 90963 | assert( pOp->p2>=(int)(pOp-aOp)+2 ); |
| 90964 | assert( aOp[pOp->p2-1].opcode==OP_IdxGT || aOp[pOp->p2-1].opcode==OP_IdxGE ); |
| 90965 | testcase( aOp[pOp->p2-1].opcode==OP_IdxGE ); |
| 90966 | assert( pOp[1].p1==aOp[pOp->p2-1].p1 ); |
| 90967 | assert( pOp[1].p2==aOp[pOp->p2-1].p2 ); |
| 90968 | assert( pOp[1].p3==aOp[pOp->p2-1].p3 ); |
| 90969 | |
| 90970 | assert( pOp->p1>0 ); |
| @@ -92847,11 +92850,13 @@ | |
| 92850 | } |
| 92851 | #endif |
| 92852 | |
| 92853 | iDb = pOp->p1; |
| 92854 | assert( iDb>=0 && iDb<db->nDb ); |
| 92855 | assert( DbHasProperty(db, iDb, DB_SchemaLoaded) |
| 92856 | || db->mallocFailed |
| 92857 | || (CORRUPT_DB && (db->flags & SQLITE_NoSchemaError)!=0) ); |
| 92858 | |
| 92859 | #ifndef SQLITE_OMIT_ALTERTABLE |
| 92860 | if( pOp->p4.z==0 ){ |
| 92861 | sqlite3SchemaClear(db->aDb[iDb].pSchema); |
| 92862 | db->mDbFlags &= ~DBFLAG_SchemaKnownOk; |
| @@ -102622,11 +102627,11 @@ | |
| 102627 | ** Create and return a deep copy of the object passed as the second |
| 102628 | ** argument. If an OOM condition is encountered, NULL is returned |
| 102629 | ** and the db->mallocFailed flag set. |
| 102630 | */ |
| 102631 | #ifndef SQLITE_OMIT_CTE |
| 102632 | SQLITE_PRIVATE With *sqlite3WithDup(sqlite3 *db, With *p){ |
| 102633 | With *pRet = 0; |
| 102634 | if( p ){ |
| 102635 | sqlite3_int64 nByte = sizeof(*p) + sizeof(p->a[0]) * (p->nCte-1); |
| 102636 | pRet = sqlite3DbMallocZero(db, nByte); |
| 102637 | if( pRet ){ |
| @@ -102640,11 +102645,11 @@ | |
| 102645 | } |
| 102646 | } |
| 102647 | return pRet; |
| 102648 | } |
| 102649 | #else |
| 102650 | # define sqlite3WithDup(x,y) 0 |
| 102651 | #endif |
| 102652 | |
| 102653 | #ifndef SQLITE_OMIT_WINDOWFUNC |
| 102654 | /* |
| 102655 | ** The gatherSelectWindows() procedure and its helper routine |
| @@ -102844,11 +102849,11 @@ | |
| 102849 | pNew->iOffset = 0; |
| 102850 | pNew->selFlags = p->selFlags & ~SF_UsesEphemeral; |
| 102851 | pNew->addrOpenEphm[0] = -1; |
| 102852 | pNew->addrOpenEphm[1] = -1; |
| 102853 | pNew->nSelectRow = p->nSelectRow; |
| 102854 | pNew->pWith = sqlite3WithDup(db, p->pWith); |
| 102855 | #ifndef SQLITE_OMIT_WINDOWFUNC |
| 102856 | pNew->pWin = 0; |
| 102857 | pNew->pWinDefn = sqlite3WindowListDup(db, p->pWinDefn); |
| 102858 | if( p->pWin && db->mallocFailed==0 ) gatherSelectWindows(pNew); |
| 102859 | #endif |
| @@ -108184,19 +108189,34 @@ | |
| 108189 | ** to select statement pSelect. |
| 108190 | */ |
| 108191 | static void renameWalkWith(Walker *pWalker, Select *pSelect){ |
| 108192 | With *pWith = pSelect->pWith; |
| 108193 | if( pWith ){ |
| 108194 | Parse *pParse = pWalker->pParse; |
| 108195 | int i; |
| 108196 | With *pCopy = 0; |
| 108197 | assert( pWith->nCte>0 ); |
| 108198 | if( (pWith->a[0].pSelect->selFlags & SF_Expanded)==0 ){ |
| 108199 | /* Push a copy of the With object onto the with-stack. We use a copy |
| 108200 | ** here as the original will be expanded and resolved (flags SF_Expanded |
| 108201 | ** and SF_Resolved) below. And the parser code that uses the with-stack |
| 108202 | ** fails if the Select objects on it have already been expanded and |
| 108203 | ** resolved. */ |
| 108204 | pCopy = sqlite3WithDup(pParse->db, pWith); |
| 108205 | pCopy = sqlite3WithPush(pParse, pCopy, 1); |
| 108206 | } |
| 108207 | for(i=0; i<pWith->nCte; i++){ |
| 108208 | Select *p = pWith->a[i].pSelect; |
| 108209 | NameContext sNC; |
| 108210 | memset(&sNC, 0, sizeof(sNC)); |
| 108211 | sNC.pParse = pParse; |
| 108212 | if( pCopy ) sqlite3SelectPrep(sNC.pParse, p, &sNC); |
| 108213 | sqlite3WalkSelect(pWalker, p); |
| 108214 | sqlite3RenameExprlistUnmap(pParse, pWith->a[i].pCols); |
| 108215 | } |
| 108216 | if( pCopy && pParse->pWith==pCopy ){ |
| 108217 | pParse->pWith = pCopy->pOuter; |
| 108218 | } |
| 108219 | } |
| 108220 | } |
| 108221 | |
| 108222 | /* |
| @@ -108219,11 +108239,15 @@ | |
| 108239 | */ |
| 108240 | static int renameUnmapSelectCb(Walker *pWalker, Select *p){ |
| 108241 | Parse *pParse = pWalker->pParse; |
| 108242 | int i; |
| 108243 | if( pParse->nErr ) return WRC_Abort; |
| 108244 | if( p->selFlags & (SF_View|SF_CopyCte) ){ |
| 108245 | testcase( pSelect->selFlags & SF_View ); |
| 108246 | testcase( pSelect->selFlags & SF_CopyCte ); |
| 108247 | return WRC_Prune; |
| 108248 | } |
| 108249 | if( ALWAYS(p->pEList) ){ |
| 108250 | ExprList *pList = p->pEList; |
| 108251 | for(i=0; i<pList->nExpr; i++){ |
| 108252 | if( pList->a[i].zEName && pList->a[i].eEName==ENAME_NAME ){ |
| 108253 | sqlite3RenameTokenRemap(pParse, 0, (void*)pList->a[i].zEName); |
| @@ -108327,11 +108351,15 @@ | |
| 108351 | ** This is a Walker select callback. It does nothing. It is only required |
| 108352 | ** because without a dummy callback, sqlite3WalkExpr() and similar do not |
| 108353 | ** descend into sub-select statements. |
| 108354 | */ |
| 108355 | static int renameColumnSelectCb(Walker *pWalker, Select *p){ |
| 108356 | if( p->selFlags & (SF_View|SF_CopyCte) ){ |
| 108357 | testcase( pSelect->selFlags & SF_View ); |
| 108358 | testcase( pSelect->selFlags & SF_CopyCte ); |
| 108359 | return WRC_Prune; |
| 108360 | } |
| 108361 | renameWalkWith(pWalker, p); |
| 108362 | return WRC_Continue; |
| 108363 | } |
| 108364 | |
| 108365 | /* |
| @@ -108963,11 +108991,15 @@ | |
| 108991 | */ |
| 108992 | static int renameTableSelectCb(Walker *pWalker, Select *pSelect){ |
| 108993 | int i; |
| 108994 | RenameCtx *p = pWalker->u.pRename; |
| 108995 | SrcList *pSrc = pSelect->pSrc; |
| 108996 | if( pSelect->selFlags & (SF_View|SF_CopyCte) ){ |
| 108997 | testcase( pSelect->selFlags & SF_View ); |
| 108998 | testcase( pSelect->selFlags & SF_CopyCte ); |
| 108999 | return WRC_Prune; |
| 109000 | } |
| 109001 | if( NEVER(pSrc==0) ){ |
| 109002 | assert( pWalker->pParse->db->mallocFailed ); |
| 109003 | return WRC_Abort; |
| 109004 | } |
| 109005 | for(i=0; i<pSrc->nSrc; i++){ |
| @@ -116705,11 +116737,11 @@ | |
| 116737 | const char *zDb = db->aDb[iDb].zDbSName; |
| 116738 | const char *zTab = SCHEMA_TABLE(iDb); |
| 116739 | if( sqlite3AuthCheck(pParse, SQLITE_DELETE, zTab, 0, zDb) ){ |
| 116740 | goto exit_drop_index; |
| 116741 | } |
| 116742 | if( !OMIT_TEMPDB && iDb==1 ) code = SQLITE_DROP_TEMP_INDEX; |
| 116743 | if( sqlite3AuthCheck(pParse, code, pIndex->zName, pTab->zName, zDb) ){ |
| 116744 | goto exit_drop_index; |
| 116745 | } |
| 116746 | } |
| 116747 | #endif |
| @@ -119237,17 +119269,19 @@ | |
| 119269 | ){ |
| 119270 | /* This column was already computed by the previous index */ |
| 119271 | continue; |
| 119272 | } |
| 119273 | sqlite3ExprCodeLoadIndexColumn(pParse, pIdx, iDataCur, j, regBase+j); |
| 119274 | if( pIdx->aiColumn[j]>=0 ){ |
| 119275 | /* If the column affinity is REAL but the number is an integer, then it |
| 119276 | ** might be stored in the table as an integer (using a compact |
| 119277 | ** representation) then converted to REAL by an OP_RealAffinity opcode. |
| 119278 | ** But we are getting ready to store this value back into an index, where |
| 119279 | ** it should be converted by to INTEGER again. So omit the |
| 119280 | ** OP_RealAffinity opcode if it is present */ |
| 119281 | sqlite3VdbeDeletePriorOpcode(v, OP_RealAffinity); |
| 119282 | } |
| 119283 | } |
| 119284 | if( regOut ){ |
| 119285 | sqlite3VdbeAddOp3(v, OP_MakeRecord, regBase, nCol, regOut); |
| 119286 | } |
| 119287 | sqlite3ReleaseTempRange(pParse, regBase, nCol); |
| @@ -127401,11 +127435,11 @@ | |
| 127435 | sqlite3_loadext_entry xInit; |
| 127436 | char *zErrmsg = 0; |
| 127437 | const char *zEntry; |
| 127438 | char *zAltEntry = 0; |
| 127439 | void **aHandle; |
| 127440 | u64 nMsg = strlen(zFile); |
| 127441 | int ii; |
| 127442 | int rc; |
| 127443 | |
| 127444 | /* Shared library endings to try if zFile cannot be loaded as written */ |
| 127445 | static const char *azEndings[] = { |
| @@ -127435,30 +127469,26 @@ | |
| 127469 | return SQLITE_ERROR; |
| 127470 | } |
| 127471 | |
| 127472 | zEntry = zProc ? zProc : "sqlite3_extension_init"; |
| 127473 | |
| 127474 | /* tag-20210611-1. Some dlopen() implementations will segfault if given |
| 127475 | ** an oversize filename. Most filesystems have a pathname limit of 4K, |
| 127476 | ** so limit the extension filename length to about twice that. |
| 127477 | ** https://sqlite.org/forum/forumpost/08a0d6d9bf */ |
| 127478 | if( nMsg>SQLITE_MAX_PATHLEN ) goto extension_not_found; |
| 127479 | |
| 127480 | handle = sqlite3OsDlOpen(pVfs, zFile); |
| 127481 | #if SQLITE_OS_UNIX || SQLITE_OS_WIN |
| 127482 | for(ii=0; ii<ArraySize(azEndings) && handle==0; ii++){ |
| 127483 | char *zAltFile = sqlite3_mprintf("%s.%s", zFile, azEndings[ii]); |
| 127484 | if( zAltFile==0 ) return SQLITE_NOMEM_BKPT; |
| 127485 | handle = sqlite3OsDlOpen(pVfs, zAltFile); |
| 127486 | sqlite3_free(zAltFile); |
| 127487 | } |
| 127488 | #endif |
| 127489 | if( handle==0 ) goto extension_not_found; |
| 127490 | xInit = (sqlite3_loadext_entry)sqlite3OsDlSym(pVfs, handle, zEntry); |
| 127491 | |
| 127492 | /* If no entry point was specified and the default legacy |
| 127493 | ** entry point name "sqlite3_extension_init" was not found, then |
| 127494 | ** construct an entry point name "sqlite3_X_init" where the X is |
| @@ -127491,14 +127521,15 @@ | |
| 127521 | zEntry = zAltEntry; |
| 127522 | xInit = (sqlite3_loadext_entry)sqlite3OsDlSym(pVfs, handle, zEntry); |
| 127523 | } |
| 127524 | if( xInit==0 ){ |
| 127525 | if( pzErrMsg ){ |
| 127526 | nMsg += strlen(zEntry) + 300; |
| 127527 | *pzErrMsg = zErrmsg = sqlite3_malloc64(nMsg); |
| 127528 | if( zErrmsg ){ |
| 127529 | assert( nMsg<0x7fffffff ); /* zErrmsg would be NULL if not so */ |
| 127530 | sqlite3_snprintf((int)nMsg, zErrmsg, |
| 127531 | "no entry point [%s] in shared library [%s]", zEntry, zFile); |
| 127532 | sqlite3OsDlError(pVfs, nMsg-1, zErrmsg); |
| 127533 | } |
| 127534 | } |
| 127535 | sqlite3OsDlClose(pVfs, handle); |
| @@ -127528,10 +127559,23 @@ | |
| 127559 | sqlite3DbFree(db, db->aExtension); |
| 127560 | db->aExtension = aHandle; |
| 127561 | |
| 127562 | db->aExtension[db->nExtension++] = handle; |
| 127563 | return SQLITE_OK; |
| 127564 | |
| 127565 | extension_not_found: |
| 127566 | if( pzErrMsg ){ |
| 127567 | nMsg += 300; |
| 127568 | *pzErrMsg = zErrmsg = sqlite3_malloc64(nMsg); |
| 127569 | if( zErrmsg ){ |
| 127570 | assert( nMsg<0x7fffffff ); /* zErrmsg would be NULL if not so */ |
| 127571 | sqlite3_snprintf((int)nMsg, zErrmsg, |
| 127572 | "unable to open shared library [%.*s]", SQLITE_MAX_PATHLEN, zFile); |
| 127573 | sqlite3OsDlError(pVfs, nMsg-1, zErrmsg); |
| 127574 | } |
| 127575 | } |
| 127576 | return SQLITE_ERROR; |
| 127577 | } |
| 127578 | SQLITE_API int sqlite3_load_extension( |
| 127579 | sqlite3 *db, /* Load the extension into this database connection */ |
| 127580 | const char *zFile, /* Name of the shared library containing extension */ |
| 127581 | const char *zProc, /* Entry point. Use "sqlite3_extension_init" if 0 */ |
| @@ -131368,13 +131412,15 @@ | |
| 131412 | if( rc==SQLITE_OK ){ |
| 131413 | sqlite3AnalysisLoad(db, iDb); |
| 131414 | } |
| 131415 | #endif |
| 131416 | } |
| 131417 | assert( pDb == &(db->aDb[iDb]) ); |
| 131418 | if( db->mallocFailed ){ |
| 131419 | rc = SQLITE_NOMEM_BKPT; |
| 131420 | sqlite3ResetAllSchemasOfConnection(db); |
| 131421 | pDb = &db->aDb[iDb]; |
| 131422 | }else |
| 131423 | if( rc==SQLITE_OK || (db->flags&SQLITE_NoSchemaError)){ |
| 131424 | /* Hack: If the SQLITE_NoSchemaError flag is set, then consider |
| 131425 | ** the schema loaded, even if errors (other than OOM) occurred. In |
| 131426 | ** this situation the current sqlite3_prepare() operation will fail, |
| @@ -136426,10 +136472,11 @@ | |
| 136472 | ** a known value due to WHERE clause constraints of the form COLUMN=VALUE. |
| 136473 | */ |
| 136474 | typedef struct WhereConst WhereConst; |
| 136475 | struct WhereConst { |
| 136476 | Parse *pParse; /* Parsing context */ |
| 136477 | u8 *pOomFault; /* Pointer to pParse->db->mallocFailed */ |
| 136478 | int nConst; /* Number for COLUMN=CONSTANT terms */ |
| 136479 | int nChng; /* Number of times a constant is propagated */ |
| 136480 | int bHasAffBlob; /* At least one column in apExpr[] as affinity BLOB */ |
| 136481 | Expr **apExpr; /* [i*2] is COLUMN and [i*2+1] is VALUE */ |
| 136482 | }; |
| @@ -136525,10 +136572,11 @@ | |
| 136572 | WhereConst *pConst, |
| 136573 | Expr *pExpr, |
| 136574 | int bIgnoreAffBlob |
| 136575 | ){ |
| 136576 | int i; |
| 136577 | if( pConst->pOomFault[0] ) return WRC_Prune; |
| 136578 | if( pExpr->op!=TK_COLUMN ) return WRC_Continue; |
| 136579 | if( ExprHasProperty(pExpr, EP_FixedCol|EP_FromJoin) ){ |
| 136580 | testcase( ExprHasProperty(pExpr, EP_FixedCol) ); |
| 136581 | testcase( ExprHasProperty(pExpr, EP_FromJoin) ); |
| 136582 | return WRC_Continue; |
| @@ -136545,10 +136593,11 @@ | |
| 136593 | pConst->nChng++; |
| 136594 | ExprClearProperty(pExpr, EP_Leaf); |
| 136595 | ExprSetProperty(pExpr, EP_FixedCol); |
| 136596 | assert( pExpr->pLeft==0 ); |
| 136597 | pExpr->pLeft = sqlite3ExprDup(pConst->pParse->db, pConst->apExpr[i*2+1], 0); |
| 136598 | if( pConst->pParse->db->mallocFailed ) return WRC_Prune; |
| 136599 | break; |
| 136600 | } |
| 136601 | return WRC_Prune; |
| 136602 | } |
| 136603 | |
| @@ -136577,10 +136626,11 @@ | |
| 136626 | if( pConst->bHasAffBlob ){ |
| 136627 | if( (pExpr->op>=TK_EQ && pExpr->op<=TK_GE) |
| 136628 | || pExpr->op==TK_IS |
| 136629 | ){ |
| 136630 | propagateConstantExprRewriteOne(pConst, pExpr->pLeft, 0); |
| 136631 | if( pConst->pOomFault[0] ) return WRC_Prune; |
| 136632 | if( sqlite3ExprAffinity(pExpr->pLeft)!=SQLITE_AFF_TEXT ){ |
| 136633 | propagateConstantExprRewriteOne(pConst, pExpr->pRight, 0); |
| 136634 | } |
| 136635 | } |
| 136636 | } |
| @@ -136644,10 +136694,11 @@ | |
| 136694 | ){ |
| 136695 | WhereConst x; |
| 136696 | Walker w; |
| 136697 | int nChng = 0; |
| 136698 | x.pParse = pParse; |
| 136699 | x.pOomFault = &pParse->db->mallocFailed; |
| 136700 | do{ |
| 136701 | x.nConst = 0; |
| 136702 | x.nChng = 0; |
| 136703 | x.apExpr = 0; |
| 136704 | x.bHasAffBlob = 0; |
| @@ -137099,25 +137150,33 @@ | |
| 137150 | ** onto the top of the stack. If argument bFree is true, then this |
| 137151 | ** WITH clause will never be popped from the stack but should instead |
| 137152 | ** be freed along with the Parse object. In other cases, when |
| 137153 | ** bFree==0, the With object will be freed along with the SELECT |
| 137154 | ** statement with which it is associated. |
| 137155 | ** |
| 137156 | ** This routine returns a copy of pWith. Or, if bFree is true and |
| 137157 | ** the pWith object is destroyed immediately due to an OOM condition, |
| 137158 | ** then this routine return NULL. |
| 137159 | ** |
| 137160 | ** If bFree is true, do not continue to use the pWith pointer after |
| 137161 | ** calling this routine, Instead, use only the return value. |
| 137162 | */ |
| 137163 | SQLITE_PRIVATE With *sqlite3WithPush(Parse *pParse, With *pWith, u8 bFree){ |
| 137164 | if( pWith ){ |
| 137165 | if( bFree ){ |
| 137166 | pWith = (With*)sqlite3ParserAddCleanup(pParse, |
| 137167 | (void(*)(sqlite3*,void*))sqlite3WithDelete, |
| 137168 | pWith); |
| 137169 | if( pWith==0 ) return 0; |
| 137170 | } |
| 137171 | if( pParse->nErr==0 ){ |
| 137172 | assert( pParse->pWith!=pWith ); |
| 137173 | pWith->pOuter = pParse->pWith; |
| 137174 | pParse->pWith = pWith; |
| 137175 | } |
| 137176 | } |
| 137177 | return pWith; |
| 137178 | } |
| 137179 | |
| 137180 | /* |
| 137181 | ** This function checks if argument pFrom refers to a CTE declared by |
| 137182 | ** a WITH clause on the stack currently maintained by the parser (on the |
| @@ -137206,10 +137265,11 @@ | |
| 137265 | pTab->iPKey = -1; |
| 137266 | pTab->nRowLogEst = 200; assert( 200==sqlite3LogEst(1048576) ); |
| 137267 | pTab->tabFlags |= TF_Ephemeral | TF_NoVisibleRowid; |
| 137268 | pFrom->pSelect = sqlite3SelectDup(db, pCte->pSelect, 0); |
| 137269 | if( db->mallocFailed ) return 2; |
| 137270 | pFrom->pSelect->selFlags |= SF_CopyCte; |
| 137271 | assert( pFrom->pSelect ); |
| 137272 | pFrom->fg.isCte = 1; |
| 137273 | pFrom->u2.pCteUse = pCteUse; |
| 137274 | pCteUse->nUse++; |
| 137275 | if( pCteUse->nUse>=2 && pCteUse->eM10d==M10d_Any ){ |
| @@ -137474,10 +137534,11 @@ | |
| 137534 | ){ |
| 137535 | sqlite3ErrorMsg(pParse, "access to view \"%s\" prohibited", |
| 137536 | pTab->zName); |
| 137537 | } |
| 137538 | #ifndef SQLITE_OMIT_VIRTUALTABLE |
| 137539 | assert( SQLITE_VTABRISK_Normal==1 && SQLITE_VTABRISK_High==2 ); |
| 137540 | if( IsVirtual(pTab) |
| 137541 | && pFrom->fg.fromDDL |
| 137542 | && ALWAYS(pTab->pVTable!=0) |
| 137543 | && pTab->pVTable->eVtabRisk > ((db->flags & SQLITE_TrustedSchema)!=0) |
| 137544 | ){ |
| @@ -183221,12 +183282,12 @@ | |
| 183282 | int nPrev, /* Size of buffer zPrev in bytes */ |
| 183283 | const char *zNext, /* Buffer containing next term */ |
| 183284 | int nNext /* Size of buffer zNext in bytes */ |
| 183285 | ){ |
| 183286 | int n; |
| 183287 | for(n=0; n<nPrev && n<nNext && zPrev[n]==zNext[n]; n++); |
| 183288 | assert_fts3_nc( n<nNext ); |
| 183289 | return n; |
| 183290 | } |
| 183291 | |
| 183292 | /* |
| 183293 | ** Add term zTerm to the SegmentNode. It is guaranteed that zTerm is larger |
| @@ -184221,11 +184282,11 @@ | |
| 184282 | iDelta = (i64)((u64)iDocid - (u64)iPrev); |
| 184283 | } |
| 184284 | |
| 184285 | nByte = sqlite3Fts3VarintLen(iDelta) + (isRequirePos?nList+1:0); |
| 184286 | |
| 184287 | rc = fts3GrowSegReaderBuffer(pCsr, nByte+nDoclist+FTS3_NODE_PADDING); |
| 184288 | if( rc ) return rc; |
| 184289 | |
| 184290 | if( isFirst ){ |
| 184291 | char *a = &pCsr->aBuffer[nDoclist]; |
| 184292 | int nWrite; |
| @@ -216164,10 +216225,11 @@ | |
| 216225 | return 1; |
| 216226 | }else{ |
| 216227 | i64 iOff = *piOff; |
| 216228 | int iVal; |
| 216229 | fts5FastGetVarint32(a, i, iVal); |
| 216230 | assert( iVal>=0 ); |
| 216231 | if( iVal<=1 ){ |
| 216232 | if( iVal==0 ){ |
| 216233 | *pi = i; |
| 216234 | return 0; |
| 216235 | } |
| @@ -216177,13 +216239,16 @@ | |
| 216239 | if( iVal<2 ){ |
| 216240 | /* This is a corrupt record. So stop parsing it here. */ |
| 216241 | *piOff = -1; |
| 216242 | return 1; |
| 216243 | } |
| 216244 | *piOff = iOff + ((iVal-2) & 0x7FFFFFFF); |
| 216245 | }else{ |
| 216246 | *piOff = (iOff & (i64)0x7FFFFFFF<<32)+((iOff + (iVal-2)) & 0x7FFFFFFF); |
| 216247 | } |
| 216248 | *pi = i; |
| 216249 | assert( *piOff>=iOff ); |
| 216250 | return 0; |
| 216251 | } |
| 216252 | } |
| 216253 | |
| 216254 | |
| @@ -216218,18 +216283,20 @@ | |
| 216283 | static void sqlite3Fts5PoslistSafeAppend( |
| 216284 | Fts5Buffer *pBuf, |
| 216285 | i64 *piPrev, |
| 216286 | i64 iPos |
| 216287 | ){ |
| 216288 | if( iPos>=*piPrev ){ |
| 216289 | static const i64 colmask = ((i64)(0x7FFFFFFF)) << 32; |
| 216290 | if( (iPos & colmask) != (*piPrev & colmask) ){ |
| 216291 | pBuf->p[pBuf->n++] = 1; |
| 216292 | pBuf->n += sqlite3Fts5PutVarint(&pBuf->p[pBuf->n], (iPos>>32)); |
| 216293 | *piPrev = (iPos & colmask); |
| 216294 | } |
| 216295 | pBuf->n += sqlite3Fts5PutVarint(&pBuf->p[pBuf->n], (iPos-*piPrev)+2); |
| 216296 | *piPrev = iPos; |
| 216297 | } |
| 216298 | } |
| 216299 | |
| 216300 | static int sqlite3Fts5PoslistWriterAppend( |
| 216301 | Fts5Buffer *pBuf, |
| 216302 | Fts5PoslistWriter *pWriter, |
| @@ -224157,11 +224224,11 @@ | |
| 224224 | pIter->base.nData = p-aCopy; |
| 224225 | return; |
| 224226 | } |
| 224227 | fts5BufferSafeAppendBlob(&pIter->poslist, aCopy, p-aCopy); |
| 224228 | } |
| 224229 | if( p>=pEnd ){ |
| 224230 | pIter->base.pData = pIter->poslist.p; |
| 224231 | pIter->base.nData = pIter->poslist.n; |
| 224232 | return; |
| 224233 | } |
| 224234 | aCopy = p++; |
| @@ -225953,11 +226020,11 @@ | |
| 226020 | Fts5Buffer *p1, /* First list to merge */ |
| 226021 | int nBuf, /* Number of buffers in array aBuf[] */ |
| 226022 | Fts5Buffer *aBuf /* Other lists to merge in */ |
| 226023 | ){ |
| 226024 | #define fts5PrefixMergerNextPosition(p) \ |
| 226025 | sqlite3Fts5PoslistNext64((p)->aPos,(p)->iter.nPoslist,&(p)->iOff,&(p)->iPos) |
| 226026 | #define FTS5_MERGE_NLIST 16 |
| 226027 | PrefixMerger aMerger[FTS5_MERGE_NLIST]; |
| 226028 | PrefixMerger *pHead = 0; |
| 226029 | int i; |
| 226030 | int nOut = 0; |
| @@ -230501,11 +230568,11 @@ | |
| 230568 | int nArg, /* Number of args */ |
| 230569 | sqlite3_value **apUnused /* Function arguments */ |
| 230570 | ){ |
| 230571 | assert( nArg==0 ); |
| 230572 | UNUSED_PARAM2(nArg, apUnused); |
| 230573 | sqlite3_result_text(pCtx, "fts5: 2021-06-14 20:41:20 e5a5acd6006133c5da4a7dd79726dbaa41c0d60ebeda890f848a6aafe5f9ef70", -1, SQLITE_TRANSIENT); |
| 230574 | } |
| 230575 | |
| 230576 | /* |
| 230577 | ** Return true if zName is the extension on one of the shadow tables used |
| 230578 | ** by this module. |
| @@ -235427,12 +235494,12 @@ | |
| 235494 | } |
| 235495 | #endif /* SQLITE_CORE */ |
| 235496 | #endif /* !defined(SQLITE_CORE) || defined(SQLITE_ENABLE_STMTVTAB) */ |
| 235497 | |
| 235498 | /************** End of stmt.c ************************************************/ |
| 235499 | #if __LINE__!=235499 |
| 235500 | #undef SQLITE_SOURCE_ID |
| 235501 | #define SQLITE_SOURCE_ID "2021-06-14 20:41:20 e5a5acd6006133c5da4a7dd79726dbaa41c0d60ebeda890f848a6aafe5f9alt2" |
| 235502 | #endif |
| 235503 | /* Return the source-id for this library */ |
| 235504 | SQLITE_API const char *sqlite3_sourceid(void){ return SQLITE_SOURCE_ID; } |
| 235505 | /************************** End of sqlite3.c ******************************/ |
| 235506 |
+1
-1
| --- 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.36.0" |
| 127 | 127 | #define SQLITE_VERSION_NUMBER 3036000 |
| 128 | -#define SQLITE_SOURCE_ID "2021-06-07 00:41:18 2aa9368b63b42ac7c700516f109edcc6098c12b850eae591afed4e51a3f41819" | |
| 128 | +#define SQLITE_SOURCE_ID "2021-06-14 20:41:20 e5a5acd6006133c5da4a7dd79726dbaa41c0d60ebeda890f848a6aafe5f9ef70" | |
| 129 | 129 | |
| 130 | 130 | /* |
| 131 | 131 | ** CAPI3REF: Run-Time Library Version Numbers |
| 132 | 132 | ** KEYWORDS: sqlite3_version sqlite3_sourceid |
| 133 | 133 | ** |
| 134 | 134 |
| --- 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.36.0" |
| 127 | #define SQLITE_VERSION_NUMBER 3036000 |
| 128 | #define SQLITE_SOURCE_ID "2021-06-07 00:41:18 2aa9368b63b42ac7c700516f109edcc6098c12b850eae591afed4e51a3f41819" |
| 129 | |
| 130 | /* |
| 131 | ** CAPI3REF: Run-Time Library Version Numbers |
| 132 | ** KEYWORDS: sqlite3_version sqlite3_sourceid |
| 133 | ** |
| 134 |
| --- 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.36.0" |
| 127 | #define SQLITE_VERSION_NUMBER 3036000 |
| 128 | #define SQLITE_SOURCE_ID "2021-06-14 20:41:20 e5a5acd6006133c5da4a7dd79726dbaa41c0d60ebeda890f848a6aafe5f9ef70" |
| 129 | |
| 130 | /* |
| 131 | ** CAPI3REF: Run-Time Library Version Numbers |
| 132 | ** KEYWORDS: sqlite3_version sqlite3_sourceid |
| 133 | ** |
| 134 |
+11
| --- src/stat.c | ||
| +++ src/stat.c | ||
| @@ -60,10 +60,11 @@ | ||
| 60 | 60 | void stats_for_email(void){ |
| 61 | 61 | const char *zDest = db_get("email-send-method",0); |
| 62 | 62 | int nSub, nASub, nPend, nDPend; |
| 63 | 63 | const char *zDir, *zDb, *zCmd, *zRelay; |
| 64 | 64 | int iCutoff; |
| 65 | + double rDigest; | |
| 65 | 66 | @ <tr><th>Outgoing Email:</th><td> |
| 66 | 67 | if( fossil_strcmp(zDest,"pipe")==0 |
| 67 | 68 | && (zCmd = db_get("email-send-command",0))!=0 |
| 68 | 69 | ){ |
| 69 | 70 | @ Piped to command "%h(zCmd)" |
| @@ -117,10 +118,20 @@ | ||
| 117 | 118 | nASub = db_int(0, "SELECT count(*) FROM subscriber WHERE sverified" |
| 118 | 119 | " AND NOT sdonotcall AND length(ssub)>1" |
| 119 | 120 | " AND lastContact>=%d;", iCutoff); |
| 120 | 121 | @ %,d(nASub) active, %,d(nSub) total |
| 121 | 122 | @ </td></tr> |
| 123 | + rDigest = db_double(-1.0, "SELECT (julianday('now') - value)*24.0" | |
| 124 | + " FROM config WHERE name='email-last-digest'"); | |
| 125 | + if( rDigest>0.0 ){ | |
| 126 | + @ <tr><th>Last Digest:</th><td>Approximately \ | |
| 127 | + if( rDigest>48.0 ){ | |
| 128 | + @ %.1f(rDigest/24.0) days ago</td> | |
| 129 | + }else{ | |
| 130 | + @ %.1f(rDigest) hours ago</td> | |
| 131 | + } | |
| 132 | + } | |
| 122 | 133 | } |
| 123 | 134 | |
| 124 | 135 | /* |
| 125 | 136 | ** WEBPAGE: stat |
| 126 | 137 | ** |
| 127 | 138 |
| --- src/stat.c | |
| +++ src/stat.c | |
| @@ -60,10 +60,11 @@ | |
| 60 | void stats_for_email(void){ |
| 61 | const char *zDest = db_get("email-send-method",0); |
| 62 | int nSub, nASub, nPend, nDPend; |
| 63 | const char *zDir, *zDb, *zCmd, *zRelay; |
| 64 | int iCutoff; |
| 65 | @ <tr><th>Outgoing Email:</th><td> |
| 66 | if( fossil_strcmp(zDest,"pipe")==0 |
| 67 | && (zCmd = db_get("email-send-command",0))!=0 |
| 68 | ){ |
| 69 | @ Piped to command "%h(zCmd)" |
| @@ -117,10 +118,20 @@ | |
| 117 | nASub = db_int(0, "SELECT count(*) FROM subscriber WHERE sverified" |
| 118 | " AND NOT sdonotcall AND length(ssub)>1" |
| 119 | " AND lastContact>=%d;", iCutoff); |
| 120 | @ %,d(nASub) active, %,d(nSub) total |
| 121 | @ </td></tr> |
| 122 | } |
| 123 | |
| 124 | /* |
| 125 | ** WEBPAGE: stat |
| 126 | ** |
| 127 |
| --- src/stat.c | |
| +++ src/stat.c | |
| @@ -60,10 +60,11 @@ | |
| 60 | void stats_for_email(void){ |
| 61 | const char *zDest = db_get("email-send-method",0); |
| 62 | int nSub, nASub, nPend, nDPend; |
| 63 | const char *zDir, *zDb, *zCmd, *zRelay; |
| 64 | int iCutoff; |
| 65 | double rDigest; |
| 66 | @ <tr><th>Outgoing Email:</th><td> |
| 67 | if( fossil_strcmp(zDest,"pipe")==0 |
| 68 | && (zCmd = db_get("email-send-command",0))!=0 |
| 69 | ){ |
| 70 | @ Piped to command "%h(zCmd)" |
| @@ -117,10 +118,20 @@ | |
| 118 | nASub = db_int(0, "SELECT count(*) FROM subscriber WHERE sverified" |
| 119 | " AND NOT sdonotcall AND length(ssub)>1" |
| 120 | " AND lastContact>=%d;", iCutoff); |
| 121 | @ %,d(nASub) active, %,d(nSub) total |
| 122 | @ </td></tr> |
| 123 | rDigest = db_double(-1.0, "SELECT (julianday('now') - value)*24.0" |
| 124 | " FROM config WHERE name='email-last-digest'"); |
| 125 | if( rDigest>0.0 ){ |
| 126 | @ <tr><th>Last Digest:</th><td>Approximately \ |
| 127 | if( rDigest>48.0 ){ |
| 128 | @ %.1f(rDigest/24.0) days ago</td> |
| 129 | }else{ |
| 130 | @ %.1f(rDigest) hours ago</td> |
| 131 | } |
| 132 | } |
| 133 | } |
| 134 | |
| 135 | /* |
| 136 | ** WEBPAGE: stat |
| 137 | ** |
| 138 |
+3
-3
| --- src/sync.c | ||
| +++ src/sync.c | ||
| @@ -192,11 +192,11 @@ | ||
| 192 | 192 | ** |
| 193 | 193 | ** Usage: %fossil pull ?URL? ?options? |
| 194 | 194 | ** |
| 195 | 195 | ** Pull all sharable changes from a remote repository into the local |
| 196 | 196 | ** repository. Sharable changes include public check-ins, edits to |
| 197 | -** wiki pages, tickets, and tech-notes, as well as forum content. Add | |
| 197 | +** wiki pages, tickets, tech-notes, and forum posts. Add | |
| 198 | 198 | ** the --private option to pull private branches. Use the |
| 199 | 199 | ** "configuration pull" command to pull website configuration details. |
| 200 | 200 | ** |
| 201 | 201 | ** If URL is not specified, then the URL from the most recent clone, push, |
| 202 | 202 | ** pull, remote, or sync command is used. See "fossil help clone" for |
| @@ -243,11 +243,11 @@ | ||
| 243 | 243 | ** |
| 244 | 244 | ** Usage: %fossil push ?URL? ?options? |
| 245 | 245 | ** |
| 246 | 246 | ** Push all sharable changes from the local repository to a remote |
| 247 | 247 | ** repository. Sharable changes include public check-ins, edits to |
| 248 | -** wiki pages, tickets, and tech-notes, as well as forum content. Use | |
| 248 | +** wiki pages, tickets, tech-notes, and forum posts. Use | |
| 249 | 249 | ** --private to also push private branches. Use the "configuration |
| 250 | 250 | ** push" command to push website configuration details. |
| 251 | 251 | ** |
| 252 | 252 | ** If URL is not specified, then the URL from the most recent clone, push, |
| 253 | 253 | ** pull, remote, or sync command is used. See "fossil help clone" for |
| @@ -290,11 +290,11 @@ | ||
| 290 | 290 | ** |
| 291 | 291 | ** Usage: %fossil sync ?URL? ?options? |
| 292 | 292 | ** |
| 293 | 293 | ** Synchronize all sharable changes between the local repository and a |
| 294 | 294 | ** remote repository. Sharable changes include public check-ins and |
| 295 | -** edits to wiki pages, tickets, and technical notes. | |
| 295 | +** edits to wiki pages, tickets, forum posts, and technical notes. | |
| 296 | 296 | ** |
| 297 | 297 | ** If URL is not specified, then the URL from the most recent clone, push, |
| 298 | 298 | ** pull, remote, or sync command is used. See "fossil help clone" for |
| 299 | 299 | ** details on the URL formats. |
| 300 | 300 | ** |
| 301 | 301 |
| --- src/sync.c | |
| +++ src/sync.c | |
| @@ -192,11 +192,11 @@ | |
| 192 | ** |
| 193 | ** Usage: %fossil pull ?URL? ?options? |
| 194 | ** |
| 195 | ** Pull all sharable changes from a remote repository into the local |
| 196 | ** repository. Sharable changes include public check-ins, edits to |
| 197 | ** wiki pages, tickets, and tech-notes, as well as forum content. Add |
| 198 | ** the --private option to pull private branches. Use the |
| 199 | ** "configuration pull" command to pull website configuration details. |
| 200 | ** |
| 201 | ** If URL is not specified, then the URL from the most recent clone, push, |
| 202 | ** pull, remote, or sync command is used. See "fossil help clone" for |
| @@ -243,11 +243,11 @@ | |
| 243 | ** |
| 244 | ** Usage: %fossil push ?URL? ?options? |
| 245 | ** |
| 246 | ** Push all sharable changes from the local repository to a remote |
| 247 | ** repository. Sharable changes include public check-ins, edits to |
| 248 | ** wiki pages, tickets, and tech-notes, as well as forum content. Use |
| 249 | ** --private to also push private branches. Use the "configuration |
| 250 | ** push" command to push website configuration details. |
| 251 | ** |
| 252 | ** If URL is not specified, then the URL from the most recent clone, push, |
| 253 | ** pull, remote, or sync command is used. See "fossil help clone" for |
| @@ -290,11 +290,11 @@ | |
| 290 | ** |
| 291 | ** Usage: %fossil sync ?URL? ?options? |
| 292 | ** |
| 293 | ** Synchronize all sharable changes between the local repository and a |
| 294 | ** remote repository. Sharable changes include public check-ins and |
| 295 | ** edits to wiki pages, tickets, and technical notes. |
| 296 | ** |
| 297 | ** If URL is not specified, then the URL from the most recent clone, push, |
| 298 | ** pull, remote, or sync command is used. See "fossil help clone" for |
| 299 | ** details on the URL formats. |
| 300 | ** |
| 301 |
| --- src/sync.c | |
| +++ src/sync.c | |
| @@ -192,11 +192,11 @@ | |
| 192 | ** |
| 193 | ** Usage: %fossil pull ?URL? ?options? |
| 194 | ** |
| 195 | ** Pull all sharable changes from a remote repository into the local |
| 196 | ** repository. Sharable changes include public check-ins, edits to |
| 197 | ** wiki pages, tickets, tech-notes, and forum posts. Add |
| 198 | ** the --private option to pull private branches. Use the |
| 199 | ** "configuration pull" command to pull website configuration details. |
| 200 | ** |
| 201 | ** If URL is not specified, then the URL from the most recent clone, push, |
| 202 | ** pull, remote, or sync command is used. See "fossil help clone" for |
| @@ -243,11 +243,11 @@ | |
| 243 | ** |
| 244 | ** Usage: %fossil push ?URL? ?options? |
| 245 | ** |
| 246 | ** Push all sharable changes from the local repository to a remote |
| 247 | ** repository. Sharable changes include public check-ins, edits to |
| 248 | ** wiki pages, tickets, tech-notes, and forum posts. Use |
| 249 | ** --private to also push private branches. Use the "configuration |
| 250 | ** push" command to push website configuration details. |
| 251 | ** |
| 252 | ** If URL is not specified, then the URL from the most recent clone, push, |
| 253 | ** pull, remote, or sync command is used. See "fossil help clone" for |
| @@ -290,11 +290,11 @@ | |
| 290 | ** |
| 291 | ** Usage: %fossil sync ?URL? ?options? |
| 292 | ** |
| 293 | ** Synchronize all sharable changes between the local repository and a |
| 294 | ** remote repository. Sharable changes include public check-ins and |
| 295 | ** edits to wiki pages, tickets, forum posts, and technical notes. |
| 296 | ** |
| 297 | ** If URL is not specified, then the URL from the most recent clone, push, |
| 298 | ** pull, remote, or sync command is used. See "fossil help clone" for |
| 299 | ** details on the URL formats. |
| 300 | ** |
| 301 |
+12
-2
| --- src/th_main.c | ||
| +++ src/th_main.c | ||
| @@ -2976,11 +2976,12 @@ | ||
| 2976 | 2976 | ** COMMAND: test-th-eval |
| 2977 | 2977 | ** |
| 2978 | 2978 | ** Usage: %fossil test-th-eval SCRIPT |
| 2979 | 2979 | ** |
| 2980 | 2980 | ** Evaluate SCRIPT as if it were a header or footer or ticket rendering |
| 2981 | -** script and show the results on standard output. | |
| 2981 | +** script and show the results on standard output. SCRIPT may be either | |
| 2982 | +** a filename or a string of th1 script code. | |
| 2982 | 2983 | ** |
| 2983 | 2984 | ** Options: |
| 2984 | 2985 | ** |
| 2985 | 2986 | ** --cgi Include a CGI response header in the output |
| 2986 | 2987 | ** --http Include an HTTP response header in the output |
| @@ -2990,11 +2991,13 @@ | ||
| 2990 | 2991 | ** --th-trace Trace TH1 execution (for debugging purposes) |
| 2991 | 2992 | */ |
| 2992 | 2993 | void test_th_eval(void){ |
| 2993 | 2994 | int rc; |
| 2994 | 2995 | const char *zRc; |
| 2996 | + const char *zCode = 0; | |
| 2995 | 2997 | int forceCgi, fullHttpReply; |
| 2998 | + Blob code = empty_blob; | |
| 2996 | 2999 | Th_InitTraceLog(); |
| 2997 | 3000 | forceCgi = find_option("cgi", 0, 0)!=0; |
| 2998 | 3001 | fullHttpReply = find_option("http", 0, 0)!=0; |
| 2999 | 3002 | if( fullHttpReply ) forceCgi = 1; |
| 3000 | 3003 | if( forceCgi ) Th_ForceCgi(fullHttpReply); |
| @@ -3012,16 +3015,23 @@ | ||
| 3012 | 3015 | g.useLocalauth = 1; |
| 3013 | 3016 | } |
| 3014 | 3017 | verify_all_options(); |
| 3015 | 3018 | if( g.argc!=3 ){ |
| 3016 | 3019 | usage("script"); |
| 3020 | + } | |
| 3021 | + if(file_isfile(g.argv[2], ExtFILE)){ | |
| 3022 | + blob_read_from_file(&code, g.argv[2], ExtFILE); | |
| 3023 | + zCode = blob_str(&code); | |
| 3024 | + }else{ | |
| 3025 | + zCode = g.argv[2]; | |
| 3017 | 3026 | } |
| 3018 | 3027 | Th_FossilInit(TH_INIT_DEFAULT); |
| 3019 | - rc = Th_Eval(g.interp, 0, g.argv[2], -1); | |
| 3028 | + rc = Th_Eval(g.interp, 0, zCode, -1); | |
| 3020 | 3029 | zRc = Th_ReturnCodeName(rc, 1); |
| 3021 | 3030 | fossil_print("%s%s%s\n", zRc, zRc ? ": " : "", Th_GetResult(g.interp, 0)); |
| 3022 | 3031 | Th_PrintTraceLog(); |
| 3032 | + blob_reset(&code); | |
| 3023 | 3033 | if( forceCgi ) cgi_reply(); |
| 3024 | 3034 | } |
| 3025 | 3035 | |
| 3026 | 3036 | /* |
| 3027 | 3037 | ** COMMAND: test-th-source |
| 3028 | 3038 |
| --- src/th_main.c | |
| +++ src/th_main.c | |
| @@ -2976,11 +2976,12 @@ | |
| 2976 | ** COMMAND: test-th-eval |
| 2977 | ** |
| 2978 | ** Usage: %fossil test-th-eval SCRIPT |
| 2979 | ** |
| 2980 | ** Evaluate SCRIPT as if it were a header or footer or ticket rendering |
| 2981 | ** script and show the results on standard output. |
| 2982 | ** |
| 2983 | ** Options: |
| 2984 | ** |
| 2985 | ** --cgi Include a CGI response header in the output |
| 2986 | ** --http Include an HTTP response header in the output |
| @@ -2990,11 +2991,13 @@ | |
| 2990 | ** --th-trace Trace TH1 execution (for debugging purposes) |
| 2991 | */ |
| 2992 | void test_th_eval(void){ |
| 2993 | int rc; |
| 2994 | const char *zRc; |
| 2995 | int forceCgi, fullHttpReply; |
| 2996 | Th_InitTraceLog(); |
| 2997 | forceCgi = find_option("cgi", 0, 0)!=0; |
| 2998 | fullHttpReply = find_option("http", 0, 0)!=0; |
| 2999 | if( fullHttpReply ) forceCgi = 1; |
| 3000 | if( forceCgi ) Th_ForceCgi(fullHttpReply); |
| @@ -3012,16 +3015,23 @@ | |
| 3012 | g.useLocalauth = 1; |
| 3013 | } |
| 3014 | verify_all_options(); |
| 3015 | if( g.argc!=3 ){ |
| 3016 | usage("script"); |
| 3017 | } |
| 3018 | Th_FossilInit(TH_INIT_DEFAULT); |
| 3019 | rc = Th_Eval(g.interp, 0, g.argv[2], -1); |
| 3020 | zRc = Th_ReturnCodeName(rc, 1); |
| 3021 | fossil_print("%s%s%s\n", zRc, zRc ? ": " : "", Th_GetResult(g.interp, 0)); |
| 3022 | Th_PrintTraceLog(); |
| 3023 | if( forceCgi ) cgi_reply(); |
| 3024 | } |
| 3025 | |
| 3026 | /* |
| 3027 | ** COMMAND: test-th-source |
| 3028 |
| --- src/th_main.c | |
| +++ src/th_main.c | |
| @@ -2976,11 +2976,12 @@ | |
| 2976 | ** COMMAND: test-th-eval |
| 2977 | ** |
| 2978 | ** Usage: %fossil test-th-eval SCRIPT |
| 2979 | ** |
| 2980 | ** Evaluate SCRIPT as if it were a header or footer or ticket rendering |
| 2981 | ** script and show the results on standard output. SCRIPT may be either |
| 2982 | ** a filename or a string of th1 script code. |
| 2983 | ** |
| 2984 | ** Options: |
| 2985 | ** |
| 2986 | ** --cgi Include a CGI response header in the output |
| 2987 | ** --http Include an HTTP response header in the output |
| @@ -2990,11 +2991,13 @@ | |
| 2991 | ** --th-trace Trace TH1 execution (for debugging purposes) |
| 2992 | */ |
| 2993 | void test_th_eval(void){ |
| 2994 | int rc; |
| 2995 | const char *zRc; |
| 2996 | const char *zCode = 0; |
| 2997 | int forceCgi, fullHttpReply; |
| 2998 | Blob code = empty_blob; |
| 2999 | Th_InitTraceLog(); |
| 3000 | forceCgi = find_option("cgi", 0, 0)!=0; |
| 3001 | fullHttpReply = find_option("http", 0, 0)!=0; |
| 3002 | if( fullHttpReply ) forceCgi = 1; |
| 3003 | if( forceCgi ) Th_ForceCgi(fullHttpReply); |
| @@ -3012,16 +3015,23 @@ | |
| 3015 | g.useLocalauth = 1; |
| 3016 | } |
| 3017 | verify_all_options(); |
| 3018 | if( g.argc!=3 ){ |
| 3019 | usage("script"); |
| 3020 | } |
| 3021 | if(file_isfile(g.argv[2], ExtFILE)){ |
| 3022 | blob_read_from_file(&code, g.argv[2], ExtFILE); |
| 3023 | zCode = blob_str(&code); |
| 3024 | }else{ |
| 3025 | zCode = g.argv[2]; |
| 3026 | } |
| 3027 | Th_FossilInit(TH_INIT_DEFAULT); |
| 3028 | rc = Th_Eval(g.interp, 0, zCode, -1); |
| 3029 | zRc = Th_ReturnCodeName(rc, 1); |
| 3030 | fossil_print("%s%s%s\n", zRc, zRc ? ": " : "", Th_GetResult(g.interp, 0)); |
| 3031 | Th_PrintTraceLog(); |
| 3032 | blob_reset(&code); |
| 3033 | if( forceCgi ) cgi_reply(); |
| 3034 | } |
| 3035 | |
| 3036 | /* |
| 3037 | ** COMMAND: test-th-source |
| 3038 |
+23
-2
| --- src/tkt.c | ||
| +++ src/tkt.c | ||
| @@ -378,10 +378,12 @@ | ||
| 378 | 378 | ** schema for the ticketing system. Only allow |
| 379 | 379 | ** |
| 380 | 380 | ** CREATE TABLE |
| 381 | 381 | ** CREATE INDEX |
| 382 | 382 | ** CREATE VIEW |
| 383 | +** DROP INDEX | |
| 384 | +** DROP VIEW | |
| 383 | 385 | ** |
| 384 | 386 | ** And for objects in "main" or "repository" whose names |
| 385 | 387 | ** begin with "ticket" or "fx_". Also allow |
| 386 | 388 | ** |
| 387 | 389 | ** INSERT |
| @@ -393,10 +395,13 @@ | ||
| 393 | 395 | ** |
| 394 | 396 | ** Of particular importance for security is that this routine |
| 395 | 397 | ** disallows data changes on the "config" table, as that could |
| 396 | 398 | ** allow a malicious server to modify settings in such a way as |
| 397 | 399 | ** to cause a remote code execution. |
| 400 | +** | |
| 401 | +** Use the "fossil test-db-prepare --auth-ticket SQL" command to perform | |
| 402 | +** manual testing of this authorizer. | |
| 398 | 403 | */ |
| 399 | 404 | static int ticket_schema_auth( |
| 400 | 405 | void *pNErr, |
| 401 | 406 | int eCode, |
| 402 | 407 | const char *z0, |
| @@ -403,10 +408,11 @@ | ||
| 403 | 408 | const char *z1, |
| 404 | 409 | const char *z2, |
| 405 | 410 | const char *z3 |
| 406 | 411 | ){ |
| 407 | 412 | switch( eCode ){ |
| 413 | + case SQLITE_DROP_VIEW: | |
| 408 | 414 | case SQLITE_CREATE_VIEW: |
| 409 | 415 | case SQLITE_CREATE_TABLE: { |
| 410 | 416 | if( sqlite3_stricmp(z2,"main")!=0 |
| 411 | 417 | && sqlite3_stricmp(z2,"repository")!=0 |
| 412 | 418 | ){ |
| @@ -417,10 +423,11 @@ | ||
| 417 | 423 | ){ |
| 418 | 424 | goto ticket_schema_error; |
| 419 | 425 | } |
| 420 | 426 | break; |
| 421 | 427 | } |
| 428 | + case SQLITE_DROP_INDEX: | |
| 422 | 429 | case SQLITE_CREATE_INDEX: { |
| 423 | 430 | if( sqlite3_stricmp(z2,"main")!=0 |
| 424 | 431 | && sqlite3_stricmp(z2,"repository")!=0 |
| 425 | 432 | ){ |
| 426 | 433 | goto ticket_schema_error; |
| @@ -463,10 +470,24 @@ | ||
| 463 | 470 | ticket_schema_error: |
| 464 | 471 | if( pNErr ) *(int*)pNErr = 1; |
| 465 | 472 | return SQLITE_DENY; |
| 466 | 473 | } |
| 467 | 474 | |
| 475 | +/* | |
| 476 | +** Activate the ticket schema authorizer. Must be followed by | |
| 477 | +** an eventual call to ticket_unrestrict_sql(). | |
| 478 | +*/ | |
| 479 | +void ticket_restrict_sql(int * pNErr){ | |
| 480 | + db_set_authorizer(ticket_schema_auth,(void*)pNErr,"Ticket-Schema"); | |
| 481 | +} | |
| 482 | +/* | |
| 483 | +** Deactivate the ticket schema authorizer. | |
| 484 | +*/ | |
| 485 | +void ticket_unrestrict_sql(void){ | |
| 486 | + db_clear_authorizer(); | |
| 487 | +} | |
| 488 | + | |
| 468 | 489 | |
| 469 | 490 | /* |
| 470 | 491 | ** Recreate the TICKET and TICKETCHNG tables. |
| 471 | 492 | */ |
| 472 | 493 | void ticket_create_table(int separateConnection){ |
| @@ -475,18 +496,18 @@ | ||
| 475 | 496 | db_multi_exec( |
| 476 | 497 | "DROP TABLE IF EXISTS ticket;" |
| 477 | 498 | "DROP TABLE IF EXISTS ticketchng;" |
| 478 | 499 | ); |
| 479 | 500 | zSql = ticket_table_schema(); |
| 480 | - db_set_authorizer(ticket_schema_auth,0,"Ticket-Schema"); | |
| 501 | + ticket_restrict_sql(0); | |
| 481 | 502 | if( separateConnection ){ |
| 482 | 503 | if( db_transaction_nesting_depth() ) db_end_transaction(0); |
| 483 | 504 | db_init_database(g.zRepositoryName, zSql, 0); |
| 484 | 505 | }else{ |
| 485 | 506 | db_multi_exec("%s", zSql/*safe-for-%s*/); |
| 486 | 507 | } |
| 487 | - db_clear_authorizer(); | |
| 508 | + ticket_unrestrict_sql(); | |
| 488 | 509 | fossil_free(zSql); |
| 489 | 510 | } |
| 490 | 511 | |
| 491 | 512 | /* |
| 492 | 513 | ** Repopulate the TICKET and TICKETCHNG tables from scratch using all |
| 493 | 514 |
| --- src/tkt.c | |
| +++ src/tkt.c | |
| @@ -378,10 +378,12 @@ | |
| 378 | ** schema for the ticketing system. Only allow |
| 379 | ** |
| 380 | ** CREATE TABLE |
| 381 | ** CREATE INDEX |
| 382 | ** CREATE VIEW |
| 383 | ** |
| 384 | ** And for objects in "main" or "repository" whose names |
| 385 | ** begin with "ticket" or "fx_". Also allow |
| 386 | ** |
| 387 | ** INSERT |
| @@ -393,10 +395,13 @@ | |
| 393 | ** |
| 394 | ** Of particular importance for security is that this routine |
| 395 | ** disallows data changes on the "config" table, as that could |
| 396 | ** allow a malicious server to modify settings in such a way as |
| 397 | ** to cause a remote code execution. |
| 398 | */ |
| 399 | static int ticket_schema_auth( |
| 400 | void *pNErr, |
| 401 | int eCode, |
| 402 | const char *z0, |
| @@ -403,10 +408,11 @@ | |
| 403 | const char *z1, |
| 404 | const char *z2, |
| 405 | const char *z3 |
| 406 | ){ |
| 407 | switch( eCode ){ |
| 408 | case SQLITE_CREATE_VIEW: |
| 409 | case SQLITE_CREATE_TABLE: { |
| 410 | if( sqlite3_stricmp(z2,"main")!=0 |
| 411 | && sqlite3_stricmp(z2,"repository")!=0 |
| 412 | ){ |
| @@ -417,10 +423,11 @@ | |
| 417 | ){ |
| 418 | goto ticket_schema_error; |
| 419 | } |
| 420 | break; |
| 421 | } |
| 422 | case SQLITE_CREATE_INDEX: { |
| 423 | if( sqlite3_stricmp(z2,"main")!=0 |
| 424 | && sqlite3_stricmp(z2,"repository")!=0 |
| 425 | ){ |
| 426 | goto ticket_schema_error; |
| @@ -463,10 +470,24 @@ | |
| 463 | ticket_schema_error: |
| 464 | if( pNErr ) *(int*)pNErr = 1; |
| 465 | return SQLITE_DENY; |
| 466 | } |
| 467 | |
| 468 | |
| 469 | /* |
| 470 | ** Recreate the TICKET and TICKETCHNG tables. |
| 471 | */ |
| 472 | void ticket_create_table(int separateConnection){ |
| @@ -475,18 +496,18 @@ | |
| 475 | db_multi_exec( |
| 476 | "DROP TABLE IF EXISTS ticket;" |
| 477 | "DROP TABLE IF EXISTS ticketchng;" |
| 478 | ); |
| 479 | zSql = ticket_table_schema(); |
| 480 | db_set_authorizer(ticket_schema_auth,0,"Ticket-Schema"); |
| 481 | if( separateConnection ){ |
| 482 | if( db_transaction_nesting_depth() ) db_end_transaction(0); |
| 483 | db_init_database(g.zRepositoryName, zSql, 0); |
| 484 | }else{ |
| 485 | db_multi_exec("%s", zSql/*safe-for-%s*/); |
| 486 | } |
| 487 | db_clear_authorizer(); |
| 488 | fossil_free(zSql); |
| 489 | } |
| 490 | |
| 491 | /* |
| 492 | ** Repopulate the TICKET and TICKETCHNG tables from scratch using all |
| 493 |
| --- src/tkt.c | |
| +++ src/tkt.c | |
| @@ -378,10 +378,12 @@ | |
| 378 | ** schema for the ticketing system. Only allow |
| 379 | ** |
| 380 | ** CREATE TABLE |
| 381 | ** CREATE INDEX |
| 382 | ** CREATE VIEW |
| 383 | ** DROP INDEX |
| 384 | ** DROP VIEW |
| 385 | ** |
| 386 | ** And for objects in "main" or "repository" whose names |
| 387 | ** begin with "ticket" or "fx_". Also allow |
| 388 | ** |
| 389 | ** INSERT |
| @@ -393,10 +395,13 @@ | |
| 395 | ** |
| 396 | ** Of particular importance for security is that this routine |
| 397 | ** disallows data changes on the "config" table, as that could |
| 398 | ** allow a malicious server to modify settings in such a way as |
| 399 | ** to cause a remote code execution. |
| 400 | ** |
| 401 | ** Use the "fossil test-db-prepare --auth-ticket SQL" command to perform |
| 402 | ** manual testing of this authorizer. |
| 403 | */ |
| 404 | static int ticket_schema_auth( |
| 405 | void *pNErr, |
| 406 | int eCode, |
| 407 | const char *z0, |
| @@ -403,10 +408,11 @@ | |
| 408 | const char *z1, |
| 409 | const char *z2, |
| 410 | const char *z3 |
| 411 | ){ |
| 412 | switch( eCode ){ |
| 413 | case SQLITE_DROP_VIEW: |
| 414 | case SQLITE_CREATE_VIEW: |
| 415 | case SQLITE_CREATE_TABLE: { |
| 416 | if( sqlite3_stricmp(z2,"main")!=0 |
| 417 | && sqlite3_stricmp(z2,"repository")!=0 |
| 418 | ){ |
| @@ -417,10 +423,11 @@ | |
| 423 | ){ |
| 424 | goto ticket_schema_error; |
| 425 | } |
| 426 | break; |
| 427 | } |
| 428 | case SQLITE_DROP_INDEX: |
| 429 | case SQLITE_CREATE_INDEX: { |
| 430 | if( sqlite3_stricmp(z2,"main")!=0 |
| 431 | && sqlite3_stricmp(z2,"repository")!=0 |
| 432 | ){ |
| 433 | goto ticket_schema_error; |
| @@ -463,10 +470,24 @@ | |
| 470 | ticket_schema_error: |
| 471 | if( pNErr ) *(int*)pNErr = 1; |
| 472 | return SQLITE_DENY; |
| 473 | } |
| 474 | |
| 475 | /* |
| 476 | ** Activate the ticket schema authorizer. Must be followed by |
| 477 | ** an eventual call to ticket_unrestrict_sql(). |
| 478 | */ |
| 479 | void ticket_restrict_sql(int * pNErr){ |
| 480 | db_set_authorizer(ticket_schema_auth,(void*)pNErr,"Ticket-Schema"); |
| 481 | } |
| 482 | /* |
| 483 | ** Deactivate the ticket schema authorizer. |
| 484 | */ |
| 485 | void ticket_unrestrict_sql(void){ |
| 486 | db_clear_authorizer(); |
| 487 | } |
| 488 | |
| 489 | |
| 490 | /* |
| 491 | ** Recreate the TICKET and TICKETCHNG tables. |
| 492 | */ |
| 493 | void ticket_create_table(int separateConnection){ |
| @@ -475,18 +496,18 @@ | |
| 496 | db_multi_exec( |
| 497 | "DROP TABLE IF EXISTS ticket;" |
| 498 | "DROP TABLE IF EXISTS ticketchng;" |
| 499 | ); |
| 500 | zSql = ticket_table_schema(); |
| 501 | ticket_restrict_sql(0); |
| 502 | if( separateConnection ){ |
| 503 | if( db_transaction_nesting_depth() ) db_end_transaction(0); |
| 504 | db_init_database(g.zRepositoryName, zSql, 0); |
| 505 | }else{ |
| 506 | db_multi_exec("%s", zSql/*safe-for-%s*/); |
| 507 | } |
| 508 | ticket_unrestrict_sql(); |
| 509 | fossil_free(zSql); |
| 510 | } |
| 511 | |
| 512 | /* |
| 513 | ** Repopulate the TICKET and TICKETCHNG tables from scratch using all |
| 514 |
+21
-4
| --- www/changes.wiki | ||
| +++ www/changes.wiki | ||
| @@ -1,9 +1,12 @@ | ||
| 1 | 1 | <title>Change Log</title> |
| 2 | 2 | |
| 3 | 3 | <a name='v2_16'></a> |
| 4 | 4 | <h2>Changes for Version 2.16 (pending)</h2> |
| 5 | + * <b>Security:</b> Fix the client-side TLS so that it verifies that the | |
| 6 | + server hostname matches its certificate. <b>Upgrading to | |
| 7 | + the patch is recommended.</b> | |
| 5 | 8 | * The [/brlist|/brlist web page] allows the user to |
| 6 | 9 | select multiple branches to be displayed together in a single |
| 7 | 10 | timeline. |
| 8 | 11 | * The [./forum.wiki|Forum] provides a hyperlink on the author of each |
| 9 | 12 | post that goes to a timeline of recent posts by that same author. |
| @@ -16,18 +19,28 @@ | ||
| 16 | 19 | pages now default to markdown format. |
| 17 | 20 | * The [/help?cmd=/login|/login] page now links to a user's forum post |
| 18 | 21 | timeline if the repository has forum posts. |
| 19 | 22 | * Tags may now be propagated for forum posts, wiki pages, and technotes. |
| 20 | 23 | The [/help?cmd=tag|tag command] can now manipulate and list such tags. |
| 24 | + * [./caps/login-groups.md|Login-Groups] are now show on the repository | |
| 25 | + list of the "[/help?cmd=all|fossil all ui]" command. | |
| 26 | + * Administrators can configure [./alerts|email alerts] to expire | |
| 27 | + a specific number of days (ex: 365) after the last user contact with | |
| 28 | + the Fossil server. This can prevents alert emails being sent to | |
| 29 | + abandoned email accounts forever. | |
| 21 | 30 | * Submenu of the [/help?cmd=/rptview|/rptview] and |
| 22 | 31 | [/help?cmd=/wiki|/wiki] pages may be |
| 23 | 32 | [branch/rptview-submenu-paralink|extended with auxiliary hyperlinks]. |
| 24 | 33 | |
| 25 | 34 | <a name='v2_15'></a> |
| 26 | -<h2>Changes for Version 2.15 (2021-03-26) and Patch 2.15.1 on (2021-04-07)</h2> | |
| 35 | +<h2>Changes for Version 2.15 (2021-03-26) and Patch 2.15.1 on (2021-04-07) | |
| 36 | + and 2.15.2 on (2021-06-15)</h2> | |
| 37 | + * <b>Patch 2.15.2:</b> Fix the client-side TLS so that it verifies that the | |
| 38 | + server hostname matches its certificate. <b>Upgrading to | |
| 39 | + the patch is recommended.</b> | |
| 27 | 40 | * <b>Patch 2.15.1:</b> Fix a data exfiltration bug in the server. <b>Upgrading to |
| 28 | - the patch is recommended.</b><p> | |
| 41 | + the patch is recommended.</b> | |
| 29 | 42 | * The [./defcsp.md|default CSP] has been relaxed slightly to allow |
| 30 | 43 | images to be loaded from any URL. All other resources are still |
| 31 | 44 | locked down by default. |
| 32 | 45 | * The built-in skins all use the "[/help?cmd=mainmenu|mainmenu]" |
| 33 | 46 | setting to determine the content of the main menu. |
| @@ -102,13 +115,17 @@ | ||
| 102 | 115 | versions of a wiki (by the means of anchoring a "baseline" version) |
| 103 | 116 | and the ability to squeeze several sequential edits made by the same |
| 104 | 117 | user into a single "recycled" row (the latest edit in that sequence). |
| 105 | 118 | |
| 106 | 119 | <a name='v2_14'></a> |
| 107 | -<h2>Changes for Version 2.14 (2021-01-20) and Patch 2.14.1 on (2021-04-07)</h2> | |
| 120 | +<h2>Changes for Version 2.14 (2021-01-20) and Patch 2.14.1 on (2021-04-07) | |
| 121 | + and 2.14.2 on (2021-06-15)</h2> | |
| 122 | + * <b>Patch 2.14.2:</b> Fix the client-side TLS so that it verifies that the | |
| 123 | + server hostname matches its certificate. <b>Upgrading to | |
| 124 | + the patch is recommended.</b>< | |
| 108 | 125 | * <b>Patch 2.14.1:</b> Fix a data exfiltration bug in the server. |
| 109 | - <b>Upgrading to the patch is recommended.</b><p> | |
| 126 | + <b>Upgrading to the patch is recommended.</b> | |
| 110 | 127 | * <b>Schema Update Notice #1:</b> |
| 111 | 128 | This release drops a trigger from the database schema (replacing |
| 112 | 129 | it with a TEMP trigger that is created as needed). This |
| 113 | 130 | change happens automatically the first time you |
| 114 | 131 | add content to a repository using Fossil 2.14 or later. No |
| 115 | 132 |
| --- www/changes.wiki | |
| +++ www/changes.wiki | |
| @@ -1,9 +1,12 @@ | |
| 1 | <title>Change Log</title> |
| 2 | |
| 3 | <a name='v2_16'></a> |
| 4 | <h2>Changes for Version 2.16 (pending)</h2> |
| 5 | * The [/brlist|/brlist web page] allows the user to |
| 6 | select multiple branches to be displayed together in a single |
| 7 | timeline. |
| 8 | * The [./forum.wiki|Forum] provides a hyperlink on the author of each |
| 9 | post that goes to a timeline of recent posts by that same author. |
| @@ -16,18 +19,28 @@ | |
| 16 | pages now default to markdown format. |
| 17 | * The [/help?cmd=/login|/login] page now links to a user's forum post |
| 18 | timeline if the repository has forum posts. |
| 19 | * Tags may now be propagated for forum posts, wiki pages, and technotes. |
| 20 | The [/help?cmd=tag|tag command] can now manipulate and list such tags. |
| 21 | * Submenu of the [/help?cmd=/rptview|/rptview] and |
| 22 | [/help?cmd=/wiki|/wiki] pages may be |
| 23 | [branch/rptview-submenu-paralink|extended with auxiliary hyperlinks]. |
| 24 | |
| 25 | <a name='v2_15'></a> |
| 26 | <h2>Changes for Version 2.15 (2021-03-26) and Patch 2.15.1 on (2021-04-07)</h2> |
| 27 | * <b>Patch 2.15.1:</b> Fix a data exfiltration bug in the server. <b>Upgrading to |
| 28 | the patch is recommended.</b><p> |
| 29 | * The [./defcsp.md|default CSP] has been relaxed slightly to allow |
| 30 | images to be loaded from any URL. All other resources are still |
| 31 | locked down by default. |
| 32 | * The built-in skins all use the "[/help?cmd=mainmenu|mainmenu]" |
| 33 | setting to determine the content of the main menu. |
| @@ -102,13 +115,17 @@ | |
| 102 | versions of a wiki (by the means of anchoring a "baseline" version) |
| 103 | and the ability to squeeze several sequential edits made by the same |
| 104 | user into a single "recycled" row (the latest edit in that sequence). |
| 105 | |
| 106 | <a name='v2_14'></a> |
| 107 | <h2>Changes for Version 2.14 (2021-01-20) and Patch 2.14.1 on (2021-04-07)</h2> |
| 108 | * <b>Patch 2.14.1:</b> Fix a data exfiltration bug in the server. |
| 109 | <b>Upgrading to the patch is recommended.</b><p> |
| 110 | * <b>Schema Update Notice #1:</b> |
| 111 | This release drops a trigger from the database schema (replacing |
| 112 | it with a TEMP trigger that is created as needed). This |
| 113 | change happens automatically the first time you |
| 114 | add content to a repository using Fossil 2.14 or later. No |
| 115 |
| --- www/changes.wiki | |
| +++ www/changes.wiki | |
| @@ -1,9 +1,12 @@ | |
| 1 | <title>Change Log</title> |
| 2 | |
| 3 | <a name='v2_16'></a> |
| 4 | <h2>Changes for Version 2.16 (pending)</h2> |
| 5 | * <b>Security:</b> Fix the client-side TLS so that it verifies that the |
| 6 | server hostname matches its certificate. <b>Upgrading to |
| 7 | the patch is recommended.</b> |
| 8 | * The [/brlist|/brlist web page] allows the user to |
| 9 | select multiple branches to be displayed together in a single |
| 10 | timeline. |
| 11 | * The [./forum.wiki|Forum] provides a hyperlink on the author of each |
| 12 | post that goes to a timeline of recent posts by that same author. |
| @@ -16,18 +19,28 @@ | |
| 19 | pages now default to markdown format. |
| 20 | * The [/help?cmd=/login|/login] page now links to a user's forum post |
| 21 | timeline if the repository has forum posts. |
| 22 | * Tags may now be propagated for forum posts, wiki pages, and technotes. |
| 23 | The [/help?cmd=tag|tag command] can now manipulate and list such tags. |
| 24 | * [./caps/login-groups.md|Login-Groups] are now show on the repository |
| 25 | list of the "[/help?cmd=all|fossil all ui]" command. |
| 26 | * Administrators can configure [./alerts|email alerts] to expire |
| 27 | a specific number of days (ex: 365) after the last user contact with |
| 28 | the Fossil server. This can prevents alert emails being sent to |
| 29 | abandoned email accounts forever. |
| 30 | * Submenu of the [/help?cmd=/rptview|/rptview] and |
| 31 | [/help?cmd=/wiki|/wiki] pages may be |
| 32 | [branch/rptview-submenu-paralink|extended with auxiliary hyperlinks]. |
| 33 | |
| 34 | <a name='v2_15'></a> |
| 35 | <h2>Changes for Version 2.15 (2021-03-26) and Patch 2.15.1 on (2021-04-07) |
| 36 | and 2.15.2 on (2021-06-15)</h2> |
| 37 | * <b>Patch 2.15.2:</b> Fix the client-side TLS so that it verifies that the |
| 38 | server hostname matches its certificate. <b>Upgrading to |
| 39 | the patch is recommended.</b> |
| 40 | * <b>Patch 2.15.1:</b> Fix a data exfiltration bug in the server. <b>Upgrading to |
| 41 | the patch is recommended.</b> |
| 42 | * The [./defcsp.md|default CSP] has been relaxed slightly to allow |
| 43 | images to be loaded from any URL. All other resources are still |
| 44 | locked down by default. |
| 45 | * The built-in skins all use the "[/help?cmd=mainmenu|mainmenu]" |
| 46 | setting to determine the content of the main menu. |
| @@ -102,13 +115,17 @@ | |
| 115 | versions of a wiki (by the means of anchoring a "baseline" version) |
| 116 | and the ability to squeeze several sequential edits made by the same |
| 117 | user into a single "recycled" row (the latest edit in that sequence). |
| 118 | |
| 119 | <a name='v2_14'></a> |
| 120 | <h2>Changes for Version 2.14 (2021-01-20) and Patch 2.14.1 on (2021-04-07) |
| 121 | and 2.14.2 on (2021-06-15)</h2> |
| 122 | * <b>Patch 2.14.2:</b> Fix the client-side TLS so that it verifies that the |
| 123 | server hostname matches its certificate. <b>Upgrading to |
| 124 | the patch is recommended.</b>< |
| 125 | * <b>Patch 2.14.1:</b> Fix a data exfiltration bug in the server. |
| 126 | <b>Upgrading to the patch is recommended.</b> |
| 127 | * <b>Schema Update Notice #1:</b> |
| 128 | This release drops a trigger from the database schema (replacing |
| 129 | it with a TEMP trigger that is created as needed). This |
| 130 | change happens automatically the first time you |
| 131 | add content to a repository using Fossil 2.14 or later. No |
| 132 |
+21
-4
| --- www/changes.wiki | ||
| +++ www/changes.wiki | ||
| @@ -1,9 +1,12 @@ | ||
| 1 | 1 | <title>Change Log</title> |
| 2 | 2 | |
| 3 | 3 | <a name='v2_16'></a> |
| 4 | 4 | <h2>Changes for Version 2.16 (pending)</h2> |
| 5 | + * <b>Security:</b> Fix the client-side TLS so that it verifies that the | |
| 6 | + server hostname matches its certificate. <b>Upgrading to | |
| 7 | + the patch is recommended.</b> | |
| 5 | 8 | * The [/brlist|/brlist web page] allows the user to |
| 6 | 9 | select multiple branches to be displayed together in a single |
| 7 | 10 | timeline. |
| 8 | 11 | * The [./forum.wiki|Forum] provides a hyperlink on the author of each |
| 9 | 12 | post that goes to a timeline of recent posts by that same author. |
| @@ -16,18 +19,28 @@ | ||
| 16 | 19 | pages now default to markdown format. |
| 17 | 20 | * The [/help?cmd=/login|/login] page now links to a user's forum post |
| 18 | 21 | timeline if the repository has forum posts. |
| 19 | 22 | * Tags may now be propagated for forum posts, wiki pages, and technotes. |
| 20 | 23 | The [/help?cmd=tag|tag command] can now manipulate and list such tags. |
| 24 | + * [./caps/login-groups.md|Login-Groups] are now show on the repository | |
| 25 | + list of the "[/help?cmd=all|fossil all ui]" command. | |
| 26 | + * Administrators can configure [./alerts|email alerts] to expire | |
| 27 | + a specific number of days (ex: 365) after the last user contact with | |
| 28 | + the Fossil server. This can prevents alert emails being sent to | |
| 29 | + abandoned email accounts forever. | |
| 21 | 30 | * Submenu of the [/help?cmd=/rptview|/rptview] and |
| 22 | 31 | [/help?cmd=/wiki|/wiki] pages may be |
| 23 | 32 | [branch/rptview-submenu-paralink|extended with auxiliary hyperlinks]. |
| 24 | 33 | |
| 25 | 34 | <a name='v2_15'></a> |
| 26 | -<h2>Changes for Version 2.15 (2021-03-26) and Patch 2.15.1 on (2021-04-07)</h2> | |
| 35 | +<h2>Changes for Version 2.15 (2021-03-26) and Patch 2.15.1 on (2021-04-07) | |
| 36 | + and 2.15.2 on (2021-06-15)</h2> | |
| 37 | + * <b>Patch 2.15.2:</b> Fix the client-side TLS so that it verifies that the | |
| 38 | + server hostname matches its certificate. <b>Upgrading to | |
| 39 | + the patch is recommended.</b> | |
| 27 | 40 | * <b>Patch 2.15.1:</b> Fix a data exfiltration bug in the server. <b>Upgrading to |
| 28 | - the patch is recommended.</b><p> | |
| 41 | + the patch is recommended.</b> | |
| 29 | 42 | * The [./defcsp.md|default CSP] has been relaxed slightly to allow |
| 30 | 43 | images to be loaded from any URL. All other resources are still |
| 31 | 44 | locked down by default. |
| 32 | 45 | * The built-in skins all use the "[/help?cmd=mainmenu|mainmenu]" |
| 33 | 46 | setting to determine the content of the main menu. |
| @@ -102,13 +115,17 @@ | ||
| 102 | 115 | versions of a wiki (by the means of anchoring a "baseline" version) |
| 103 | 116 | and the ability to squeeze several sequential edits made by the same |
| 104 | 117 | user into a single "recycled" row (the latest edit in that sequence). |
| 105 | 118 | |
| 106 | 119 | <a name='v2_14'></a> |
| 107 | -<h2>Changes for Version 2.14 (2021-01-20) and Patch 2.14.1 on (2021-04-07)</h2> | |
| 120 | +<h2>Changes for Version 2.14 (2021-01-20) and Patch 2.14.1 on (2021-04-07) | |
| 121 | + and 2.14.2 on (2021-06-15)</h2> | |
| 122 | + * <b>Patch 2.14.2:</b> Fix the client-side TLS so that it verifies that the | |
| 123 | + server hostname matches its certificate. <b>Upgrading to | |
| 124 | + the patch is recommended.</b>< | |
| 108 | 125 | * <b>Patch 2.14.1:</b> Fix a data exfiltration bug in the server. |
| 109 | - <b>Upgrading to the patch is recommended.</b><p> | |
| 126 | + <b>Upgrading to the patch is recommended.</b> | |
| 110 | 127 | * <b>Schema Update Notice #1:</b> |
| 111 | 128 | This release drops a trigger from the database schema (replacing |
| 112 | 129 | it with a TEMP trigger that is created as needed). This |
| 113 | 130 | change happens automatically the first time you |
| 114 | 131 | add content to a repository using Fossil 2.14 or later. No |
| 115 | 132 |
| --- www/changes.wiki | |
| +++ www/changes.wiki | |
| @@ -1,9 +1,12 @@ | |
| 1 | <title>Change Log</title> |
| 2 | |
| 3 | <a name='v2_16'></a> |
| 4 | <h2>Changes for Version 2.16 (pending)</h2> |
| 5 | * The [/brlist|/brlist web page] allows the user to |
| 6 | select multiple branches to be displayed together in a single |
| 7 | timeline. |
| 8 | * The [./forum.wiki|Forum] provides a hyperlink on the author of each |
| 9 | post that goes to a timeline of recent posts by that same author. |
| @@ -16,18 +19,28 @@ | |
| 16 | pages now default to markdown format. |
| 17 | * The [/help?cmd=/login|/login] page now links to a user's forum post |
| 18 | timeline if the repository has forum posts. |
| 19 | * Tags may now be propagated for forum posts, wiki pages, and technotes. |
| 20 | The [/help?cmd=tag|tag command] can now manipulate and list such tags. |
| 21 | * Submenu of the [/help?cmd=/rptview|/rptview] and |
| 22 | [/help?cmd=/wiki|/wiki] pages may be |
| 23 | [branch/rptview-submenu-paralink|extended with auxiliary hyperlinks]. |
| 24 | |
| 25 | <a name='v2_15'></a> |
| 26 | <h2>Changes for Version 2.15 (2021-03-26) and Patch 2.15.1 on (2021-04-07)</h2> |
| 27 | * <b>Patch 2.15.1:</b> Fix a data exfiltration bug in the server. <b>Upgrading to |
| 28 | the patch is recommended.</b><p> |
| 29 | * The [./defcsp.md|default CSP] has been relaxed slightly to allow |
| 30 | images to be loaded from any URL. All other resources are still |
| 31 | locked down by default. |
| 32 | * The built-in skins all use the "[/help?cmd=mainmenu|mainmenu]" |
| 33 | setting to determine the content of the main menu. |
| @@ -102,13 +115,17 @@ | |
| 102 | versions of a wiki (by the means of anchoring a "baseline" version) |
| 103 | and the ability to squeeze several sequential edits made by the same |
| 104 | user into a single "recycled" row (the latest edit in that sequence). |
| 105 | |
| 106 | <a name='v2_14'></a> |
| 107 | <h2>Changes for Version 2.14 (2021-01-20) and Patch 2.14.1 on (2021-04-07)</h2> |
| 108 | * <b>Patch 2.14.1:</b> Fix a data exfiltration bug in the server. |
| 109 | <b>Upgrading to the patch is recommended.</b><p> |
| 110 | * <b>Schema Update Notice #1:</b> |
| 111 | This release drops a trigger from the database schema (replacing |
| 112 | it with a TEMP trigger that is created as needed). This |
| 113 | change happens automatically the first time you |
| 114 | add content to a repository using Fossil 2.14 or later. No |
| 115 |
| --- www/changes.wiki | |
| +++ www/changes.wiki | |
| @@ -1,9 +1,12 @@ | |
| 1 | <title>Change Log</title> |
| 2 | |
| 3 | <a name='v2_16'></a> |
| 4 | <h2>Changes for Version 2.16 (pending)</h2> |
| 5 | * <b>Security:</b> Fix the client-side TLS so that it verifies that the |
| 6 | server hostname matches its certificate. <b>Upgrading to |
| 7 | the patch is recommended.</b> |
| 8 | * The [/brlist|/brlist web page] allows the user to |
| 9 | select multiple branches to be displayed together in a single |
| 10 | timeline. |
| 11 | * The [./forum.wiki|Forum] provides a hyperlink on the author of each |
| 12 | post that goes to a timeline of recent posts by that same author. |
| @@ -16,18 +19,28 @@ | |
| 19 | pages now default to markdown format. |
| 20 | * The [/help?cmd=/login|/login] page now links to a user's forum post |
| 21 | timeline if the repository has forum posts. |
| 22 | * Tags may now be propagated for forum posts, wiki pages, and technotes. |
| 23 | The [/help?cmd=tag|tag command] can now manipulate and list such tags. |
| 24 | * [./caps/login-groups.md|Login-Groups] are now show on the repository |
| 25 | list of the "[/help?cmd=all|fossil all ui]" command. |
| 26 | * Administrators can configure [./alerts|email alerts] to expire |
| 27 | a specific number of days (ex: 365) after the last user contact with |
| 28 | the Fossil server. This can prevents alert emails being sent to |
| 29 | abandoned email accounts forever. |
| 30 | * Submenu of the [/help?cmd=/rptview|/rptview] and |
| 31 | [/help?cmd=/wiki|/wiki] pages may be |
| 32 | [branch/rptview-submenu-paralink|extended with auxiliary hyperlinks]. |
| 33 | |
| 34 | <a name='v2_15'></a> |
| 35 | <h2>Changes for Version 2.15 (2021-03-26) and Patch 2.15.1 on (2021-04-07) |
| 36 | and 2.15.2 on (2021-06-15)</h2> |
| 37 | * <b>Patch 2.15.2:</b> Fix the client-side TLS so that it verifies that the |
| 38 | server hostname matches its certificate. <b>Upgrading to |
| 39 | the patch is recommended.</b> |
| 40 | * <b>Patch 2.15.1:</b> Fix a data exfiltration bug in the server. <b>Upgrading to |
| 41 | the patch is recommended.</b> |
| 42 | * The [./defcsp.md|default CSP] has been relaxed slightly to allow |
| 43 | images to be loaded from any URL. All other resources are still |
| 44 | locked down by default. |
| 45 | * The built-in skins all use the "[/help?cmd=mainmenu|mainmenu]" |
| 46 | setting to determine the content of the main menu. |
| @@ -102,13 +115,17 @@ | |
| 115 | versions of a wiki (by the means of anchoring a "baseline" version) |
| 116 | and the ability to squeeze several sequential edits made by the same |
| 117 | user into a single "recycled" row (the latest edit in that sequence). |
| 118 | |
| 119 | <a name='v2_14'></a> |
| 120 | <h2>Changes for Version 2.14 (2021-01-20) and Patch 2.14.1 on (2021-04-07) |
| 121 | and 2.14.2 on (2021-06-15)</h2> |
| 122 | * <b>Patch 2.14.2:</b> Fix the client-side TLS so that it verifies that the |
| 123 | server hostname matches its certificate. <b>Upgrading to |
| 124 | the patch is recommended.</b>< |
| 125 | * <b>Patch 2.14.1:</b> Fix a data exfiltration bug in the server. |
| 126 | <b>Upgrading to the patch is recommended.</b> |
| 127 | * <b>Schema Update Notice #1:</b> |
| 128 | This release drops a trigger from the database schema (replacing |
| 129 | it with a TEMP trigger that is created as needed). This |
| 130 | change happens automatically the first time you |
| 131 | add content to a repository using Fossil 2.14 or later. No |
| 132 |
+2
-2
| --- www/index.wiki | ||
| +++ www/index.wiki | ||
| @@ -82,15 +82,15 @@ | ||
| 82 | 82 | the repository are consistent prior to each commit. |
| 83 | 83 | |
| 84 | 84 | 8. <b>Free and Open-Source</b> - [../COPYRIGHT-BSD2.txt|2-clause BSD license]. |
| 85 | 85 | |
| 86 | 86 | <hr> |
| 87 | -<h3>Latest Release: 2.15 ([/timeline?c=version-2.15|2021-03-26])</h3> | |
| 87 | +<h3>Latest Release: 2.15.2 ([/timeline?c=version-2.15.2|2021-06-15])</h3> | |
| 88 | 88 | |
| 89 | 89 | * [/uv/download.html|Download] |
| 90 | 90 | * [./changes.wiki#v2_15|Change Summary] |
| 91 | - * [/timeline?p=version-2.15&bt=version-2.14&y=ci|Check-ins in version 2.15] | |
| 91 | + * [/timeline?p=version-2.15.2&bt=version-2.14&y=ci|Check-ins in version 2.15.2] | |
| 92 | 92 | * [/timeline?df=version-2.15&y=ci|Check-ins derived from the 2.15 release] |
| 93 | 93 | * [/timeline?t=release|Timeline of all past releases] |
| 94 | 94 | |
| 95 | 95 | <hr> |
| 96 | 96 | <h3>Quick Start</h3> |
| 97 | 97 |
| --- www/index.wiki | |
| +++ www/index.wiki | |
| @@ -82,15 +82,15 @@ | |
| 82 | the repository are consistent prior to each commit. |
| 83 | |
| 84 | 8. <b>Free and Open-Source</b> - [../COPYRIGHT-BSD2.txt|2-clause BSD license]. |
| 85 | |
| 86 | <hr> |
| 87 | <h3>Latest Release: 2.15 ([/timeline?c=version-2.15|2021-03-26])</h3> |
| 88 | |
| 89 | * [/uv/download.html|Download] |
| 90 | * [./changes.wiki#v2_15|Change Summary] |
| 91 | * [/timeline?p=version-2.15&bt=version-2.14&y=ci|Check-ins in version 2.15] |
| 92 | * [/timeline?df=version-2.15&y=ci|Check-ins derived from the 2.15 release] |
| 93 | * [/timeline?t=release|Timeline of all past releases] |
| 94 | |
| 95 | <hr> |
| 96 | <h3>Quick Start</h3> |
| 97 |
| --- www/index.wiki | |
| +++ www/index.wiki | |
| @@ -82,15 +82,15 @@ | |
| 82 | the repository are consistent prior to each commit. |
| 83 | |
| 84 | 8. <b>Free and Open-Source</b> - [../COPYRIGHT-BSD2.txt|2-clause BSD license]. |
| 85 | |
| 86 | <hr> |
| 87 | <h3>Latest Release: 2.15.2 ([/timeline?c=version-2.15.2|2021-06-15])</h3> |
| 88 | |
| 89 | * [/uv/download.html|Download] |
| 90 | * [./changes.wiki#v2_15|Change Summary] |
| 91 | * [/timeline?p=version-2.15.2&bt=version-2.14&y=ci|Check-ins in version 2.15.2] |
| 92 | * [/timeline?df=version-2.15&y=ci|Check-ins derived from the 2.15 release] |
| 93 | * [/timeline?t=release|Timeline of all past releases] |
| 94 | |
| 95 | <hr> |
| 96 | <h3>Quick Start</h3> |
| 97 |
+2
-2
| --- www/javascript.md | ||
| +++ www/javascript.md | ||
| @@ -572,11 +572,11 @@ | ||
| 572 | 572 | gateway bidirectional should be possible as well, as long as it properly |
| 573 | 573 | uses SQLite transactions. |
| 574 | 574 | |
| 575 | 575 | ### <a id="brlist"></a>List of branches |
| 576 | 576 | |
| 577 | -Since Fossil 2.16 [`/brlist`](/brlist) page uses JavaScript to enable | |
| 577 | +Since Fossil 2.16 the [`/brlist`](/brlist) page uses JavaScript to enable | |
| 578 | 578 | selection of several branches for further study via `/timeline`. |
| 579 | 579 | Client-side script interactively responds to checkboxes' events |
| 580 | 580 | and constructs a special hyperlink in the submenu. |
| 581 | 581 | Clicking this hyperlink loads a `/timeline` page that shows |
| 582 | 582 | only these selected branches (and the related check-ins). |
| @@ -619,12 +619,12 @@ | ||
| 619 | 619 | 2) Features end users request which catch the interest of one or more |
| 620 | 620 | developers, provided the developer(s) in question are in a position to |
| 621 | 621 | expend the effort. |
| 622 | 622 | |
| 623 | 623 | 3) Features end users and co-contributors can convince a developer into |
| 624 | -coding even when they really don't want to. 𘘉 | |
| 624 | +coding even when they really don't want to. | |
| 625 | 625 | |
| 626 | 626 | In all of this, Fossil's project lead understandably has the final |
| 627 | 627 | say-so in whether any given feature indeed gets merged into the mainline |
| 628 | 628 | trunk. Development of any given feature, no matter how much effort was |
| 629 | 629 | involved, does not guarantee its eventual inclusion into the public |
| 630 | 630 | releases. |
| 631 | 631 |
| --- www/javascript.md | |
| +++ www/javascript.md | |
| @@ -572,11 +572,11 @@ | |
| 572 | gateway bidirectional should be possible as well, as long as it properly |
| 573 | uses SQLite transactions. |
| 574 | |
| 575 | ### <a id="brlist"></a>List of branches |
| 576 | |
| 577 | Since Fossil 2.16 [`/brlist`](/brlist) page uses JavaScript to enable |
| 578 | selection of several branches for further study via `/timeline`. |
| 579 | Client-side script interactively responds to checkboxes' events |
| 580 | and constructs a special hyperlink in the submenu. |
| 581 | Clicking this hyperlink loads a `/timeline` page that shows |
| 582 | only these selected branches (and the related check-ins). |
| @@ -619,12 +619,12 @@ | |
| 619 | 2) Features end users request which catch the interest of one or more |
| 620 | developers, provided the developer(s) in question are in a position to |
| 621 | expend the effort. |
| 622 | |
| 623 | 3) Features end users and co-contributors can convince a developer into |
| 624 | coding even when they really don't want to. 𘘉 |
| 625 | |
| 626 | In all of this, Fossil's project lead understandably has the final |
| 627 | say-so in whether any given feature indeed gets merged into the mainline |
| 628 | trunk. Development of any given feature, no matter how much effort was |
| 629 | involved, does not guarantee its eventual inclusion into the public |
| 630 | releases. |
| 631 |
| --- www/javascript.md | |
| +++ www/javascript.md | |
| @@ -572,11 +572,11 @@ | |
| 572 | gateway bidirectional should be possible as well, as long as it properly |
| 573 | uses SQLite transactions. |
| 574 | |
| 575 | ### <a id="brlist"></a>List of branches |
| 576 | |
| 577 | Since Fossil 2.16 the [`/brlist`](/brlist) page uses JavaScript to enable |
| 578 | selection of several branches for further study via `/timeline`. |
| 579 | Client-side script interactively responds to checkboxes' events |
| 580 | and constructs a special hyperlink in the submenu. |
| 581 | Clicking this hyperlink loads a `/timeline` page that shows |
| 582 | only these selected branches (and the related check-ins). |
| @@ -619,12 +619,12 @@ | |
| 619 | 2) Features end users request which catch the interest of one or more |
| 620 | developers, provided the developer(s) in question are in a position to |
| 621 | expend the effort. |
| 622 | |
| 623 | 3) Features end users and co-contributors can convince a developer into |
| 624 | coding even when they really don't want to. |
| 625 | |
| 626 | In all of this, Fossil's project lead understandably has the final |
| 627 | say-so in whether any given feature indeed gets merged into the mainline |
| 628 | trunk. Development of any given feature, no matter how much effort was |
| 629 | involved, does not guarantee its eventual inclusion into the public |
| 630 | releases. |
| 631 |
+1
| --- www/mkindex.tcl | ||
| +++ www/mkindex.tcl | ||
| @@ -115,10 +115,11 @@ | ||
| 115 | 115 | theory1.wiki {Thoughts On The Design Of The Fossil DVCS} |
| 116 | 116 | tickets.wiki {The Fossil Ticket System} |
| 117 | 117 | unvers.wiki {Unversioned Files} |
| 118 | 118 | webpage-ex.md {Webpage Examples} |
| 119 | 119 | webui.wiki {The Fossil Web Interface} |
| 120 | + whyallinone.md {Why Bundle Forum, Wiki, and other Web Software With Your DVCS?} | |
| 120 | 121 | whyusefossil.wiki {Why You Should Use Fossil} |
| 121 | 122 | whyusefossil.wiki {Benefits Of Version Control} |
| 122 | 123 | wikitheory.wiki {Wiki In Fossil} |
| 123 | 124 | /wiki_rules {Wiki Formatting Rules} |
| 124 | 125 | } |
| 125 | 126 |
| --- www/mkindex.tcl | |
| +++ www/mkindex.tcl | |
| @@ -115,10 +115,11 @@ | |
| 115 | theory1.wiki {Thoughts On The Design Of The Fossil DVCS} |
| 116 | tickets.wiki {The Fossil Ticket System} |
| 117 | unvers.wiki {Unversioned Files} |
| 118 | webpage-ex.md {Webpage Examples} |
| 119 | webui.wiki {The Fossil Web Interface} |
| 120 | whyusefossil.wiki {Why You Should Use Fossil} |
| 121 | whyusefossil.wiki {Benefits Of Version Control} |
| 122 | wikitheory.wiki {Wiki In Fossil} |
| 123 | /wiki_rules {Wiki Formatting Rules} |
| 124 | } |
| 125 |
| --- www/mkindex.tcl | |
| +++ www/mkindex.tcl | |
| @@ -115,10 +115,11 @@ | |
| 115 | theory1.wiki {Thoughts On The Design Of The Fossil DVCS} |
| 116 | tickets.wiki {The Fossil Ticket System} |
| 117 | unvers.wiki {Unversioned Files} |
| 118 | webpage-ex.md {Webpage Examples} |
| 119 | webui.wiki {The Fossil Web Interface} |
| 120 | whyallinone.md {Why Bundle Forum, Wiki, and other Web Software With Your DVCS?} |
| 121 | whyusefossil.wiki {Why You Should Use Fossil} |
| 122 | whyusefossil.wiki {Benefits Of Version Control} |
| 123 | wikitheory.wiki {Wiki In Fossil} |
| 124 | /wiki_rules {Wiki Formatting Rules} |
| 125 | } |
| 126 |
| --- www/permutedindex.html | ||
| +++ www/permutedindex.html | ||
| @@ -125,9 +125,10 @@ | ||
| 125 | 125 | <li><a href="fiveminutes.wiki">Up and Running in 5 Minutes as a Single User</a></li> |
| 126 | 126 | <li><a href="javascript.md">Use of JavaScript in Fossil</a></li> |
| 127 | 127 | <li><a href="caps/ref.html">User Capability Reference</a></li> |
| 128 | 128 | <li><a href="ssl.wiki">Using SSL with Fossil</a></li> |
| 129 | 129 | <li><a href="webpage-ex.md">Webpage Examples</a></li> |
| 130 | +<li><a href="whyallinone.md">Why Bundle Forum, Wiki, and other Web Software With Your DVCS?</a></li> | |
| 130 | 131 | <li><a href="whyusefossil.wiki">Why You Should Use Fossil</a></li> |
| 131 | 132 | <li><a href="../../../wiki_rules">Wiki Formatting Rules</a></li> |
| 132 | 133 | <li><a href="wikitheory.wiki">Wiki In Fossil</a></li> |
| 133 | 134 | </ul></div> |
| 134 | 135 | |
| 135 | 136 | ADDED www/whyallinone.md |
| --- www/permutedindex.html | |
| +++ www/permutedindex.html | |
| @@ -125,9 +125,10 @@ | |
| 125 | <li><a href="fiveminutes.wiki">Up and Running in 5 Minutes as a Single User</a></li> |
| 126 | <li><a href="javascript.md">Use of JavaScript in Fossil</a></li> |
| 127 | <li><a href="caps/ref.html">User Capability Reference</a></li> |
| 128 | <li><a href="ssl.wiki">Using SSL with Fossil</a></li> |
| 129 | <li><a href="webpage-ex.md">Webpage Examples</a></li> |
| 130 | <li><a href="whyusefossil.wiki">Why You Should Use Fossil</a></li> |
| 131 | <li><a href="../../../wiki_rules">Wiki Formatting Rules</a></li> |
| 132 | <li><a href="wikitheory.wiki">Wiki In Fossil</a></li> |
| 133 | </ul></div> |
| 134 | |
| 135 | DDED www/whyallinone.md |
| --- www/permutedindex.html | |
| +++ www/permutedindex.html | |
| @@ -125,9 +125,10 @@ | |
| 125 | <li><a href="fiveminutes.wiki">Up and Running in 5 Minutes as a Single User</a></li> |
| 126 | <li><a href="javascript.md">Use of JavaScript in Fossil</a></li> |
| 127 | <li><a href="caps/ref.html">User Capability Reference</a></li> |
| 128 | <li><a href="ssl.wiki">Using SSL with Fossil</a></li> |
| 129 | <li><a href="webpage-ex.md">Webpage Examples</a></li> |
| 130 | <li><a href="whyallinone.md">Why Bundle Forum, Wiki, and other Web Software With Your DVCS?</a></li> |
| 131 | <li><a href="whyusefossil.wiki">Why You Should Use Fossil</a></li> |
| 132 | <li><a href="../../../wiki_rules">Wiki Formatting Rules</a></li> |
| 133 | <li><a href="wikitheory.wiki">Wiki In Fossil</a></li> |
| 134 | </ul></div> |
| 135 | |
| 136 | DDED www/whyallinone.md |
+231
| --- a/www/whyallinone.md | ||
| +++ b/www/whyallinone.md | ||
| @@ -0,0 +1,231 @@ | ||
| 1 | +# Why Add Forum, Wiki, and Web Software To Your DVCS? | |
| 2 | + | |
| 3 | +One notable feature of Fossil is that it bundles | |
| 4 | +[bug tracking](./bugtheory.wiki), | |
| 5 | +[wiki](./wikitheory.wiki), | |
| 6 | +[forum](./forum.wiki), | |
| 7 | +[chat](./chat.md), and | |
| 8 | +[technotes](./event.wiki) | |
| 9 | +with distributed version control to give you an | |
| 10 | +all-in-one software project management system. | |
| 11 | + | |
| 12 | +A commenter on [Hacker News](https://news.ycombinator.com/item?id=27437895) | |
| 13 | +takes exception to this idea, writing: | |
| 14 | + | |
| 15 | +> *I don't want forum/web software built into my dvcs.* | |
| 16 | +> *I don't see how this improves over git.* | |
| 17 | + | |
| 18 | +This commenter may hold whatever opinions he wishes, of course. | |
| 19 | +However, there are many good reasons why bundling other project management | |
| 20 | +features with the DVCS might be useful for a given project: | |
| 21 | + | |
| 22 | + 1. There is a single software package to install and manage for the | |
| 23 | + project website. | |
| 24 | + The alternative is to select, install, configure, learn about, | |
| 25 | + manage, and maintain separate DVCS, wiki, | |
| 26 | + ticketing, forum, | |
| 27 | + chat, documentation, and whatever other software packages your project needs. | |
| 28 | + Less time spent on project administration details means more | |
| 29 | + time available to spend on the project itself. | |
| 30 | + | |
| 31 | + 2. Fossil’s autosync feature gives you an implicit backup of the | |
| 32 | + wiki, tickets, forum, and so forth simply by cloning the | |
| 33 | + repository to another machine and using that clone regularly. | |
| 34 | + Since the typical Fossil usage pattern is to stand the repo up on a | |
| 35 | + central server and have the developers clone that repository down | |
| 36 | + to their personal machines, if the server falls over, the last | |
| 37 | + developer to do anything that resulted in an autosync has a | |
| 38 | + functional and up-to-date backup. | |
| 39 | + | |
| 40 | + There are [limitations to relying on Fossil’s autosync feature for | |
| 41 | + backup purposes](./backup.md), but that document gives two methods | |
| 42 | + for more complete backups, both of which are easily automated. The | |
| 43 | + Fossil project itself is distributed across three data centers in | |
| 44 | + this manner via cron. | |
| 45 | + | |
| 46 | + 3. Remote workers get more than just the source code: | |
| 47 | + they get the entire website including versioned documentation, | |
| 48 | + wiki articles, tickets, forum posts, and so forth. This supports | |
| 49 | + off-network development when traveling, when riding out Internet | |
| 50 | + service failures, and when workers must sync with multiple remote | |
| 51 | + servers, as when working alternately from home and in some central | |
| 52 | + office. | |
| 53 | + | |
| 54 | + Feature-competitive Fossil alternatives typically solve this same | |
| 55 | + problem with centralization, which generally means that only the | |
| 56 | + DVCS piece still works in these situations where the developer is | |
| 57 | + unable to contact the central server. Why accept the limitation of | |
| 58 | + having a distributed clone of the code repo alone? | |
| 59 | + | |
| 60 | + Centralization doesn’t work for every project. If you enjoy the | |
| 61 | + benefits of truly distributed (read: non-centralized) version | |
| 62 | + control, you may also benefit from distributed forums, distributed | |
| 63 | + ticket tracking, distributed wiki article publishing, and so | |
| 64 | + forth. | |
| 65 | + | |
| 66 | + 4. Integration of all of these features allows easy hyperlinks between | |
| 67 | + check-in comments, wiki pages, forum posts, and tickets. More, | |
| 68 | + because the software sees both sides of the link, referrer and | |
| 69 | + referent, it can provide automatic back-references. | |
| 70 | + | |
| 71 | + A common situation in a Fossil project is that: | |
| 72 | + | |
| 73 | + * a forum post refers to a versioned project document that shows | |
| 74 | + that the software isn’t behaving as documented; | |
| 75 | + * a developer triages that forum report as a verified bug, filing | |
| 76 | + a ticket to prioritize and track the resolution; | |
| 77 | + * developers chat about the problem, referring to the ticket and | |
| 78 | + thereby indirectly referring to the forum post, plus perhaps to | |
| 79 | + other Fossil-managed resources such as a wiki document giving | |
| 80 | + design principles that guide the proper fix; and finally | |
| 81 | + * the commit message resolving the ticket includes a reference to | |
| 82 | + the ticket it resolves. | |
| 83 | + | |
| 84 | + Since Fossil sees that the commit refers to a ticket, the ticket | |
| 85 | + page automatically also refers back to the commit, closing the | |
| 86 | + loop. A latecomer may arrive at the ticket via a web search, and | |
| 87 | + from that see that it was closed following a commit. They can | |
| 88 | + follow the link from the initial ticket message to the forum | |
| 89 | + thread to catch up on the discussion leading to the fix and likely | |
| 90 | + find a follow-up post from the initial reporting user saying | |
| 91 | + whether the fix worked for them. If further work was needed, the | |
| 92 | + latecomer can likely find it from that forum thread. | |
| 93 | + | |
| 94 | + This works even in a remote off-network clone: the developer can | |
| 95 | + pull up the project web site via an `http://localhost` link and | |
| 96 | + follow these links around the loop. | |
| 97 | + | |
| 98 | + Fossil allows breaking some of these project facilities out into | |
| 99 | + separate repositories, as when the public forum is kept separate | |
| 100 | + from the actual software development repository for administration | |
| 101 | + reasons. By using Fossil’s [interwiki link | |
| 102 | + feature](./interwiki.md), you can get this same internal linking | |
| 103 | + from ticket to commit to forum to wiki even across these | |
| 104 | + administrative boundaries, even with remote off-network clones, | |
| 105 | + simply by adjusting the interwiki map to match the remote clone’s | |
| 106 | + network configuration. | |
| 107 | + | |
| 108 | + 5. Bundling all of these services gives [single sign-on][SSO] (SSO) for all | |
| 109 | + aspects of the project. The same username/password works for code, | |
| 110 | + wiki, forum, tickets, and chat. | |
| 111 | + | |
| 112 | + If you choose to administratively separate some of these features | |
| 113 | + by setting up multiple cooperating Fossil repositories, its skinning groups feature](./caps/login-groups.md) allows asymmetric SSO | |
| 114 | + across these administrative boundaries so that, for example, users | |
| 115 | + allowed to commit to the code repository also get a forum | |
| 116 | + repository login, but self-registered forum users don’t | |
| 117 | + automatically get the ability to commit to the code repo. | |
| 118 | + | |
| 119 | + 6. Bundling all of these features reduces the number of external | |
| 120 | + dependencies for the project. | |
| 121 | + | |
| 122 | + Take the first two points above: standing up a Fossil repo backup | |
| 123 | + on a new server may be as simple as copying the backup to the new | |
| 124 | + server and [configuring its stock HTTP server to point at the | |
| 125 | + backup repository via CGI](./server/any/cgi.md). | |
| 126 | + | |
| 127 | + Consider: If you had good backups for all of the elements in a | |
| 128 | + Git + [Jira] + [Discord] + [MediaWiki] + [Sphinx] lash-up, how long | |
| 129 | + would it take you to stand up a replacement? That lash-up | |
| 130 | + certainly has more features combined than Fossil alone, but are | |
| 131 | + they worth the administration and hosting costs they impose? | |
| 132 | + Fossil’s feature set suffices for the SQLite project it was | |
| 133 | + created to serve, as well as for many others; is your project | |
| 134 | + sufficiently more complex, such that it *needs* all of those extra | |
| 135 | + features and their concomitant complexity? | |
| 136 | + | |
| 137 | + Considerations such as these push many into centralized hosting | |
| 138 | + services such as GitHub, GitLab, Bitbucket, and so forth, but that | |
| 139 | + just takes you back to point 3 above. | |
| 140 | + | |
| 141 | + 7. Hosting all of these elements within a single service gives a | |
| 142 | + consistent look-and-feel across all aspects of the project. | |
| 143 | + | |
| 144 | + Skinning independent software packages’ web interfaces to make | |
| 145 | + them appear unified is more work than [skinning] everything once, as | |
| 146 | + in Fossil, and even then, you can’t make independently-developed | |
| 147 | + software look like it was produced by a single entity without | |
| 148 | + resorting to heroic levels of customization. If you use a separate | |
| 149 | + DVCS web front end, chat system, forum manager, documentation | |
| 150 | + system, ticket tracker, and so on, you are likely to be relegated | |
| 151 | + to simply matching colors and fonts; you *might* also get the | |
| 152 | + ability to add a common logo to the header of all of these | |
| 153 | + independent pieces. The pieces won’t look unified, because they | |
| 154 | + weren’t developed that way. | |
| 155 | + | |
| 156 | + The Fossil project’s | |
| 157 | + skinning system lets you affect all of its elements globally from the | |
| 158 | + single skin editor. | |
| 159 | + | |
| 160 | + Or not: there’s a feature in Fossil that lets skin customizations | |
| 161 | + apply to only *some* Fossil features. The initial impetus behind | |
| 162 | + this feature was that one of our users wanted Markdown to be | |
| 163 | + rendered with different indentation in forum posts than in | |
| 164 | + [embedded documentation][edoc] owing to the inherent differences between | |
| 165 | + the two presentation modalities. | |
| 166 | + | |
| 167 | + A user taking advantage of this per-feature CSS capability who | |
| 168 | + wishes to change a UI element common to all Fossil features — say, | |
| 169 | + to change the font for literal code blocks — may still make such a | |
| 170 | + change globally. Opting into this per-feature CSS doesn’t fork all | |
| 171 | + skinning efforts: UI elements not explicitly reskinned on a | |
| 172 | + per-feature basis inherit the global skinning. | |
| 173 | + | |
| 174 | + But it goes futher. Fossil has a feature for [project-specific | |
| 175 | + extensions](./serverext.wiki), which backs the [SQLite Release | |
| 176 | + Checklist][srckl], for instance. You wouldn’t know by looking at | |
| 177 | + that page that it’s produced by software that isn’t actually part | |
| 178 | + of Fossil: the extension only delivers the core of the page, | |
| 179 | + and Fossil’s skining wraps it in a way that lets it inherit all of | |
| 180 | + the project-level skinning customizations. | |
| 181 | + | |
| 182 | + 8. Unifying all of these features within Fossil | |
| 183 | + means we have a single Markdown interpreter common to all | |
| 184 | + elements. If you lash multiple software systems together, even if | |
| 185 | + they can all agree on Markdown as a common document markup | |
| 186 | + language — hardly a given, as shown by the MediaWiki and Sphinx | |
| 187 | + elements in point 6’s example above — they’re likely to render your text | |
| 188 | + using different — possibly even incompatibly-different — Markdown | |
| 189 | + dialects. | |
| 190 | + | |
| 191 | + This costs you Fossil | |
| 192 | +can link out to these systems, and they back into Fossil, letting you | |
| 193 | +use Fossiln-controlled | |
| 194 | + project document. A developer on a Fossil-backed project may simply copy-paste the forum post | |
| 195 | + text into the new document and save it, not needing to carefully | |
| 196 | + check that it still renders properly under the second Markdown | |
| 197 | + rendering engine. Similarly, if a user reports a potential bug via | |
| 198 | + the forum, the developer can copy interesting pieces of the | |
| 199 | + Markdown from the post into a ticket comment, again without | |
| 200 | + needing to fiddle with dialect incompatibilities. | |
| 201 | + | |
| 202 | + 9. Fossil is [free, open-source software](../COPYRIGHT-BSD2.txt), | |
| 203 | + through and through. Git-backed lash-ups tend to incorporate | |
| 204 | + either proprietary add-ons or proprietary hosting systems that | |
| 205 | + produce vendor lock-in. Fossil gives you the freedom to take your | |
| 206 | + complete backup (point 2) of the project including its | |
| 207 | + idiosyncratic customizations and stand it up elsewhere on | |
| 208 | + commodity hardware and software stacks. | |
| 209 | + | |
| 210 | +All of this having been said, the non-DVCS features of Fossil are | |
| 211 | +optional. Its forum and chat features are disabled by default, and you | |
| 212 | +can disable the ticket-tracking and wiki features with a quick | |
| 213 | +configuration change to its [role-based access control system](./caps/). | |
| 214 | +When you’re ready to turn these additional features on, you can do so | |
| 215 | +with a few mouse clicks. | |
| 216 | + | |
| 217 | +Because Fossil is web-native out of the box, if you’ve delegated these | |
| 218 | +features to outside systems to flesh out Git’s DVCS-only nature, you are | |
| 219 | +free to do the same with Fossil. One of the many things the [skinning] | |
| 220 | +facility allows is replacing the built-in links to the wiki, forum, | |
| 221 | +ticket system, etc. with links to external systems. How easy those | |
| 222 | +systems make it to link back into Fossil is up to their developers. | |
| 223 | + | |
| 224 | +[Discord]: https://discord.com/ | |
| 225 | +[edoc]: ./embeddeddoc.wiki | |
| 226 | +[Jira]: https://www.atlassian.com/software/jira | |
| 227 | +[MediaWiki]: https://www.mediawiki.org/ | |
| 228 | +[skinning]: ./customskin.md | |
| 229 | +[Sphinx]: https://www.sphinx-doc.org/en/master/ | |
| 230 | +[SSO]: https://en.wikipedia.org/wiki/Single_sign-on | |
| 231 | +[srckl]: https://www.sqlite.org/src/ext/checklist/top/inde |
| --- a/www/whyallinone.md | |
| +++ b/www/whyallinone.md | |
| @@ -0,0 +1,231 @@ | |
| --- a/www/whyallinone.md | |
| +++ b/www/whyallinone.md | |
| @@ -0,0 +1,231 @@ | |
| 1 | # Why Add Forum, Wiki, and Web Software To Your DVCS? |
| 2 | |
| 3 | One notable feature of Fossil is that it bundles |
| 4 | [bug tracking](./bugtheory.wiki), |
| 5 | [wiki](./wikitheory.wiki), |
| 6 | [forum](./forum.wiki), |
| 7 | [chat](./chat.md), and |
| 8 | [technotes](./event.wiki) |
| 9 | with distributed version control to give you an |
| 10 | all-in-one software project management system. |
| 11 | |
| 12 | A commenter on [Hacker News](https://news.ycombinator.com/item?id=27437895) |
| 13 | takes exception to this idea, writing: |
| 14 | |
| 15 | > *I don't want forum/web software built into my dvcs.* |
| 16 | > *I don't see how this improves over git.* |
| 17 | |
| 18 | This commenter may hold whatever opinions he wishes, of course. |
| 19 | However, there are many good reasons why bundling other project management |
| 20 | features with the DVCS might be useful for a given project: |
| 21 | |
| 22 | 1. There is a single software package to install and manage for the |
| 23 | project website. |
| 24 | The alternative is to select, install, configure, learn about, |
| 25 | manage, and maintain separate DVCS, wiki, |
| 26 | ticketing, forum, |
| 27 | chat, documentation, and whatever other software packages your project needs. |
| 28 | Less time spent on project administration details means more |
| 29 | time available to spend on the project itself. |
| 30 | |
| 31 | 2. Fossil’s autosync feature gives you an implicit backup of the |
| 32 | wiki, tickets, forum, and so forth simply by cloning the |
| 33 | repository to another machine and using that clone regularly. |
| 34 | Since the typical Fossil usage pattern is to stand the repo up on a |
| 35 | central server and have the developers clone that repository down |
| 36 | to their personal machines, if the server falls over, the last |
| 37 | developer to do anything that resulted in an autosync has a |
| 38 | functional and up-to-date backup. |
| 39 | |
| 40 | There are [limitations to relying on Fossil’s autosync feature for |
| 41 | backup purposes](./backup.md), but that document gives two methods |
| 42 | for more complete backups, both of which are easily automated. The |
| 43 | Fossil project itself is distributed across three data centers in |
| 44 | this manner via cron. |
| 45 | |
| 46 | 3. Remote workers get more than just the source code: |
| 47 | they get the entire website including versioned documentation, |
| 48 | wiki articles, tickets, forum posts, and so forth. This supports |
| 49 | off-network development when traveling, when riding out Internet |
| 50 | service failures, and when workers must sync with multiple remote |
| 51 | servers, as when working alternately from home and in some central |
| 52 | office. |
| 53 | |
| 54 | Feature-competitive Fossil alternatives typically solve this same |
| 55 | problem with centralization, which generally means that only the |
| 56 | DVCS piece still works in these situations where the developer is |
| 57 | unable to contact the central server. Why accept the limitation of |
| 58 | having a distributed clone of the code repo alone? |
| 59 | |
| 60 | Centralization doesn’t work for every project. If you enjoy the |
| 61 | benefits of truly distributed (read: non-centralized) version |
| 62 | control, you may also benefit from distributed forums, distributed |
| 63 | ticket tracking, distributed wiki article publishing, and so |
| 64 | forth. |
| 65 | |
| 66 | 4. Integration of all of these features allows easy hyperlinks between |
| 67 | check-in comments, wiki pages, forum posts, and tickets. More, |
| 68 | because the software sees both sides of the link, referrer and |
| 69 | referent, it can provide automatic back-references. |
| 70 | |
| 71 | A common situation in a Fossil project is that: |
| 72 | |
| 73 | * a forum post refers to a versioned project document that shows |
| 74 | that the software isn’t behaving as documented; |
| 75 | * a developer triages that forum report as a verified bug, filing |
| 76 | a ticket to prioritize and track the resolution; |
| 77 | * developers chat about the problem, referring to the ticket and |
| 78 | thereby indirectly referring to the forum post, plus perhaps to |
| 79 | other Fossil-managed resources such as a wiki document giving |
| 80 | design principles that guide the proper fix; and finally |
| 81 | * the commit message resolving the ticket includes a reference to |
| 82 | the ticket it resolves. |
| 83 | |
| 84 | Since Fossil sees that the commit refers to a ticket, the ticket |
| 85 | page automatically also refers back to the commit, closing the |
| 86 | loop. A latecomer may arrive at the ticket via a web search, and |
| 87 | from that see that it was closed following a commit. They can |
| 88 | follow the link from the initial ticket message to the forum |
| 89 | thread to catch up on the discussion leading to the fix and likely |
| 90 | find a follow-up post from the initial reporting user saying |
| 91 | whether the fix worked for them. If further work was needed, the |
| 92 | latecomer can likely find it from that forum thread. |
| 93 | |
| 94 | This works even in a remote off-network clone: the developer can |
| 95 | pull up the project web site via an `http://localhost` link and |
| 96 | follow these links around the loop. |
| 97 | |
| 98 | Fossil allows breaking some of these project facilities out into |
| 99 | separate repositories, as when the public forum is kept separate |
| 100 | from the actual software development repository for administration |
| 101 | reasons. By using Fossil’s [interwiki link |
| 102 | feature](./interwiki.md), you can get this same internal linking |
| 103 | from ticket to commit to forum to wiki even across these |
| 104 | administrative boundaries, even with remote off-network clones, |
| 105 | simply by adjusting the interwiki map to match the remote clone’s |
| 106 | network configuration. |
| 107 | |
| 108 | 5. Bundling all of these services gives [single sign-on][SSO] (SSO) for all |
| 109 | aspects of the project. The same username/password works for code, |
| 110 | wiki, forum, tickets, and chat. |
| 111 | |
| 112 | If you choose to administratively separate some of these features |
| 113 | by setting up multiple cooperating Fossil repositories, its skinning groups feature](./caps/login-groups.md) allows asymmetric SSO |
| 114 | across these administrative boundaries so that, for example, users |
| 115 | allowed to commit to the code repository also get a forum |
| 116 | repository login, but self-registered forum users don’t |
| 117 | automatically get the ability to commit to the code repo. |
| 118 | |
| 119 | 6. Bundling all of these features reduces the number of external |
| 120 | dependencies for the project. |
| 121 | |
| 122 | Take the first two points above: standing up a Fossil repo backup |
| 123 | on a new server may be as simple as copying the backup to the new |
| 124 | server and [configuring its stock HTTP server to point at the |
| 125 | backup repository via CGI](./server/any/cgi.md). |
| 126 | |
| 127 | Consider: If you had good backups for all of the elements in a |
| 128 | Git + [Jira] + [Discord] + [MediaWiki] + [Sphinx] lash-up, how long |
| 129 | would it take you to stand up a replacement? That lash-up |
| 130 | certainly has more features combined than Fossil alone, but are |
| 131 | they worth the administration and hosting costs they impose? |
| 132 | Fossil’s feature set suffices for the SQLite project it was |
| 133 | created to serve, as well as for many others; is your project |
| 134 | sufficiently more complex, such that it *needs* all of those extra |
| 135 | features and their concomitant complexity? |
| 136 | |
| 137 | Considerations such as these push many into centralized hosting |
| 138 | services such as GitHub, GitLab, Bitbucket, and so forth, but that |
| 139 | just takes you back to point 3 above. |
| 140 | |
| 141 | 7. Hosting all of these elements within a single service gives a |
| 142 | consistent look-and-feel across all aspects of the project. |
| 143 | |
| 144 | Skinning independent software packages’ web interfaces to make |
| 145 | them appear unified is more work than [skinning] everything once, as |
| 146 | in Fossil, and even then, you can’t make independently-developed |
| 147 | software look like it was produced by a single entity without |
| 148 | resorting to heroic levels of customization. If you use a separate |
| 149 | DVCS web front end, chat system, forum manager, documentation |
| 150 | system, ticket tracker, and so on, you are likely to be relegated |
| 151 | to simply matching colors and fonts; you *might* also get the |
| 152 | ability to add a common logo to the header of all of these |
| 153 | independent pieces. The pieces won’t look unified, because they |
| 154 | weren’t developed that way. |
| 155 | |
| 156 | The Fossil project’s |
| 157 | skinning system lets you affect all of its elements globally from the |
| 158 | single skin editor. |
| 159 | |
| 160 | Or not: there’s a feature in Fossil that lets skin customizations |
| 161 | apply to only *some* Fossil features. The initial impetus behind |
| 162 | this feature was that one of our users wanted Markdown to be |
| 163 | rendered with different indentation in forum posts than in |
| 164 | [embedded documentation][edoc] owing to the inherent differences between |
| 165 | the two presentation modalities. |
| 166 | |
| 167 | A user taking advantage of this per-feature CSS capability who |
| 168 | wishes to change a UI element common to all Fossil features — say, |
| 169 | to change the font for literal code blocks — may still make such a |
| 170 | change globally. Opting into this per-feature CSS doesn’t fork all |
| 171 | skinning efforts: UI elements not explicitly reskinned on a |
| 172 | per-feature basis inherit the global skinning. |
| 173 | |
| 174 | But it goes futher. Fossil has a feature for [project-specific |
| 175 | extensions](./serverext.wiki), which backs the [SQLite Release |
| 176 | Checklist][srckl], for instance. You wouldn’t know by looking at |
| 177 | that page that it’s produced by software that isn’t actually part |
| 178 | of Fossil: the extension only delivers the core of the page, |
| 179 | and Fossil’s skining wraps it in a way that lets it inherit all of |
| 180 | the project-level skinning customizations. |
| 181 | |
| 182 | 8. Unifying all of these features within Fossil |
| 183 | means we have a single Markdown interpreter common to all |
| 184 | elements. If you lash multiple software systems together, even if |
| 185 | they can all agree on Markdown as a common document markup |
| 186 | language — hardly a given, as shown by the MediaWiki and Sphinx |
| 187 | elements in point 6’s example above — they’re likely to render your text |
| 188 | using different — possibly even incompatibly-different — Markdown |
| 189 | dialects. |
| 190 | |
| 191 | This costs you Fossil |
| 192 | can link out to these systems, and they back into Fossil, letting you |
| 193 | use Fossiln-controlled |
| 194 | project document. A developer on a Fossil-backed project may simply copy-paste the forum post |
| 195 | text into the new document and save it, not needing to carefully |
| 196 | check that it still renders properly under the second Markdown |
| 197 | rendering engine. Similarly, if a user reports a potential bug via |
| 198 | the forum, the developer can copy interesting pieces of the |
| 199 | Markdown from the post into a ticket comment, again without |
| 200 | needing to fiddle with dialect incompatibilities. |
| 201 | |
| 202 | 9. Fossil is [free, open-source software](../COPYRIGHT-BSD2.txt), |
| 203 | through and through. Git-backed lash-ups tend to incorporate |
| 204 | either proprietary add-ons or proprietary hosting systems that |
| 205 | produce vendor lock-in. Fossil gives you the freedom to take your |
| 206 | complete backup (point 2) of the project including its |
| 207 | idiosyncratic customizations and stand it up elsewhere on |
| 208 | commodity hardware and software stacks. |
| 209 | |
| 210 | All of this having been said, the non-DVCS features of Fossil are |
| 211 | optional. Its forum and chat features are disabled by default, and you |
| 212 | can disable the ticket-tracking and wiki features with a quick |
| 213 | configuration change to its [role-based access control system](./caps/). |
| 214 | When you’re ready to turn these additional features on, you can do so |
| 215 | with a few mouse clicks. |
| 216 | |
| 217 | Because Fossil is web-native out of the box, if you’ve delegated these |
| 218 | features to outside systems to flesh out Git’s DVCS-only nature, you are |
| 219 | free to do the same with Fossil. One of the many things the [skinning] |
| 220 | facility allows is replacing the built-in links to the wiki, forum, |
| 221 | ticket system, etc. with links to external systems. How easy those |
| 222 | systems make it to link back into Fossil is up to their developers. |
| 223 | |
| 224 | [Discord]: https://discord.com/ |
| 225 | [edoc]: ./embeddeddoc.wiki |
| 226 | [Jira]: https://www.atlassian.com/software/jira |
| 227 | [MediaWiki]: https://www.mediawiki.org/ |
| 228 | [skinning]: ./customskin.md |
| 229 | [Sphinx]: https://www.sphinx-doc.org/en/master/ |
| 230 | [SSO]: https://en.wikipedia.org/wiki/Single_sign-on |
| 231 | [srckl]: https://www.sqlite.org/src/ext/checklist/top/inde |